暂存
This commit is contained in:
@@ -133,6 +133,8 @@
|
||||
<Compile Include="ARA_CompInteractiveProducer\CompInteractiveProducer.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\JobDriver_StartProduction.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\CompRefuelableNutrition.cs" />
|
||||
<Compile Include="Building_NutrientVat.cs" />
|
||||
<Compile Include="DefModExtension_NutrientVat.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\DataContracts.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\CompTemperatureRuinableDamage.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\CompQueuedInteractiveProducer.cs" />
|
||||
|
||||
369
Source/ArachnaeSwarm/Building_NutrientVat.cs
Normal file
369
Source/ArachnaeSwarm/Building_NutrientVat.cs
Normal file
@@ -0,0 +1,369 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public class Building_NutrientVat : Building_Enterable, IThingHolder, IThingHolderWithDrawnPawn
|
||||
{
|
||||
private CompRefuelableNutrition cachedRefuelableComp;
|
||||
private Graphic cachedTopGraphic;
|
||||
|
||||
// IThingHolderWithDrawnPawn implementation
|
||||
public float HeldPawnDrawPos_Y => DrawPos.y + 0.03658537f;
|
||||
public float HeldPawnBodyAngle => base.Rotation.AsAngle;
|
||||
public PawnPosture HeldPawnPosture => PawnPosture.LayingOnGroundFaceUp;
|
||||
|
||||
private Graphic TopGraphic
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cachedTopGraphic == null)
|
||||
{
|
||||
var modExtension = def.GetModExtension<DefModExtension_NutrientVat>();
|
||||
if (modExtension != null && !modExtension.topGraphicPath.NullOrEmpty())
|
||||
{
|
||||
cachedTopGraphic = GraphicDatabase.Get(modExtension.graphicClass, modExtension.topGraphicPath, ShaderDatabase.Transparent, def.graphicData.drawSize, Color.white, Color.white);
|
||||
}
|
||||
}
|
||||
return cachedTopGraphic;
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for BioStarvation
|
||||
private const float BiostarvationGainPerDayNoFood = 0.5f;
|
||||
private const float BiostarvationFallPerDayFed = 0.1f;
|
||||
|
||||
public override Vector3 PawnDrawOffset => Vector3.zero;
|
||||
|
||||
public CompRefuelableNutrition RefuelableComp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cachedRefuelableComp == null)
|
||||
{
|
||||
cachedRefuelableComp = this.TryGetComp<CompRefuelableNutrition>();
|
||||
}
|
||||
return cachedRefuelableComp;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasNutrition => RefuelableComp != null && RefuelableComp.HasFuel;
|
||||
|
||||
public float BiostarvationDailyOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!base.Working)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
return HasNutrition ? -BiostarvationFallPerDayFed : BiostarvationGainPerDayNoFood;
|
||||
}
|
||||
}
|
||||
|
||||
public float NutritionConsumedPerDay
|
||||
{
|
||||
get
|
||||
{
|
||||
if (selectedPawn != null)
|
||||
{
|
||||
// Let's use the base consumption rate from the original GrowthVat
|
||||
float num = 3f;
|
||||
Hediff biostarvation = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||
if (biostarvation != null && biostarvation.Severity > 0)
|
||||
{
|
||||
// Increase consumption when biostarving, same as original
|
||||
num *= 1.1f;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||
{
|
||||
base.SpawnSetup(map, respawningAfterLoad);
|
||||
cachedRefuelableComp = this.TryGetComp<CompRefuelableNutrition>();
|
||||
}
|
||||
|
||||
public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
|
||||
{
|
||||
if (mode != DestroyMode.WillReplace)
|
||||
{
|
||||
if (selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||
{
|
||||
Notify_PawnRemoved();
|
||||
}
|
||||
}
|
||||
base.DeSpawn(mode);
|
||||
}
|
||||
|
||||
protected override void Tick()
|
||||
{
|
||||
base.Tick();
|
||||
|
||||
if (selectedPawn != null && (selectedPawn.Destroyed || !innerContainer.Contains(selectedPawn)))
|
||||
{
|
||||
OnStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (base.Working && selectedPawn != null)
|
||||
{
|
||||
// Update BioStarvation
|
||||
float biostarvationOffset = BiostarvationDailyOffset / 60000f * HediffDefOf.BioStarvation.maxSeverity;
|
||||
Hediff biostarvation = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||
|
||||
if (biostarvation != null)
|
||||
{
|
||||
biostarvation.Severity += biostarvationOffset;
|
||||
if (biostarvation.ShouldRemove)
|
||||
{
|
||||
selectedPawn.health.RemoveHediff(biostarvation);
|
||||
}
|
||||
}
|
||||
else if (biostarvationOffset > 0f)
|
||||
{
|
||||
Hediff hediff = HediffMaker.MakeHediff(HediffDefOf.BioStarvation, selectedPawn);
|
||||
hediff.Severity = biostarvationOffset;
|
||||
selectedPawn.health.AddHediff(hediff);
|
||||
}
|
||||
|
||||
// Check for failure
|
||||
if (biostarvation != null && biostarvation.Severity >= HediffDefOf.BioStarvation.maxSeverity)
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update nutrition consumption rate on the component
|
||||
RefuelableComp.currentConsumptionRate = NutritionConsumedPerDay;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not working, consumption is zero
|
||||
if(RefuelableComp != null)
|
||||
{
|
||||
RefuelableComp.currentConsumptionRate = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override AcceptanceReport CanAcceptPawn(Pawn pawn)
|
||||
{
|
||||
if (base.Working)
|
||||
{
|
||||
return "Occupied".Translate();
|
||||
}
|
||||
if (selectedPawn != null && selectedPawn != pawn)
|
||||
{
|
||||
return "WaitingForPawn".Translate(selectedPawn.Named("PAWN"));
|
||||
}
|
||||
if (pawn.health.hediffSet.HasHediff(HediffDefOf.BioStarvation))
|
||||
{
|
||||
return "PawnBiostarving".Translate(pawn.Named("PAWN"));
|
||||
}
|
||||
return pawn.IsColonist && !pawn.IsQuestLodger();
|
||||
}
|
||||
|
||||
public override void TryAcceptPawn(Pawn pawn)
|
||||
{
|
||||
if (!CanAcceptPawn(pawn))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectedPawn = pawn;
|
||||
bool deselected = pawn.DeSpawnOrDeselect();
|
||||
if (innerContainer.TryAddOrTransfer(pawn))
|
||||
{
|
||||
startTick = Find.TickManager.TicksGame;
|
||||
}
|
||||
if (deselected)
|
||||
{
|
||||
Find.Selector.Select(pawn, playSound: false, forceDesignatorDeselect: false);
|
||||
}
|
||||
}
|
||||
|
||||
private void Finish()
|
||||
{
|
||||
if (selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||
{
|
||||
Notify_PawnRemoved();
|
||||
innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _);
|
||||
OnStop();
|
||||
}
|
||||
}
|
||||
|
||||
private void Fail()
|
||||
{
|
||||
if (selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||
{
|
||||
Notify_PawnRemoved();
|
||||
innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _);
|
||||
Hediff firstHediffOfDef = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||
selectedPawn.Kill(null, firstHediffOfDef);
|
||||
}
|
||||
OnStop();
|
||||
}
|
||||
|
||||
private void OnStop()
|
||||
{
|
||||
selectedPawn = null;
|
||||
startTick = -1;
|
||||
if (RefuelableComp != null)
|
||||
{
|
||||
RefuelableComp.currentConsumptionRate = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void Notify_PawnRemoved()
|
||||
{
|
||||
// You can add sound effects here if you want, e.g., SoundDefOf.GrowthVat_Open.PlayOneShot(SoundInfo.InMap(this));
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> GetGizmos()
|
||||
{
|
||||
// Keep base gizmos
|
||||
foreach (Gizmo gizmo in base.GetGizmos())
|
||||
{
|
||||
yield return gizmo;
|
||||
}
|
||||
|
||||
if (base.Working)
|
||||
{
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "CommandCancelGrowth".Translate(), // Label can be changed
|
||||
defaultDesc = "CommandCancelGrowthDesc".Translate(), // Desc can be changed
|
||||
icon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel"),
|
||||
action = () =>
|
||||
{
|
||||
Finish();
|
||||
innerContainer.TryDropAll(InteractionCell, base.Map, ThingPlaceMode.Near);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectedPawn != null)
|
||||
{
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "CommandCancelLoad".Translate(),
|
||||
defaultDesc = "CommandCancelLoadDesc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel"),
|
||||
action = () =>
|
||||
{
|
||||
if (selectedPawn?.CurJobDef == JobDefOf.EnterBuilding)
|
||||
{
|
||||
selectedPawn.jobs.EndCurrentJob(JobCondition.InterruptForced);
|
||||
}
|
||||
OnStop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var command_Action = new Command_Action
|
||||
{
|
||||
defaultLabel = "InsertPerson".Translate() + "...",
|
||||
defaultDesc = "InsertPersonGrowthVatDesc".Translate(), // Desc can be changed
|
||||
icon = Building_GrowthVat.InsertPawnIcon.Texture,
|
||||
action = () =>
|
||||
{
|
||||
List<FloatMenuOption> list = new List<FloatMenuOption>();
|
||||
foreach (Pawn p in base.Map.mapPawns.AllPawnsSpawned)
|
||||
{
|
||||
if ((bool)CanAcceptPawn(p))
|
||||
{
|
||||
list.Add(new FloatMenuOption(p.LabelCap, () => SelectPawn(p), p, Color.white));
|
||||
}
|
||||
}
|
||||
if (!list.Any())
|
||||
{
|
||||
list.Add(new FloatMenuOption("NoViablePawns".Translate(), null));
|
||||
}
|
||||
Find.WindowStack.Add(new FloatMenu(list));
|
||||
}
|
||||
};
|
||||
if (!base.AnyAcceptablePawns)
|
||||
{
|
||||
command_Action.Disable("NoViablePawns".Translate());
|
||||
}
|
||||
yield return command_Action;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetInspectString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.Append(base.GetInspectString());
|
||||
|
||||
if (base.Working && selectedPawn != null)
|
||||
{
|
||||
stringBuilder.AppendLineIfNotEmpty().Append("CasketContains".Translate().ToString() + ": " + selectedPawn.NameShortColored.Resolve());
|
||||
|
||||
Hediff biostarvation = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
|
||||
if (biostarvation != null && biostarvation.Severity > 0f)
|
||||
{
|
||||
string text = ((BiostarvationDailyOffset >= 0f) ? "+" : string.Empty);
|
||||
stringBuilder.AppendLineIfNotEmpty().Append(string.Format("{0}: {1} ({2})", "Biostarvation".Translate(), biostarvation.Severity.ToStringPercent(), "PerDay".Translate(text + BiostarvationDailyOffset.ToStringPercent())));
|
||||
}
|
||||
}
|
||||
else if (selectedPawn != null)
|
||||
{
|
||||
stringBuilder.AppendLineIfNotEmpty().Append("WaitingForPawn".Translate(selectedPawn.Named("PAWN")).Resolve());
|
||||
}
|
||||
|
||||
// The inspect string from CompRefuelableNutrition will be automatically added by the game.
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public override IEnumerable<FloatMenuOption> GetFloatMenuOptions(Pawn selPawn)
|
||||
{
|
||||
foreach (FloatMenuOption floatMenuOption in base.GetFloatMenuOptions(selPawn))
|
||||
{
|
||||
yield return floatMenuOption;
|
||||
}
|
||||
if (!selPawn.CanReach(this, PathEndMode.InteractionCell, Danger.Deadly))
|
||||
{
|
||||
yield return new FloatMenuOption("CannotEnterBuilding".Translate(this) + ": " + "NoPath".Translate().CapitalizeFirst(), null);
|
||||
yield break;
|
||||
}
|
||||
AcceptanceReport acceptanceReport = CanAcceptPawn(selPawn);
|
||||
if (acceptanceReport.Accepted)
|
||||
{
|
||||
yield return FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption("EnterBuilding".Translate(this), () => SelectPawn(selPawn)), selPawn, this);
|
||||
}
|
||||
else if (!acceptanceReport.Reason.NullOrEmpty())
|
||||
{
|
||||
yield return new FloatMenuOption("CannotEnterBuilding".Translate(this) + ": " + acceptanceReport.Reason.CapitalizeFirst(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public override void DynamicDrawPhaseAt(DrawPhase phase, Vector3 drawLoc, bool flip = false)
|
||||
{
|
||||
if (base.Working && selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||
{
|
||||
selectedPawn.Drawer.renderer.DynamicDrawPhaseAt(phase, drawLoc + PawnDrawOffset, null, neverAimWeapon: true);
|
||||
}
|
||||
base.DynamicDrawPhaseAt(phase, drawLoc, flip);
|
||||
}
|
||||
|
||||
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
|
||||
{
|
||||
base.DrawAt(drawLoc, flip);
|
||||
// Draw the top graphic if it exists
|
||||
if (TopGraphic != null)
|
||||
{
|
||||
TopGraphic.Draw(DrawPos + Altitudes.AltIncVect * 2f, base.Rotation, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/ArachnaeSwarm/DefModExtension_NutrientVat.cs
Normal file
11
Source/ArachnaeSwarm/DefModExtension_NutrientVat.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class DefModExtension_NutrientVat : DefModExtension
|
||||
{
|
||||
public string topGraphicPath;
|
||||
public Type graphicClass = typeof(Graphic_Multi); // Default to Graphic_Multi if not specified in XML
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user