Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions com.zerogamestudio.zeroengine.extraction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ZeroEngine Extraction Core

面向撤离搜打玩法的通用纯逻辑包,负责物品网格、所有权、Raid 会话、掉落、回收、经济和结算等领域规则。

## 边界

- 包内可以保存通用 DTO、确定性规则、事务合同与纯逻辑服务。
- 包内不得引用 POB 的 ScriptableObject、玩家组件、存档槽、场景 actor、UI、Addressables 或 Console。
- POB 的配置解析、角色投影、共享钱包、场景绑定和表现留在 POB adapter。
- 现有 `POB.Extraction.Core` assembly 名在迁移阶段保留,避免无关的引用破坏;若后续改名,必须单独提供兼容迁移。

## 安装

消费者使用经过测试的 Git commit pin,不使用本机 `file:` 路径:

```json
"com.zerogamestudio.zeroengine.extraction": "https://github.com/liuzqk/zeroengine.git?path=com.zerogamestudio.zeroengine.extraction#<tested-commit>"
```

## 验证

- 包级合同测试位于 `Tests/Editor`。
- 通用行为先在包内测试;消费者项目随后运行自己的 adapter 回归测试。
- 发布前检查包内不存在 `POB` 项目路径、Unity 场景/Prefab 或本机绝对路径依赖。

## 迁移基线

- 来源:POB Plastic `cs:16334` 的 `Packages/com.zerogamestudio.zeroengine.extraction`。
- 原始文件数:183。
- 原始总字节数:213273。
- 原始稳定树 SHA-256:`21db143662621b18e255ae3d9e5cdf1b944605b5d7a63072d7783d022f41e838`。
- 树哈希输入按相对路径排序,每项为 `relative/path + NUL + lowercase(file SHA-256) + LF` 的 UTF-8 拼接。
- 上游首提交只允许在这份原样基线上补 package metadata 与本 README;领域代码和测试不得静默改写。
7 changes: 7 additions & 0 deletions com.zerogamestudio.zeroengine.extraction/README.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions com.zerogamestudio.zeroengine.extraction/Runtime.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;

