暂存
This commit is contained in:
Binary file not shown.
@@ -65,4 +65,82 @@
|
||||
</comps>
|
||||
</ThingDef>
|
||||
|
||||
<ThingDef ParentName="BuildingBase">
|
||||
<defName>ARA_GrowthVat</defName>
|
||||
<label>阿拉克涅捕获茧</label>
|
||||
<description>用来存放猎物的茧。</description>
|
||||
<thingClass>ArachnaeSwarm.Building_NutrientVat</thingClass>
|
||||
<containedPawnsSelectable>true</containedPawnsSelectable>
|
||||
<tickerType>Normal</tickerType>
|
||||
<graphicData>
|
||||
<texPath>ArachnaeSwarm/Building/ARA_GrowthVat</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
<shaderType>CutoutComplex</shaderType>
|
||||
<drawSize>(2.5,2.5)</drawSize>
|
||||
<shadowData>
|
||||
<volume>(0.85, 0.3, 1.7)</volume>
|
||||
</shadowData>
|
||||
</graphicData>
|
||||
<castEdgeShadows>true</castEdgeShadows>
|
||||
<defaultPlacingRot>North</defaultPlacingRot>
|
||||
<size>(1,2)</size>
|
||||
<statBases>
|
||||
<MaxHitPoints>500</MaxHitPoints>
|
||||
<WorkToBuild>8000</WorkToBuild>
|
||||
<Mass>30</Mass>
|
||||
<Flammability>0.5</Flammability>
|
||||
</statBases>
|
||||
<costList>
|
||||
<Steel>150</Steel>
|
||||
<ComponentIndustrial>4</ComponentIndustrial>
|
||||
</costList>
|
||||
<altitudeLayer>Building</altitudeLayer>
|
||||
<passability>PassThroughOnly</passability>
|
||||
<pathCost>42</pathCost>
|
||||
<blockWind>true</blockWind>
|
||||
<drawerType>MapMeshAndRealTime</drawerType>
|
||||
<fillPercent>0.5</fillPercent>
|
||||
<canOverlapZones>false</canOverlapZones>
|
||||
<designationCategory>ARA_Buildings</designationCategory>
|
||||
<uiOrder>2200</uiOrder>
|
||||
<hasInteractionCell>true</hasInteractionCell>
|
||||
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||
<rotatable>false</rotatable>
|
||||
<inspectorTabs>
|
||||
<li>ITab_BiosculpterNutritionStorage</li>
|
||||
<li>ITab_Genes</li>
|
||||
</inspectorTabs>
|
||||
<researchPrerequisites>
|
||||
<li>GrowthVats</li>
|
||||
</researchPrerequisites>
|
||||
<building>
|
||||
<ai_chillDestination>false</ai_chillDestination>
|
||||
<haulToContainerDuration>120</haulToContainerDuration>
|
||||
<workTableRoomRole>Laboratory</workTableRoomRole>
|
||||
</building>
|
||||
<constructionSkillPrerequisite>4</constructionSkillPrerequisite>
|
||||
<!-- ... 其他建筑属性 ... -->
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||
<fuelCapacity>100.0</fuelCapacity>
|
||||
<fuelFilter>
|
||||
<categories>
|
||||
<li>Foods</li>
|
||||
</categories>
|
||||
</fuelFilter>
|
||||
<fuelGizmoLabel>生物质</fuelGizmoLabel>
|
||||
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
|
||||
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
|
||||
</li>
|
||||
</comps>
|
||||
<modExtensions>
|
||||
<li Class="ArachnaeSwarm.DefModExtension_NutrientVat">
|
||||
<!-- 在这里配置您的顶部贴图 -->
|
||||
<topGraphicPath>ArachnaeSwarm/Building/ARA_GrowthVatTop</topGraphicPath>
|
||||
<!-- 如果是单张贴图,使用 Graphic_Single -->
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
</li>
|
||||
</modExtensions>
|
||||
</ThingDef>
|
||||
|
||||
</Defs>
|
||||
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVat.png
Normal file
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVatTop.png
Normal file
BIN
Content/Textures/ArachnaeSwarm/Building/ARA_GrowthVatTop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
BIN
非公开资源/Content/Textures/Building/ARA_GrowthVat.psd
Normal file
BIN
非公开资源/Content/Textures/Building/ARA_GrowthVat.psd
Normal file
Binary file not shown.
Reference in New Issue
Block a user