Compare commits
14
Commits
53b6b261dd
...
refactor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7106e0adbe | ||
|
|
9fc0ef2216 | ||
|
|
3d785d5139 | ||
|
|
eec09a0bb4 | ||
|
|
1422db3b19 | ||
|
|
a38a8d4b39 | ||
|
|
55506eff9a | ||
|
|
2e54acbc85 | ||
|
|
dc2aa59d58 | ||
|
|
c76fd85a2c | ||
|
|
777c58b812 | ||
|
|
aa44170d56 | ||
|
|
0c92fd9886 | ||
|
|
26aaa945a9 |
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(unzip -l \"Assets/GameFramework/Libraries/GameFramework.xml\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Unity generated folders
|
|
||||||
Library/
|
|
||||||
Temp/
|
|
||||||
Obj/
|
|
||||||
Logs/
|
|
||||||
Build/
|
|
||||||
Builds/
|
|
||||||
UserSettings/
|
|
||||||
MemoryCaptures/
|
|
||||||
Recordings/
|
|
||||||
|
|
||||||
# IDE / tool caches
|
|
||||||
.vs/
|
|
||||||
.idea/
|
|
||||||
.gradle/
|
|
||||||
.cache/
|
|
||||||
|
|
||||||
# OS / misc
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Node / frontend caches if present
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Test / coverage outputs
|
|
||||||
coverage/
|
|
||||||
TestResults/
|
|
||||||
|
|
||||||
# Large binary assets that usually don't help code editing
|
|
||||||
*.apk
|
|
||||||
*.aab
|
|
||||||
*.unitypackage
|
|
||||||
*.zip
|
|
||||||
*.7z
|
|
||||||
*.rar
|
|
||||||
|
|
||||||
# Optional: generated csproj/sln if you mainly use Unity-generated ones
|
|
||||||
# *.csproj
|
|
||||||
# *.sln
|
|
||||||
+7
-1
@@ -98,14 +98,20 @@ InitTestScene*.unity*
|
|||||||
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
|
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
|
||||||
|
|
||||||
/[Aa]ssets/StreamingAssets/
|
/[Aa]ssets/StreamingAssets/
|
||||||
|
!/[Aa]ssets/StreamingAssets/.gitkeep
|
||||||
|
|
||||||
/.vscode
|
/.vscode
|
||||||
/.dotnet-home
|
/.dotnet-home
|
||||||
|
|
||||||
/[Aa]ssets/RawResources
|
/[Aa]ssets/RawResources
|
||||||
/[Aa]ssets/RawResources.meta
|
/[Aa]ssets/RawResources.meta
|
||||||
|
|
||||||
/.dotnet
|
/.dotnet
|
||||||
/.idea
|
/.idea
|
||||||
~$*.xlsx
|
~$*.xlsx
|
||||||
/tools
|
/tools
|
||||||
/node_modules
|
/node_modules
|
||||||
package.json
|
package.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
/.omc
|
||||||
|
/.omx
|
||||||
Generated
-15
@@ -1,15 +0,0 @@
|
|||||||
# 默认忽略的文件
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Rider 忽略的文件
|
|
||||||
/modules.xml
|
|
||||||
/.idea.GeometryTD.iml
|
|
||||||
/projectSettingsUpdater.xml
|
|
||||||
/contentModel.xml
|
|
||||||
# 已忽略包含查询文件的默认文件夹
|
|
||||||
/queries/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
# 基于编辑器的 HTTP 客户端请求
|
|
||||||
/httpRequests/
|
|
||||||
-8
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="UserContentModel">
|
|
||||||
<attachedFolders />
|
|
||||||
<explicitIncludes />
|
|
||||||
<explicitExcludes />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-6
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
# Project Philosophy
|
|
||||||
|
|
||||||
This project prioritizes **clarity, correctness, and maintainability** over defensive boilerplate or unnecessary abstraction.
|
|
||||||
|
|
||||||
AI-generated code should:
|
|
||||||
|
|
||||||
* follow existing architecture
|
|
||||||
* keep logic simple and explicit
|
|
||||||
* avoid hiding bugs
|
|
||||||
* avoid speculative abstractions
|
|
||||||
|
|
||||||
If uncertain, prefer **simple direct code** over generalized frameworks.
|
|
||||||
|
|
||||||
## Repository Guidelines
|
|
||||||
|
|
||||||
### Project Structure & Module Organization
|
|
||||||
- `Assets/` contains all Unity assets and code. Game-specific content lives under `Assets/GameMain/`.
|
|
||||||
- `Assets/GameMain/Scripts/` holds gameplay code organized by domain (`Procedure/`, `Entity/`, `UI/`, `Scene/`, `Sound/`, `Utility/`).
|
|
||||||
- `Assets/GameMain/Scenes/` stores Unity scenes (start from `Assets/Launcher.unity`).
|
|
||||||
- `Assets/GameMain/Configs/` and `Assets/GameMain/DataTables/` store runtime configuration and data tables.
|
|
||||||
- `StreamingAssets/` is for runtime-loaded files that must be preserved on build.
|
|
||||||
- `docs/` contains design notes (see `docs/GameDesign.md`).
|
|
||||||
- `数据表/` is a top-level data table workspace; keep it in sync with `Assets/GameMain/DataTables/` when exporting.
|
|
||||||
|
|
||||||
### Build, Test, and Development Commands
|
|
||||||
- Open the project with Unity Hub and load `GeometryTD` as a Unity project.
|
|
||||||
- Play locally via the Unity Editor Play button (no custom CLI runner is defined in this repo).
|
|
||||||
- Build via Unity: `File > Build Settings...` then choose target and build.
|
|
||||||
- IDE support: open `GeometryTD.sln` or `Assembly-CSharp.csproj` for C# navigation and tooling.
|
|
||||||
|
|
||||||
### Coding Style & Naming Conventions
|
|
||||||
- C# uses 4-space indentation and Allman braces (see `Assets/GameMain/Scripts/Procedure/ProcedureLaunch.cs`).
|
|
||||||
- Types, methods, and public members use `PascalCase`; locals and parameters use `camelCase`.
|
|
||||||
- Namespaces follow `GeometryTD.*` by feature area (example: `GeometryTD.Procedure`).
|
|
||||||
- Keep Unity `.meta` files with their assets; do not delete or regenerate them manually.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Design Principles
|
|
||||||
|
|
||||||
### 1. Prefer explicit logic over abstraction
|
|
||||||
|
|
||||||
Do not introduce abstractions unless they are clearly justified.
|
|
||||||
|
|
||||||
Avoid creating layers such as:
|
|
||||||
|
|
||||||
```
|
|
||||||
Manager
|
|
||||||
Service
|
|
||||||
Provider
|
|
||||||
Repository
|
|
||||||
Coordinator
|
|
||||||
Bridge
|
|
||||||
```
|
|
||||||
|
|
||||||
unless the architecture explicitly requires them.
|
|
||||||
|
|
||||||
Simple gameplay logic should remain simple.
|
|
||||||
|
|
||||||
Bad example:
|
|
||||||
|
|
||||||
```
|
|
||||||
EnemyService -> EnemyManager -> EnemyRepository
|
|
||||||
```
|
|
||||||
|
|
||||||
Good example:
|
|
||||||
|
|
||||||
```
|
|
||||||
EnemySpawner
|
|
||||||
Enemy
|
|
||||||
EnemyAI
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. Do not create abstractions for a single implementation
|
|
||||||
|
|
||||||
Avoid introducing interfaces unless multiple implementations are expected.
|
|
||||||
|
|
||||||
Bad:
|
|
||||||
|
|
||||||
```
|
|
||||||
IEnemyService
|
|
||||||
EnemyService
|
|
||||||
```
|
|
||||||
|
|
||||||
Good:
|
|
||||||
|
|
||||||
```
|
|
||||||
EnemySystem
|
|
||||||
```
|
|
||||||
|
|
||||||
Interfaces are only appropriate when:
|
|
||||||
|
|
||||||
* multiple implementations are required
|
|
||||||
* plugin/mod systems are involved
|
|
||||||
* testing requires mocking
|
|
||||||
* architecture explicitly defines extension points
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. Avoid speculative generalization
|
|
||||||
|
|
||||||
Do not design systems for hypothetical future use.
|
|
||||||
|
|
||||||
Implement only what the current feature requires.
|
|
||||||
|
|
||||||
Avoid:
|
|
||||||
|
|
||||||
* generic service layers
|
|
||||||
* premature plugin systems
|
|
||||||
* unnecessary configuration frameworks
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Defensive Programming Policy
|
|
||||||
|
|
||||||
### Boundary validation only
|
|
||||||
|
|
||||||
Validation is appropriate only at **true system boundaries**, such as:
|
|
||||||
|
|
||||||
* user input
|
|
||||||
* network input
|
|
||||||
* file/config loading
|
|
||||||
* inspector/external data
|
|
||||||
* public API boundaries
|
|
||||||
|
|
||||||
Internal gameplay logic should assume that invariants are already satisfied.
|
|
||||||
|
|
||||||
Do not add redundant validation inside internal code.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Do not hide errors
|
|
||||||
|
|
||||||
Do not silently ignore invalid states.
|
|
||||||
|
|
||||||
Avoid code such as:
|
|
||||||
|
|
||||||
```
|
|
||||||
if (enemy == null)
|
|
||||||
return;
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```
|
|
||||||
if (config == null)
|
|
||||||
continue;
|
|
||||||
```
|
|
||||||
|
|
||||||
unless the behavior is intentionally designed.
|
|
||||||
|
|
||||||
Unexpected states should be **visible**, not hidden.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Prefer fail-fast over silent failure
|
|
||||||
|
|
||||||
If a condition should never happen in correct execution:
|
|
||||||
|
|
||||||
Use assertions.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
Debug.Assert(config != null);
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not convert invariant violations into no-op behavior.
|
|
||||||
|
|
||||||
Bad:
|
|
||||||
|
|
||||||
```
|
|
||||||
if (config == null)
|
|
||||||
return;
|
|
||||||
```
|
|
||||||
|
|
||||||
Good:
|
|
||||||
|
|
||||||
```
|
|
||||||
Debug.Assert(config != null);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Do not weaken required dependencies
|
|
||||||
|
|
||||||
Required references must remain required.
|
|
||||||
|
|
||||||
Do not turn required dependencies into optional ones by adding defensive checks.
|
|
||||||
|
|
||||||
Bad:
|
|
||||||
|
|
||||||
```
|
|
||||||
if (enemy.Config == null)
|
|
||||||
return;
|
|
||||||
```
|
|
||||||
|
|
||||||
If a dependency is required by design, assume it exists.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
Do not introduce `try/catch` blocks unless there is a **clear recovery strategy**.
|
|
||||||
|
|
||||||
Avoid:
|
|
||||||
|
|
||||||
```
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DoSomething();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Debug.LogError(e);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Exceptions should generally propagate during development so bugs are visible.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Early Return Policy
|
|
||||||
|
|
||||||
Early returns are acceptable for **normal control flow simplification**.
|
|
||||||
|
|
||||||
However, do not use early returns to hide invariant violations or missing required state.
|
|
||||||
|
|
||||||
Bad:
|
|
||||||
|
|
||||||
```
|
|
||||||
if (enemy == null)
|
|
||||||
return;
|
|
||||||
```
|
|
||||||
|
|
||||||
Good:
|
|
||||||
|
|
||||||
```
|
|
||||||
if (!enemy.IsAlive)
|
|
||||||
return;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
Avoid excessive logging.
|
|
||||||
|
|
||||||
Do not log inside per-frame loops unless necessary.
|
|
||||||
|
|
||||||
Avoid logs like:
|
|
||||||
|
|
||||||
```
|
|
||||||
Debug.Log("Updating enemy");
|
|
||||||
```
|
|
||||||
|
|
||||||
Use logging only when it provides meaningful debugging value.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Utility Classes
|
|
||||||
|
|
||||||
Avoid creating generic utility classes such as:
|
|
||||||
|
|
||||||
```
|
|
||||||
GameUtils
|
|
||||||
CommonHelper
|
|
||||||
MathHelper
|
|
||||||
```
|
|
||||||
|
|
||||||
Prefer placing behavior in the domain object responsible for it.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
Bad:
|
|
||||||
|
|
||||||
```
|
|
||||||
DamageHelper.CalculateDamage(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
Good:
|
|
||||||
|
|
||||||
```
|
|
||||||
enemy.CalculateDamage(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Data Structures
|
|
||||||
|
|
||||||
Avoid large context objects with many loosely related fields.
|
|
||||||
|
|
||||||
Bad:
|
|
||||||
|
|
||||||
```
|
|
||||||
EffectContext
|
|
||||||
{
|
|
||||||
int value;
|
|
||||||
int count;
|
|
||||||
float rate;
|
|
||||||
object source;
|
|
||||||
object target;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Prefer explicit parameters or strongly typed structures.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Generation Rules for AI
|
|
||||||
|
|
||||||
Before adding any of the following:
|
|
||||||
|
|
||||||
* null check
|
|
||||||
* range check
|
|
||||||
* fallback default
|
|
||||||
* try/catch
|
|
||||||
* abstraction layer
|
|
||||||
|
|
||||||
First determine:
|
|
||||||
|
|
||||||
1. Is this a boundary input?
|
|
||||||
2. Is the value optional by design?
|
|
||||||
3. Is there a real recovery strategy?
|
|
||||||
|
|
||||||
If the answer is **no**, do not add defensive code.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Preferred Coding Style
|
|
||||||
|
|
||||||
Prefer:
|
|
||||||
|
|
||||||
* simple classes
|
|
||||||
* explicit logic
|
|
||||||
* minimal indirection
|
|
||||||
* clear ownership of behavior
|
|
||||||
|
|
||||||
Avoid:
|
|
||||||
|
|
||||||
* unnecessary patterns
|
|
||||||
* speculative architecture
|
|
||||||
* defensive boilerplate
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Guiding Principle
|
|
||||||
|
|
||||||
Prefer:
|
|
||||||
|
|
||||||
**clear failure over hidden corruption**
|
|
||||||
|
|
||||||
A bug should surface near its source rather than being silently ignored.
|
|
||||||
@@ -4,3 +4,7 @@
|
|||||||
1 元素底座 [2,2.1,2.2,2.3,2.5] 0.2 Fire [Fire,BurnSpread,IgniteBurst,Inferno]
|
1 元素底座 [2,2.1,2.2,2.3,2.5] 0.2 Fire [Fire,BurnSpread,IgniteBurst,Inferno]
|
||||||
2 控制底座 [4,4.2,4.4,4.6,4.8] 0.1 Ice [Ice,FreezeMask,Shatter,AbsoluteZero]
|
2 控制底座 [4,4.2,4.4,4.6,4.8] 0.1 Ice [Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
3 穿透底座 [1,1,1,1,1] 0 Physics [Pierce,Crit,Overpenetrate,Execution]
|
3 穿透底座 [1,1,1,1,1] 0 Physics [Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
4 12底座 [1,1,1,1,1] 0.1 Poison [Fire,BurnSpread,IgniteBurst,Inferno,Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
|
5 23底座 [1,1,1,1,1] 0.1 Water [Ice,FreezeMask,Shatter,AbsoluteZero,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
6 13底座 [1,1,1,1,1] 0.1 Earth [Fire,BurnSpread,IgniteBurst,Inferno,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
7 123底座 [1,1,1,1,1] 0.1 Fire [Fire,BurnSpread,IgniteBurst,Inferno,Ice,FreezeMask,Shatter,AbsoluteZero,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
|||||||
@@ -4,3 +4,7 @@
|
|||||||
1 元素轴承 [10,12,13,14,15] 15 [2,2,2,2,2] 0.3 [Fire,BurnSpread,IgniteBurst,Inferno]
|
1 元素轴承 [10,12,13,14,15] 15 [2,2,2,2,2] 0.3 [Fire,BurnSpread,IgniteBurst,Inferno]
|
||||||
2 控制轴承 [20,25,30,32,35] 0 [6,6.5,7,8,8] 0.2 [Ice,FreezeMask,Shatter,AbsoluteZero]
|
2 控制轴承 [20,25,30,32,35] 0 [6,6.5,7,8,8] 0.2 [Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
3 穿透轴承 [60,70,80,90,100] 20 [4,4.5,5,5.5,6] 0 [Pierce,Crit,Overpenetrate,Execution]
|
3 穿透轴承 [60,70,80,90,100] 20 [4,4.5,5,5.5,6] 0 [Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
4 12轴承 [60,70,80,90,100] 20 [4,4.5,5,5.5,6] 0 [Fire,BurnSpread,IgniteBurst,Inferno,Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
|
5 23轴承 [60,70,80,90,100] 20 [4,4.5,5,5.5,6] 0 [Ice,FreezeMask,Shatter,AbsoluteZero,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
6 13轴承 [60,70,80,90,100] 20 [4,4.5,5,5.5,6] 0 [Fire,BurnSpread,IgniteBurst,Inferno,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
7 123轴承 [60,70,80,90,100] 20 [4,4.5,5,5.5,6] 0 [Fire,BurnSpread,IgniteBurst,Inferno,Ice,FreezeMask,Shatter,AbsoluteZero,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
# Id 策划备注 AssetName
|
# Id 策划备注 AssetName
|
||||||
# int string
|
# int string
|
||||||
# 实体编号 资源名称
|
# 实体编号 资源名称
|
||||||
101 测试枪口 TestMuzzle
|
|
||||||
201 测试轴承 TestBearing
|
|
||||||
301 测试底座 TestBase
|
|
||||||
401 防御塔实体 TowerEntity
|
401 防御塔实体 TowerEntity
|
||||||
501 普通子弹 NormalBullet
|
501 普通子弹 NormalBullet
|
||||||
1001 测试普通敌人 TestEnemy
|
1001 测试普通敌人 TestEnemy
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Id 列1 Title Description Options
|
# Id 列1 Title Description Option1 Option2 Option3 Option4
|
||||||
# int string string string
|
# int string string string string string string
|
||||||
# 事件编号 策划备注 事件题目 事件描述 事件选项
|
# 事件编号 策划备注 事件题目 事件描述 选项 1 选项 2 选项 3 选项 4
|
||||||
1 赌马 一名商人邀请你下注。赢了就能赚一笔。 [{"optionText":"下注 100(金)- 稳健:70% 赢 150","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":150}}],"probability":0.7},{"optionText":"下注 100(金)- 激进:30% 赢 250","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":250}}],"probability":0.3}]
|
1 赌马 一名商人邀请你下注。赢了就能赚一笔。 {"optionText":"下注 100(金)- 稳健:70% 赢 150","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":150}}],"probability":0.7} {"optionText":"下注 100(金)- 激进:30% 赢 250","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":250}}],"probability":0.3}
|
||||||
2 工匠的熔炉 工匠以金币交换防御塔组件。 [{"optionText":"交出 3 个白色组件,获得 50 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":3,"Rarity":"White"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":3,"Rarity":"White"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":50}}]},{"optionText":"交出 2 个绿色组件,获得 70 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":2,"Rarity":"Green"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":2,"Rarity":"Green"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":70}}]},{"optionText":"交出 1 个蓝色组件,获得 80 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":1,"Rarity":"Blue"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":1,"Rarity":"Blue"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":80}}]},{"optionText":"拒绝","rewardEffects":[]}]
|
2 工匠的熔炉 工匠愿意熔炼你的旧组件,换给你一件更有希望的新货。 {"optionText":"交出 2 个白色组件,获得 1 个白色或绿色组件","requirements":[{"type":"CompCountAtLeast","param":{"Count":2,"Rarity":"White"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":2,"Rarity":"White"}}],"rewardEffects":[{"type":"AddRandomComps","param":{"Count":1,"MinRarity":"White","MaxRarity":"Green"}}]} {"optionText":"交出 2 个绿色组件,获得 1 个绿色或蓝色组件","requirements":[{"type":"CompCountAtLeast","param":{"Count":2,"Rarity":"Green"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":2,"Rarity":"Green"}}],"rewardEffects":[{"type":"AddRandomComps","param":{"Count":1,"MinRarity":"Green","MaxRarity":"Blue"}}]} {"optionText":"交出 2 个蓝色组件,获得 1 个蓝色或紫色组件","requirements":[{"type":"CompCountAtLeast","param":{"Count":2,"Rarity":"Blue"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":2,"Rarity":"Blue"}}],"rewardEffects":[{"type":"AddRandomComps","param":{"Count":1,"MinRarity":"Blue","MaxRarity":"Purple"}}]} {"optionText":"拒绝","requirements":[],"rewardEffects":[]}
|
||||||
3 代价与回报 某种黑暗力量向你索取代价。 [{"optionText":"展示你的防御塔","requirements":[{"type":"TowerCountAtLeast","param":{"Count":2}}],"rewardEffects":[{"type":"AddGold","param":{"Count":50}}]},{"optionText":"离开","rewardEffects":[]}]
|
3 代价与回报 某种黑暗力量承诺给你金币,但它索取的代价会落在你的一座防御塔上。 {"optionText":"让黑暗力量抽取一座防御塔 20 点耐久,获得 50 金币","requirements":[{"type":"TowerCountAtLeast","param":{"Count":1}}],"costEffects":[{"type":"DamageRandomTowersEndurance","param":{"Count":1,"Amount":20}}],"rewardEffects":[{"type":"AddGold","param":{"Count":50}}]} {"optionText":"离开","requirements":[],"rewardEffects":[]}
|
||||||
|
|||||||
@@ -4,3 +4,7 @@
|
|||||||
1 元素枪口 [20,30,40,50,80] 10 0.05 NormalBullet [Fire,BurnSpread,IgniteBurst,Inferno]
|
1 元素枪口 [20,30,40,50,80] 10 0.05 NormalBullet [Fire,BurnSpread,IgniteBurst,Inferno]
|
||||||
2 控制枪口 [30,50,70,90,100] 20 0.01 NormalBullet [Ice,FreezeMask,Shatter,AbsoluteZero]
|
2 控制枪口 [30,50,70,90,100] 20 0.01 NormalBullet [Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
3 穿透枪口 [50,55,60,80,90] 30 0.02 NormalBullet [Pierce,Crit,Overpenetrate,Execution]
|
3 穿透枪口 [50,55,60,80,90] 30 0.02 NormalBullet [Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
4 12枪口 [50,55,60,80,90] 30 0.02 NormalBullet [Fire,BurnSpread,IgniteBurst,Inferno,Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
|
5 23枪口 [50,55,60,80,90] 30 0.02 NormalBullet [Ice,FreezeMask,Shatter,AbsoluteZero,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
6 13枪口 [50,55,60,80,90] 30 0.02 NormalBullet [Fire,BurnSpread,IgniteBurst,Inferno,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
7 123枪口 [50,55,60,80,90] 30 0.02 NormalBullet [Fire,BurnSpread,IgniteBurst,Inferno,Ice,FreezeMask,Shatter,AbsoluteZero,Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
|||||||
@@ -1,48 +1,24 @@
|
|||||||
# Id 列1 LevelThemeType Rarity ItemType ItemId Weight MinPhase MaxPhase
|
# Id 列1 LevelThemeType ItemType ItemId Weights MinPhase MaxPhase
|
||||||
# int LevelThemeType RarityType ItemType int int int int
|
# int LevelThemeType ItemType int int[] int[] int[]
|
||||||
# 道具池号 策划备注 关卡所属主题类型 道具稀有度 道具类型 道具Id 权重 出现的最低波次 出现的最高波次
|
# 道具池号 策划备注 关卡所属主题类型 道具类型 道具Id 权重 出现的最低波次 出现的最高波次
|
||||||
1 平原战斗池 Plain White MuzzleComp 1 10 1 10
|
1 平原战斗池 Plain MuzzleComp 1 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
2 Plain White MuzzleComp 2 10 1 10
|
2 Plain MuzzleComp 2 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
3 Plain White MuzzleComp 3 10 1 10
|
3 Plain MuzzleComp 3 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
4 Plain White BearingComp 1 10 1 10
|
4 Plain MuzzleComp 4 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
5 Plain White BearingComp 2 10 1 10
|
5 Plain MuzzleComp 5 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
6 Plain White BearingComp 3 10 1 10
|
6 Plain MuzzleComp 6 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
7 Plain White BaseComp 1 10 1 10
|
7 Plain MuzzleComp 7 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
8 Plain White BaseComp 2 10 1 10
|
8 Plain BearingComp 1 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
9 Plain White BaseComp 3 10 1 10
|
9 Plain BearingComp 2 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
10 Plain Green MuzzleComp 1 5 2 10
|
10 Plain BearingComp 3 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
11 Plain Green MuzzleComp 2 5 2 10
|
11 Plain BearingComp 4 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
12 Plain Green MuzzleComp 3 5 2 10
|
12 Plain BearingComp 5 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
13 Plain Green BearingComp 1 5 2 10
|
13 Plain BearingComp 6 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
14 Plain Green BearingComp 2 5 2 10
|
14 Plain BearingComp 7 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
15 Plain Green BearingComp 3 5 2 10
|
15 Plain BaseComp 1 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
16 Plain Green BaseComp 1 5 2 10
|
16 Plain BaseComp 2 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
17 Plain Green BaseComp 2 5 2 10
|
17 Plain BaseComp 3 [20,10,6,4,2] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
18 Plain Green BaseComp 3 5 2 10
|
18 Plain BaseComp 4 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
19 Plain Blue MuzzleComp 1 3 3 15
|
19 Plain BaseComp 5 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
20 Plain Blue MuzzleComp 2 3 3 15
|
20 Plain BaseComp 6 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
21 Plain Blue MuzzleComp 3 3 3 15
|
21 Plain BaseComp 7 [15,8,5,3,1] [1,2,3,4,5] [10,10,15,20,50]
|
||||||
22 Plain Blue BearingComp 1 3 3 15
|
|
||||||
23 Plain Blue BearingComp 2 3 3 15
|
|
||||||
24 Plain Blue BearingComp 3 3 3 15
|
|
||||||
25 Plain Blue BaseComp 1 3 3 15
|
|
||||||
26 Plain Blue BaseComp 2 3 3 15
|
|
||||||
27 Plain Blue BaseComp 3 3 3 15
|
|
||||||
28 Plain Purple MuzzleComp 1 2 4 20
|
|
||||||
29 Plain Purple MuzzleComp 2 2 4 20
|
|
||||||
30 Plain Purple MuzzleComp 3 2 4 20
|
|
||||||
31 Plain Purple BearingComp 1 2 4 20
|
|
||||||
32 Plain Purple BearingComp 2 2 4 20
|
|
||||||
33 Plain Purple BearingComp 3 2 4 20
|
|
||||||
34 Plain Purple BaseComp 1 2 4 20
|
|
||||||
35 Plain Purple BaseComp 2 2 4 20
|
|
||||||
36 Plain Purple BaseComp 3 2 4 20
|
|
||||||
37 Plain Red MuzzleComp 1 1 5 50
|
|
||||||
38 Plain Red MuzzleComp 2 1 5 50
|
|
||||||
39 Plain Red MuzzleComp 3 1 5 50
|
|
||||||
40 Plain Red BearingComp 1 1 5 50
|
|
||||||
41 Plain Red BearingComp 2 1 5 50
|
|
||||||
42 Plain Red BearingComp 3 1 5 50
|
|
||||||
43 Plain Red BaseComp 1 1 5 50
|
|
||||||
44 Plain Red BaseComp 2 1 5 50
|
|
||||||
45 Plain Red BaseComp 3 1 5 50
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ public partial class GameEntry
|
|||||||
|
|
||||||
public static InventoryGenerationComponent InventoryGeneration { get; private set; }
|
public static InventoryGenerationComponent InventoryGeneration { get; private set; }
|
||||||
|
|
||||||
|
public static TagRegistryComponent TagRegistry { get; private set; }
|
||||||
|
|
||||||
private static void InitCustomComponents()
|
private static void InitCustomComponents()
|
||||||
{
|
{
|
||||||
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
|
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
|
||||||
@@ -38,5 +40,6 @@ public partial class GameEntry
|
|||||||
ResolutionAdapter = UnityGameFramework.Runtime.GameEntry.GetComponent<ResolutionAdapterComponent>();
|
ResolutionAdapter = UnityGameFramework.Runtime.GameEntry.GetComponent<ResolutionAdapterComponent>();
|
||||||
SpriteCache = UnityGameFramework.Runtime.GameEntry.GetComponent<SpriteCacheComponent>();
|
SpriteCache = UnityGameFramework.Runtime.GameEntry.GetComponent<SpriteCacheComponent>();
|
||||||
InventoryGeneration = UnityGameFramework.Runtime.GameEntry.GetComponent<InventoryGenerationComponent>();
|
InventoryGeneration = UnityGameFramework.Runtime.GameEntry.GetComponent<InventoryGenerationComponent>();
|
||||||
|
TagRegistry = UnityGameFramework.Runtime.GameEntry.GetComponent<TagRegistryComponent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "GeometryTD.Runtime",
|
||||||
|
"rootNamespace": "GeometryTD.Runtime",
|
||||||
|
"references": [
|
||||||
|
"GUID:363c5eb08ff8e6a439b85e37b8c20d96",
|
||||||
|
"GUID:75469ad4d38634e559750d17036d5f7c",
|
||||||
|
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||||
|
"GUID:771b79705393da244ae720aa28ae8a69",
|
||||||
|
"GUID:c53de6f5028489b4689382a86bfbd807",
|
||||||
|
"GUID:0f19320f1ba33834b896afc5a5aaae67"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Android"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "GeometryTD.Gameplay",
|
||||||
|
"rootNamespace": "GeometryTD.Gameplay",
|
||||||
|
"references": [
|
||||||
|
"GUID:771b79705393da244ae720aa28ae8a69"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Android"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c53de6f5028489b4689382a86bfbd807
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -177,11 +177,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
|
|
||||||
public void StartCombat(
|
public void StartCombat(
|
||||||
int levelId = 0,
|
int levelId = 0,
|
||||||
string runId = null,
|
RunNodeExecutionContext context = null)
|
||||||
int runSeed = 0,
|
|
||||||
int nodeId = 0,
|
|
||||||
RunNodeType nodeType = RunNodeType.None,
|
|
||||||
int sequenceIndex = -1)
|
|
||||||
{
|
{
|
||||||
if (!EnsureCombatRuntimeInitialized())
|
if (!EnsureCombatRuntimeInitialized())
|
||||||
{
|
{
|
||||||
@@ -232,11 +228,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
selectedLevel,
|
selectedLevel,
|
||||||
phaseList,
|
phaseList,
|
||||||
_selectedSpawnEntriesByPhaseId,
|
_selectedSpawnEntriesByPhaseId,
|
||||||
runId,
|
context))
|
||||||
runSeed,
|
|
||||||
nodeId,
|
|
||||||
nodeType,
|
|
||||||
sequenceIndex))
|
|
||||||
{
|
{
|
||||||
CurrentLevel = null;
|
CurrentLevel = null;
|
||||||
return;
|
return;
|
||||||
|
|||||||
+15
-19
@@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GeometryTD.CustomEvent;
|
|
||||||
using GeometryTD.DataTable;
|
using GeometryTD.DataTable;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Definition;
|
||||||
using GeometryTD.Entity;
|
using GeometryTD.Entity;
|
||||||
using GeometryTD.Procedure;
|
using GeometryTD.Procedure;
|
||||||
using GeometryTD.UI;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
@@ -69,11 +67,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
DRLevel level,
|
DRLevel level,
|
||||||
IReadOnlyList<DRLevelPhase> phases,
|
IReadOnlyList<DRLevelPhase> phases,
|
||||||
IReadOnlyDictionary<int, IReadOnlyList<DRLevelSpawnEntry>> spawnEntriesByPhaseId,
|
IReadOnlyDictionary<int, IReadOnlyList<DRLevelSpawnEntry>> spawnEntriesByPhaseId,
|
||||||
string runId = null,
|
RunNodeExecutionContext context = null)
|
||||||
int runSeed = 0,
|
|
||||||
int nodeId = 0,
|
|
||||||
RunNodeType nodeType = RunNodeType.None,
|
|
||||||
int sequenceIndex = -1)
|
|
||||||
{
|
{
|
||||||
if (!_initialized || _runtime.Entity == null)
|
if (!_initialized || _runtime.Entity == null)
|
||||||
{
|
{
|
||||||
@@ -95,16 +89,12 @@ namespace GeometryTD.CustomComponent
|
|||||||
_runtime.DidCombatWin = true;
|
_runtime.DidCombatWin = true;
|
||||||
|
|
||||||
_runtime.CurrentLevel = level;
|
_runtime.CurrentLevel = level;
|
||||||
_runtime.RunId = runId;
|
_runtime.NodeContext = context != null ? context.Clone() : null;
|
||||||
_runtime.RunSeed = runSeed;
|
_runtime.NextDropOrdinal = 0;
|
||||||
_runtime.NodeId = nodeId;
|
_runtime.NextRewardOrdinal = 0;
|
||||||
_runtime.NodeType = nodeType;
|
|
||||||
_runtime.SequenceIndex = sequenceIndex;
|
|
||||||
GameEntry.InventoryGeneration.ConfigureRunContext(runSeed, sequenceIndex);
|
|
||||||
_runtime.CombatRunResourceStore.InitializeForCombat(level);
|
_runtime.CombatRunResourceStore.InitializeForCombat(level);
|
||||||
for (int i = 0; i < phases.Count; i++)
|
foreach (var phase in phases)
|
||||||
{
|
{
|
||||||
DRLevelPhase phase = phases[i];
|
|
||||||
if (phase != null)
|
if (phase != null)
|
||||||
{
|
{
|
||||||
_runtime.PhaseBuffer.Add(phase);
|
_runtime.PhaseBuffer.Add(phase);
|
||||||
@@ -122,7 +112,8 @@ namespace GeometryTD.CustomComponent
|
|||||||
_runtime.PhaseLoopRuntime.SetPhases(_runtime.PhaseBuffer);
|
_runtime.PhaseLoopRuntime.SetPhases(_runtime.PhaseBuffer);
|
||||||
if (_runtime.PhaseLoopRuntime.PhaseCount <= 0)
|
if (_runtime.PhaseLoopRuntime.PhaseCount <= 0)
|
||||||
{
|
{
|
||||||
return _coordinator.HandleStartFailure($"CombatScheduler start failed. Level '{level.Id}' has no phase data.");
|
return _coordinator.HandleStartFailure(
|
||||||
|
$"CombatScheduler start failed. Level '{level.Id}' has no phase data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeState(new CombatLoadingState(_runtime, _coordinator));
|
ChangeState(new CombatLoadingState(_runtime, _coordinator));
|
||||||
@@ -201,7 +192,13 @@ namespace GeometryTD.CustomComponent
|
|||||||
enemy,
|
enemy,
|
||||||
_runtime.PhaseLoopRuntime.DisplayPhaseIndex,
|
_runtime.PhaseLoopRuntime.DisplayPhaseIndex,
|
||||||
_coordinator.ResolveCurrentThemeType());
|
_coordinator.ResolveCurrentThemeType());
|
||||||
EnemyDropResult result = GameEntry.InventoryGeneration.ResolveEnemyDrop(context);
|
int nextDropOrdinal = _runtime.NextDropOrdinal;
|
||||||
|
EnemyDropResult result = GameEntry.InventoryGeneration.ResolveEnemyDrop(
|
||||||
|
context,
|
||||||
|
_runtime.NodeContext != null ? _runtime.NodeContext.RunSeed : 0,
|
||||||
|
_runtime.NodeContext != null ? _runtime.NodeContext.SequenceIndex : -1,
|
||||||
|
ref nextDropOrdinal);
|
||||||
|
_runtime.NextDropOrdinal = nextDropOrdinal;
|
||||||
_runtime.CombatRunResourceStore.AddEnemyDefeatedReward(result.Coin, result.Gold);
|
_runtime.CombatRunResourceStore.AddEnemyDefeatedReward(result.Coin, result.Gold);
|
||||||
_runtime.CombatRunResourceStore.AddEnemyDefeatedLoot(result.LootItem);
|
_runtime.CombatRunResourceStore.AddEnemyDefeatedLoot(result.LootItem);
|
||||||
}
|
}
|
||||||
@@ -326,6 +323,5 @@ namespace GeometryTD.CustomComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-15
@@ -55,11 +55,9 @@ namespace GeometryTD.CustomComponent
|
|||||||
_runtime.DidCombatWin = true;
|
_runtime.DidCombatWin = true;
|
||||||
_runtime.IsCompleted = false;
|
_runtime.IsCompleted = false;
|
||||||
_runtime.NodeEnterFired = false;
|
_runtime.NodeEnterFired = false;
|
||||||
_runtime.RunId = null;
|
_runtime.NodeContext = null;
|
||||||
_runtime.RunSeed = 0;
|
_runtime.NextDropOrdinal = 0;
|
||||||
_runtime.NodeId = 0;
|
_runtime.NextRewardOrdinal = 0;
|
||||||
_runtime.NodeType = RunNodeType.None;
|
|
||||||
_runtime.SequenceIndex = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CleanupAllCombatEntities()
|
public void CleanupAllCombatEntities()
|
||||||
@@ -201,16 +199,20 @@ namespace GeometryTD.CustomComponent
|
|||||||
public void CompleteNormalCombatAndNotify(bool didCombatWin)
|
public void CompleteNormalCombatAndNotify(bool didCombatWin)
|
||||||
{
|
{
|
||||||
CompleteCombat(didCombatWin);
|
CompleteCombat(didCombatWin);
|
||||||
|
RunNodeExecutionContext nodeContext = _runtime.NodeContext;
|
||||||
GameEntry.Event.Fire(
|
GameEntry.Event.Fire(
|
||||||
this,
|
this,
|
||||||
NodeCompleteEventArgs.Create(
|
NodeCompleteEventArgs.Create(
|
||||||
_runtime.RunId,
|
nodeContext?.RunId,
|
||||||
_runtime.NodeId,
|
nodeContext?.NodeId ?? 0,
|
||||||
_runtime.NodeType,
|
nodeContext?.NodeType ?? RunNodeType.None,
|
||||||
_runtime.SequenceIndex,
|
nodeContext?.SequenceIndex ?? -1,
|
||||||
RunNodeCompletionStatus.Completed,
|
RunNodeCompletionStatus.Completed,
|
||||||
didCombatWin,
|
didCombatWin,
|
||||||
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null));
|
nodeContext != null
|
||||||
|
? nodeContext.CreateCompletionSnapshot(
|
||||||
|
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)
|
||||||
|
: null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CompleteFailureCombatAndNotify()
|
public void CompleteFailureCombatAndNotify()
|
||||||
@@ -220,16 +222,20 @@ namespace GeometryTD.CustomComponent
|
|||||||
CloseRewardSelectForm();
|
CloseRewardSelectForm();
|
||||||
CloseDialogForm();
|
CloseDialogForm();
|
||||||
CompleteCombat(false);
|
CompleteCombat(false);
|
||||||
|
RunNodeExecutionContext nodeContext = _runtime.NodeContext;
|
||||||
GameEntry.Event.Fire(
|
GameEntry.Event.Fire(
|
||||||
this,
|
this,
|
||||||
NodeCompleteEventArgs.Create(
|
NodeCompleteEventArgs.Create(
|
||||||
_runtime.RunId,
|
nodeContext?.RunId,
|
||||||
_runtime.NodeId,
|
nodeContext?.NodeId ?? 0,
|
||||||
_runtime.NodeType,
|
nodeContext?.NodeType ?? RunNodeType.None,
|
||||||
_runtime.SequenceIndex,
|
nodeContext?.SequenceIndex ?? -1,
|
||||||
RunNodeCompletionStatus.Exception,
|
RunNodeCompletionStatus.Exception,
|
||||||
false,
|
false,
|
||||||
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null));
|
nodeContext != null
|
||||||
|
? nodeContext.CreateCompletionSnapshot(
|
||||||
|
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)
|
||||||
|
: null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HandleStartFailure(string errorMessage)
|
public bool HandleStartFailure(string errorMessage)
|
||||||
|
|||||||
+3
-5
@@ -29,10 +29,8 @@ namespace GeometryTD.CustomComponent
|
|||||||
public bool IsCompleted { get; set; }
|
public bool IsCompleted { get; set; }
|
||||||
public bool NodeEnterFired { get; set; }
|
public bool NodeEnterFired { get; set; }
|
||||||
public CombatSettlementContext SettlementContext { get; set; }
|
public CombatSettlementContext SettlementContext { get; set; }
|
||||||
public string RunId { get; set; }
|
public RunNodeExecutionContext NodeContext { get; set; }
|
||||||
public int RunSeed { get; set; }
|
public int NextDropOrdinal { get; set; }
|
||||||
public int NodeId { get; set; }
|
public int NextRewardOrdinal { get; set; }
|
||||||
public RunNodeType NodeType { get; set; }
|
|
||||||
public int SequenceIndex { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-212
@@ -11,10 +11,8 @@ namespace GeometryTD.CustomComponent
|
|||||||
public sealed class CombatSettlementService
|
public sealed class CombatSettlementService
|
||||||
{
|
{
|
||||||
private const int RewardSelectDisplayCount = 3;
|
private const int RewardSelectDisplayCount = 3;
|
||||||
private const float FullBaseHpGoldBonusRate = 0.3f;
|
private readonly CombatSettlementCalculator _calculator = new();
|
||||||
private const float HighBaseHpGoldBonusRate = 0.1f;
|
private readonly CombatSettlementCommitter _committer = new();
|
||||||
private const float HighBaseHpThreshold = 0.8f;
|
|
||||||
private const float SettlementTowerEnduranceLoss = 1f;
|
|
||||||
|
|
||||||
public CombatSettlementContext BuildSettlementContext(
|
public CombatSettlementContext BuildSettlementContext(
|
||||||
bool didCombatWin,
|
bool didCombatWin,
|
||||||
@@ -22,69 +20,25 @@ namespace GeometryTD.CustomComponent
|
|||||||
int defeatedEnemyCount,
|
int defeatedEnemyCount,
|
||||||
CombatRunResourceStore resourceStore)
|
CombatRunResourceStore resourceStore)
|
||||||
{
|
{
|
||||||
bool shouldOpenFullBaseHpRewardSelect = false;
|
return _calculator.BuildSettlementContext(
|
||||||
ResolveSettlementByBaseHp(
|
|
||||||
didCombatWin,
|
didCombatWin,
|
||||||
currentLevel,
|
currentLevel,
|
||||||
resourceStore,
|
defeatedEnemyCount,
|
||||||
out int currentBaseHp,
|
resourceStore);
|
||||||
out int maxBaseHp,
|
|
||||||
out int levelRewardGold,
|
|
||||||
out float bonusRate,
|
|
||||||
out int bonusGold,
|
|
||||||
out shouldOpenFullBaseHpRewardSelect);
|
|
||||||
|
|
||||||
CombatSettlementContext settlementContext = new CombatSettlementContext
|
|
||||||
{
|
|
||||||
};
|
|
||||||
settlementContext.Result.DidCombatWin = didCombatWin;
|
|
||||||
settlementContext.Result.FinalCoin = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentCoin : 0);
|
|
||||||
settlementContext.Result.FinalBaseHp = currentBaseHp;
|
|
||||||
settlementContext.Result.MaxBaseHp = maxBaseHp;
|
|
||||||
settlementContext.Result.DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
|
|
||||||
settlementContext.Result.GainedGold = Mathf.Max(0, resourceStore != null ? resourceStore.GainedGold : 0);
|
|
||||||
settlementContext.Result.RewardInventory = resourceStore != null
|
|
||||||
? resourceStore.GetRewardInventorySnapshot()
|
|
||||||
: new BackpackInventoryData();
|
|
||||||
PopulateEnduranceSettlement(settlementContext, resourceStore);
|
|
||||||
settlementContext.Flags.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect;
|
|
||||||
settlementContext.Flags.DidEnterRewardSelection = false;
|
|
||||||
settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount;
|
|
||||||
settlementContext.Summary.GainedGold = settlementContext.Result.GainedGold;
|
|
||||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
|
||||||
|
|
||||||
Log.Info(
|
|
||||||
"Combat settlement resolved. Level={0}, BaseHp={1}/{2}, LevelReward={3}, BonusRate={4:P0}, BonusGold={5}, FullHpRewardSelect={6}, EnduranceTargets={7}.",
|
|
||||||
currentLevel != null ? currentLevel.Id : 0,
|
|
||||||
currentBaseHp,
|
|
||||||
maxBaseHp,
|
|
||||||
levelRewardGold,
|
|
||||||
bonusRate,
|
|
||||||
bonusGold,
|
|
||||||
shouldOpenFullBaseHpRewardSelect,
|
|
||||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count);
|
|
||||||
return settlementContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CommitSettlementInventory(CombatSettlementContext settlementContext)
|
public void CommitSettlementInventory(CombatSettlementContext settlementContext)
|
||||||
{
|
{
|
||||||
if (settlementContext == null || settlementContext.Flags.IsCommitted)
|
_committer.CommitSettlementInventory(settlementContext);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BackpackInventoryData rewardInventory = settlementContext.Result.RewardInventory ?? new BackpackInventoryData();
|
|
||||||
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
|
||||||
settlementContext.Result.RewardInventory = rewardInventory;
|
|
||||||
settlementContext.Summary.RewardInventory = rewardInventory;
|
|
||||||
settlementContext.Result.Endurance.AffectedTowerCount = ApplyDeferredSettlementEndurance(settlementContext);
|
|
||||||
settlementContext.Flags.IsCommitted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryPrepareRewardSelection(
|
public bool TryPrepareRewardSelection(
|
||||||
CombatSettlementContext settlementContext,
|
CombatSettlementContext settlementContext,
|
||||||
int displayPhaseIndex,
|
int displayPhaseIndex,
|
||||||
LevelThemeType themeType,
|
LevelThemeType themeType,
|
||||||
|
int runSeed,
|
||||||
|
int sequenceIndex,
|
||||||
|
ref int nextRewardOrdinal,
|
||||||
RewardSelectFormUseCase rewardSelectFormUseCase,
|
RewardSelectFormUseCase rewardSelectFormUseCase,
|
||||||
Action<RewardSelectItemRawData> onRewardSelected,
|
Action<RewardSelectItemRawData> onRewardSelected,
|
||||||
Action onGiveUp)
|
Action onGiveUp)
|
||||||
@@ -97,36 +51,22 @@ namespace GeometryTD.CustomComponent
|
|||||||
IReadOnlyList<TowerCompItemData> candidateItems = GameEntry.InventoryGeneration.BuildRewardCandidates(
|
IReadOnlyList<TowerCompItemData> candidateItems = GameEntry.InventoryGeneration.BuildRewardCandidates(
|
||||||
displayPhaseIndex,
|
displayPhaseIndex,
|
||||||
themeType,
|
themeType,
|
||||||
RewardSelectDisplayCount);
|
RewardSelectDisplayCount,
|
||||||
|
runSeed,
|
||||||
|
sequenceIndex,
|
||||||
|
ref nextRewardOrdinal);
|
||||||
if (candidateItems == null || candidateItems.Count <= 0)
|
if (candidateItems == null || candidateItems.Count <= 0)
|
||||||
{
|
{
|
||||||
settlementContext.Flags.ShouldOpenRewardSelection = false;
|
settlementContext.Flags.ShouldOpenRewardSelection = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RewardSelectItemRawData> rewardPool = new List<RewardSelectItemRawData>(candidateItems.Count);
|
|
||||||
foreach (var item in candidateItems)
|
|
||||||
{
|
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rewardPool.Add(BuildRewardSelectRawData(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rewardPool.Count <= 0)
|
|
||||||
{
|
|
||||||
settlementContext.Flags.ShouldOpenRewardSelection = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
rewardSelectFormUseCase.SetCallbacks(onRewardSelected, onGiveUp);
|
rewardSelectFormUseCase.SetCallbacks(onRewardSelected, onGiveUp);
|
||||||
rewardSelectFormUseCase.ConfigureRewardPool(
|
rewardSelectFormUseCase.ConfigureRewardCandidates(
|
||||||
rewardPool,
|
candidateItems,
|
||||||
displayCount: RewardSelectDisplayCount,
|
displayCount: RewardSelectDisplayCount,
|
||||||
refreshCost: 0,
|
refreshCost: 0,
|
||||||
allowRefreshOnce: false,
|
allowRotateOnce: false,
|
||||||
allowGiveUp: false,
|
allowGiveUp: false,
|
||||||
tipText: "基地满血奖励:请选择 1 个组件");
|
tipText: "基地满血奖励:请选择 1 个组件");
|
||||||
|
|
||||||
@@ -144,13 +84,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
|
|
||||||
public void ApplySelectedReward(CombatSettlementContext settlementContext, RewardSelectItemRawData selectedReward)
|
public void ApplySelectedReward(CombatSettlementContext settlementContext, RewardSelectItemRawData selectedReward)
|
||||||
{
|
{
|
||||||
if (settlementContext?.Result.RewardInventory == null || selectedReward?.SourceItem == null)
|
_committer.ApplySelectedReward(settlementContext, selectedReward);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TryAppendRewardComponent(settlementContext.Result.RewardInventory, selectedReward.SourceItem);
|
|
||||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenCombatFinishForm(
|
public void OpenCombatFinishForm(
|
||||||
@@ -166,134 +100,5 @@ namespace GeometryTD.CustomComponent
|
|||||||
GameEntry.UIRouter.OpenUI(UIFormType.CombatFinishForm);
|
GameEntry.UIRouter.OpenUI(UIFormType.CombatFinishForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ResolveSettlementByBaseHp(
|
|
||||||
bool didCombatWin,
|
|
||||||
DRLevel currentLevel,
|
|
||||||
CombatRunResourceStore resourceStore,
|
|
||||||
out int currentBaseHp,
|
|
||||||
out int maxBaseHp,
|
|
||||||
out int levelRewardGold,
|
|
||||||
out float bonusRate,
|
|
||||||
out int bonusGold,
|
|
||||||
out bool shouldOpenFullBaseHpRewardSelect)
|
|
||||||
{
|
|
||||||
currentBaseHp = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentBaseHp : 0);
|
|
||||||
maxBaseHp = resourceStore != null ? Mathf.Max(0, resourceStore.MaxBaseHp) : 0;
|
|
||||||
if (maxBaseHp > 0)
|
|
||||||
{
|
|
||||||
currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp);
|
|
||||||
}
|
|
||||||
|
|
||||||
levelRewardGold = currentLevel != null ? Mathf.Max(0, currentLevel.RewardGold) : 0;
|
|
||||||
bonusRate = 0f;
|
|
||||||
bonusGold = 0;
|
|
||||||
shouldOpenFullBaseHpRewardSelect = false;
|
|
||||||
|
|
||||||
if (!didCombatWin || resourceStore == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxBaseHp > 0 && currentBaseHp >= maxBaseHp)
|
|
||||||
{
|
|
||||||
bonusRate = FullBaseHpGoldBonusRate;
|
|
||||||
shouldOpenFullBaseHpRewardSelect = true;
|
|
||||||
}
|
|
||||||
else if (maxBaseHp > 0)
|
|
||||||
{
|
|
||||||
float hpRate = (float)currentBaseHp / maxBaseHp;
|
|
||||||
if (hpRate >= HighBaseHpThreshold)
|
|
||||||
{
|
|
||||||
bonusRate = HighBaseHpGoldBonusRate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int goldForBonusCalculation = Mathf.Max(0, resourceStore.GainedGold) + levelRewardGold;
|
|
||||||
bonusGold = bonusRate > 0f ? Mathf.FloorToInt(goldForBonusCalculation * bonusRate) : 0;
|
|
||||||
int settlementGold = levelRewardGold + bonusGold;
|
|
||||||
resourceStore.AddSettlementGold(settlementGold);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PopulateEnduranceSettlement(
|
|
||||||
CombatSettlementContext settlementContext,
|
|
||||||
CombatRunResourceStore resourceStore)
|
|
||||||
{
|
|
||||||
if (settlementContext == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CombatSettlementEnduranceResult enduranceResult = settlementContext.Result.Endurance;
|
|
||||||
enduranceResult.TargetTowerInstanceIds.Clear();
|
|
||||||
enduranceResult.EnduranceLossPerComponent = SettlementTowerEnduranceLoss;
|
|
||||||
|
|
||||||
IReadOnlyList<long> participantTowerIds = resourceStore?.GetParticipantTowerInstanceIdSnapshot();
|
|
||||||
if (participantTowerIds == null || participantTowerIds.Count <= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < participantTowerIds.Count; i++)
|
|
||||||
{
|
|
||||||
long towerId = participantTowerIds[i];
|
|
||||||
if (towerId > 0)
|
|
||||||
{
|
|
||||||
enduranceResult.TargetTowerInstanceIds.Add(towerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ApplyDeferredSettlementEndurance(CombatSettlementContext settlementContext)
|
|
||||||
{
|
|
||||||
if (settlementContext == null ||
|
|
||||||
settlementContext.Result.Endurance.EnduranceLossPerComponent <= 0f ||
|
|
||||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count <= 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerInventoryComponent inventory = GameEntry.PlayerInventory;
|
|
||||||
if (inventory == null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inventory.ReduceTowerEndurance(
|
|
||||||
settlementContext.Result.Endurance.TargetTowerInstanceIds,
|
|
||||||
settlementContext.Result.Endurance.EnduranceLossPerComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryAppendRewardComponent(BackpackInventoryData rewardInventory, TowerCompItemData selectedItem)
|
|
||||||
{
|
|
||||||
if (rewardInventory == null || selectedItem == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedItem is MuzzleCompItemData muzzleComp)
|
|
||||||
{
|
|
||||||
rewardInventory.MuzzleComponents.Add(muzzleComp);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedItem is BearingCompItemData bearingComp)
|
|
||||||
{
|
|
||||||
rewardInventory.BearingComponents.Add(bearingComp);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedItem is BaseCompItemData baseComp)
|
|
||||||
{
|
|
||||||
rewardInventory.BaseComponents.Add(baseComp);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RewardSelectItemRawData BuildRewardSelectRawData(TowerCompItemData item)
|
|
||||||
{
|
|
||||||
return RewardSelectItemRawDataBuilder.Build(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -16,16 +16,23 @@ namespace GeometryTD.CustomComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
Coordinator.EnsureRewardSelectFormUseCaseBound();
|
Coordinator.EnsureRewardSelectFormUseCaseBound();
|
||||||
|
int nextRewardOrdinal = Runtime.NextRewardOrdinal;
|
||||||
if (!Runtime.CombatSettlementService.TryPrepareRewardSelection(
|
if (!Runtime.CombatSettlementService.TryPrepareRewardSelection(
|
||||||
Runtime.SettlementContext,
|
Runtime.SettlementContext,
|
||||||
Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
|
Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
|
||||||
Coordinator.ResolveCurrentThemeType(),
|
Coordinator.ResolveCurrentThemeType(),
|
||||||
|
Runtime.NodeContext != null ? Runtime.NodeContext.RunSeed : 0,
|
||||||
|
Runtime.NodeContext != null ? Runtime.NodeContext.SequenceIndex : -1,
|
||||||
|
ref nextRewardOrdinal,
|
||||||
Runtime.RewardSelectFormUseCase,
|
Runtime.RewardSelectFormUseCase,
|
||||||
Coordinator.OnFullBaseHpRewardSelected,
|
Coordinator.OnFullBaseHpRewardSelected,
|
||||||
Coordinator.OnFullBaseHpRewardGiveUp))
|
Coordinator.OnFullBaseHpRewardGiveUp))
|
||||||
{
|
{
|
||||||
Coordinator.ChangeState(new CombatFinishFormState(Runtime, Coordinator));
|
Coordinator.ChangeState(new CombatFinishFormState(Runtime, Coordinator));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Runtime.NextRewardOrdinal = nextRewardOrdinal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnExit()
|
public override void OnExit()
|
||||||
|
|||||||
+5
-4
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GeometryTD.CustomEvent;
|
using GeometryTD.CustomEvent;
|
||||||
using GeometryTD.DataTable;
|
using GeometryTD.DataTable;
|
||||||
|
using GeometryTD.Procedure;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.CustomComponent
|
namespace GeometryTD.CustomComponent
|
||||||
@@ -41,10 +42,10 @@ namespace GeometryTD.CustomComponent
|
|||||||
GameEntry.Event.Fire(
|
GameEntry.Event.Fire(
|
||||||
Coordinator,
|
Coordinator,
|
||||||
NodeEnterEventArgs.Create(
|
NodeEnterEventArgs.Create(
|
||||||
Runtime.RunId,
|
Runtime.NodeContext?.RunId,
|
||||||
Runtime.NodeId,
|
Runtime.NodeContext?.NodeId ?? 0,
|
||||||
Runtime.NodeType,
|
Runtime.NodeContext?.NodeType ?? RunNodeType.None,
|
||||||
Runtime.SequenceIndex));
|
Runtime.NodeContext?.SequenceIndex ?? -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info(
|
Log.Info(
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using GeometryTD.DataTable;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public sealed class CombatSettlementCalculator
|
||||||
|
{
|
||||||
|
private const float FullBaseHpGoldBonusRate = 0.3f;
|
||||||
|
private const float HighBaseHpGoldBonusRate = 0.1f;
|
||||||
|
private const float HighBaseHpThreshold = 0.8f;
|
||||||
|
private const float SettlementTowerEnduranceLoss = 1f;
|
||||||
|
|
||||||
|
public CombatSettlementContext BuildSettlementContext(
|
||||||
|
bool didCombatWin,
|
||||||
|
DRLevel currentLevel,
|
||||||
|
int defeatedEnemyCount,
|
||||||
|
CombatRunResourceStore resourceStore)
|
||||||
|
{
|
||||||
|
bool shouldOpenFullBaseHpRewardSelect = false;
|
||||||
|
ResolveSettlementByBaseHp(
|
||||||
|
didCombatWin,
|
||||||
|
currentLevel,
|
||||||
|
resourceStore,
|
||||||
|
out int currentBaseHp,
|
||||||
|
out int maxBaseHp,
|
||||||
|
out int levelRewardGold,
|
||||||
|
out float bonusRate,
|
||||||
|
out int bonusGold,
|
||||||
|
out shouldOpenFullBaseHpRewardSelect);
|
||||||
|
|
||||||
|
CombatSettlementContext settlementContext = new CombatSettlementContext();
|
||||||
|
settlementContext.Result.DidCombatWin = didCombatWin;
|
||||||
|
settlementContext.Result.FinalCoin = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentCoin : 0);
|
||||||
|
settlementContext.Result.FinalBaseHp = currentBaseHp;
|
||||||
|
settlementContext.Result.MaxBaseHp = maxBaseHp;
|
||||||
|
settlementContext.Result.DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
|
||||||
|
settlementContext.Result.GainedGold = Mathf.Max(0, resourceStore != null ? resourceStore.GainedGold : 0);
|
||||||
|
settlementContext.Result.RewardInventory = resourceStore != null
|
||||||
|
? resourceStore.GetRewardInventorySnapshot()
|
||||||
|
: new BackpackInventoryData();
|
||||||
|
PopulateEnduranceSettlement(settlementContext, resourceStore);
|
||||||
|
settlementContext.Flags.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect;
|
||||||
|
settlementContext.Flags.DidEnterRewardSelection = false;
|
||||||
|
settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount;
|
||||||
|
settlementContext.Summary.GainedGold = settlementContext.Result.GainedGold;
|
||||||
|
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||||
|
|
||||||
|
Log.Info(
|
||||||
|
"Combat settlement resolved. Level={0}, BaseHp={1}/{2}, LevelReward={3}, BonusRate={4:P0}, BonusGold={5}, FullHpRewardSelect={6}, EnduranceTargets={7}.",
|
||||||
|
currentLevel != null ? currentLevel.Id : 0,
|
||||||
|
currentBaseHp,
|
||||||
|
maxBaseHp,
|
||||||
|
levelRewardGold,
|
||||||
|
bonusRate,
|
||||||
|
bonusGold,
|
||||||
|
shouldOpenFullBaseHpRewardSelect,
|
||||||
|
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count);
|
||||||
|
return settlementContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ResolveSettlementByBaseHp(
|
||||||
|
bool didCombatWin,
|
||||||
|
DRLevel currentLevel,
|
||||||
|
CombatRunResourceStore resourceStore,
|
||||||
|
out int currentBaseHp,
|
||||||
|
out int maxBaseHp,
|
||||||
|
out int levelRewardGold,
|
||||||
|
out float bonusRate,
|
||||||
|
out int bonusGold,
|
||||||
|
out bool shouldOpenFullBaseHpRewardSelect)
|
||||||
|
{
|
||||||
|
currentBaseHp = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentBaseHp : 0);
|
||||||
|
maxBaseHp = resourceStore != null ? Mathf.Max(0, resourceStore.MaxBaseHp) : 0;
|
||||||
|
if (maxBaseHp > 0)
|
||||||
|
{
|
||||||
|
currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp);
|
||||||
|
}
|
||||||
|
|
||||||
|
levelRewardGold = currentLevel != null ? Mathf.Max(0, currentLevel.RewardGold) : 0;
|
||||||
|
bonusRate = 0f;
|
||||||
|
bonusGold = 0;
|
||||||
|
shouldOpenFullBaseHpRewardSelect = false;
|
||||||
|
|
||||||
|
if (!didCombatWin || resourceStore == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxBaseHp > 0 && currentBaseHp >= maxBaseHp)
|
||||||
|
{
|
||||||
|
bonusRate = FullBaseHpGoldBonusRate;
|
||||||
|
shouldOpenFullBaseHpRewardSelect = true;
|
||||||
|
}
|
||||||
|
else if (maxBaseHp > 0)
|
||||||
|
{
|
||||||
|
float hpRate = (float)currentBaseHp / maxBaseHp;
|
||||||
|
if (hpRate >= HighBaseHpThreshold)
|
||||||
|
{
|
||||||
|
bonusRate = HighBaseHpGoldBonusRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int goldForBonusCalculation = Mathf.Max(0, resourceStore.GainedGold) + levelRewardGold;
|
||||||
|
bonusGold = bonusRate > 0f ? Mathf.FloorToInt(goldForBonusCalculation * bonusRate) : 0;
|
||||||
|
int settlementGold = levelRewardGold + bonusGold;
|
||||||
|
resourceStore.AddSettlementGold(settlementGold);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PopulateEnduranceSettlement(
|
||||||
|
CombatSettlementContext settlementContext,
|
||||||
|
CombatRunResourceStore resourceStore)
|
||||||
|
{
|
||||||
|
if (settlementContext == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CombatSettlementEnduranceResult enduranceResult = settlementContext.Result.Endurance;
|
||||||
|
enduranceResult.TargetTowerInstanceIds.Clear();
|
||||||
|
enduranceResult.EnduranceLossPerComponent = SettlementTowerEnduranceLoss;
|
||||||
|
|
||||||
|
IReadOnlyList<long> participantTowerIds = resourceStore?.GetParticipantTowerInstanceIdSnapshot();
|
||||||
|
if (participantTowerIds == null || participantTowerIds.Count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < participantTowerIds.Count; i++)
|
||||||
|
{
|
||||||
|
long towerId = participantTowerIds[i];
|
||||||
|
if (towerId > 0)
|
||||||
|
{
|
||||||
|
enduranceResult.TargetTowerInstanceIds.Add(towerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c8aa78b2f9ff4eb4bf83b8c4f652ce24
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
using GeometryTD.Definition;
|
||||||
|
using GeometryTD.UI;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public sealed class CombatSettlementCommitter
|
||||||
|
{
|
||||||
|
public void CommitSettlementInventory(CombatSettlementContext settlementContext)
|
||||||
|
{
|
||||||
|
if (settlementContext == null || settlementContext.Flags.IsCommitted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackpackInventoryData rewardInventory = settlementContext.Result.RewardInventory ?? new BackpackInventoryData();
|
||||||
|
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
||||||
|
settlementContext.Result.RewardInventory = rewardInventory;
|
||||||
|
settlementContext.Summary.RewardInventory = rewardInventory;
|
||||||
|
settlementContext.Result.Endurance.AffectedTowerCount = ApplyDeferredSettlementEndurance(settlementContext);
|
||||||
|
settlementContext.Flags.IsCommitted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplySelectedReward(CombatSettlementContext settlementContext, RewardSelectItemRawData selectedReward)
|
||||||
|
{
|
||||||
|
if (settlementContext?.Result.RewardInventory == null || selectedReward?.SourceItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryAppendRewardComponent(settlementContext.Result.RewardInventory, selectedReward.SourceItem);
|
||||||
|
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ApplyDeferredSettlementEndurance(CombatSettlementContext settlementContext)
|
||||||
|
{
|
||||||
|
if (settlementContext == null ||
|
||||||
|
settlementContext.Result.Endurance.EnduranceLossPerComponent <= 0f ||
|
||||||
|
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerInventoryComponent inventory = GameEntry.PlayerInventory;
|
||||||
|
if (inventory == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inventory.ReduceTowerEndurance(
|
||||||
|
settlementContext.Result.Endurance.TargetTowerInstanceIds,
|
||||||
|
settlementContext.Result.Endurance.EnduranceLossPerComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryAppendRewardComponent(BackpackInventoryData rewardInventory, TowerCompItemData selectedItem)
|
||||||
|
{
|
||||||
|
if (rewardInventory == null || selectedItem == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedItem is MuzzleCompItemData muzzleComp)
|
||||||
|
{
|
||||||
|
rewardInventory.MuzzleComponents.Add(muzzleComp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedItem is BearingCompItemData bearingComp)
|
||||||
|
{
|
||||||
|
rewardInventory.BearingComponents.Add(bearingComp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedItem is BaseCompItemData baseComp)
|
||||||
|
{
|
||||||
|
rewardInventory.BaseComponents.Add(baseComp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1e4e69cf76f34e5baec16562d765fb85
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"reference": "GUID:c53de6f5028489b4689382a86bfbd807"
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5304296ea426f4d4b8067b7b64a3ad48
|
||||||
|
AssemblyDefinitionReferenceImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GameFramework.DataTable;
|
using GameFramework.DataTable;
|
||||||
|
using GeometryTD.Core;
|
||||||
using GeometryTD.CustomEvent;
|
using GeometryTD.CustomEvent;
|
||||||
using GeometryTD.DataTable;
|
using GeometryTD.DataTable;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Definition;
|
||||||
@@ -14,10 +15,8 @@ namespace GeometryTD.CustomComponent
|
|||||||
{
|
{
|
||||||
public class EventNodeComponent : GameFrameworkComponent
|
public class EventNodeComponent : GameFrameworkComponent
|
||||||
{
|
{
|
||||||
private string _activeRunId;
|
private RunNodeExecutionContext _activeContext;
|
||||||
private int _activeNodeId;
|
private EventItem _activeEvent;
|
||||||
private RunNodeType _activeNodeType = RunNodeType.None;
|
|
||||||
private int _activeSequenceIndex = -1;
|
|
||||||
|
|
||||||
private readonly List<EventItem> _eventItems = new List<EventItem>();
|
private readonly List<EventItem> _eventItems = new List<EventItem>();
|
||||||
|
|
||||||
@@ -54,7 +53,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
Log.Info("EventNodeComponent initialized with {0} events.", _eventItems.Count);
|
Log.Info("EventNodeComponent initialized with {0} events.", _eventItems.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartEvent(string runId = null, int nodeId = 0, RunNodeType nodeType = RunNodeType.None, int sequenceIndex = -1)
|
public void StartEvent(RunNodeExecutionContext context = null)
|
||||||
{
|
{
|
||||||
if (!_initialized)
|
if (!_initialized)
|
||||||
{
|
{
|
||||||
@@ -67,22 +66,29 @@ namespace GeometryTD.CustomComponent
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = Random.Range(0, _eventItems.Count);
|
|
||||||
EventItem randomEvent = _eventItems[index];
|
|
||||||
|
|
||||||
if (_eventFormUseCase == null)
|
if (_eventFormUseCase == null)
|
||||||
{
|
{
|
||||||
Log.Warning("EventNodeComponent StartEvent failed. Event form is not initialized.");
|
Log.Warning("EventNodeComponent StartEvent failed. Event form is not initialized.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_activeRunId = runId;
|
_activeContext = context != null ? context.Clone() : null;
|
||||||
_activeNodeId = nodeId;
|
_activeEvent = SelectActiveEvent(_activeContext);
|
||||||
_activeNodeType = nodeType;
|
if (_activeEvent == null)
|
||||||
_activeSequenceIndex = sequenceIndex;
|
{
|
||||||
_eventFormUseCase.SetCurrentEvent(randomEvent);
|
Log.Warning("EventNodeComponent StartEvent failed. No active event could be resolved.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventFormUseCase.BindEvent(_activeEvent, CreateEventOptionExecutionContext(_activeContext));
|
||||||
GameEntry.UIRouter.OpenUI(UIFormType.EventForm);
|
GameEntry.UIRouter.OpenUI(UIFormType.EventForm);
|
||||||
GameEntry.Event.Fire(this, NodeEnterEventArgs.Create(runId, nodeId, nodeType, sequenceIndex));
|
GameEntry.Event.Fire(
|
||||||
|
this,
|
||||||
|
NodeEnterEventArgs.Create(
|
||||||
|
_activeContext?.RunId,
|
||||||
|
_activeContext?.NodeId ?? 0,
|
||||||
|
_activeContext?.NodeType ?? RunNodeType.None,
|
||||||
|
_activeContext?.SequenceIndex ?? -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndEvent()
|
public void EndEvent()
|
||||||
@@ -91,22 +97,24 @@ namespace GeometryTD.CustomComponent
|
|||||||
GameEntry.Event.Fire(
|
GameEntry.Event.Fire(
|
||||||
this,
|
this,
|
||||||
NodeCompleteEventArgs.Create(
|
NodeCompleteEventArgs.Create(
|
||||||
_activeRunId,
|
_activeContext?.RunId,
|
||||||
_activeNodeId,
|
_activeContext?.NodeId ?? 0,
|
||||||
_activeNodeType,
|
_activeContext?.NodeType ?? RunNodeType.None,
|
||||||
_activeSequenceIndex,
|
_activeContext?.SequenceIndex ?? -1,
|
||||||
RunNodeCompletionStatus.Completed,
|
RunNodeCompletionStatus.Completed,
|
||||||
true,
|
true,
|
||||||
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null));
|
_activeContext != null
|
||||||
|
? _activeContext.CreateCompletionSnapshot(
|
||||||
|
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)
|
||||||
|
: null));
|
||||||
ClearActiveNodeContext();
|
ClearActiveNodeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearActiveNodeContext()
|
private void ClearActiveNodeContext()
|
||||||
{
|
{
|
||||||
_activeRunId = null;
|
_activeContext = null;
|
||||||
_activeNodeId = 0;
|
_activeEvent = null;
|
||||||
_activeNodeType = RunNodeType.None;
|
_eventFormUseCase?.Clear();
|
||||||
_activeSequenceIndex = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EventOption[] ParseOptions(string optionsRaw)
|
private static EventOption[] ParseOptions(string optionsRaw)
|
||||||
@@ -200,5 +208,41 @@ namespace GeometryTD.CustomComponent
|
|||||||
|
|
||||||
return effects.ToArray();
|
return effects.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EventItem SelectActiveEvent(RunNodeExecutionContext context)
|
||||||
|
{
|
||||||
|
if (_eventItems.Count <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
int randomIndex = Random.Range(0, _eventItems.Count);
|
||||||
|
return _eventItems[randomIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Random random = new System.Random(BuildSelectionSeed(context));
|
||||||
|
return _eventItems[random.Next(0, _eventItems.Count)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int BuildSelectionSeed(RunNodeExecutionContext context)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int seed = 17;
|
||||||
|
seed = seed * 31 + context.RunSeed;
|
||||||
|
seed = seed * 31 + context.SequenceIndex;
|
||||||
|
seed = seed * 31 + context.NodeId;
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EventOptionExecutionContext CreateEventOptionExecutionContext(RunNodeExecutionContext context)
|
||||||
|
{
|
||||||
|
return new EventOptionExecutionContext(
|
||||||
|
context?.RunSeed ?? 0,
|
||||||
|
context?.SequenceIndex ?? -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"reference": "GUID:e67bd2d1b65d48ce8120141996e55b2b"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bd7362132664f0c499e39663c24ade2d
|
||||||
|
AssemblyDefinitionReferenceImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"reference": "GUID:0f19320f1ba33834b896afc5a5aaae67"
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4c1cfd0d064e8b3418adeca47c20ae1d
|
||||||
|
AssemblyDefinitionReferenceImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -3,15 +3,24 @@ using GameFramework.DataTable;
|
|||||||
using GeometryTD.DataTable;
|
using GeometryTD.DataTable;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Definition;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Random = System.Random;
|
||||||
|
|
||||||
namespace GeometryTD.CustomComponent
|
namespace GeometryTD.CustomComponent
|
||||||
{
|
{
|
||||||
public sealed class DropPoolRoller
|
public sealed class DropPoolRoller
|
||||||
{
|
{
|
||||||
private const float RarityCurveScalePhase = 30f;
|
private const float RarityCurveScalePhase = 30f;
|
||||||
|
private static readonly RarityType[] OrderedRarities =
|
||||||
|
{
|
||||||
|
RarityType.White,
|
||||||
|
RarityType.Green,
|
||||||
|
RarityType.Blue,
|
||||||
|
RarityType.Purple,
|
||||||
|
RarityType.Red
|
||||||
|
};
|
||||||
|
|
||||||
private readonly List<DROutGameDropPool> _eligibleRowBuffer = new();
|
private readonly List<DROutGameDropPool> _eligibleRowBuffer = new();
|
||||||
private readonly Dictionary<RarityType, float> _rarityWeightBuffer = new();
|
private readonly float[] _rarityWeightBuffer = new float[OrderedRarities.Length];
|
||||||
private readonly IDataTable<DROutGameDropPool> _dropPoolTable;
|
private readonly IDataTable<DROutGameDropPool> _dropPoolTable;
|
||||||
|
|
||||||
public DropPoolRoller(IDataTable<DROutGameDropPool> dropPoolTable)
|
public DropPoolRoller(IDataTable<DROutGameDropPool> dropPoolTable)
|
||||||
@@ -19,9 +28,15 @@ namespace GeometryTD.CustomComponent
|
|||||||
_dropPoolTable = dropPoolTable;
|
_dropPoolTable = dropPoolTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRollRow(int displayPhaseIndex, LevelThemeType themeType, out DROutGameDropPool selectedRow)
|
public bool TryRollRow(
|
||||||
|
int displayPhaseIndex,
|
||||||
|
LevelThemeType themeType,
|
||||||
|
Random random,
|
||||||
|
out DROutGameDropPool selectedRow,
|
||||||
|
out RarityType selectedRarity)
|
||||||
{
|
{
|
||||||
selectedRow = null;
|
selectedRow = null;
|
||||||
|
selectedRarity = RarityType.None;
|
||||||
|
|
||||||
DROutGameDropPool[] allRows = _dropPoolTable.GetAllDataRows();
|
DROutGameDropPool[] allRows = _dropPoolTable.GetAllDataRows();
|
||||||
if (allRows == null || allRows.Length <= 0)
|
if (allRows == null || allRows.Length <= 0)
|
||||||
@@ -35,22 +50,29 @@ namespace GeometryTD.CustomComponent
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RarityType selectedRarity = RollRarity(displayPhaseIndex);
|
selectedRarity = RollRarity(displayPhaseIndex, random);
|
||||||
if (selectedRarity == RarityType.None)
|
if (selectedRarity == RarityType.None)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalWeight = 0;
|
int totalWeight = 0;
|
||||||
for (int i = 0; i < _eligibleRowBuffer.Count; i++)
|
DROutGameDropPool fallbackRow = null;
|
||||||
|
foreach (var row in _eligibleRowBuffer)
|
||||||
{
|
{
|
||||||
DROutGameDropPool row = _eligibleRowBuffer[i];
|
if (!IsEligibleAtPhase(row, selectedRarity, displayPhaseIndex))
|
||||||
if (row.Rarity != selectedRarity)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalWeight += Mathf.Max(1, row.Weight);
|
int rowWeight = row.GetWeight(selectedRarity);
|
||||||
|
if (rowWeight <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackRow = row;
|
||||||
|
totalWeight += rowWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalWeight <= 0)
|
if (totalWeight <= 0)
|
||||||
@@ -58,16 +80,22 @@ namespace GeometryTD.CustomComponent
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int randomWeight = Random.Range(1, totalWeight + 1);
|
int randomWeight = random.Next(1, totalWeight + 1);
|
||||||
int cumulativeWeight = 0;
|
int cumulativeWeight = 0;
|
||||||
foreach (var row in _eligibleRowBuffer)
|
foreach (var row in _eligibleRowBuffer)
|
||||||
{
|
{
|
||||||
if (row.Rarity != selectedRarity)
|
if (!IsEligibleAtPhase(row, selectedRarity, displayPhaseIndex))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cumulativeWeight += Mathf.Max(1, row.Weight);
|
int rowWeight = row.GetWeight(selectedRarity);
|
||||||
|
if (rowWeight <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cumulativeWeight += rowWeight;
|
||||||
if (randomWeight <= cumulativeWeight)
|
if (randomWeight <= cumulativeWeight)
|
||||||
{
|
{
|
||||||
selectedRow = row;
|
selectedRow = row;
|
||||||
@@ -75,7 +103,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedRow = _eligibleRowBuffer[_eligibleRowBuffer.Count - 1];
|
selectedRow = fallbackRow;
|
||||||
return selectedRow != null;
|
return selectedRow != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,9 +114,8 @@ namespace GeometryTD.CustomComponent
|
|||||||
{
|
{
|
||||||
_eligibleRowBuffer.Clear();
|
_eligibleRowBuffer.Clear();
|
||||||
|
|
||||||
for (int i = 0; i < allRows.Length; i++)
|
foreach (var row in allRows)
|
||||||
{
|
{
|
||||||
DROutGameDropPool row = allRows[i];
|
|
||||||
if (row == null)
|
if (row == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -99,7 +126,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displayPhaseIndex < row.MinPhase || displayPhaseIndex > row.MaxPhase)
|
if (!IsEligibleAtPhase(row, displayPhaseIndex))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -108,35 +135,45 @@ namespace GeometryTD.CustomComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RarityType RollRarity(int displayPhaseIndex)
|
private RarityType RollRarity(int displayPhaseIndex, Random random)
|
||||||
{
|
{
|
||||||
_rarityWeightBuffer.Clear();
|
for (int i = 0; i < _rarityWeightBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
_rarityWeightBuffer[i] = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
float phaseT = Mathf.Clamp01((displayPhaseIndex - 1) / RarityCurveScalePhase);
|
float phaseT = Mathf.Clamp01((displayPhaseIndex - 1) / RarityCurveScalePhase);
|
||||||
|
|
||||||
for (int i = 0; i < _eligibleRowBuffer.Count; i++)
|
foreach (var row in _eligibleRowBuffer)
|
||||||
{
|
{
|
||||||
DROutGameDropPool row = _eligibleRowBuffer[i];
|
for (int rarityIndex = 0; rarityIndex < OrderedRarities.Length; rarityIndex++)
|
||||||
float curveWeight = GetRarityCurveWeight(row.Rarity, phaseT);
|
|
||||||
if (curveWeight <= 0f)
|
|
||||||
{
|
{
|
||||||
continue;
|
RarityType rarity = OrderedRarities[rarityIndex];
|
||||||
}
|
if (!IsEligibleAtPhase(row, rarity, displayPhaseIndex))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
float rowWeight = Mathf.Max(1, row.Weight) * curveWeight;
|
int rowWeight = row.GetWeight(rarity);
|
||||||
if (_rarityWeightBuffer.TryGetValue(row.Rarity, out float existingWeight))
|
if (rowWeight <= 0)
|
||||||
{
|
{
|
||||||
_rarityWeightBuffer[row.Rarity] = existingWeight + rowWeight;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
float curveWeight = GetRarityCurveWeight(rarity, phaseT);
|
||||||
_rarityWeightBuffer[row.Rarity] = rowWeight;
|
if (curveWeight <= 0f)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_rarityWeightBuffer[rarityIndex] += rowWeight * curveWeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float totalWeight = 0f;
|
float totalWeight = 0f;
|
||||||
foreach (var pair in _rarityWeightBuffer)
|
foreach (var weight in _rarityWeightBuffer)
|
||||||
{
|
{
|
||||||
totalWeight += Mathf.Max(0f, pair.Value);
|
totalWeight += Mathf.Max(0f, weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalWeight <= 0f)
|
if (totalWeight <= 0f)
|
||||||
@@ -144,25 +181,54 @@ namespace GeometryTD.CustomComponent
|
|||||||
return RarityType.None;
|
return RarityType.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
float randomWeight = Random.value * totalWeight;
|
float randomWeight = (float)(random.NextDouble() * totalWeight);
|
||||||
float cumulativeWeight = 0f;
|
float cumulativeWeight = 0f;
|
||||||
foreach (var pair in _rarityWeightBuffer)
|
for (int rarityIndex = 0; rarityIndex < _rarityWeightBuffer.Length; rarityIndex++)
|
||||||
{
|
{
|
||||||
cumulativeWeight += Mathf.Max(0f, pair.Value);
|
cumulativeWeight += Mathf.Max(0f, _rarityWeightBuffer[rarityIndex]);
|
||||||
if (randomWeight <= cumulativeWeight)
|
if (randomWeight <= cumulativeWeight)
|
||||||
{
|
{
|
||||||
return pair.Key;
|
return OrderedRarities[rarityIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var pair in _rarityWeightBuffer)
|
for (int rarityIndex = 0; rarityIndex < _rarityWeightBuffer.Length; rarityIndex++)
|
||||||
{
|
{
|
||||||
return pair.Key;
|
if (_rarityWeightBuffer[rarityIndex] > 0f)
|
||||||
|
{
|
||||||
|
return OrderedRarities[rarityIndex];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RarityType.None;
|
return RarityType.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsEligibleAtPhase(DROutGameDropPool row, int displayPhaseIndex)
|
||||||
|
{
|
||||||
|
for (int rarityIndex = 0; rarityIndex < OrderedRarities.Length; rarityIndex++)
|
||||||
|
{
|
||||||
|
RarityType rarity = OrderedRarities[rarityIndex];
|
||||||
|
if (IsEligibleAtPhase(row, rarity, displayPhaseIndex))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsEligibleAtPhase(DROutGameDropPool row, RarityType rarity, int displayPhaseIndex)
|
||||||
|
{
|
||||||
|
if (row.GetWeight(rarity) <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minPhase = row.GetMinPhase(rarity);
|
||||||
|
int maxPhase = row.GetMaxPhase(rarity);
|
||||||
|
return displayPhaseIndex >= minPhase && displayPhaseIndex <= maxPhase;
|
||||||
|
}
|
||||||
|
|
||||||
private static float GetRarityCurveWeight(RarityType rarityType, float phaseT)
|
private static float GetRarityCurveWeight(RarityType rarityType, float phaseT)
|
||||||
{
|
{
|
||||||
float hump = Mathf.Exp(-Mathf.Pow((phaseT - 0.35f) / 0.28f, 2f));
|
float hump = Mathf.Exp(-Mathf.Pow((phaseT - 0.35f) / 0.28f, 2f));
|
||||||
@@ -183,4 +249,4 @@ namespace GeometryTD.CustomComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"reference": "GUID:c53de6f5028489b4689382a86bfbd807"
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6bc6ae941e9ae4c43a4f75e3405d0eaf
|
||||||
|
AssemblyDefinitionReferenceImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+245
-136
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GameFramework.DataTable;
|
using GameFramework.DataTable;
|
||||||
using GeometryTD.DataTable;
|
using GeometryTD.DataTable;
|
||||||
@@ -7,22 +6,12 @@ using GeometryTD.Factory;
|
|||||||
using GeometryTD.UI;
|
using GeometryTD.UI;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
using Random = UnityEngine.Random;
|
using Random = System.Random;
|
||||||
|
|
||||||
namespace GeometryTD.CustomComponent
|
namespace GeometryTD.CustomComponent
|
||||||
{
|
{
|
||||||
public sealed class InventoryGenerationComponent : GameFrameworkComponent
|
public sealed class InventoryGenerationComponent : GameFrameworkComponent
|
||||||
{
|
{
|
||||||
private const float DropChanceBase = 0.05f;
|
|
||||||
private const float DropChancePerPhase = 0.2f;
|
|
||||||
private const float DropChanceCap = 0.2f;
|
|
||||||
|
|
||||||
private long _nextTempInstanceId = 1000000;
|
|
||||||
private int _runSeed;
|
|
||||||
private int _nodeSequenceIndex = -1;
|
|
||||||
private int _nextDropTagOrdinal;
|
|
||||||
private int _nextRewardTagOrdinal;
|
|
||||||
|
|
||||||
private readonly List<DRShopPrice> _shopPriceRows = new();
|
private readonly List<DRShopPrice> _shopPriceRows = new();
|
||||||
private IDataTable<DRShopPrice> _shopPriceTable;
|
private IDataTable<DRShopPrice> _shopPriceTable;
|
||||||
private IDataTable<DROutGameDropPool> _dropPoolTable;
|
private IDataTable<DROutGameDropPool> _dropPoolTable;
|
||||||
@@ -32,22 +21,19 @@ namespace GeometryTD.CustomComponent
|
|||||||
private ShopGoodsBuilder _shopGoodsBuilder;
|
private ShopGoodsBuilder _shopGoodsBuilder;
|
||||||
private DropPoolRoller _dropPoolRoller;
|
private DropPoolRoller _dropPoolRoller;
|
||||||
private RewardCandidateBuilder _rewardCandidateBuilder;
|
private RewardCandidateBuilder _rewardCandidateBuilder;
|
||||||
|
private OutGameDropItemBuilder _outGameDropItemBuilder;
|
||||||
public void ConfigureRunContext(int runSeed, int sequenceIndex)
|
|
||||||
{
|
|
||||||
_runSeed = runSeed;
|
|
||||||
_nodeSequenceIndex = sequenceIndex;
|
|
||||||
_nextDropTagOrdinal = 0;
|
|
||||||
_nextRewardTagOrdinal = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<GoodsItemRawData> BuildShopGoods(int goodsCount, int runSeed = 0, int sequenceIndex = -1)
|
public List<GoodsItemRawData> BuildShopGoods(int goodsCount, int runSeed = 0, int sequenceIndex = -1)
|
||||||
{
|
{
|
||||||
EnsureShopBuilder();
|
EnsureShopBuilder();
|
||||||
return _shopGoodsBuilder.BuildGoods(goodsCount, runSeed, sequenceIndex, AllocateTempInstanceId);
|
return _shopGoodsBuilder.BuildGoods(goodsCount, runSeed, sequenceIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnemyDropResult ResolveEnemyDrop(in EnemyDropContext context)
|
public EnemyDropResult ResolveEnemyDrop(
|
||||||
|
in EnemyDropContext context,
|
||||||
|
int runSeed,
|
||||||
|
int sequenceIndex,
|
||||||
|
ref int nextDropOrdinal)
|
||||||
{
|
{
|
||||||
DREnemy enemy = context.Enemy;
|
DREnemy enemy = context.Enemy;
|
||||||
if (enemy == null)
|
if (enemy == null)
|
||||||
@@ -61,14 +47,25 @@ namespace GeometryTD.CustomComponent
|
|||||||
? Mathf.Clamp01(enemy.DropPercent * 0.01f)
|
? Mathf.Clamp01(enemy.DropPercent * 0.01f)
|
||||||
: Mathf.Clamp01(enemy.DropPercent);
|
: Mathf.Clamp01(enemy.DropPercent);
|
||||||
|
|
||||||
if (enemy.DropGold > 0 && dropRate > 0f && Random.value <= dropRate)
|
int dropOrdinal = nextDropOrdinal;
|
||||||
|
nextDropOrdinal++;
|
||||||
|
InventoryGenerationRandomContext randomContext =
|
||||||
|
new(runSeed, sequenceIndex, InventoryTagSourceType.Drop, dropOrdinal);
|
||||||
|
Random random = randomContext.CreateRandom();
|
||||||
|
|
||||||
|
if (enemy.DropGold > 0 && dropRate > 0f && random.NextDouble() <= dropRate)
|
||||||
{
|
{
|
||||||
gold = Mathf.Max(0, enemy.DropGold);
|
gold = Mathf.Max(0, enemy.DropGold);
|
||||||
}
|
}
|
||||||
|
|
||||||
TowerCompItemData lootItem = null;
|
TowerCompItemData lootItem = null;
|
||||||
if (ShouldRollOutGameItem(context.DisplayPhaseIndex) &&
|
if (OutGameDropRuleService.ShouldRollOutGameItem(context.DisplayPhaseIndex, random) &&
|
||||||
TryRollOutGameItem(context.DisplayPhaseIndex, context.ThemeType, out TowerCompItemData droppedItem))
|
TryRollOutGameItem(
|
||||||
|
context.DisplayPhaseIndex,
|
||||||
|
context.ThemeType,
|
||||||
|
randomContext,
|
||||||
|
random,
|
||||||
|
out TowerCompItemData droppedItem))
|
||||||
{
|
{
|
||||||
lootItem = droppedItem;
|
lootItem = droppedItem;
|
||||||
}
|
}
|
||||||
@@ -79,28 +76,104 @@ namespace GeometryTD.CustomComponent
|
|||||||
public IReadOnlyList<TowerCompItemData> BuildRewardCandidates(
|
public IReadOnlyList<TowerCompItemData> BuildRewardCandidates(
|
||||||
int displayPhaseIndex,
|
int displayPhaseIndex,
|
||||||
LevelThemeType themeType,
|
LevelThemeType themeType,
|
||||||
int candidateCount)
|
int candidateCount,
|
||||||
|
int runSeed,
|
||||||
|
int sequenceIndex,
|
||||||
|
ref int nextRewardOrdinal)
|
||||||
{
|
{
|
||||||
RewardCandidateBuilder rewardCandidateBuilder = EnsureRewardCandidateBuilder();
|
RewardCandidateBuilder rewardCandidateBuilder = EnsureRewardCandidateBuilder();
|
||||||
return rewardCandidateBuilder.BuildCandidates(
|
int rewardOrdinal = nextRewardOrdinal;
|
||||||
|
IReadOnlyList<TowerCompItemData> candidates = rewardCandidateBuilder.BuildCandidates(
|
||||||
displayPhaseIndex,
|
displayPhaseIndex,
|
||||||
themeType,
|
themeType,
|
||||||
candidateCount,
|
candidateCount,
|
||||||
|
CreateNextRewardRandomContext,
|
||||||
BuildRewardCandidateItem);
|
BuildRewardCandidateItem);
|
||||||
|
nextRewardOrdinal = rewardOrdinal;
|
||||||
|
return candidates;
|
||||||
|
|
||||||
|
InventoryGenerationRandomContext CreateNextRewardRandomContext()
|
||||||
|
{
|
||||||
|
return new InventoryGenerationRandomContext(
|
||||||
|
runSeed,
|
||||||
|
sequenceIndex,
|
||||||
|
InventoryTagSourceType.Reward,
|
||||||
|
rewardOrdinal++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<TowerCompItemData> BuildEventRewardComponents(
|
||||||
|
int count,
|
||||||
|
RarityType rarity,
|
||||||
|
int runSeed,
|
||||||
|
int sequenceIndex,
|
||||||
|
int eventId,
|
||||||
|
int optionIndex,
|
||||||
|
int effectIndex)
|
||||||
|
{
|
||||||
|
return BuildEventRewardComponents(
|
||||||
|
count,
|
||||||
|
rarity,
|
||||||
|
rarity,
|
||||||
|
runSeed,
|
||||||
|
sequenceIndex,
|
||||||
|
eventId,
|
||||||
|
optionIndex,
|
||||||
|
effectIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<TowerCompItemData> BuildEventRewardComponents(
|
||||||
|
int count,
|
||||||
|
RarityType minRarity,
|
||||||
|
RarityType maxRarity,
|
||||||
|
int runSeed,
|
||||||
|
int sequenceIndex,
|
||||||
|
int eventId,
|
||||||
|
int optionIndex,
|
||||||
|
int effectIndex)
|
||||||
|
{
|
||||||
|
EnsureComponentTables();
|
||||||
|
|
||||||
|
int resolvedCount = Mathf.Max(0, count);
|
||||||
|
if (resolvedCount <= 0)
|
||||||
|
{
|
||||||
|
return System.Array.Empty<TowerCompItemData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
RarityType normalizedMinRarity = InventoryRarityRuleService.NormalizeComponentRarity(minRarity);
|
||||||
|
RarityType normalizedMaxRarity = InventoryRarityRuleService.NormalizeComponentRarity(maxRarity);
|
||||||
|
if (normalizedMinRarity > normalizedMaxRarity)
|
||||||
|
{
|
||||||
|
throw new System.InvalidOperationException(
|
||||||
|
$"Event reward rarity range is invalid: {normalizedMinRarity} > {normalizedMaxRarity}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TowerCompItemData> result = new List<TowerCompItemData>(resolvedCount);
|
||||||
|
for (int i = 0; i < resolvedCount; i++)
|
||||||
|
{
|
||||||
|
InventoryGenerationRandomContext randomContext = CreateEventRandomContext(
|
||||||
|
runSeed,
|
||||||
|
sequenceIndex,
|
||||||
|
eventId,
|
||||||
|
optionIndex,
|
||||||
|
effectIndex,
|
||||||
|
i);
|
||||||
|
Random random = randomContext.CreateRandom();
|
||||||
|
result.Add(BuildRandomEventComponentItem(normalizedMinRarity, normalizedMaxRarity, randomContext, random));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureShopTables()
|
private void EnsureShopTables()
|
||||||
{
|
{
|
||||||
_shopPriceTable ??= GameEntry.DataTable.GetDataTable<DRShopPrice>();
|
_shopPriceTable ??= GameEntry.DataTable.GetDataTable<DRShopPrice>();
|
||||||
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
EnsureComponentTables();
|
||||||
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
|
||||||
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
|
||||||
|
|
||||||
if (_shopPriceTable == null || _muzzleCompTable == null || _bearingCompTable == null ||
|
if (_shopPriceTable == null)
|
||||||
_baseCompTable == null)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new System.InvalidOperationException(
|
||||||
"InventoryGenerationComponent requires ShopPrice, MuzzleComp, BearingComp, and BaseComp data tables.");
|
"InventoryGenerationComponent requires ShopPrice data table.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_shopPriceRows.Count > 0)
|
if (_shopPriceRows.Count > 0)
|
||||||
@@ -111,7 +184,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
DRShopPrice[] rows = _shopPriceTable.GetAllDataRows();
|
DRShopPrice[] rows = _shopPriceTable.GetAllDataRows();
|
||||||
if (rows == null || rows.Length <= 0)
|
if (rows == null || rows.Length <= 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new System.InvalidOperationException(
|
||||||
"InventoryGenerationComponent requires at least one shop price row.");
|
"InventoryGenerationComponent requires at least one shop price row.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +198,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
|
|
||||||
if (_shopPriceRows.Count <= 0)
|
if (_shopPriceRows.Count <= 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("InventoryGenerationComponent requires non-null shop price rows.");
|
throw new System.InvalidOperationException("InventoryGenerationComponent requires non-null shop price rows.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,22 +212,15 @@ namespace GeometryTD.CustomComponent
|
|||||||
_baseCompTable);
|
_baseCompTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long AllocateTempInstanceId()
|
|
||||||
{
|
|
||||||
return _nextTempInstanceId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureDropTables()
|
private void EnsureDropTables()
|
||||||
{
|
{
|
||||||
_dropPoolTable ??= GameEntry.DataTable.GetDataTable<DROutGameDropPool>();
|
_dropPoolTable ??= GameEntry.DataTable.GetDataTable<DROutGameDropPool>();
|
||||||
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
EnsureComponentTables();
|
||||||
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
|
||||||
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
|
||||||
|
|
||||||
if (_dropPoolTable == null || _muzzleCompTable == null || _bearingCompTable == null || _baseCompTable == null)
|
if (_dropPoolTable == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new System.InvalidOperationException(
|
||||||
"InventoryGenerationComponent requires OutGameDropPool, MuzzleComp, BearingComp, and BaseComp data tables.");
|
"InventoryGenerationComponent requires OutGameDropPool data table.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,103 +237,45 @@ namespace GeometryTD.CustomComponent
|
|||||||
return _rewardCandidateBuilder;
|
return _rewardCandidateBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ShouldRollOutGameItem(int displayPhaseIndex)
|
private OutGameDropItemBuilder EnsureOutGameDropItemBuilder()
|
||||||
{
|
{
|
||||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
EnsureDropTables();
|
||||||
float dropChance = Mathf.Clamp(DropChanceBase + (phaseIndex - 1) * DropChancePerPhase, 0f, DropChanceCap);
|
_outGameDropItemBuilder ??= new OutGameDropItemBuilder(
|
||||||
return Random.value <= dropChance;
|
_muzzleCompTable,
|
||||||
|
_bearingCompTable,
|
||||||
|
_baseCompTable);
|
||||||
|
return _outGameDropItemBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryRollOutGameItem(int displayPhaseIndex, LevelThemeType themeType, out TowerCompItemData droppedItem)
|
private bool TryRollOutGameItem(
|
||||||
|
int displayPhaseIndex,
|
||||||
|
LevelThemeType themeType,
|
||||||
|
InventoryGenerationRandomContext randomContext,
|
||||||
|
Random random,
|
||||||
|
out TowerCompItemData droppedItem)
|
||||||
{
|
{
|
||||||
droppedItem = null;
|
droppedItem = null;
|
||||||
DropPoolRoller dropPoolRoller = EnsureDropPoolRoller();
|
DropPoolRoller dropPoolRoller = EnsureDropPoolRoller();
|
||||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||||
if (!dropPoolRoller.TryRollRow(phaseIndex, themeType, out DROutGameDropPool selectedRow) || selectedRow == null)
|
if (!dropPoolRoller.TryRollRow(
|
||||||
|
phaseIndex,
|
||||||
|
themeType,
|
||||||
|
random,
|
||||||
|
out DROutGameDropPool selectedRow,
|
||||||
|
out RarityType selectedRarity) || selectedRow == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryBuildDropItem(selectedRow, InventoryTagSourceType.Drop, AllocateDropTagOrdinal(), out droppedItem);
|
return EnsureOutGameDropItemBuilder().TryBuildItem(selectedRow, selectedRarity, randomContext, out droppedItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryBuildDropItem(
|
private TowerCompItemData BuildRewardCandidateItem(
|
||||||
DROutGameDropPool row,
|
DROutGameDropPool row,
|
||||||
InventoryTagSourceType sourceType,
|
RarityType rarity,
|
||||||
int localOrdinal,
|
InventoryGenerationRandomContext randomContext)
|
||||||
out TowerCompItemData droppedItem)
|
|
||||||
{
|
{
|
||||||
droppedItem = null;
|
if (!EnsureOutGameDropItemBuilder().TryBuildItem(row, rarity, randomContext, out TowerCompItemData droppedItem))
|
||||||
if (row.ItemId <= 0 || string.IsNullOrWhiteSpace(row.ItemType))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string itemType = row.ItemType.Trim();
|
|
||||||
if (itemType.Equals("MuzzleComp", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
DRMuzzleComp config = _muzzleCompTable.GetDataRow(row.ItemId);
|
|
||||||
if (config == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
droppedItem = ComponentItemFactory.CreateMuzzle(
|
|
||||||
config,
|
|
||||||
AllocateTempInstanceId(),
|
|
||||||
row.Rarity,
|
|
||||||
CreateRandomContext(sourceType, localOrdinal, config.Id));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemType.Equals("BearingComp", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
DRBearingComp config = _bearingCompTable.GetDataRow(row.ItemId);
|
|
||||||
if (config == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
droppedItem = ComponentItemFactory.CreateBearing(
|
|
||||||
config,
|
|
||||||
AllocateTempInstanceId(),
|
|
||||||
row.Rarity,
|
|
||||||
CreateRandomContext(sourceType, localOrdinal, config.Id));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemType.Equals("BaseComp", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
DRBaseComp config = _baseCompTable.GetDataRow(row.ItemId);
|
|
||||||
if (config == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
droppedItem = ComponentItemFactory.CreateBase(
|
|
||||||
config,
|
|
||||||
AllocateTempInstanceId(),
|
|
||||||
row.Rarity,
|
|
||||||
CreateRandomContext(sourceType, localOrdinal, config.Id));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int AllocateDropTagOrdinal()
|
|
||||||
{
|
|
||||||
return _nextDropTagOrdinal++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int AllocateRewardTagOrdinal()
|
|
||||||
{
|
|
||||||
return _nextRewardTagOrdinal++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TowerCompItemData BuildRewardCandidateItem(DROutGameDropPool row)
|
|
||||||
{
|
|
||||||
if (!TryBuildDropItem(row, InventoryTagSourceType.Reward, AllocateRewardTagOrdinal(), out TowerCompItemData droppedItem))
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -275,16 +283,117 @@ namespace GeometryTD.CustomComponent
|
|||||||
return droppedItem;
|
return droppedItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InventoryTagRandomContext CreateRandomContext(
|
private void EnsureComponentTables()
|
||||||
InventoryTagSourceType sourceType,
|
|
||||||
int localOrdinal,
|
|
||||||
int configId)
|
|
||||||
{
|
{
|
||||||
return sourceType switch
|
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
||||||
|
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
||||||
|
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
||||||
|
|
||||||
|
if (_muzzleCompTable == null || _bearingCompTable == null || _baseCompTable == null)
|
||||||
{
|
{
|
||||||
InventoryTagSourceType.Reward => InventoryTagRandomContext.CreateReward(_runSeed, _nodeSequenceIndex, localOrdinal, configId),
|
throw new System.InvalidOperationException(
|
||||||
_ => InventoryTagRandomContext.CreateDrop(_runSeed, _nodeSequenceIndex, localOrdinal, configId)
|
"InventoryGenerationComponent requires MuzzleComp, BearingComp, and BaseComp data tables.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TowerCompItemData BuildRandomEventComponentItem(
|
||||||
|
RarityType minRarity,
|
||||||
|
RarityType maxRarity,
|
||||||
|
InventoryGenerationRandomContext randomContext,
|
||||||
|
Random random)
|
||||||
|
{
|
||||||
|
RarityType rarity = ResolveEventRewardRarity(minRarity, maxRarity, random);
|
||||||
|
int slotRoll = random.Next(0, 3);
|
||||||
|
return slotRoll switch
|
||||||
|
{
|
||||||
|
0 => BuildRandomEventMuzzleItem(rarity, randomContext, random),
|
||||||
|
1 => BuildRandomEventBearingItem(rarity, randomContext, random),
|
||||||
|
_ => BuildRandomEventBaseItem(rarity, randomContext, random)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static RarityType ResolveEventRewardRarity(
|
||||||
|
RarityType minRarity,
|
||||||
|
RarityType maxRarity,
|
||||||
|
Random random)
|
||||||
|
{
|
||||||
|
if (minRarity >= maxRarity)
|
||||||
|
{
|
||||||
|
return minRarity;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rarityValue = random.Next((int)minRarity, (int)maxRarity + 1);
|
||||||
|
return (RarityType)rarityValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MuzzleCompItemData BuildRandomEventMuzzleItem(
|
||||||
|
RarityType rarity,
|
||||||
|
InventoryGenerationRandomContext randomContext,
|
||||||
|
Random random)
|
||||||
|
{
|
||||||
|
DRMuzzleComp[] rows = _muzzleCompTable.GetAllDataRows();
|
||||||
|
DRMuzzleComp config = rows[random.Next(0, rows.Length)];
|
||||||
|
return ComponentItemFactory.CreateMuzzle(
|
||||||
|
config,
|
||||||
|
randomContext.CreateStableItemInstanceId(),
|
||||||
|
rarity,
|
||||||
|
randomContext.CreateTagRandomContext(config.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BearingCompItemData BuildRandomEventBearingItem(
|
||||||
|
RarityType rarity,
|
||||||
|
InventoryGenerationRandomContext randomContext,
|
||||||
|
Random random)
|
||||||
|
{
|
||||||
|
DRBearingComp[] rows = _bearingCompTable.GetAllDataRows();
|
||||||
|
DRBearingComp config = rows[random.Next(0, rows.Length)];
|
||||||
|
return ComponentItemFactory.CreateBearing(
|
||||||
|
config,
|
||||||
|
randomContext.CreateStableItemInstanceId(),
|
||||||
|
rarity,
|
||||||
|
randomContext.CreateTagRandomContext(config.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BaseCompItemData BuildRandomEventBaseItem(
|
||||||
|
RarityType rarity,
|
||||||
|
InventoryGenerationRandomContext randomContext,
|
||||||
|
Random random)
|
||||||
|
{
|
||||||
|
DRBaseComp[] rows = _baseCompTable.GetAllDataRows();
|
||||||
|
DRBaseComp config = rows[random.Next(0, rows.Length)];
|
||||||
|
return ComponentItemFactory.CreateBase(
|
||||||
|
config,
|
||||||
|
randomContext.CreateStableItemInstanceId(),
|
||||||
|
rarity,
|
||||||
|
randomContext.CreateTagRandomContext(config.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InventoryGenerationRandomContext CreateEventRandomContext(
|
||||||
|
int runSeed,
|
||||||
|
int sequenceIndex,
|
||||||
|
int eventId,
|
||||||
|
int optionIndex,
|
||||||
|
int effectIndex,
|
||||||
|
int itemIndex)
|
||||||
|
{
|
||||||
|
return new InventoryGenerationRandomContext(
|
||||||
|
runSeed,
|
||||||
|
sequenceIndex,
|
||||||
|
InventoryTagSourceType.Event,
|
||||||
|
BuildEventLocalOrdinal(eventId, optionIndex, effectIndex, itemIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int BuildEventLocalOrdinal(int eventId, int optionIndex, int effectIndex, int itemIndex)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int value = 17;
|
||||||
|
value = value * 31 + eventId;
|
||||||
|
value = value * 31 + optionIndex;
|
||||||
|
value = value * 31 + effectIndex;
|
||||||
|
value = value * 31 + itemIndex;
|
||||||
|
return value & int.MaxValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using Random = System.Random;
|
||||||
|
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public readonly struct InventoryGenerationRandomContext
|
||||||
|
{
|
||||||
|
public InventoryGenerationRandomContext(
|
||||||
|
int runSeed,
|
||||||
|
int nodeSequenceIndex,
|
||||||
|
InventoryTagSourceType sourceType,
|
||||||
|
int localOrdinal)
|
||||||
|
{
|
||||||
|
RunSeed = runSeed;
|
||||||
|
NodeSequenceIndex = nodeSequenceIndex;
|
||||||
|
SourceType = sourceType;
|
||||||
|
LocalOrdinal = localOrdinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RunSeed { get; }
|
||||||
|
|
||||||
|
public int NodeSequenceIndex { get; }
|
||||||
|
|
||||||
|
public InventoryTagSourceType SourceType { get; }
|
||||||
|
|
||||||
|
public int LocalOrdinal { get; }
|
||||||
|
|
||||||
|
public Random CreateRandom()
|
||||||
|
{
|
||||||
|
return new Random(BuildSeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
public long CreateStableItemInstanceId()
|
||||||
|
{
|
||||||
|
long normalizedSource = ((long)Math.Max(0, (int)SourceType) + 1L) << 48;
|
||||||
|
long normalizedSequence = ((long)Math.Max(0, NodeSequenceIndex) + 1L) << 24;
|
||||||
|
long normalizedOrdinal = (uint)(Math.Max(0, LocalOrdinal) + 1);
|
||||||
|
return normalizedSource | normalizedSequence | normalizedOrdinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InventoryTagRandomContext CreateTagRandomContext(int configId)
|
||||||
|
{
|
||||||
|
return SourceType switch
|
||||||
|
{
|
||||||
|
InventoryTagSourceType.Shop => InventoryTagRandomContext.CreateShop(RunSeed, NodeSequenceIndex, LocalOrdinal, configId),
|
||||||
|
InventoryTagSourceType.Reward => InventoryTagRandomContext.CreateReward(RunSeed, NodeSequenceIndex, LocalOrdinal, configId),
|
||||||
|
InventoryTagSourceType.Event => InventoryTagRandomContext.CreateEvent(RunSeed, NodeSequenceIndex, LocalOrdinal, configId),
|
||||||
|
_ => InventoryTagRandomContext.CreateDrop(RunSeed, NodeSequenceIndex, LocalOrdinal, configId)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private int BuildSeed()
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int seed = 17;
|
||||||
|
seed = seed * 31 + RunSeed;
|
||||||
|
seed = seed * 31 + NodeSequenceIndex;
|
||||||
|
seed = seed * 31 + (int)SourceType;
|
||||||
|
seed = seed * 31 + LocalOrdinal;
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 95b2ab672a7049ef9356a90d87baca50
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Random = System.Random;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public static class OutGameDropRuleService
|
||||||
|
{
|
||||||
|
private const float DropChanceBase = 0.05f;
|
||||||
|
private const float DropChancePerPhase = 0.2f;
|
||||||
|
private const float DropChanceCap = 0.2f;
|
||||||
|
|
||||||
|
public static bool ShouldRollOutGameItem(int displayPhaseIndex, Random random)
|
||||||
|
{
|
||||||
|
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||||
|
float dropChance = Mathf.Clamp(DropChanceBase + (phaseIndex - 1) * DropChancePerPhase, 0f, DropChanceCap);
|
||||||
|
return random.NextDouble() <= dropChance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 519549c7542f40efabf83a2b1b2dff44
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+28
-7
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using GeometryTD.DataTable;
|
using GeometryTD.DataTable;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Definition;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Random = System.Random;
|
||||||
|
|
||||||
namespace GeometryTD.CustomComponent
|
namespace GeometryTD.CustomComponent
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,8 @@ namespace GeometryTD.CustomComponent
|
|||||||
int displayPhaseIndex,
|
int displayPhaseIndex,
|
||||||
LevelThemeType themeType,
|
LevelThemeType themeType,
|
||||||
int candidateCount,
|
int candidateCount,
|
||||||
Func<DROutGameDropPool, TowerCompItemData> buildRewardItem)
|
Func<InventoryGenerationRandomContext> createRandomContext,
|
||||||
|
Func<DROutGameDropPool, RarityType, InventoryGenerationRandomContext, TowerCompItemData> buildRewardItem)
|
||||||
{
|
{
|
||||||
int resolvedCount = Mathf.Max(0, candidateCount);
|
int resolvedCount = Mathf.Max(0, candidateCount);
|
||||||
if (resolvedCount <= 0)
|
if (resolvedCount <= 0)
|
||||||
@@ -28,7 +30,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<TowerCompItemData> candidates = new List<TowerCompItemData>(resolvedCount);
|
List<TowerCompItemData> candidates = new List<TowerCompItemData>(resolvedCount);
|
||||||
HashSet<int> selectedPoolRowIds = new HashSet<int>();
|
HashSet<long> selectedPoolEntryKeys = new HashSet<long>();
|
||||||
int maxAttempts = Mathf.Max(resolvedCount * 6, resolvedCount);
|
int maxAttempts = Mathf.Max(resolvedCount * 6, resolvedCount);
|
||||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||||
|
|
||||||
@@ -36,17 +38,24 @@ namespace GeometryTD.CustomComponent
|
|||||||
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
||||||
{
|
{
|
||||||
attempts++;
|
attempts++;
|
||||||
if (!_dropPoolRoller.TryRollRow(phaseIndex, themeType, out DROutGameDropPool selectedRow) || selectedRow == null)
|
InventoryGenerationRandomContext randomContext = createRandomContext();
|
||||||
|
Random random = randomContext.CreateRandom();
|
||||||
|
if (!_dropPoolRoller.TryRollRow(
|
||||||
|
phaseIndex,
|
||||||
|
themeType,
|
||||||
|
random,
|
||||||
|
out DROutGameDropPool selectedRow,
|
||||||
|
out RarityType selectedRarity) || selectedRow == null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedPoolRowIds.Add(selectedRow.Id))
|
if (!selectedPoolEntryKeys.Add(BuildSelectionKey(selectedRow.Id, selectedRarity)))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TowerCompItemData candidate = buildRewardItem(selectedRow);
|
TowerCompItemData candidate = buildRewardItem(selectedRow, selectedRarity, randomContext);
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -59,12 +68,19 @@ namespace GeometryTD.CustomComponent
|
|||||||
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
||||||
{
|
{
|
||||||
attempts++;
|
attempts++;
|
||||||
if (!_dropPoolRoller.TryRollRow(phaseIndex, themeType, out DROutGameDropPool selectedRow) || selectedRow == null)
|
InventoryGenerationRandomContext randomContext = createRandomContext();
|
||||||
|
Random random = randomContext.CreateRandom();
|
||||||
|
if (!_dropPoolRoller.TryRollRow(
|
||||||
|
phaseIndex,
|
||||||
|
themeType,
|
||||||
|
random,
|
||||||
|
out DROutGameDropPool selectedRow,
|
||||||
|
out RarityType selectedRarity) || selectedRow == null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TowerCompItemData candidate = buildRewardItem(selectedRow);
|
TowerCompItemData candidate = buildRewardItem(selectedRow, selectedRarity, randomContext);
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -75,5 +91,10 @@ namespace GeometryTD.CustomComponent
|
|||||||
|
|
||||||
return candidates;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long BuildSelectionKey(int rowId, RarityType rarity)
|
||||||
|
{
|
||||||
|
return ((long)rowId << 32) | (uint)rarity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using GeometryTD.Definition;
|
|||||||
using GeometryTD.Factory;
|
using GeometryTD.Factory;
|
||||||
using GeometryTD.UI;
|
using GeometryTD.UI;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Random = System.Random;
|
||||||
|
|
||||||
namespace GeometryTD.CustomComponent
|
namespace GeometryTD.CustomComponent
|
||||||
{
|
{
|
||||||
@@ -32,8 +33,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
public List<GoodsItemRawData> BuildGoods(
|
public List<GoodsItemRawData> BuildGoods(
|
||||||
int goodsCount,
|
int goodsCount,
|
||||||
int runSeed,
|
int runSeed,
|
||||||
int sequenceIndex,
|
int sequenceIndex)
|
||||||
Func<long> allocateInstanceId)
|
|
||||||
{
|
{
|
||||||
if (goodsCount <= 0)
|
if (goodsCount <= 0)
|
||||||
{
|
{
|
||||||
@@ -43,7 +43,9 @@ namespace GeometryTD.CustomComponent
|
|||||||
List<GoodsItemRawData> goodsItems = new(goodsCount);
|
List<GoodsItemRawData> goodsItems = new(goodsCount);
|
||||||
for (int i = 0; i < goodsCount; i++)
|
for (int i = 0; i < goodsCount; i++)
|
||||||
{
|
{
|
||||||
goodsItems.Add(BuildGoodsItem(i, runSeed, sequenceIndex, allocateInstanceId));
|
InventoryGenerationRandomContext randomContext =
|
||||||
|
new(runSeed, sequenceIndex, InventoryTagSourceType.Shop, i);
|
||||||
|
goodsItems.Add(BuildGoodsItem(i, randomContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
return goodsItems;
|
return goodsItems;
|
||||||
@@ -51,18 +53,17 @@ namespace GeometryTD.CustomComponent
|
|||||||
|
|
||||||
private GoodsItemRawData BuildGoodsItem(
|
private GoodsItemRawData BuildGoodsItem(
|
||||||
int goodsIndex,
|
int goodsIndex,
|
||||||
int runSeed,
|
InventoryGenerationRandomContext randomContext)
|
||||||
int sequenceIndex,
|
|
||||||
Func<long> allocateInstanceId)
|
|
||||||
{
|
{
|
||||||
TowerCompItemData sourceItem = BuildRandomComponentItem(goodsIndex, runSeed, sequenceIndex, allocateInstanceId);
|
Random random = randomContext.CreateRandom();
|
||||||
|
TowerCompItemData sourceItem = BuildRandomComponentItem(randomContext, random);
|
||||||
return new GoodsItemRawData
|
return new GoodsItemRawData
|
||||||
{
|
{
|
||||||
GoodsIndex = goodsIndex,
|
GoodsIndex = goodsIndex,
|
||||||
Title = sourceItem.Name,
|
Title = sourceItem.Name,
|
||||||
TypeText = BuildTypeText(sourceItem.SlotType),
|
TypeText = BuildTypeText(sourceItem.SlotType),
|
||||||
Description = BuildDescription(sourceItem),
|
Description = BuildDescription(sourceItem),
|
||||||
Price = ResolveRandomPrice(sourceItem.Rarity),
|
Price = ResolveRandomPrice(sourceItem.Rarity, random),
|
||||||
Tags = sourceItem.Tags != null ? (TagType[])sourceItem.Tags.Clone() : Array.Empty<TagType>(),
|
Tags = sourceItem.Tags != null ? (TagType[])sourceItem.Tags.Clone() : Array.Empty<TagType>(),
|
||||||
IconAreaContext = BuildIconAreaContext(sourceItem),
|
IconAreaContext = BuildIconAreaContext(sourceItem),
|
||||||
SourceItem = sourceItem,
|
SourceItem = sourceItem,
|
||||||
@@ -71,83 +72,58 @@ namespace GeometryTD.CustomComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TowerCompItemData BuildRandomComponentItem(
|
private TowerCompItemData BuildRandomComponentItem(
|
||||||
int goodsIndex,
|
InventoryGenerationRandomContext randomContext,
|
||||||
int runSeed,
|
Random random)
|
||||||
int sequenceIndex,
|
|
||||||
Func<long> allocateInstanceId)
|
|
||||||
{
|
{
|
||||||
int slotRoll = UnityEngine.Random.Range(0, 3);
|
int slotRoll = random.Next(0, 3);
|
||||||
DRShopPrice priceRow = _shopPriceRows[UnityEngine.Random.Range(0, _shopPriceRows.Count)];
|
DRShopPrice priceRow = _shopPriceRows[random.Next(0, _shopPriceRows.Count)];
|
||||||
RarityType rarity = InventoryRarityRuleService.NormalizeComponentRarity(
|
RarityType rarity = InventoryRarityRuleService.NormalizeComponentRarity(
|
||||||
priceRow != null ? priceRow.Rarity : RarityType.White);
|
priceRow != null ? priceRow.Rarity : RarityType.White);
|
||||||
|
|
||||||
return slotRoll switch
|
return slotRoll switch
|
||||||
{
|
{
|
||||||
0 => BuildRandomMuzzleItem(rarity, goodsIndex, runSeed, sequenceIndex, allocateInstanceId),
|
0 => BuildRandomMuzzleItem(rarity, randomContext, random),
|
||||||
1 => BuildRandomBearingItem(rarity, goodsIndex, runSeed, sequenceIndex, allocateInstanceId),
|
1 => BuildRandomBearingItem(rarity, randomContext, random),
|
||||||
_ => BuildRandomBaseItem(rarity, goodsIndex, runSeed, sequenceIndex, allocateInstanceId)
|
_ => BuildRandomBaseItem(rarity, randomContext, random)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private MuzzleCompItemData BuildRandomMuzzleItem(
|
private MuzzleCompItemData BuildRandomMuzzleItem(
|
||||||
RarityType rarity,
|
RarityType rarity,
|
||||||
int goodsIndex,
|
InventoryGenerationRandomContext randomContext,
|
||||||
int runSeed,
|
Random random)
|
||||||
int sequenceIndex,
|
|
||||||
Func<long> allocateInstanceId)
|
|
||||||
{
|
{
|
||||||
DRMuzzleComp[] rows = _muzzleCompTable.GetAllDataRows();
|
DRMuzzleComp[] rows = _muzzleCompTable.GetAllDataRows();
|
||||||
DRMuzzleComp config = rows[UnityEngine.Random.Range(0, rows.Length)];
|
DRMuzzleComp config = rows[random.Next(0, rows.Length)];
|
||||||
long instanceId = allocateInstanceId();
|
long instanceId = randomContext.CreateStableItemInstanceId();
|
||||||
InventoryTagRandomContext randomContext =
|
return ComponentItemFactory.CreateMuzzle(config, instanceId, rarity, randomContext.CreateTagRandomContext(config.Id));
|
||||||
InventoryTagRandomContext.CreateShop(runSeed, sequenceIndex, goodsIndex, config.Id);
|
|
||||||
return ComponentItemFactory.CreateMuzzle(config, instanceId, rarity, randomContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BearingCompItemData BuildRandomBearingItem(
|
private BearingCompItemData BuildRandomBearingItem(
|
||||||
RarityType rarity,
|
RarityType rarity,
|
||||||
int goodsIndex,
|
InventoryGenerationRandomContext randomContext,
|
||||||
int runSeed,
|
Random random)
|
||||||
int sequenceIndex,
|
|
||||||
Func<long> allocateInstanceId)
|
|
||||||
{
|
{
|
||||||
DRBearingComp[] rows = _bearingCompTable.GetAllDataRows();
|
DRBearingComp[] rows = _bearingCompTable.GetAllDataRows();
|
||||||
DRBearingComp config = rows[UnityEngine.Random.Range(0, rows.Length)];
|
DRBearingComp config = rows[random.Next(0, rows.Length)];
|
||||||
long instanceId = allocateInstanceId();
|
long instanceId = randomContext.CreateStableItemInstanceId();
|
||||||
InventoryTagRandomContext randomContext =
|
return ComponentItemFactory.CreateBearing(config, instanceId, rarity, randomContext.CreateTagRandomContext(config.Id));
|
||||||
InventoryTagRandomContext.CreateShop(runSeed, sequenceIndex, goodsIndex, config.Id);
|
|
||||||
return ComponentItemFactory.CreateBearing(config, instanceId, rarity, randomContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BaseCompItemData BuildRandomBaseItem(
|
private BaseCompItemData BuildRandomBaseItem(
|
||||||
RarityType rarity,
|
RarityType rarity,
|
||||||
int goodsIndex,
|
InventoryGenerationRandomContext randomContext,
|
||||||
int runSeed,
|
Random random)
|
||||||
int sequenceIndex,
|
|
||||||
Func<long> allocateInstanceId)
|
|
||||||
{
|
{
|
||||||
DRBaseComp[] rows = _baseCompTable.GetAllDataRows();
|
DRBaseComp[] rows = _baseCompTable.GetAllDataRows();
|
||||||
DRBaseComp config = rows[UnityEngine.Random.Range(0, rows.Length)];
|
DRBaseComp config = rows[random.Next(0, rows.Length)];
|
||||||
long instanceId = allocateInstanceId();
|
long instanceId = randomContext.CreateStableItemInstanceId();
|
||||||
InventoryTagRandomContext randomContext =
|
return ComponentItemFactory.CreateBase(config, instanceId, rarity, randomContext.CreateTagRandomContext(config.Id));
|
||||||
InventoryTagRandomContext.CreateShop(runSeed, sequenceIndex, goodsIndex, config.Id);
|
|
||||||
return ComponentItemFactory.CreateBase(config, instanceId, rarity, randomContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ResolveRandomPrice(RarityType rarity)
|
private int ResolveRandomPrice(RarityType rarity, Random random)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _shopPriceRows.Count; i++)
|
return ShopPriceRuleService.ResolveRandomBuyPrice(rarity, random);
|
||||||
{
|
|
||||||
DRShopPrice row = _shopPriceRows[i];
|
|
||||||
if (row != null && row.Rarity == rarity)
|
|
||||||
{
|
|
||||||
int min = Mathf.Max(0, row.MinPrice);
|
|
||||||
int max = Mathf.Max(min, row.MaxPrice);
|
|
||||||
return UnityEngine.Random.Range(min, max + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IconAreaContext BuildIconAreaContext(TowerCompItemData item)
|
private static IconAreaContext BuildIconAreaContext(TowerCompItemData item)
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"reference": "GUID:c53de6f5028489b4689382a86bfbd807"
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 93d673219f2a1a94599e67456ea33f7f
|
||||||
|
AssemblyDefinitionReferenceImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -14,6 +14,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
private PlayerInventoryCommandModel _commandModel;
|
private PlayerInventoryCommandModel _commandModel;
|
||||||
private PlayerInventoryTowerRosterService _towerRosterService;
|
private PlayerInventoryTowerRosterService _towerRosterService;
|
||||||
private PlayerInventoryTowerAssemblyService _towerAssemblyService;
|
private PlayerInventoryTowerAssemblyService _towerAssemblyService;
|
||||||
|
private PlayerInventoryTradeService _tradeService;
|
||||||
|
|
||||||
public int Gold
|
public int Gold
|
||||||
{
|
{
|
||||||
@@ -100,6 +101,12 @@ namespace GeometryTD.CustomComponent
|
|||||||
return _commandModel.TryConsumeGold(costGold);
|
return _commandModel.TryConsumeGold(costGold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryPurchaseComponent(TowerCompItemData item, int price)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return _tradeService.TryPurchaseComponent(item, price);
|
||||||
|
}
|
||||||
|
|
||||||
public void AddGold(int gainGold)
|
public void AddGold(int gainGold)
|
||||||
{
|
{
|
||||||
EnsureInitialized();
|
EnsureInitialized();
|
||||||
@@ -138,6 +145,24 @@ namespace GeometryTD.CustomComponent
|
|||||||
return _towerRosterService.ReduceTowerEndurance(towerInstanceIds, enduranceLoss);
|
return _towerRosterService.ReduceTowerEndurance(towerInstanceIds, enduranceLoss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryDisassembleTower(long towerInstanceId)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return _towerAssemblyService.TryDisassembleTower(towerInstanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetSaleCandidate(long itemId, out PlayerInventorySaleCandidate candidate)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return _tradeService.TryGetSaleCandidate(itemId, out candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TrySellItems(IReadOnlyCollection<long> itemIds, out PlayerInventorySaleResult result)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return _tradeService.TrySellItems(itemIds, out result);
|
||||||
|
}
|
||||||
|
|
||||||
private void EnsureInitialized()
|
private void EnsureInitialized()
|
||||||
{
|
{
|
||||||
if (_queryModel.IsInitialized)
|
if (_queryModel.IsInitialized)
|
||||||
@@ -155,6 +180,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
_commandModel ??= new PlayerInventoryCommandModel(_state);
|
_commandModel ??= new PlayerInventoryCommandModel(_state);
|
||||||
_towerRosterService ??= new PlayerInventoryTowerRosterService(_queryModel, MaxParticipantTowerCount);
|
_towerRosterService ??= new PlayerInventoryTowerRosterService(_queryModel, MaxParticipantTowerCount);
|
||||||
_towerAssemblyService ??= new PlayerInventoryTowerAssemblyService(_queryModel, _commandModel);
|
_towerAssemblyService ??= new PlayerInventoryTowerAssemblyService(_queryModel, _commandModel);
|
||||||
|
_tradeService ??= new PlayerInventoryTradeService(_queryModel, _commandModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-2
@@ -123,7 +123,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
int[] values = new int[TowerLevelCount];
|
int[] values = new int[TowerLevelCount];
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
values[i] = baseValue + perLevel * i;
|
values[i] = Mathf.Max(0, baseValue + perLevel * i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
@@ -135,7 +135,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
float[] values = new float[TowerLevelCount];
|
float[] values = new float[TowerLevelCount];
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (int i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
values[i] = baseValue + perLevel * i;
|
values[i] = Mathf.Max(0f, baseValue + perLevel * i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
@@ -180,5 +180,37 @@ namespace GeometryTD.CustomComponent
|
|||||||
_drBaseComp ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
_drBaseComp ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
||||||
return _drBaseComp;
|
return _drBaseComp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryDisassembleTower(long towerInstanceId)
|
||||||
|
{
|
||||||
|
BackpackInventoryData inventory = _queryModel.Inventory;
|
||||||
|
if (towerInstanceId <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_queryModel.TryGetTowerById(towerInstanceId, out TowerItemData tower) || tower == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_queryModel.TryGetComponentById(inventory.MuzzleComponents, tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzleComp) ||
|
||||||
|
!_queryModel.TryGetComponentById(inventory.BearingComponents, tower.BearingComponentInstanceId, out BearingCompItemData bearingComp) ||
|
||||||
|
!_queryModel.TryGetComponentById(inventory.BaseComponents, tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryParticipantUtility.TryRemoveParticipantTower(inventory, towerInstanceId, MaxParticipantTowerCount);
|
||||||
|
|
||||||
|
muzzleComp.IsAssembledIntoTower = false;
|
||||||
|
bearingComp.IsAssembledIntoTower = false;
|
||||||
|
baseComp.IsAssembledIntoTower = false;
|
||||||
|
|
||||||
|
inventory.Towers.Remove(tower);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int MaxParticipantTowerCount = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-96
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using GeometryTD.Core;
|
||||||
using GeometryTD.CustomUtility;
|
using GeometryTD.CustomUtility;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Definition;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -36,101 +37,10 @@ namespace GeometryTD.CustomComponent
|
|||||||
|
|
||||||
public int ReduceTowerEndurance(IReadOnlyList<long> towerInstanceIds, float enduranceLoss)
|
public int ReduceTowerEndurance(IReadOnlyList<long> towerInstanceIds, float enduranceLoss)
|
||||||
{
|
{
|
||||||
float resolvedLoss = Mathf.Max(0f, enduranceLoss);
|
return InventoryTowerEnduranceUtility.ReduceTowerEndurance(
|
||||||
BackpackInventoryData inventory = _queryModel.Inventory;
|
_queryModel.Inventory,
|
||||||
if (resolvedLoss <= 0f ||
|
towerInstanceIds,
|
||||||
towerInstanceIds == null ||
|
enduranceLoss);
|
||||||
towerInstanceIds.Count <= 0 ||
|
|
||||||
inventory.Towers == null ||
|
|
||||||
inventory.Towers.Count <= 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<long, MuzzleCompItemData> muzzleMap = BuildComponentMap(inventory.MuzzleComponents);
|
|
||||||
Dictionary<long, BearingCompItemData> bearingMap = BuildComponentMap(inventory.BearingComponents);
|
|
||||||
Dictionary<long, BaseCompItemData> baseMap = BuildComponentMap(inventory.BaseComponents);
|
|
||||||
HashSet<long> processedTowerIds = new HashSet<long>();
|
|
||||||
|
|
||||||
int affectedCount = 0;
|
|
||||||
for (int i = 0; i < towerInstanceIds.Count; i++)
|
|
||||||
{
|
|
||||||
long towerInstanceId = towerInstanceIds[i];
|
|
||||||
if (towerInstanceId <= 0 || !processedTowerIds.Add(towerInstanceId))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!InventoryParticipantUtility.TryGetTowerById(inventory, towerInstanceId, out TowerItemData tower) ||
|
|
||||||
tower == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool towerAffected = false;
|
|
||||||
if (muzzleMap.TryGetValue(tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzleComp))
|
|
||||||
{
|
|
||||||
towerAffected |= TryReduceComponentEndurance(muzzleComp, resolvedLoss);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bearingMap.TryGetValue(tower.BearingComponentInstanceId, out BearingCompItemData bearingComp))
|
|
||||||
{
|
|
||||||
towerAffected |= TryReduceComponentEndurance(bearingComp, resolvedLoss);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseMap.TryGetValue(tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
|
|
||||||
{
|
|
||||||
towerAffected |= TryReduceComponentEndurance(baseComp, resolvedLoss);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (towerAffected)
|
|
||||||
{
|
|
||||||
affectedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return affectedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryReduceComponentEndurance(TowerCompItemData component, float enduranceLoss)
|
|
||||||
{
|
|
||||||
if (component == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
float originalEndurance = component.Endurance;
|
|
||||||
float nextEndurance = Mathf.Clamp(originalEndurance - Mathf.Max(0f, enduranceLoss), 0f, 100f);
|
|
||||||
if (nextEndurance >= originalEndurance)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
component.Endurance = nextEndurance;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<long, TComp> BuildComponentMap<TComp>(List<TComp> components)
|
|
||||||
where TComp : TowerCompItemData
|
|
||||||
{
|
|
||||||
Dictionary<long, TComp> map = new Dictionary<long, TComp>();
|
|
||||||
if (components == null || components.Count <= 0)
|
|
||||||
{
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < components.Count; i++)
|
|
||||||
{
|
|
||||||
TComp component = components[i];
|
|
||||||
if (component == null || component.InstanceId <= 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
map[component.InstanceId] = component;
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+389
@@ -0,0 +1,389 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using GeometryTD.CustomUtility;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public enum PlayerInventorySaleFailureReason : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
InvalidSelection = 1,
|
||||||
|
ItemNotFound = 2,
|
||||||
|
AssembledComponent = 3,
|
||||||
|
ParticipantTower = 4,
|
||||||
|
MissingTowerComponent = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
public long ItemId;
|
||||||
|
public bool IsSellable;
|
||||||
|
public bool IsTower;
|
||||||
|
public int Price;
|
||||||
|
public PlayerInventorySaleFailureReason FailureReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PlayerInventorySaleResult
|
||||||
|
{
|
||||||
|
public int GainedGold;
|
||||||
|
public int SoldComponentCount;
|
||||||
|
public int SoldTowerCount;
|
||||||
|
public PlayerInventorySaleFailureReason FailureReason;
|
||||||
|
|
||||||
|
public bool IsSuccess => FailureReason == PlayerInventorySaleFailureReason.None;
|
||||||
|
public int SoldItemCount => SoldComponentCount + SoldTowerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PlayerInventoryTradeService
|
||||||
|
{
|
||||||
|
private readonly PlayerInventoryQueryModel _queryModel;
|
||||||
|
private readonly PlayerInventoryCommandModel _commandModel;
|
||||||
|
|
||||||
|
public PlayerInventoryTradeService(
|
||||||
|
PlayerInventoryQueryModel queryModel,
|
||||||
|
PlayerInventoryCommandModel commandModel)
|
||||||
|
{
|
||||||
|
_queryModel = queryModel;
|
||||||
|
_commandModel = commandModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryPurchaseComponent(TowerCompItemData item, int price)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_commandModel.TryConsumeGold(price))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackpackInventoryData inventoryDelta = WrapSingleItem(item);
|
||||||
|
PlayerInventoryMergeSummary summary = _commandModel.MergeInventory(inventoryDelta);
|
||||||
|
return summary.HasAnyGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetSaleCandidate(long itemId, out PlayerInventorySaleCandidate candidate)
|
||||||
|
{
|
||||||
|
candidate = BuildSaleCandidate(itemId);
|
||||||
|
return candidate != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TrySellItems(IReadOnlyCollection<long> itemIds, out PlayerInventorySaleResult result)
|
||||||
|
{
|
||||||
|
result = new PlayerInventorySaleResult();
|
||||||
|
if (itemIds == null || itemIds.Count <= 0)
|
||||||
|
{
|
||||||
|
result.FailureReason = PlayerInventorySaleFailureReason.InvalidSelection;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<long> uniqueIds = new HashSet<long>();
|
||||||
|
List<PlayerInventorySaleCandidate> candidates = new List<PlayerInventorySaleCandidate>(itemIds.Count);
|
||||||
|
foreach (long itemId in itemIds)
|
||||||
|
{
|
||||||
|
if (itemId <= 0 || !uniqueIds.Add(itemId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerInventorySaleCandidate candidate = BuildSaleCandidate(itemId);
|
||||||
|
if (candidate == null || !candidate.IsSellable)
|
||||||
|
{
|
||||||
|
result.FailureReason = candidate?.FailureReason ?? PlayerInventorySaleFailureReason.ItemNotFound;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates.Add(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.Count <= 0)
|
||||||
|
{
|
||||||
|
result.FailureReason = PlayerInventorySaleFailureReason.InvalidSelection;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackpackInventoryData inventory = _queryModel.Inventory;
|
||||||
|
for (int i = 0; i < candidates.Count; i++)
|
||||||
|
{
|
||||||
|
PlayerInventorySaleCandidate candidate = candidates[i];
|
||||||
|
if (candidate.IsTower)
|
||||||
|
{
|
||||||
|
if (!TryRemoveTower(inventory, candidate.ItemId))
|
||||||
|
{
|
||||||
|
result.FailureReason = PlayerInventorySaleFailureReason.MissingTowerComponent;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.SoldTowerCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!TryRemoveComponent(inventory, candidate.ItemId))
|
||||||
|
{
|
||||||
|
result.FailureReason = PlayerInventorySaleFailureReason.ItemNotFound;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.SoldComponentCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.GainedGold += Mathf.Max(0, candidate.Price);
|
||||||
|
}
|
||||||
|
|
||||||
|
_commandModel.AddGold(result.GainedGold);
|
||||||
|
result.FailureReason = PlayerInventorySaleFailureReason.None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerInventorySaleCandidate BuildSaleCandidate(long itemId)
|
||||||
|
{
|
||||||
|
if (itemId <= 0)
|
||||||
|
{
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
IsSellable = false,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.InvalidSelection
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BackpackInventoryData inventory = _queryModel.Inventory;
|
||||||
|
if (inventory == null)
|
||||||
|
{
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
IsSellable = false,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.ItemNotFound
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_queryModel.TryGetTowerById(itemId, out TowerItemData tower) && tower != null)
|
||||||
|
{
|
||||||
|
if (inventory.ParticipantTowerInstanceIds != null && inventory.ParticipantTowerInstanceIds.Contains(itemId))
|
||||||
|
{
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
IsSellable = false,
|
||||||
|
IsTower = true,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.ParticipantTower
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ShopPriceRuleService.TryResolveTowerSalePrice(tower, inventory, out int towerPrice))
|
||||||
|
{
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
IsSellable = false,
|
||||||
|
IsTower = true,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.MissingTowerComponent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
IsSellable = true,
|
||||||
|
IsTower = true,
|
||||||
|
Price = towerPrice,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetComponentById(inventory.MuzzleComponents, itemId, out MuzzleCompItemData muzzleComp))
|
||||||
|
{
|
||||||
|
return BuildComponentCandidate(muzzleComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetComponentById(inventory.BearingComponents, itemId, out BearingCompItemData bearingComp))
|
||||||
|
{
|
||||||
|
return BuildComponentCandidate(bearingComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetComponentById(inventory.BaseComponents, itemId, out BaseCompItemData baseComp))
|
||||||
|
{
|
||||||
|
return BuildComponentCandidate(baseComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = itemId,
|
||||||
|
IsSellable = false,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.ItemNotFound
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerInventorySaleCandidate BuildComponentCandidate(TowerCompItemData component)
|
||||||
|
{
|
||||||
|
if (component == null)
|
||||||
|
{
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
IsSellable = false,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.ItemNotFound
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.IsAssembledIntoTower)
|
||||||
|
{
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = component.InstanceId,
|
||||||
|
IsSellable = false,
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.AssembledComponent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PlayerInventorySaleCandidate
|
||||||
|
{
|
||||||
|
ItemId = component.InstanceId,
|
||||||
|
IsSellable = true,
|
||||||
|
IsTower = false,
|
||||||
|
Price = ShopPriceRuleService.ResolveComponentSalePrice(component),
|
||||||
|
FailureReason = PlayerInventorySaleFailureReason.None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryRemoveTower(BackpackInventoryData inventory, long towerId)
|
||||||
|
{
|
||||||
|
if (inventory?.Towers == null || towerId <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TowerItemData targetTower = null;
|
||||||
|
for (int i = 0; i < inventory.Towers.Count; i++)
|
||||||
|
{
|
||||||
|
TowerItemData tower = inventory.Towers[i];
|
||||||
|
if (tower != null && tower.InstanceId == towerId)
|
||||||
|
{
|
||||||
|
targetTower = tower;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetTower == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ContainsInstanceId(inventory.MuzzleComponents, targetTower.MuzzleComponentInstanceId) ||
|
||||||
|
!ContainsInstanceId(inventory.BearingComponents, targetTower.BearingComponentInstanceId) ||
|
||||||
|
!ContainsInstanceId(inventory.BaseComponents, targetTower.BaseComponentInstanceId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removedMuzzle = RemoveByInstanceId(inventory.MuzzleComponents, targetTower.MuzzleComponentInstanceId);
|
||||||
|
bool removedBearing = RemoveByInstanceId(inventory.BearingComponents, targetTower.BearingComponentInstanceId);
|
||||||
|
bool removedBase = RemoveByInstanceId(inventory.BaseComponents, targetTower.BaseComponentInstanceId);
|
||||||
|
if (!removedMuzzle || !removedBearing || !removedBase)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory.Towers.Remove(targetTower);
|
||||||
|
inventory.ParticipantTowerInstanceIds?.Remove(towerId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryRemoveComponent(BackpackInventoryData inventory, long itemId)
|
||||||
|
{
|
||||||
|
return RemoveByInstanceId(inventory?.MuzzleComponents, itemId) ||
|
||||||
|
RemoveByInstanceId(inventory?.BearingComponents, itemId) ||
|
||||||
|
RemoveByInstanceId(inventory?.BaseComponents, itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool RemoveByInstanceId<TItem>(List<TItem> items, long instanceId)
|
||||||
|
where TItem : class
|
||||||
|
{
|
||||||
|
if (items == null || instanceId <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
switch (items[i])
|
||||||
|
{
|
||||||
|
case TowerCompItemData component when component.InstanceId == instanceId:
|
||||||
|
items.RemoveAt(i);
|
||||||
|
return true;
|
||||||
|
case TowerItemData tower when tower.InstanceId == instanceId:
|
||||||
|
items.RemoveAt(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsInstanceId<TItem>(IReadOnlyList<TItem> items, long instanceId)
|
||||||
|
where TItem : class
|
||||||
|
{
|
||||||
|
if (items == null || instanceId <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
switch (items[i])
|
||||||
|
{
|
||||||
|
case TowerCompItemData component when component.InstanceId == instanceId:
|
||||||
|
return true;
|
||||||
|
case TowerItemData tower when tower.InstanceId == instanceId:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetComponentById<TComp>(IReadOnlyList<TComp> items, long instanceId, out TComp result)
|
||||||
|
where TComp : TowerCompItemData
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
if (items == null || instanceId <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
TComp item = items[i];
|
||||||
|
if (item != null && item.InstanceId == instanceId)
|
||||||
|
{
|
||||||
|
result = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BackpackInventoryData WrapSingleItem(TowerCompItemData item)
|
||||||
|
{
|
||||||
|
BackpackInventoryData inventory = new BackpackInventoryData();
|
||||||
|
switch (item)
|
||||||
|
{
|
||||||
|
case MuzzleCompItemData muzzleComp:
|
||||||
|
inventory.MuzzleComponents.Add(InventoryCloneUtility.CloneMuzzleComp(muzzleComp));
|
||||||
|
break;
|
||||||
|
case BearingCompItemData bearingComp:
|
||||||
|
inventory.BearingComponents.Add(InventoryCloneUtility.CloneBearingComp(bearingComp));
|
||||||
|
break;
|
||||||
|
case BaseCompItemData baseComp:
|
||||||
|
inventory.BaseComponents.Add(InventoryCloneUtility.CloneBaseComp(baseComp));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 298cf9214556453cb5e7ec7c0605c350
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -8,11 +8,7 @@ namespace GeometryTD.CustomComponent
|
|||||||
{
|
{
|
||||||
public class ShopNodeComponent : GameFrameworkComponent
|
public class ShopNodeComponent : GameFrameworkComponent
|
||||||
{
|
{
|
||||||
private string _activeRunId;
|
private RunNodeExecutionContext _activeContext;
|
||||||
private int _activeRunSeed;
|
|
||||||
private int _activeNodeId;
|
|
||||||
private RunNodeType _activeNodeType = RunNodeType.None;
|
|
||||||
private int _activeSequenceIndex = -1;
|
|
||||||
|
|
||||||
private ShopFormUseCase _shopFormUseCase;
|
private ShopFormUseCase _shopFormUseCase;
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
@@ -29,26 +25,30 @@ namespace GeometryTD.CustomComponent
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartShop(string runId = null, int runSeed = 0, int nodeId = 0, RunNodeType nodeType = RunNodeType.None, int sequenceIndex = -1)
|
public void StartShop(RunNodeExecutionContext context = null)
|
||||||
{
|
{
|
||||||
if (!_initialized)
|
if (!_initialized)
|
||||||
{
|
{
|
||||||
OnInit();
|
OnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int runSeed = context?.RunSeed ?? 0;
|
||||||
|
int sequenceIndex = context?.SequenceIndex ?? -1;
|
||||||
if (_shopFormUseCase == null || !_shopFormUseCase.PrepareForOpen(runSeed, sequenceIndex))
|
if (_shopFormUseCase == null || !_shopFormUseCase.PrepareForOpen(runSeed, sequenceIndex))
|
||||||
{
|
{
|
||||||
Log.Warning("ShopNodeComponent.StartShop() failed. Shop use case is unavailable or goods generation failed.");
|
Log.Warning("ShopNodeComponent.StartShop() failed. Shop use case is unavailable or goods generation failed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_activeRunId = runId;
|
_activeContext = context != null ? context.Clone() : null;
|
||||||
_activeRunSeed = runSeed;
|
|
||||||
_activeNodeId = nodeId;
|
|
||||||
_activeNodeType = nodeType;
|
|
||||||
_activeSequenceIndex = sequenceIndex;
|
|
||||||
GameEntry.UIRouter.OpenUI(UIFormType.ShopForm);
|
GameEntry.UIRouter.OpenUI(UIFormType.ShopForm);
|
||||||
GameEntry.Event.Fire(this, NodeEnterEventArgs.Create(runId, nodeId, nodeType, sequenceIndex));
|
GameEntry.Event.Fire(
|
||||||
|
this,
|
||||||
|
NodeEnterEventArgs.Create(
|
||||||
|
_activeContext?.RunId,
|
||||||
|
_activeContext?.NodeId ?? 0,
|
||||||
|
_activeContext?.NodeType ?? RunNodeType.None,
|
||||||
|
_activeContext?.SequenceIndex ?? -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndShop()
|
public void EndShop()
|
||||||
@@ -57,23 +57,22 @@ namespace GeometryTD.CustomComponent
|
|||||||
GameEntry.Event.Fire(
|
GameEntry.Event.Fire(
|
||||||
this,
|
this,
|
||||||
NodeCompleteEventArgs.Create(
|
NodeCompleteEventArgs.Create(
|
||||||
_activeRunId,
|
_activeContext?.RunId,
|
||||||
_activeNodeId,
|
_activeContext?.NodeId ?? 0,
|
||||||
_activeNodeType,
|
_activeContext?.NodeType ?? RunNodeType.None,
|
||||||
_activeSequenceIndex,
|
_activeContext?.SequenceIndex ?? -1,
|
||||||
RunNodeCompletionStatus.Completed,
|
RunNodeCompletionStatus.Completed,
|
||||||
true,
|
true,
|
||||||
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null));
|
_activeContext != null
|
||||||
|
? _activeContext.CreateCompletionSnapshot(
|
||||||
|
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)
|
||||||
|
: null));
|
||||||
ClearActiveNodeContext();
|
ClearActiveNodeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearActiveNodeContext()
|
private void ClearActiveNodeContext()
|
||||||
{
|
{
|
||||||
_activeRunId = null;
|
_activeContext = null;
|
||||||
_activeRunSeed = 0;
|
|
||||||
_activeNodeId = 0;
|
|
||||||
_activeNodeType = RunNodeType.None;
|
|
||||||
_activeSequenceIndex = -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 523d63397a9c41fdaaf238bfb0e95357
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"reference": "GUID:c53de6f5028489b4689382a86bfbd807"
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e09026219ecab5e44ab54879792e2b26
|
||||||
|
AssemblyDefinitionReferenceImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using GameFramework.DataTable;
|
||||||
|
using GeometryTD.DataTable;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public sealed class TagRegistryComponent : GameFrameworkComponent
|
||||||
|
{
|
||||||
|
private IDataTable<DRTagConfig> _tagConfigTable;
|
||||||
|
private IDataTable<DRTag> _tagTable;
|
||||||
|
private IDataTable<DRRarityTagBudget> _rarityTagBudgetTable;
|
||||||
|
|
||||||
|
public void OnInit()
|
||||||
|
{
|
||||||
|
ReloadAllFromLoadedTables();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadAllFromLoadedTables()
|
||||||
|
{
|
||||||
|
ReloadTagDefinitionsAndGenerationFromLoadedTables();
|
||||||
|
ReloadRarityTagBudgetFromLoadedTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadTagDefinitionsAndGenerationFromLoadedTables()
|
||||||
|
{
|
||||||
|
EnsureTagDefinitionTables();
|
||||||
|
DRTagConfig[] tagConfigRows = _tagConfigTable.GetAllDataRows();
|
||||||
|
DRTag[] tagRows = _tagTable.GetAllDataRows();
|
||||||
|
|
||||||
|
TagGenerationRuleRegistry.LoadFromRows(tagRows);
|
||||||
|
TagDefinitionRegistry.ReloadFromRows(tagConfigRows, tagRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadRarityTagBudgetFromLoadedTable()
|
||||||
|
{
|
||||||
|
EnsureRarityTagBudgetTable();
|
||||||
|
RarityTagBudgetRuleRegistry.LoadFromRows(_rarityTagBudgetTable.GetAllDataRows());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureTagDefinitionTables()
|
||||||
|
{
|
||||||
|
_tagConfigTable ??= GameEntry.DataTable.GetDataTable<DRTagConfig>();
|
||||||
|
_tagTable ??= GameEntry.DataTable.GetDataTable<DRTag>();
|
||||||
|
|
||||||
|
if (_tagConfigTable == null || _tagTable == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"TagRegistryComponent requires TagConfig and Tag data tables to be loaded before initialization.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureRarityTagBudgetTable()
|
||||||
|
{
|
||||||
|
_rarityTagBudgetTable ??= GameEntry.DataTable.GetDataTable<DRRarityTagBudget>();
|
||||||
|
if (_rarityTagBudgetTable == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"TagRegistryComponent requires RarityTagBudget data table to be loaded before initialization.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a268c57d4373494ab9b4d7167099f170
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
//------------------------------------------------------------
|
using System;
|
||||||
// Game Framework
|
|
||||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
|
||||||
// Homepage: https://gameframework.cn/
|
|
||||||
// Feedback: mailto:ellan@gameframework.cn
|
|
||||||
//------------------------------------------------------------
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public static class BinaryReaderExtension
|
public static class BinaryReaderExtension
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.CustomUtility;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 底座组件表
|
/// 底座组件表
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.CustomUtility;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 轴承组件表
|
/// 轴承组件表
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 敌人配置表
|
/// 敌人配置表
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using System.Text;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 实体表。
|
/// 实体表。
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 事件配置表
|
/// 事件配置表
|
||||||
@@ -30,6 +31,14 @@ namespace GeometryTD.DataTable
|
|||||||
/// <remarks>原始字符串(如 JSON 文本),不在此处做解析。</remarks>
|
/// <remarks>原始字符串(如 JSON 文本),不在此处做解析。</remarks>
|
||||||
public string OptionsRaw { get; private set; }
|
public string OptionsRaw { get; private set; }
|
||||||
|
|
||||||
|
public string Option1Raw { get; private set; }
|
||||||
|
|
||||||
|
public string Option2Raw { get; private set; }
|
||||||
|
|
||||||
|
public string Option3Raw { get; private set; }
|
||||||
|
|
||||||
|
public string Option4Raw { get; private set; }
|
||||||
|
|
||||||
public override bool ParseDataRow(string dataRowString, object userData)
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
{
|
{
|
||||||
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||||
@@ -44,9 +53,30 @@ namespace GeometryTD.DataTable
|
|||||||
index++;
|
index++;
|
||||||
Title = columnStrings[index++];
|
Title = columnStrings[index++];
|
||||||
Description = columnStrings[index++];
|
Description = columnStrings[index++];
|
||||||
OptionsRaw = columnStrings[index++];
|
Option1Raw = columnStrings[index++];
|
||||||
|
Option2Raw = columnStrings[index++];
|
||||||
|
Option3Raw = columnStrings[index++];
|
||||||
|
Option4Raw = columnStrings[index++];
|
||||||
|
OptionsRaw = BuildOptionsRaw(Option1Raw, Option2Raw, Option3Raw, Option4Raw);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string BuildOptionsRaw(params string[] optionColumns)
|
||||||
|
{
|
||||||
|
JArray array = new JArray();
|
||||||
|
for (int i = 0; i < optionColumns.Length; i++)
|
||||||
|
{
|
||||||
|
string optionRaw = optionColumns[i];
|
||||||
|
if (string.IsNullOrWhiteSpace(optionRaw))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
array.Add(JObject.Parse(optionRaw));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array.ToString(Newtonsoft.Json.Formatting.None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.CustomUtility;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 关卡表
|
/// 关卡表
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.CustomUtility;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.Definition;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 关卡阶段表
|
/// 关卡阶段表
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.CustomUtility;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.Definition;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 关卡阶段出怪条目表
|
/// 关卡阶段出怪条目表
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using System.Text;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 音乐配置表。
|
/// 音乐配置表。
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.CustomUtility;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 枪口组件表
|
/// 枪口组件表
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using GeometryTD.CustomUtility;
|
using System;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Core;
|
||||||
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 局外道具掉落池配置表。
|
/// 局外道具掉落池配置表。
|
||||||
@@ -14,12 +15,11 @@ namespace GeometryTD.DataTable
|
|||||||
public override int Id => m_Id;
|
public override int Id => m_Id;
|
||||||
|
|
||||||
public LevelThemeType LevelThemeType { get; private set; }
|
public LevelThemeType LevelThemeType { get; private set; }
|
||||||
public RarityType Rarity { get; private set; }
|
|
||||||
public string ItemType { get; private set; }
|
public string ItemType { get; private set; }
|
||||||
public int ItemId { get; private set; }
|
public int ItemId { get; private set; }
|
||||||
public int Weight { get; private set; }
|
public int[] Weights { get; private set; }
|
||||||
public int MinPhase { get; private set; }
|
public int[] MinPhase { get; private set; }
|
||||||
public int MaxPhase { get; private set; }
|
public int[] MaxPhase { get; private set; }
|
||||||
|
|
||||||
public override bool ParseDataRow(string dataRowString, object userData)
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
{
|
{
|
||||||
@@ -34,31 +34,110 @@ namespace GeometryTD.DataTable
|
|||||||
m_Id = int.Parse(columnStrings[index++]);
|
m_Id = int.Parse(columnStrings[index++]);
|
||||||
index++;
|
index++;
|
||||||
LevelThemeType = EnumUtility<LevelThemeType>.Get(columnStrings[index++]);
|
LevelThemeType = EnumUtility<LevelThemeType>.Get(columnStrings[index++]);
|
||||||
Rarity = EnumUtility<RarityType>.Get(columnStrings[index++]);
|
|
||||||
ItemType = columnStrings[index++];
|
ItemType = columnStrings[index++];
|
||||||
ItemId = ParseIntOrDefault(columnStrings[index++], 0);
|
ItemId = ParseIntOrDefault(columnStrings[index++], 0);
|
||||||
Weight = ParseIntOrDefault(columnStrings[index++], 1);
|
Weights = ParseWeights(columnStrings[index++]);
|
||||||
MinPhase = ParseIntOrDefault(columnStrings[index++], 1);
|
MinPhase = ParsePhases(columnStrings[index++], 1);
|
||||||
MaxPhase = ParseIntOrDefault(columnStrings[index++], int.MaxValue);
|
MaxPhase = ParsePhases(columnStrings[index++], int.MaxValue);
|
||||||
|
NormalizePhaseRanges();
|
||||||
if (Weight <= 0)
|
|
||||||
{
|
|
||||||
Weight = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MinPhase <= 0)
|
|
||||||
{
|
|
||||||
MinPhase = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MaxPhase < MinPhase)
|
|
||||||
{
|
|
||||||
MaxPhase = MinPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetWeight(RarityType rarity)
|
||||||
|
{
|
||||||
|
int index = Mathf.Clamp((int)rarity - 1, 0, RarityCount - 1);
|
||||||
|
return Weights[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMinPhase(RarityType rarity)
|
||||||
|
{
|
||||||
|
int index = Mathf.Clamp((int)rarity - 1, 0, RarityCount - 1);
|
||||||
|
return MinPhase[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMaxPhase(RarityType rarity)
|
||||||
|
{
|
||||||
|
int index = Mathf.Clamp((int)rarity - 1, 0, RarityCount - 1);
|
||||||
|
return MaxPhase[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] ParseWeights(string raw)
|
||||||
|
{
|
||||||
|
if (!raw.StartsWith('[') || !raw.EndsWith(']'))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Weights must be enclosed in square brackets.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw.Length == 2)
|
||||||
|
{
|
||||||
|
return new int[RarityCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] raws = raw.Substring(1, raw.Length - 2).Split(",");
|
||||||
|
if (raws.Length != RarityCount)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Weights must contain exactly {RarityCount} values.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] weights = new int[RarityCount];
|
||||||
|
for (int i = 0; i < raws.Length; i++)
|
||||||
|
{
|
||||||
|
weights[i] = Mathf.Max(0, int.Parse(raws[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] ParsePhases(string raw, int fallbackValue)
|
||||||
|
{
|
||||||
|
if (!raw.StartsWith('[') || !raw.EndsWith(']'))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Phase ranges must be enclosed in square brackets.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw.Length == 2)
|
||||||
|
{
|
||||||
|
int[] fallbackValues = new int[RarityCount];
|
||||||
|
for (int i = 0; i < fallbackValues.Length; i++)
|
||||||
|
{
|
||||||
|
fallbackValues[i] = fallbackValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallbackValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] raws = raw.Substring(1, raw.Length - 2).Split(",");
|
||||||
|
if (raws.Length != RarityCount)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Phase ranges must contain exactly {RarityCount} values.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] phases = new int[RarityCount];
|
||||||
|
for (int i = 0; i < raws.Length; i++)
|
||||||
|
{
|
||||||
|
phases[i] = int.Parse(raws[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return phases;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NormalizePhaseRanges()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < RarityCount; i++)
|
||||||
|
{
|
||||||
|
if (MinPhase[i] <= 0)
|
||||||
|
{
|
||||||
|
MinPhase[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MaxPhase[i] < MinPhase[i])
|
||||||
|
{
|
||||||
|
MaxPhase[i] = MinPhase[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static int ParseIntOrDefault(string raw, int fallbackValue)
|
private static int ParseIntOrDefault(string raw, int fallbackValue)
|
||||||
{
|
{
|
||||||
if (int.TryParse(raw, out int parsed))
|
if (int.TryParse(raw, out int parsed))
|
||||||
@@ -68,5 +147,7 @@ namespace GeometryTD.DataTable
|
|||||||
|
|
||||||
return fallbackValue;
|
return fallbackValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const int RarityCount = (int)RarityType.Red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using GeometryTD.CustomUtility;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.Definition;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public sealed class DRRarityTagBudget : DataRowBase
|
public sealed class DRRarityTagBudget : DataRowBase
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Text;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 场景配置表。
|
/// 场景配置表。
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using GeometryTD.CustomUtility;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.Definition;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public class DRShopPrice : DataRowBase
|
public class DRShopPrice : DataRowBase
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 声音配置表。
|
/// 声音配置表。
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.CustomUtility;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tag 表
|
/// Tag 表
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using GeometryTD.CustomUtility;
|
using GeometryTD.Core;
|
||||||
using GeometryTD.Definition;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public sealed class DRTagConfig : DataRowBase
|
public sealed class DRTagConfig : DataRowBase
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 界面配置表。
|
/// 界面配置表。
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using System.Text;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 声音配置表。
|
/// 声音配置表。
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using System;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.DataTable
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public static class DataTableExtension
|
public static class DataTableExtension
|
||||||
{
|
{
|
||||||
|
|||||||
+3
-6
@@ -1,11 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "GeometryTD.Runtime",
|
"name": "GeometryTD.Core",
|
||||||
"rootNamespace": "GeometryTD",
|
"rootNamespace": "GeometryTD.Core",
|
||||||
"references": [
|
"references": [
|
||||||
"UnityGameFramework.Editor",
|
"GUID:363c5eb08ff8e6a439b85e37b8c20d96"
|
||||||
"UnityGameFramework.Runtime",
|
|
||||||
"Unity.InputSystem",
|
|
||||||
"Unity.TextMeshPro"
|
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 771b79705393da244ae720aa28ae8a69
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public enum CombatParticipantTowerValidationFailureReason
|
public enum CombatParticipantTowerValidationFailureReason
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public static class CombatParticipantTowerValidationText
|
public static class CombatParticipantTowerValidationText
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.CustomUtility;
|
using GeometryTD.Core;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class AttackPayload
|
public sealed class AttackPayload
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 玩家背包聚合数据。
|
/// 玩家背包聚合数据。
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// Feedback: mailto:ellan@gameframework.cn
|
// Feedback: mailto:ellan@gameframework.cn
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public class BuildInfo
|
public class BuildInfo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public class EventItem
|
public class EventItem
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public class EventOption
|
public class EventOption
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using GeometryTD.Entity;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class HitStatusModifierContext
|
public sealed class HitStatusModifierContext
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Auto)]
|
[StructLayout(LayoutKind.Auto)]
|
||||||
public struct ImpactData
|
public struct ImpactData
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 背包内组件实例基类(非 DataTable,表示玩家持有物)。
|
/// 背包内组件实例基类(非 DataTable,表示玩家持有物)。
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 背包内防御塔实例数据。
|
/// 背包内防御塔实例数据。
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 防御塔独立属性快照。
|
/// 防御塔独立属性快照。
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public class VersionInfo
|
public class VersionInfo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public enum AttackMethodType : byte
|
public enum AttackMethodType : byte
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public enum AttackPropertyType : byte
|
public enum AttackPropertyType : byte
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.Definition
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 阵营类型。
|
/// 阵营类型。
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace GeometryTD.UI
|
namespace GeometryTD.Core
|
||||||
{
|
{
|
||||||
public enum CombatSelectActionType : byte
|
public enum CombatSelectActionType : byte
|
||||||
{
|
{
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user