暂存
This commit is contained in:
@@ -8,9 +8,10 @@ using UnityEngine;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Building_GlobalWorkTable : Building_WorkTable
|
||||
public class Building_GlobalWorkTable : Building_WorkTable, IThingHolder
|
||||
{
|
||||
public GlobalProductionOrderStack globalOrderStack;
|
||||
public ThingOwner innerContainer; // 用于存储待上传的原材料
|
||||
|
||||
private CompPowerTrader powerComp;
|
||||
private CompBreakdownable breakdownableComp;
|
||||
@@ -27,12 +28,14 @@ namespace WulaFallenEmpire
|
||||
public Building_GlobalWorkTable()
|
||||
{
|
||||
globalOrderStack = new GlobalProductionOrderStack(this);
|
||||
innerContainer = new ThingOwner<Thing>(this, false);
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Deep.Look(ref globalOrderStack, "globalOrderStack", this);
|
||||
Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
|
||||
}
|
||||
|
||||
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||
@@ -526,6 +529,17 @@ namespace WulaFallenEmpire
|
||||
return selectedKind;
|
||||
}
|
||||
|
||||
// IThingHolder 实现
|
||||
public void GetChildHolders(List<IThingHolder> outChildren)
|
||||
{
|
||||
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
|
||||
}
|
||||
|
||||
public ThingOwner GetDirectlyHeldThings()
|
||||
{
|
||||
return innerContainer;
|
||||
}
|
||||
|
||||
// 修改 CreateDropPod 方法
|
||||
private bool CreateDropPod(IntVec3 dropCell, List<Thing> contents)
|
||||
{
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace WulaFallenEmpire
|
||||
public bool paused = true;
|
||||
|
||||
// 生产状态
|
||||
public ProductionState state = ProductionState.Waiting;
|
||||
public ProductionState state = ProductionState.Gathering;
|
||||
|
||||
public enum ProductionState
|
||||
{
|
||||
Waiting, // 等待资源
|
||||
Producing, // 生产中
|
||||
Gathering, // 准备材料(小人搬运中)
|
||||
Producing, // 生产中(云端倒计时)
|
||||
Completed // 完成
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace WulaFallenEmpire
|
||||
Scribe_Values.Look(ref currentCount, "currentCount", 0);
|
||||
Scribe_Values.Look(ref paused, "paused", true);
|
||||
Scribe_Values.Look(ref _progress, "progress", 0f);
|
||||
Scribe_Values.Look(ref state, "state", ProductionState.Waiting);
|
||||
Scribe_Values.Look(ref state, "state", ProductionState.Gathering);
|
||||
|
||||
// 修复:加载后验证数据
|
||||
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
||||
@@ -63,7 +63,7 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
|
||||
// 新增:获取产物的成本列表
|
||||
private Dictionary<ThingDef, int> GetProductCostList()
|
||||
public Dictionary<ThingDef, int> GetProductCostList()
|
||||
{
|
||||
var costDict = new Dictionary<ThingDef, int>();
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace WulaFallenEmpire
|
||||
|
||||
if (HasEnoughResources())
|
||||
{
|
||||
if (state == ProductionState.Waiting && !paused)
|
||||
if (state == ProductionState.Gathering && !paused)
|
||||
{
|
||||
state = ProductionState.Producing;
|
||||
progress = 0f;
|
||||
@@ -270,7 +270,7 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
if (state == ProductionState.Producing)
|
||||
{
|
||||
state = ProductionState.Waiting;
|
||||
state = ProductionState.Gathering;
|
||||
progress = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
ProcessProducingOrder(order, i);
|
||||
}
|
||||
else if (order.state == GlobalProductionOrder.ProductionState.Waiting && !order.paused)
|
||||
else if (order.state == GlobalProductionOrder.ProductionState.Gathering && !order.paused)
|
||||
{
|
||||
ProcessWaitingOrder(order);
|
||||
}
|
||||
@@ -88,7 +88,7 @@ namespace WulaFallenEmpire
|
||||
if (workAmount <= 0)
|
||||
{
|
||||
Log.Error($"Invalid workAmount ({workAmount}) for recipe {order.recipe.defName}");
|
||||
order.state = GlobalProductionOrder.ProductionState.Waiting;
|
||||
order.state = GlobalProductionOrder.ProductionState.Gathering;
|
||||
order.progress = 0f;
|
||||
return;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ namespace WulaFallenEmpire
|
||||
else
|
||||
{
|
||||
// 修复:资源不足,回到等待状态
|
||||
order.state = GlobalProductionOrder.ProductionState.Waiting;
|
||||
order.state = GlobalProductionOrder.ProductionState.Gathering;
|
||||
order.progress = 0f;
|
||||
Log.Message($"[WARNING] Failed to consume resources for {order.recipe.defName}, resetting");
|
||||
}
|
||||
|
||||
@@ -480,11 +480,17 @@ namespace WulaFallenEmpire
|
||||
Widgets.Label(progressRect, $"{order.progress:P0}");
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
}
|
||||
else if (order.state == GlobalProductionOrder.ProductionState.Gathering)
|
||||
{
|
||||
string statusText = "WULA_GatheringMaterials".Translate();
|
||||
if (order.paused) statusText = $"[||] {statusText}";
|
||||
Widgets.Label(statusRect, statusText);
|
||||
}
|
||||
else
|
||||
{
|
||||
string statusText = order.state switch
|
||||
{
|
||||
GlobalProductionOrder.ProductionState.Waiting => "WULA_WaitingForResources".Translate(),
|
||||
GlobalProductionOrder.ProductionState.Gathering => "WULA_WaitingForResources".Translate(),
|
||||
GlobalProductionOrder.ProductionState.Completed => "WULA_Completed".Translate(),
|
||||
_ => "WULA_Unknown".Translate()
|
||||
};
|
||||
@@ -549,7 +555,7 @@ namespace WulaFallenEmpire
|
||||
|
||||
// 资源检查提示
|
||||
if (!order.HasEnoughResources() &&
|
||||
order.state == GlobalProductionOrder.ProductionState.Waiting &&
|
||||
order.state == GlobalProductionOrder.ProductionState.Gathering &&
|
||||
!order.paused)
|
||||
{
|
||||
TooltipHandler.TipRegion(rect, "WULA_InsufficientResources".Translate());
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class JobDriver_GlobalWorkTable : JobDriver
|
||||
{
|
||||
private const TargetIndex TableIndex = TargetIndex.A;
|
||||
private const TargetIndex IngredientIndex = TargetIndex.B;
|
||||
|
||||
protected Building_GlobalWorkTable Table => (Building_GlobalWorkTable)job.GetTarget(TableIndex).Thing;
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||
{
|
||||
if (!pawn.Reserve(Table, job, 1, -1, null, errorOnFailed))
|
||||
return false;
|
||||
|
||||
// 预约所有材料
|
||||
if (job.targetQueueB != null)
|
||||
{
|
||||
foreach (var target in job.targetQueueB)
|
||||
{
|
||||
if (!pawn.Reserve(target, job, 1, -1, null, errorOnFailed))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils()
|
||||
{
|
||||
// 1. 收集材料
|
||||
Toil collect = Toils_General.DoAtomic(delegate
|
||||
{
|
||||
// 这是一个占位符,实际收集逻辑由下面的循环生成
|
||||
});
|
||||
|
||||
foreach (var toil in CollectIngredientsToils())
|
||||
{
|
||||
yield return toil;
|
||||
}
|
||||
|
||||
// 2. 运送到工作台
|
||||
yield return Toils_Goto.GotoThing(TableIndex, PathEndMode.Touch);
|
||||
|
||||
// 3. 放入材料
|
||||
yield return new Toil
|
||||
{
|
||||
initAction = delegate
|
||||
{
|
||||
Pawn actor = GetActor();
|
||||
Building_GlobalWorkTable table = Table;
|
||||
|
||||
// 将携带的所有相关材料放入工作台
|
||||
// 注意:这里假设小人携带的都是为了这个任务
|
||||
// 实际可能需要更精确的筛选
|
||||
List<Thing> carriedThings = actor.inventory.innerContainer.ToList(); // 复制列表
|
||||
|
||||
foreach (var thing in carriedThings)
|
||||
{
|
||||
// 检查这个物品是否是订单需要的(简单检查Def)
|
||||
// 这里简化处理:直接全部放入,多余的之后再处理或留在容器中
|
||||
if (actor.inventory.innerContainer.TryTransferToContainer(thing, table.innerContainer, thing.stackCount) > 0)
|
||||
{
|
||||
// 成功放入
|
||||
}
|
||||
}
|
||||
|
||||
// 同时也处理手上拿着的(如果有)
|
||||
if (actor.carryTracker.CarriedThing != null)
|
||||
{
|
||||
actor.carryTracker.innerContainer.TryTransferToContainer(actor.carryTracker.CarriedThing, table.innerContainer);
|
||||
}
|
||||
},
|
||||
defaultCompleteMode = ToilCompleteMode.Instant
|
||||
};
|
||||
|
||||
// 4. 检查并触发上传
|
||||
yield return new Toil
|
||||
{
|
||||
initAction = delegate
|
||||
{
|
||||
CheckAndUpload();
|
||||
},
|
||||
defaultCompleteMode = ToilCompleteMode.Instant
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<Toil> CollectIngredientsToils()
|
||||
{
|
||||
// 遍历队列中的所有材料
|
||||
// 注意:RimWorld的原版 JobDriver_DoBill 的收集逻辑非常复杂
|
||||
// 这里使用简化版:走到目标 -> 拿起 -> 下一个
|
||||
|
||||
Toil extract = Toils_JobTransforms.ExtractNextTargetFromQueue(IngredientIndex);
|
||||
yield return extract;
|
||||
|
||||
Toil gotoThing = Toils_Goto.GotoThing(IngredientIndex, PathEndMode.ClosestTouch);
|
||||
yield return gotoThing;
|
||||
|
||||
Toil takeThing = Toils_Haul.StartCarryThing(IngredientIndex, false, true);
|
||||
yield return takeThing;
|
||||
|
||||
// 循环直到队列为空
|
||||
yield return Toils_Jump.JumpIfHaveTargetInQueue(IngredientIndex, extract);
|
||||
}
|
||||
|
||||
private void CheckAndUpload()
|
||||
{
|
||||
var table = Table;
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
|
||||
// 找到当前正在进行的订单
|
||||
var order = table.globalOrderStack.orders.FirstOrDefault(o => o.state == GlobalProductionOrder.ProductionState.Gathering && !o.paused);
|
||||
if (order == null) return;
|
||||
|
||||
// 检查是否满足需求
|
||||
// 这里的逻辑需要结合云端库存和本地容器库存
|
||||
// 如果 (云端 + 容器) >= 需求,则触发上传
|
||||
|
||||
var costList = order.GetProductCostList();
|
||||
bool allSatisfied = true;
|
||||
|
||||
foreach (var kvp in costList)
|
||||
{
|
||||
int needed = kvp.Value;
|
||||
int inCloud = globalStorage.GetInputStorageCount(kvp.Key);
|
||||
int inContainer = table.innerContainer.TotalStackCountOfDef(kvp.Key);
|
||||
|
||||
if (inCloud + inContainer < needed)
|
||||
{
|
||||
allSatisfied = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allSatisfied)
|
||||
{
|
||||
// 消耗容器中的材料并上传到云端
|
||||
foreach (var kvp in costList)
|
||||
{
|
||||
int needed = kvp.Value;
|
||||
int inCloud = globalStorage.GetInputStorageCount(kvp.Key);
|
||||
int missingInCloud = needed - inCloud;
|
||||
|
||||
if (missingInCloud > 0)
|
||||
{
|
||||
// 从容器中移除并添加到云端
|
||||
int taken = table.innerContainer.TryTransferToContainer(null, table.innerContainer, missingInCloud);
|
||||
// 注意:上面的 TryTransferToContainer 用法不对,因为目标是云端(虚拟)
|
||||
// 正确做法:
|
||||
|
||||
int toTake = missingInCloud;
|
||||
while (toTake > 0)
|
||||
{
|
||||
Thing t = table.innerContainer.FirstOrDefault(x => x.def == kvp.Key);
|
||||
if (t == null) break; // 理论上不应该发生,因为前面检查过了
|
||||
|
||||
int num = UnityEngine.Mathf.Min(t.stackCount, toTake);
|
||||
t.SplitOff(num).Destroy(); // 销毁实体
|
||||
globalStorage.AddToInputStorage(kvp.Key, num); // 添加虚拟库存
|
||||
toTake -= num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 切换状态
|
||||
order.state = GlobalProductionOrder.ProductionState.Producing;
|
||||
Messages.Message("WULA_OrderStarted".Translate(order.Label), table, MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WorkGiver_GlobalWorkTable : WorkGiver_Scanner
|
||||
{
|
||||
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForDef(ThingDef.Named("WULA_WeaponArmor_Productor"));
|
||||
public override PathEndMode PathEndMode => PathEndMode.Touch;
|
||||
|
||||
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
if (!(t is Building_GlobalWorkTable table) || !table.Spawned || table.IsForbidden(pawn))
|
||||
{
|
||||
// Log.Message($"[WULA_DEBUG] HasJobOnThing: Target invalid or forbidden. {t}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pawn.CanReserve(table, 1, -1, null, forced))
|
||||
{
|
||||
// Log.Message($"[WULA_DEBUG] HasJobOnThing: Cannot reserve table.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否有需要收集材料的订单
|
||||
var order = table.globalOrderStack.orders.FirstOrDefault(o => o.state == GlobalProductionOrder.ProductionState.Gathering && !o.paused);
|
||||
if (order == null)
|
||||
{
|
||||
// Log.Message($"[WULA_DEBUG] HasJobOnThing: No gathering order found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否已经有足够的材料在容器中或云端
|
||||
if (order.HasEnoughResources())
|
||||
{
|
||||
// Log.Message($"[WULA_DEBUG] HasJobOnThing: Order has enough resources.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查找所需材料
|
||||
var ingredients = FindBestIngredients(pawn, table, order);
|
||||
if (ingredients == null)
|
||||
{
|
||||
if (forced) Log.Message($"[WULA_DEBUG] HasJobOnThing: Could not find ingredients for {order.Label}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
if (!(t is Building_GlobalWorkTable table))
|
||||
return null;
|
||||
|
||||
var order = table.globalOrderStack.orders.FirstOrDefault(o => o.state == GlobalProductionOrder.ProductionState.Gathering && !o.paused);
|
||||
if (order == null)
|
||||
return null;
|
||||
|
||||
var ingredients = FindBestIngredients(pawn, table, order);
|
||||
if (ingredients == null)
|
||||
return null;
|
||||
|
||||
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("WULA_HaulToGlobalWorkTable"), t);
|
||||
job.targetQueueB = ingredients.Select(i => new LocalTargetInfo(i.Key)).ToList();
|
||||
job.countQueue = ingredients.Select(i => i.Value).ToList();
|
||||
return job;
|
||||
}
|
||||
|
||||
private List<KeyValuePair<Thing, int>> FindBestIngredients(Pawn pawn, Building_GlobalWorkTable table, GlobalProductionOrder order)
|
||||
{
|
||||
var result = new List<KeyValuePair<Thing, int>>();
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
|
||||
// 获取所需材料清单
|
||||
var neededMaterials = GetNeededMaterials(order, table, globalStorage);
|
||||
|
||||
// Log.Message($"[WULA_DEBUG] Needed materials for {order.Label}: {string.Join(", ", neededMaterials.Select(k => $"{k.Key.defName} x{k.Value}"))}");
|
||||
|
||||
foreach (var kvp in neededMaterials)
|
||||
{
|
||||
ThingDef def = kvp.Key;
|
||||
int countNeeded = kvp.Value;
|
||||
|
||||
// 在地图上查找材料
|
||||
// 注意:t.IsInAnyStorage() 可能会过滤掉放在地上的材料,如果玩家没有设置储存区
|
||||
// 为了测试,先移除 IsInAnyStorage 限制,或者确保测试时材料在储存区
|
||||
var things = pawn.Map.listerThings.ThingsOfDef(def)
|
||||
.Where(t => !t.IsForbidden(pawn) && pawn.CanReserve(t)) // 移除了 IsInAnyStorage() 以放宽条件
|
||||
.OrderBy(t => t.Position.DistanceTo(pawn.Position))
|
||||
.ToList();
|
||||
|
||||
int currentCount = 0;
|
||||
foreach (var thing in things)
|
||||
{
|
||||
int take = UnityEngine.Mathf.Min(thing.stackCount, countNeeded - currentCount);
|
||||
if (take > 0)
|
||||
{
|
||||
result.Add(new KeyValuePair<Thing, int>(thing, take));
|
||||
currentCount += take;
|
||||
if (currentCount >= countNeeded) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Log.Message($"[WULA_DEBUG] Found {currentCount}/{countNeeded} of {def.defName}");
|
||||
}
|
||||
|
||||
return result.Count > 0 ? result : null;
|
||||
}
|
||||
|
||||
private Dictionary<ThingDef, int> GetNeededMaterials(GlobalProductionOrder order, Building_GlobalWorkTable table, GlobalStorageWorldComponent storage)
|
||||
{
|
||||
var needed = new Dictionary<ThingDef, int>();
|
||||
|
||||
// 1. 计算总需求
|
||||
var totalRequired = order.GetProductCostList();
|
||||
if (totalRequired.Count == 0)
|
||||
{
|
||||
// 处理配方原料 (Ingredients) - 简化处理,假设配方只使用固定材料
|
||||
// 实际情况可能更复杂,需要处理过滤器
|
||||
foreach (var ingredient in order.recipe.ingredients)
|
||||
{
|
||||
// 这里简化:只取第一个允许的物品作为需求
|
||||
// 更好的做法是动态匹配,但这需要更复杂的逻辑
|
||||
var def = ingredient.filter.AllowedThingDefs.FirstOrDefault();
|
||||
if (def != null)
|
||||
{
|
||||
int count = (int)ingredient.GetBaseCount();
|
||||
if (needed.ContainsKey(def)) needed[def] += count;
|
||||
else needed[def] = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 减去云端已有的
|
||||
foreach (var kvp in totalRequired)
|
||||
{
|
||||
int cloudCount = storage.GetInputStorageCount(kvp.Key);
|
||||
int remaining = kvp.Value - cloudCount;
|
||||
|
||||
// 3. 减去工作台容器中已有的
|
||||
int containerCount = table.innerContainer.TotalStackCountOfDef(kvp.Key);
|
||||
remaining -= containerCount;
|
||||
|
||||
if (remaining > 0)
|
||||
{
|
||||
needed[kvp.Key] = remaining;
|
||||
}
|
||||
}
|
||||
|
||||
return needed;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user