namespace POB.Extraction
{
[Serializable]
public class ExtractionActiveRaidContentState
{
public List<string> OpenedContainerIds = new();
public List<string> OpenedLockIds = new();
public List<string> ActivatedLeverIds = new();
public List<string> UsedExtractionPointIds = new();
public List<string> WorldPickupItemInstanceIds = new();
public List<string> AppliedReceiptIds = new();
public int DeadlineExtensionSeconds;
public int ThreatLevelDelta;
public List<string> UnlockedBonusContainerGroupIds = new();
public List<ExtractionPointRuntimeState> ExtractionPointStates = new();
public ExtractionRaidLootManifest LootManifest;

internal void EnsureInitialized()
{
OpenedContainerIds ??= new List<string>();
OpenedLockIds ??= new List<string>();
ActivatedLeverIds ??= new List<string>();
UsedExtractionPointIds ??= new List<string>();
WorldPickupItemInstanceIds ??= new List<string>();
AppliedReceiptIds ??= new List<string>();
UnlockedBonusContainerGroupIds ??= new List<string>();
ExtractionPointStates ??= new List<ExtractionPointRuntimeState>();
LootManifest?.EnsureInitialized();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using UnityEngine.Scripting.APIUpdating;

namespace POB.Extraction
{
[Serializable]
[MovedFrom(true, sourceAssembly: "POB.Runtime")]
public class ExtractionBlueprintDefinition
{
public string BlueprintId;
public string CostItemDefinitionId;
public int CostQuantity;

public bool IsValid =>
!string.IsNullOrEmpty(BlueprintId)
&& !string.IsNullOrEmpty(CostItemDefinitionId)
&& CostQuantity > 0;

public ExtractionBlueprintDefinition(
string blueprintId,
string costItemDefinitionId,
int costQuantity)
{
BlueprintId = blueprintId;
CostItemDefinitionId = costItemDefinitionId;
CostQuantity = costQuantity;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace POB.Extraction
{
public static class ExtractionBlueprintService
{
public static bool IsBlueprintUnlocked(ExtractionProfileSaveData profile, string blueprintId)
{
if (profile == null || string.IsNullOrEmpty(blueprintId)) return false;
profile.EnsureInitialized();
foreach (var unlockedBlueprintId in profile.UnlockedBlueprintIds)
{
if (unlockedBlueprintId == blueprintId) return true;
}

return false;
}

public static bool TryUnlockBlueprint(
ExtractionProfileSaveData profile,
ExtractionPlayableConfig config,
string blueprintId)
{
if (profile == null || config == null || string.IsNullOrEmpty(blueprintId)) return false;
if (!config.TryGetBlueprintDefinition(blueprintId, out var blueprint) || blueprint == null || !blueprint.IsValid)
return false;

profile.EnsureInitialized();
if (IsBlueprintUnlocked(profile, blueprintId)) return true;

profile.UnlockedBlueprintIds.Add(blueprintId);
return true;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Collections.Generic;

namespace POB.Extraction
{
public static class ExtractionBlueprintTransactionService
{
public static bool TryLearnBlueprint(
ExtractionProfileSaveData profile,
ExtractionPlayableConfig config,
ExtractionBlueprintDefinition blueprint,
IExtractionItemCatalog itemCatalog,
List<ExtractionInventoryContainerType> costSourceContainers)
{
if (!CanLearnBlueprint(profile, blueprint, itemCatalog, out var costs)) return false;
if (!ExtractionItemCostService.HasEnoughItems(profile, costs, costSourceContainers)) return false;
if (!ExtractionItemCostService.TryConsumeCosts(profile, costs, costSourceContainers, out var costReceipt)) return false;

if (ExtractionBlueprintService.TryUnlockBlueprint(profile, config, blueprint.BlueprintId)) return true;

ExtractionItemCostService.RestoreCosts(profile, costReceipt, itemCatalog);
return false;
}

private static bool CanLearnBlueprint(
ExtractionProfileSaveData profile,
ExtractionBlueprintDefinition blueprint,
IExtractionItemCatalog itemCatalog,
out List<ExtractionItemCost> costs)
{
costs = null;
if (!ExtractionFeatureSwitch.Enabled) return false;
if (profile == null || blueprint == null || !blueprint.IsValid || itemCatalog == null) return false;

profile.EnsureInitialized();
if (!itemCatalog.TryGetItemDefinition(blueprint.CostItemDefinitionId, out _)) return false;
if (ExtractionBlueprintService.IsBlueprintUnlocked(profile, blueprint.BlueprintId)) return false;

costs = new List<ExtractionItemCost>
{
new ExtractionItemCost(blueprint.CostItemDefinitionId, blueprint.CostQuantity)
};
return true;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using UnityEngine.Scripting.APIUpdating;

namespace POB.Extraction
{
[Serializable]
[MovedFrom(true, sourceAssembly: "POB.Runtime")]
public class ExtractionBuybackQuoteDefinition
{
public const string DefaultCostItemDefinitionId = "buyback-token";

public string ItemDefinitionId;
public string CostItemDefinitionId;
public int CostPerUnit;
public int MinimumCost;

public bool IsValid =>
!string.IsNullOrEmpty(ItemDefinitionId)
&& !string.IsNullOrEmpty(CostItemDefinitionId)
&& CostPerUnit > 0
&& MinimumCost >= 0;

public ExtractionBuybackQuoteDefinition(
string itemDefinitionId,
int costPerUnit,
int minimumCost)
: this(itemDefinitionId, DefaultCostItemDefinitionId, costPerUnit, minimumCost)
{
}

public ExtractionBuybackQuoteDefinition(
string itemDefinitionId,
string costItemDefinitionId,
int costPerUnit,
int minimumCost)
{
ItemDefinitionId = itemDefinitionId;
CostItemDefinitionId = costItemDefinitionId;
CostPerUnit = costPerUnit;
MinimumCost = minimumCost;
}
}

public class ExtractionBuybackQuote
{
public string ItemInstanceId;
public string ItemDefinitionId;
public int Quantity;
public string CostItemDefinitionId;
public int Cost;

public ExtractionBuybackQuote(
string itemInstanceId,
string itemDefinitionId,
int quantity,
int cost)
: this(
itemInstanceId,
itemDefinitionId,
quantity,
ExtractionBuybackQuoteDefinition.DefaultCostItemDefinitionId,
cost)
{
}

public ExtractionBuybackQuote(
string itemInstanceId,
string itemDefinitionId,
int quantity,
string costItemDefinitionId,
int cost)
{
ItemInstanceId = itemInstanceId;
ItemDefinitionId = itemDefinitionId;
Quantity = quantity;
CostItemDefinitionId = costItemDefinitionId;
Cost = cost;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Collections.Generic;

namespace POB.Extraction
{
public static class ExtractionBuybackQuoteService
{
public static bool TryGetQuote(
ExtractionProfileSaveData profile,
List<ExtractionBuybackQuoteDefinition> quoteDefinitions,
string itemInstanceId,
out ExtractionBuybackQuote quote)
{
quote = null;
if (!ExtractionFeatureSwitch.Enabled) return false;
if (profile == null || quoteDefinitions == null || string.IsNullOrEmpty(itemInstanceId)) return false;
if (!profile.Items.TryGet(itemInstanceId, out var item)) return false;
if (item == null || item.Quantity <= 0) return false;
if (!profile.Ownership.TryGetContainer(itemInstanceId, out var container)) return false;
if (container != ExtractionInventoryContainerType.Lost) return false;
if (!profile.Recovery.TryGetState(itemInstanceId, out var state)) return false;
if (state != ExtractionRecoveryItemState.Recoverable) return false;
if (!TryGetQuoteDefinition(quoteDefinitions, item.DefinitionId, out var definition)) return false;

long raw = (long)definition.CostPerUnit * item.Quantity;
if (raw < definition.MinimumCost) raw = definition.MinimumCost;
if (raw > int.MaxValue) raw = int.MaxValue;
int cost = (int)raw;

quote = new ExtractionBuybackQuote(
item.InstanceId,
item.DefinitionId,
item.Quantity,
definition.CostItemDefinitionId,
cost);
return true;
}

private static bool TryGetQuoteDefinition(
List<ExtractionBuybackQuoteDefinition> quoteDefinitions,
string itemDefinitionId,
out ExtractionBuybackQuoteDefinition definition)
{
definition = null;
if (string.IsNullOrEmpty(itemDefinitionId)) return false;

foreach (var candidate in quoteDefinitions)
{
if (candidate == null || !candidate.IsValid) continue;
if (candidate.ItemDefinitionId != itemDefinitionId) continue;

definition = candidate;
return true;
}

return false;
}
}
}
Loading
Loading