整理一下
This commit is contained in:
101
Source/WulaFallenEmpire/WULA_Energy/CompChargingBed.cs
Normal file
101
Source/WulaFallenEmpire/WULA_Energy/CompChargingBed.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class CompProperties_ChargingBed : CompProperties
|
||||
{
|
||||
public HediffDef hediffDef;
|
||||
public string raceDefName;
|
||||
|
||||
public CompProperties_ChargingBed()
|
||||
{
|
||||
compClass = typeof(CompChargingBed);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompChargingBed : ThingComp
|
||||
{
|
||||
public CompProperties_ChargingBed Props => (CompProperties_ChargingBed)props;
|
||||
|
||||
private List<Pawn> chargingPawns = new List<Pawn>();
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
|
||||
var bed = (Building_Bed)parent;
|
||||
var powerComp = parent.GetComp<CompPowerTrader>();
|
||||
|
||||
// 如果床没电,停止所有充电
|
||||
if (powerComp is { PowerOn: false })
|
||||
{
|
||||
StopAllCharging();
|
||||
return;
|
||||
}
|
||||
|
||||
var currentOccupants = new HashSet<Pawn>(bed.CurOccupants);
|
||||
|
||||
// 移除已经不在床上的 pawn 的充电效果
|
||||
for (int i = chargingPawns.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var pawn = chargingPawns[i];
|
||||
if (!currentOccupants.Contains(pawn))
|
||||
{
|
||||
StopCharging(pawn);
|
||||
chargingPawns.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 为床上的新 pawn 开始充电
|
||||
foreach (var pawn in currentOccupants)
|
||||
{
|
||||
if (ShouldCharge(pawn) && !chargingPawns.Contains(pawn))
|
||||
{
|
||||
StartCharging(pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldCharge(Pawn pawn)
|
||||
{
|
||||
return pawn.def.defName == Props.raceDefName;
|
||||
}
|
||||
|
||||
private void StartCharging(Pawn pawn)
|
||||
{
|
||||
if (pawn.health.hediffSet.HasHediff(Props.hediffDef)) return;
|
||||
pawn.health.AddHediff(Props.hediffDef);
|
||||
if (!chargingPawns.Contains(pawn))
|
||||
{
|
||||
chargingPawns.Add(pawn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void StopCharging(Pawn pawn)
|
||||
{
|
||||
var hediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.hediffDef);
|
||||
if (hediff != null)
|
||||
{
|
||||
pawn.health.RemoveHediff(hediff);
|
||||
}
|
||||
}
|
||||
|
||||
private void StopAllCharging()
|
||||
{
|
||||
for (int i = chargingPawns.Count - 1; i >= 0; i--)
|
||||
{
|
||||
StopCharging(chargingPawns[i]);
|
||||
chargingPawns.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Collections.Look(ref chargingPawns, "chargingPawns", LookMode.Reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class HediffCompProperties_WulaCharging : HediffCompProperties
|
||||
{
|
||||
public float energyPerTick = 0.001f; // 每tick补充的能量量
|
||||
public int durationTicks = 600; // 持续时间,例如600ticks = 10秒
|
||||
|
||||
public HediffCompProperties_WulaCharging()
|
||||
{
|
||||
this.compClass = typeof(HediffComp_WulaCharging);
|
||||
}
|
||||
}
|
||||
|
||||
public class HediffComp_WulaCharging : HediffComp
|
||||
{
|
||||
public HediffCompProperties_WulaCharging Props => (HediffCompProperties_WulaCharging)this.props;
|
||||
|
||||
private int ticksPassed = 0;
|
||||
private Thing sourceThing; // 新增字段,用于存储能量核心物品
|
||||
|
||||
public void SetSourceThing(Thing thing)
|
||||
{
|
||||
this.sourceThing = thing;
|
||||
}
|
||||
|
||||
public override void CompPostTick(ref float severityAdjustment)
|
||||
{
|
||||
base.CompPostTick(ref severityAdjustment);
|
||||
|
||||
ticksPassed++;
|
||||
if (ticksPassed >= Props.durationTicks)
|
||||
{
|
||||
// 持续时间结束,移除Hediff
|
||||
this.parent.pawn.health.RemoveHediff(this.parent);
|
||||
return;
|
||||
}
|
||||
|
||||
Need_WulaEnergy energyNeed = this.parent.pawn.needs.TryGetNeed<Need_WulaEnergy>();
|
||||
if (energyNeed != null)
|
||||
{
|
||||
// 从sourceThing的ThingDefExtension_EnergySource获取能量量
|
||||
ThingDefExtension_EnergySource ext = sourceThing?.def.GetModExtension<ThingDefExtension_EnergySource>();
|
||||
if (ext != null)
|
||||
{
|
||||
energyNeed.CurLevel += ext.energyAmount / Props.durationTicks; // 将总能量量分摊到每个tick
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有找到能量来源扩展,则使用默认的energyPerTick
|
||||
energyNeed.CurLevel += Props.energyPerTick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void CompExposeData()
|
||||
{
|
||||
base.CompExposeData();
|
||||
Scribe_Values.Look(ref ticksPassed, "ticksPassed", 0);
|
||||
Scribe_References.Look(ref sourceThing, "sourceThing"); // 保存sourceThing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class JobDriver_FeedWulaPatient : JobDriver
|
||||
{
|
||||
private const TargetIndex FoodSourceInd = TargetIndex.A;
|
||||
private const TargetIndex PatientInd = TargetIndex.B;
|
||||
|
||||
protected Thing Food => job.GetTarget(FoodSourceInd).Thing;
|
||||
protected Pawn Patient => (Pawn)job.GetTarget(PatientInd).Thing;
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||
{
|
||||
if (!pawn.Reserve(Patient, job, 1, -1, null, errorOnFailed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!pawn.Reserve(Food, job, 1, -1, null, errorOnFailed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils()
|
||||
{
|
||||
this.FailOnDespawnedNullOrForbidden(PatientInd);
|
||||
// The job should fail if the patient is no longer in bed.
|
||||
this.FailOn(() => !Patient.InBed());
|
||||
|
||||
if (pawn.inventory != null && pawn.inventory.Contains(Food))
|
||||
{
|
||||
yield return Toils_Misc.TakeItemFromInventoryToCarrier(pawn, FoodSourceInd);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return Toils_Goto.GotoThing(FoodSourceInd, PathEndMode.ClosestTouch).FailOnForbidden(FoodSourceInd);
|
||||
yield return Toils_Ingest.PickupIngestible(FoodSourceInd, pawn);
|
||||
}
|
||||
|
||||
yield return Toils_Goto.GotoThing(PatientInd, PathEndMode.Touch);
|
||||
yield return Toils_Ingest.ChewIngestible(Patient, 1.5f, FoodSourceInd, TargetIndex.None).FailOnCannotTouch(PatientInd, PathEndMode.Touch);
|
||||
|
||||
// Custom Finalize Ingest Logic
|
||||
Toil finalizeToil = new Toil();
|
||||
finalizeToil.initAction = delegate
|
||||
{
|
||||
Pawn patient = Patient;
|
||||
Thing food = Food;
|
||||
if (patient == null || food == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's not an energy core, use the default vanilla method.
|
||||
if (food.def.defName != "WULA_Charge_Cube")
|
||||
{
|
||||
patient.needs.food.CurLevel += FoodUtility.GetNutrition(patient, food, food.def);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Our custom logic for energy core
|
||||
// 1. Apply the charging hediff
|
||||
Hediff hediff = HediffMaker.MakeHediff(HediffDef.Named("WULA_ChargingHediff"), patient);
|
||||
hediff.Severity = 1.0f;
|
||||
patient.health.AddHediff(hediff);
|
||||
|
||||
// 2. Spawn the used core
|
||||
Thing usedCore = ThingMaker.MakeThing(ThingDef.Named("WULA_Charge_Cube_No_Power"));
|
||||
GenPlace.TryPlaceThing(usedCore, pawn.Position, pawn.Map, ThingPlaceMode.Near);
|
||||
}
|
||||
|
||||
// Destroy the food item (it has been carried by the feeder)
|
||||
if (!food.Destroyed)
|
||||
{
|
||||
food.Destroy();
|
||||
}
|
||||
};
|
||||
finalizeToil.defaultCompleteMode = ToilCompleteMode.Instant;
|
||||
yield return finalizeToil;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class JobDriver_IngestWulaEnergy : JobDriver
|
||||
{
|
||||
private bool eatingFromInventory;
|
||||
|
||||
private const TargetIndex IngestibleSourceInd = TargetIndex.A;
|
||||
|
||||
private Thing IngestibleSource => job.GetTarget(IngestibleSourceInd).Thing;
|
||||
|
||||
private float ChewDurationMultiplier
|
||||
{
|
||||
get
|
||||
{
|
||||
Thing ingestibleSource = IngestibleSource;
|
||||
if (ingestibleSource.def.ingestible != null)
|
||||
{
|
||||
return 1f / pawn.GetStatValue(StatDefOf.EatingSpeed);
|
||||
}
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref eatingFromInventory, "eatingFromInventory", defaultValue: false);
|
||||
}
|
||||
|
||||
public override void Notify_Starting()
|
||||
{
|
||||
base.Notify_Starting();
|
||||
eatingFromInventory = pawn.inventory != null && pawn.inventory.Contains(IngestibleSource);
|
||||
}
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||
{
|
||||
if (pawn.Faction != null)
|
||||
{
|
||||
Thing ingestibleSource = IngestibleSource;
|
||||
int maxAmountToPickup = FoodUtility.GetMaxAmountToPickup(ingestibleSource, pawn, job.count);
|
||||
if (!pawn.Reserve(ingestibleSource, job, 10, maxAmountToPickup, null, errorOnFailed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
job.count = maxAmountToPickup;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils()
|
||||
{
|
||||
this.FailOn(() => !IngestibleSource.Destroyed && !IngestibleSource.IngestibleNow);
|
||||
|
||||
Toil chew = Toils_Ingest.ChewIngestible(pawn, ChewDurationMultiplier, IngestibleSourceInd, TargetIndex.None)
|
||||
.FailOn((Toil x) => !IngestibleSource.Spawned && (pawn.carryTracker == null || pawn.carryTracker.CarriedThing != IngestibleSource))
|
||||
.FailOnCannotTouch(IngestibleSourceInd, PathEndMode.Touch);
|
||||
|
||||
foreach (Toil item in PrepareToIngestToils(chew))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
|
||||
yield return chew;
|
||||
|
||||
// Custom Finalize Ingest Logic
|
||||
Toil finalizeToil = new Toil();
|
||||
finalizeToil.initAction = delegate
|
||||
{
|
||||
Pawn ingester = pawn;
|
||||
Thing ingestible = IngestibleSource;
|
||||
if (ingester == null || ingestible == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's not an energy core, use the default vanilla method for safety, though this job should only target energy cores.
|
||||
if (ingestible.def.defName != "WULA_Charge_Cube")
|
||||
{
|
||||
ingester.needs.food.CurLevel += FoodUtility.GetNutrition(ingester, ingestible, ingestible.def);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Our custom logic for energy core
|
||||
// 1. Apply the charging hediff
|
||||
Hediff hediff = HediffMaker.MakeHediff(HediffDef.Named("WULA_ChargingHediff"), ingester);
|
||||
hediff.Severity = 1.0f;
|
||||
ingester.health.AddHediff(hediff);
|
||||
|
||||
// 2. Spawn the used core
|
||||
Thing usedCore = ThingMaker.MakeThing(ThingDef.Named("WULA_Charge_Cube_No_Power"));
|
||||
GenPlace.TryPlaceThing(usedCore, ingester.Position, ingester.Map, ThingPlaceMode.Near);
|
||||
}
|
||||
|
||||
// Destroy the original food item
|
||||
if (!ingestible.Destroyed)
|
||||
{
|
||||
ingestible.Destroy();
|
||||
}
|
||||
};
|
||||
finalizeToil.defaultCompleteMode = ToilCompleteMode.Instant;
|
||||
yield return finalizeToil;
|
||||
}
|
||||
|
||||
private IEnumerable<Toil> PrepareToIngestToils(Toil chewToil)
|
||||
{
|
||||
if (eatingFromInventory)
|
||||
{
|
||||
yield return Toils_Misc.TakeItemFromInventoryToCarrier(pawn, IngestibleSourceInd);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return Toils_Goto.GotoThing(IngestibleSourceInd, PathEndMode.ClosestTouch).FailOnDespawnedNullOrForbidden(IngestibleSourceInd);
|
||||
yield return Toils_Ingest.PickupIngestible(IngestibleSourceInd, pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class JobGiverDefExtension_WulaPackEnergy : DefModExtension
|
||||
{
|
||||
public float packEnergyThreshold = 0.5f; // 默认打包能量阈值
|
||||
public int packEnergyCount = 10; // 默认打包数量
|
||||
}
|
||||
}
|
||||
146
Source/WulaFallenEmpire/WULA_Energy/JobGiver_WulaGetEnergy.cs
Normal file
146
Source/WulaFallenEmpire/WULA_Energy/JobGiver_WulaGetEnergy.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using RimWorld;
|
||||
using System.Linq;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class JobGiver_WulaGetEnergy : ThinkNode_JobGiver
|
||||
{
|
||||
public float minEnergyLevelPercentage = 0.3f;
|
||||
public float maxEnergyLevelPercentage = 1.0f;
|
||||
|
||||
public float emergencyPriority = 9.5f;
|
||||
|
||||
public override float GetPriority(Pawn pawn)
|
||||
{
|
||||
var energyNeed = pawn.needs.TryGetNeed<Need_WulaEnergy>();
|
||||
if (energyNeed == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
// 如果能量已充满,则不需要充电
|
||||
if (energyNeed.CurLevel >= energyNeed.MaxLevel)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
// 如果Pawn正在执行充电Job,并且能量尚未充满,则保持高优先级
|
||||
if ((pawn.CurJobDef == JobDefOf.LayDown ||
|
||||
pawn.CurJobDef == DefDatabase<JobDef>.GetNamed("WULA_IngestWulaEnergy")) &&
|
||||
energyNeed.CurLevel < energyNeed.MaxLevel)
|
||||
{
|
||||
return emergencyPriority; // 保持高优先级,直到充满
|
||||
}
|
||||
|
||||
// 如果能量低于阈值,则需要充电
|
||||
if (energyNeed.CurLevelPercentage < minEnergyLevelPercentage)
|
||||
{
|
||||
return emergencyPriority;
|
||||
}
|
||||
|
||||
return 0f; // 否则,不需要充电,返回0
|
||||
}
|
||||
|
||||
protected override Job TryGiveJob(Pawn pawn)
|
||||
{
|
||||
var energyNeed = pawn.needs.TryGetNeed<Need_WulaEnergy>();
|
||||
if (energyNeed == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (energyNeed.CurLevelPercentage >= maxEnergyLevelPercentage)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!TryFindBestEnergySourceFor(pawn, out var energySource))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (energySource is Building_Bed)
|
||||
{
|
||||
return JobMaker.MakeJob(JobDefOf.LayDown, energySource);
|
||||
}
|
||||
|
||||
var job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("WULA_IngestWulaEnergy"), energySource);
|
||||
job.count = 1;
|
||||
return job;
|
||||
}
|
||||
|
||||
private bool TryFindBestEnergySourceFor(Pawn pawn, out Thing energySource)
|
||||
{
|
||||
// 优先寻找可用的充电床
|
||||
energySource = FindChargingBed(pawn);
|
||||
if (energySource != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// No bed found, now consider consumables.
|
||||
// Check for the hediff BEFORE searching for consumables.
|
||||
if (pawn.health.hediffSet.HasHediff(HediffDef.Named("WULA_ChargingHediff")))
|
||||
{
|
||||
energySource = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 优先从背包中寻找
|
||||
Thing thing = pawn.inventory.innerContainer.FirstOrFallback(t => t.def.GetModExtension<ThingDefExtension_EnergySource>() != null && t.IngestibleNow);
|
||||
if (thing != null)
|
||||
{
|
||||
energySource = thing;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 否则,在地图上寻找
|
||||
energySource = GenClosest.ClosestThingReachable(
|
||||
pawn.Position,
|
||||
pawn.Map,
|
||||
ThingRequest.ForGroup(ThingRequestGroup.HaulableEver),
|
||||
PathEndMode.ClosestTouch,
|
||||
TraverseParms.For(pawn),
|
||||
9999f,
|
||||
t => t.def.GetModExtension<ThingDefExtension_EnergySource>() != null && t.IngestibleNow && !t.IsForbidden(pawn) && pawn.CanReserve(t)
|
||||
);
|
||||
|
||||
return energySource != null;
|
||||
}
|
||||
|
||||
private Building_Bed FindChargingBed(Pawn pawn)
|
||||
{
|
||||
// 寻找附近可用的 WULA_Charging_Station_Synth
|
||||
Building_Bed bed = (Building_Bed)GenClosest.ClosestThingReachable(
|
||||
pawn.Position,
|
||||
pawn.Map,
|
||||
ThingRequest.ForGroup(ThingRequestGroup.BuildingArtificial),
|
||||
PathEndMode.InteractionCell,
|
||||
TraverseParms.For(pawn),
|
||||
9999f,
|
||||
b =>
|
||||
{
|
||||
if (!(b is Building_Bed bed_internal)) return false;
|
||||
|
||||
if (bed_internal.GetComp<CompChargingBed>() == null) return false;
|
||||
|
||||
var powerComp = bed_internal.GetComp<CompPowerTrader>();
|
||||
|
||||
// A pawn can use a bed if:
|
||||
// 1. It has power.
|
||||
// 2. Its prisoner status matches the pawn's.
|
||||
// 3. It's not a medical bed.
|
||||
// 4. The pawn can reserve it (checks for ownership, forbidden, etc.)
|
||||
return powerComp != null &&
|
||||
powerComp.PowerOn &&
|
||||
bed_internal.ForPrisoners == pawn.IsPrisoner &&
|
||||
!bed_internal.Medical &&
|
||||
pawn.CanReserve(bed_internal);
|
||||
}
|
||||
);
|
||||
return bed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld; // For JobDefOf, ThingDefOf, StatDefOf
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class JobGiver_WulaPackEnergy : ThinkNode_JobGiver
|
||||
{
|
||||
public float packEnergyThreshold = 0.5f; // 默认打包能量阈值
|
||||
public int packEnergyCount = 2; // 默认打包数量
|
||||
|
||||
// 定义乌拉能量核心的ThingDef
|
||||
private static ThingDef WULA_Charge_Cube_Def => ThingDef.Named("WULA_Charge_Cube");
|
||||
|
||||
public override ThinkNode DeepCopy(bool resolve = true)
|
||||
{
|
||||
JobGiver_WulaPackEnergy obj = (JobGiver_WulaPackEnergy)base.DeepCopy(resolve);
|
||||
obj.packEnergyThreshold = packEnergyThreshold;
|
||||
obj.packEnergyCount = packEnergyCount;
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected override Job TryGiveJob(Pawn pawn)
|
||||
{
|
||||
if (pawn.inventory == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查背包中是否有足够的能量核心,这里可以根据Need_WulaEnergy的当前值来判断是否需要打包
|
||||
// 简化逻辑:如果能量低于某个阈值,并且背包中没有能量核心,则尝试打包
|
||||
Need_WulaEnergy energyNeed = pawn.needs.TryGetNeed<Need_WulaEnergy>();
|
||||
if (energyNeed == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 只有当能量低于阈值,并且背包中能量核心数量少于2个时,才尝试打包
|
||||
if (energyNeed.CurLevelPercentage > packEnergyThreshold || pawn.inventory.innerContainer.TotalStackCountOfDef(WULA_Charge_Cube_Def) >= 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查是否超重
|
||||
if (MassUtility.IsOverEncumbered(pawn))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 寻找地图上可触及的WULA_Charge_Cube
|
||||
Thing thing = GenClosest.ClosestThing_Regionwise_ReachablePrioritized(
|
||||
pawn.Position,
|
||||
pawn.Map,
|
||||
ThingRequest.ForDef(WULA_Charge_Cube_Def), // 只寻找WULA_Charge_Cube
|
||||
PathEndMode.ClosestTouch,
|
||||
TraverseParms.For(pawn),
|
||||
20f, // 搜索距离
|
||||
delegate(Thing t)
|
||||
{
|
||||
// 检查物品是否被禁止,是否可预留,是否社交得体
|
||||
return !t.IsForbidden(pawn) && pawn.CanReserve(t) && t.IsSociallyProper(pawn);
|
||||
},
|
||||
(Thing x) => 0f // 优先级,这里可以根据距离或其他因素调整
|
||||
);
|
||||
|
||||
if (thing == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算需要打包的数量,限制在1到2个
|
||||
int countToTake = Mathf.Min(thing.stackCount, 2); // 限制为最多2个
|
||||
if (WULA_Charge_Cube_Def != null)
|
||||
{
|
||||
countToTake = Mathf.Min(countToTake, WULA_Charge_Cube_Def.stackLimit);
|
||||
}
|
||||
|
||||
// 创建TakeInventory Job
|
||||
Job job = JobMaker.MakeJob(JobDefOf.TakeInventory, thing);
|
||||
job.count = countToTake;
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class NeedDefExtension_Energy : DefModExtension
|
||||
{
|
||||
// 能量每天的消耗值
|
||||
public float fallPerDay = 1.6f;
|
||||
// 能量上限
|
||||
public float maxLevel = 1.0f;
|
||||
// 运送能量的阈值
|
||||
public float deliverEnergyThreshold = 0.5f;
|
||||
// 自动摄取能量的阈值
|
||||
public float autoIngestThreshold = 0.5f;
|
||||
}
|
||||
}
|
||||
117
Source/WulaFallenEmpire/WULA_Energy/Need_WulaEnergy.cs
Normal file
117
Source/WulaFallenEmpire/WULA_Energy/Need_WulaEnergy.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using WulaFallenEmpire;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Need_WulaEnergy : Need
|
||||
{
|
||||
private NeedDefExtension_Energy ext;
|
||||
|
||||
private NeedDefExtension_Energy Ext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ext == null)
|
||||
{
|
||||
if (def == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ext = def.GetModExtension<NeedDefExtension_Energy>();
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
|
||||
private float EnergyFallPerTick
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Ext != null)
|
||||
{
|
||||
// 从XML读取每天消耗值,并转换为每tick消耗值,并应用StatDef因子
|
||||
return (Ext.fallPerDay / 60000f) * pawn.GetStatValue(WulaStatDefOf.WulaEnergyFallRateFactor);
|
||||
}
|
||||
// 如果XML中没有定义,则使用一个默认值
|
||||
return 2.6666667E-05f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsShutdown => CurLevel <= 0.01f;
|
||||
|
||||
public override int GUIChangeArrow
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsFrozen) return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public override float MaxLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Ext != null)
|
||||
{
|
||||
// 应用StatDef偏移量
|
||||
return Ext.maxLevel + pawn.GetStatValue(WulaStatDefOf.WulaEnergyMaxLevelOffset);
|
||||
}
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
public Need_WulaEnergy(Pawn pawn) : base(pawn)
|
||||
{
|
||||
}
|
||||
|
||||
public override void NeedInterval()
|
||||
{
|
||||
if (!IsFrozen)
|
||||
{
|
||||
CurLevel -= EnergyFallPerTick * 150f;
|
||||
}
|
||||
|
||||
if (IsShutdown)
|
||||
{
|
||||
HealthUtility.AdjustSeverity(pawn, HediffDef.Named("WULA_Shutdown"), 0.05f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("WULA_Shutdown"));
|
||||
if (hediff != null)
|
||||
{
|
||||
pawn.health.RemoveHediff(hediff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetInitialLevel()
|
||||
{
|
||||
CurLevelPercentage = 1.0f;
|
||||
}
|
||||
|
||||
public override string GetTipString()
|
||||
{
|
||||
return (LabelCap + ": " + CurLevelPercentage.ToStringPercent()).Colorize(ColoredText.TipSectionTitleColor) + "\n" +
|
||||
def.description;
|
||||
}
|
||||
|
||||
public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = int.MaxValue, float customMargin = -1f, bool drawArrows = true, bool doTooltip = true, Rect? rectForTooltip = null, bool drawLabel = true)
|
||||
{
|
||||
if (threshPercents == null)
|
||||
{
|
||||
threshPercents = new System.Collections.Generic.List<float>();
|
||||
}
|
||||
threshPercents.Clear();
|
||||
base.DrawOnGUI(rect, maxThresholdMarkers, customMargin, drawArrows, doTooltip, rectForTooltip, drawLabel);
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class ThingDefExtension_EnergySource : DefModExtension
|
||||
{
|
||||
public float energyAmount = 1.0f; // Amount of energy this item provides
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WorkGiverDefExtension_FeedWula : DefModExtension
|
||||
{
|
||||
public float feedThreshold = 0.25f;
|
||||
public ThingDef energySourceDef;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WorkGiver_FeedWulaPatient : WorkGiver_Scanner
|
||||
{
|
||||
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForGroup(ThingRequestGroup.Pawn);
|
||||
|
||||
public override PathEndMode PathEndMode => PathEndMode.ClosestTouch;
|
||||
|
||||
public override Danger MaxPathDanger(Pawn pawn) => Danger.Deadly;
|
||||
|
||||
public override IEnumerable<Thing> PotentialWorkThingsGlobal(Pawn pawn)
|
||||
{
|
||||
return pawn.Map.mapPawns.AllPawns.Where(p => p.needs.TryGetNeed<Need_WulaEnergy>() != null && p.InBed());
|
||||
}
|
||||
|
||||
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
if (!(t is Pawn patient) || patient == pawn)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果病患正在充能,则不需要喂食
|
||||
if (patient.health.hediffSet.HasHediff(DefDatabase<HediffDef>.GetNamed("WULA_ChargingHediff")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Need_WulaEnergy energyNeed = patient.needs.TryGetNeed<Need_WulaEnergy>();
|
||||
var extension = def.GetModExtension<WorkGiverDefExtension_FeedWula>();
|
||||
if (energyNeed == null || energyNeed.CurLevelPercentage >= extension.feedThreshold)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// A Wula patient should be fed if they are in bed. If the job is not forced, they must also be unable to move.
|
||||
if (!patient.InBed() || (!forced && patient.health.capacities.CapableOf(PawnCapacityDefOf.Moving)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pawn.CanReserveAndReach(patient, PathEndMode.Touch, Danger.Deadly, 1, -1, null, forced))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryFindBestEnergySourceFor(pawn, patient, out _, out _))
|
||||
{
|
||||
JobFailReason.Is("NoWulaEnergyToFeed".Translate(patient.LabelShort, patient));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
Pawn patient = (Pawn)t;
|
||||
if (TryFindBestEnergySourceFor(pawn, patient, out Thing energySource, out _))
|
||||
{
|
||||
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("WULA_FeedWulaPatient"), energySource, patient);
|
||||
job.count = 1;
|
||||
return job;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool TryFindBestEnergySourceFor(Pawn getter, Pawn eater, out Thing energySource, out ThingDef energyDef)
|
||||
{
|
||||
energySource = null;
|
||||
energyDef = null;
|
||||
|
||||
var allowedThings = getter.Map.listerThings.ThingsInGroup(ThingRequestGroup.HaulableEver)
|
||||
.Where(x => x.def.GetModExtension<ThingDefExtension_EnergySource>() != null);
|
||||
|
||||
Thing thing = GenClosest.ClosestThing_Global(eater.Position, allowedThings, 99999f,
|
||||
t => t.IngestibleNow && !t.IsForbidden(getter) && getter.CanReserve(t));
|
||||
|
||||
if (thing != null)
|
||||
{
|
||||
energySource = thing;
|
||||
energyDef = thing.def;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WorkGiver_Warden_DeliverEnergy : WorkGiver_Scanner
|
||||
{
|
||||
private WorkGiverDefExtension_FeedWula ext;
|
||||
|
||||
private WorkGiverDefExtension_FeedWula Ext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ext == null)
|
||||
{
|
||||
ext = def.GetModExtension<WorkGiverDefExtension_FeedWula>();
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
|
||||
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForDef(ThingDef.Named("WulaSpecies"));
|
||||
|
||||
public override PathEndMode PathEndMode => PathEndMode.ClosestTouch;
|
||||
|
||||
public override Danger MaxPathDanger(Pawn pawn) => Danger.Deadly;
|
||||
|
||||
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
Pawn prisoner = t as Pawn;
|
||||
|
||||
if (prisoner == null || prisoner == pawn || !prisoner.IsPrisonerOfColony || !prisoner.guest.CanBeBroughtFood)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Need_WulaEnergy energyNeed = prisoner.needs.TryGetNeed<Need_WulaEnergy>();
|
||||
if (energyNeed == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (energyNeed.CurLevelPercentage > Ext.feedThreshold)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WardenFeedUtility.ShouldBeFed(prisoner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pawn.CanReserveAndReach(prisoner, PathEndMode.Touch, Danger.Deadly, 1, -1, null, forced))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Ext == null || Ext.energySourceDef == null)
|
||||
{
|
||||
Log.ErrorOnce("WorkGiver_Warden_DeliverEnergy is missing the DefModExtension with a valid energySourceDef.", def.GetHashCode());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FindBestEnergySourceFor(pawn, prisoner, out _, out _))
|
||||
{
|
||||
JobFailReason.Is("NoFood".Translate());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
Pawn prisoner = (Pawn)t;
|
||||
if (FindBestEnergySourceFor(pawn, prisoner, out Thing energySource, out _))
|
||||
{
|
||||
Job job = JobMaker.MakeJob(JobDefOf.DeliverFood, energySource, prisoner);
|
||||
job.count = 1;
|
||||
job.targetC = RCellFinder.SpotToChewStandingNear(prisoner, energySource);
|
||||
return job;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool FindBestEnergySourceFor(Pawn getter, Pawn eater, out Thing foodSource, out ThingDef foodDef)
|
||||
{
|
||||
foodSource = null;
|
||||
foodDef = null;
|
||||
|
||||
if (Ext == null || Ext.energySourceDef == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there's already an energy source in the eater's room that the eater can reach and use.
|
||||
Thing existingEnergyInRoom = GenClosest.ClosestThingReachable(
|
||||
eater.Position, // Start search from eater's position
|
||||
eater.Map,
|
||||
ThingRequest.ForDef(Ext.energySourceDef),
|
||||
PathEndMode.OnCell,
|
||||
TraverseParms.For(eater, Danger.Deadly, TraverseMode.ByPawn, false), // Use eater's traverse parms
|
||||
9999f,
|
||||
(Thing x) => !x.IsForbidden(eater) && eater.CanReserve(x) && x.GetRoom() == eater.GetRoom()
|
||||
);
|
||||
|
||||
if (existingEnergyInRoom != null)
|
||||
{
|
||||
// If there's already an energy source in the room, no need for the warden to bring another.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search for an energy source anywhere, now that we've confirmed none are in the room.
|
||||
foodSource = GenClosest.ClosestThingReachable(
|
||||
getter.Position,
|
||||
getter.Map,
|
||||
ThingRequest.ForDef(Ext.energySourceDef),
|
||||
PathEndMode.OnCell,
|
||||
TraverseParms.For(getter),
|
||||
9999f,
|
||||
(Thing x) => !x.IsForbidden(getter) && getter.CanReserve(x)
|
||||
);
|
||||
|
||||
if (foodSource != null)
|
||||
{
|
||||
foodDef = foodSource.def;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using System.Linq;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WorkGiver_Warden_FeedWula : WorkGiver_Scanner
|
||||
{
|
||||
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForGroup(ThingRequestGroup.Pawn);
|
||||
|
||||
public override PathEndMode PathEndMode => PathEndMode.ClosestTouch;
|
||||
|
||||
public override Danger MaxPathDanger(Pawn pawn) => Danger.Deadly;
|
||||
|
||||
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
if (!(t is Pawn prisoner) || pawn == prisoner)
|
||||
return false;
|
||||
|
||||
if (!ShouldFeed(pawn, prisoner))
|
||||
return false;
|
||||
|
||||
Need_WulaEnergy energyNeed = prisoner.needs.TryGetNeed<Need_WulaEnergy>();
|
||||
var extension = def.GetModExtension<WorkGiverDefExtension_FeedWula>();
|
||||
if (energyNeed == null || energyNeed.CurLevelPercentage >= extension.feedThreshold)
|
||||
return false;
|
||||
|
||||
if (prisoner.health.hediffSet.HasHediff(DefDatabase<HediffDef>.GetNamed("WULA_ChargingHediff")))
|
||||
return false;
|
||||
|
||||
if (!prisoner.InBed() || (!forced && prisoner.health.capacities.CapableOf(PawnCapacityDefOf.Moving)))
|
||||
return false;
|
||||
|
||||
if (!pawn.CanReserveAndReach(prisoner, PathEndMode.Touch, Danger.Deadly, 1, -1, null, forced))
|
||||
return false;
|
||||
|
||||
if (!TryFindBestEnergySourceFor(pawn, prisoner, out _, out _))
|
||||
{
|
||||
JobFailReason.Is("NoWulaEnergyToFeed".Translate(prisoner.LabelShort, prisoner));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||
{
|
||||
Pawn prisoner = (Pawn)t;
|
||||
if (TryFindBestEnergySourceFor(pawn, prisoner, out Thing energySource, out _))
|
||||
{
|
||||
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("WULA_FeedWulaPatient"), energySource, prisoner);
|
||||
job.count = 1;
|
||||
return job;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool ShouldFeed(Pawn warden, Pawn prisoner)
|
||||
{
|
||||
return prisoner.IsPrisonerOfColony && prisoner.guest.CanBeBroughtFood && prisoner.needs.TryGetNeed<Need_WulaEnergy>() != null;
|
||||
}
|
||||
|
||||
private bool TryFindBestEnergySourceFor(Pawn getter, Pawn eater, out Thing energySource, out ThingDef energyDef)
|
||||
{
|
||||
energySource = null;
|
||||
energyDef = null;
|
||||
|
||||
var allowedThings = getter.Map.listerThings.ThingsInGroup(ThingRequestGroup.HaulableEver)
|
||||
.Where(x => x.def.GetModExtension<ThingDefExtension_EnergySource>() != null);
|
||||
|
||||
Thing thing = GenClosest.ClosestThing_Global(eater.Position, allowedThings, 99999f,
|
||||
t => t.IngestibleNow && !t.IsForbidden(getter) && getter.CanReserve(t));
|
||||
|
||||
if (thing != null)
|
||||
{
|
||||
energySource = thing;
|
||||
energyDef = thing.def;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/WulaFallenEmpire/WULA_Energy/WulaCaravanEnergyDef.cs
Normal file
11
Source/WulaFallenEmpire/WULA_Energy/WulaCaravanEnergyDef.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class WulaCaravanEnergyDef : Def
|
||||
{
|
||||
public float consumeThreshold = 0.9f;
|
||||
public string energyItemDefName;
|
||||
public string hediffDefNameToAdd;
|
||||
}
|
||||
}
|
||||
17
Source/WulaFallenEmpire/WULA_Energy/WulaStatDefOf.cs
Normal file
17
Source/WulaFallenEmpire/WULA_Energy/WulaStatDefOf.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
[DefOf]
|
||||
public static class WulaStatDefOf
|
||||
{
|
||||
public static StatDef WulaEnergyMaxLevelOffset;
|
||||
public static StatDef WulaEnergyFallRateFactor;
|
||||
|
||||
static WulaStatDefOf()
|
||||
{
|
||||
DefOfHelper.EnsureInitializedInCtor(typeof(WulaStatDefOf));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user