暂存
This commit is contained in:
Binary file not shown.
@@ -8,12 +8,19 @@
|
||||
<description>将自己转换为一个坚固的静态建筑形态,或从建筑形态恢复。</description>
|
||||
<iconPath>UI/Commands/Attack</iconPath> <!-- TODO: 替换为你的图标路径 -->
|
||||
<cooldownTicksRange>600</cooldownTicksRange>
|
||||
<hotKey>Misc12</hotKey>
|
||||
<targetRequired>false</targetRequired>
|
||||
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
|
||||
<verbProperties>
|
||||
<verbClass>Verb_CastAbility</verbClass>
|
||||
<warmupTime>1.5</warmupTime>
|
||||
<range>0</range>
|
||||
<drawAimPie>false</drawAimPie>
|
||||
<requireLineOfSight>false</requireLineOfSight>
|
||||
<nonInterruptingSelfCast>true</nonInterruptingSelfCast>
|
||||
<warmupTime>1</warmupTime>
|
||||
<range>19.9</range>
|
||||
<targetable>false</targetable>
|
||||
<targetParams>
|
||||
<canTargetSelf>true</canTargetSelf>
|
||||
<canTargetSelf>True</canTargetSelf>
|
||||
</targetParams>
|
||||
</verbProperties>
|
||||
<comps>
|
||||
@@ -23,53 +30,56 @@
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
|
||||
<ThingDef ParentName="BenchBase">
|
||||
<ThingDef ParentName="BuildingBase">
|
||||
<defName>ARA_MorphableResearchBench</defName>
|
||||
<hasInteractionCell>true</hasInteractionCell>
|
||||
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||
<label>阿拉克涅织域织座</label>
|
||||
<description>一个供阿拉克涅虫族进行研究的活体结构,可以让虫群尽情地探索变异和进化方向。</description>
|
||||
<description>一个供阿拉克涅虫族进行研究的活体结构,可以让虫群尽情地探索变异和进化方向。其研究能力完全取决于内部的阿拉克涅织域种。</description>
|
||||
<thingClass>ArachnaeSwarm.Building_Morphable</thingClass>
|
||||
<tickerType>Normal</tickerType>
|
||||
<size>(3,3)</size>
|
||||
<stuffCategories Inherit="False"/>
|
||||
<costStuffCount>0</costStuffCount>
|
||||
<minifiedDef Inherit="False"/>
|
||||
<thingCategories Inherit="False"/>
|
||||
<costList>
|
||||
<ARA_Carapace>50</ARA_Carapace>
|
||||
</costList>
|
||||
<graphicData>
|
||||
<texPath>ArachnaeSwarm/Building/ARA_ResearchBench</texPath>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
<shaderType>CutoutComplex</shaderType>
|
||||
<drawSize>(3,4.5)</drawSize>
|
||||
</graphicData>
|
||||
<castEdgeShadows>false</castEdgeShadows>
|
||||
<staticSunShadowHeight>0</staticSunShadowHeight>
|
||||
<altitudeLayer>Building</altitudeLayer>
|
||||
<passability>PassThroughOnly</passability>
|
||||
<passability>Impassable</passability>
|
||||
<castEdgeShadows>false</castEdgeShadows>
|
||||
<fillPercent>0.8</fillPercent>
|
||||
<staticSunShadowHeight>0</staticSunShadowHeight>
|
||||
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
|
||||
<pathCost>50</pathCost>
|
||||
<statBases>
|
||||
<MaxHitPoints>250</MaxHitPoints>
|
||||
<WorkToBuild>2800</WorkToBuild>
|
||||
<Flammability>1.0</Flammability>
|
||||
<ResearchSpeedFactor>1.0</ResearchSpeedFactor>
|
||||
</statBases>
|
||||
<placeWorkers>
|
||||
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
|
||||
</placeWorkers>
|
||||
<fillPercent>0.8</fillPercent>
|
||||
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||
<hasInteractionCell>true</hasInteractionCell>
|
||||
<uiOrder>2600</uiOrder>
|
||||
<surfaceType>Item</surfaceType>
|
||||
<building>
|
||||
<workTableRoomRole>Laboratory</workTableRoomRole>
|
||||
<workTableNotInRoomRoleFactor>0.8</workTableNotInRoomRoleFactor>
|
||||
</building>
|
||||
<!-- 不可建造,只能通过变形生成 -->
|
||||
<comps Inherit="False">
|
||||
<li Class="CompProperties_ReportWorkSpeed">
|
||||
<workSpeedStat>ResearchSpeedFactor</workSpeedStat>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_Morphable" />
|
||||
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||
<fuelFilter>
|
||||
<categories>
|
||||
<li>Foods</li>
|
||||
</categories>
|
||||
</fuelFilter>
|
||||
<fuelCapacity>10.0</fuelCapacity>
|
||||
<targetFuelLevelConfigurable>false</targetFuelLevelConfigurable>
|
||||
<fuelGizmoLabel>营养</fuelGizmoLabel>
|
||||
<outOfFuelMessage>没有营养</outOfFuelMessage>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using RimWorld;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
@@ -6,13 +8,125 @@ namespace ArachnaeSwarm
|
||||
public class Building_Morphable : Building
|
||||
{
|
||||
private CompMorphable compMorphable;
|
||||
private CompRefuelableNutrition compRefuelable;
|
||||
private Effecter researchEffecter;
|
||||
|
||||
public float virtualRest; // Public for external access
|
||||
private bool forceSleep;
|
||||
|
||||
public float VirtualRestMax => 1.0f;
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref virtualRest, "virtualRest", 1f);
|
||||
Scribe_Values.Look(ref forceSleep, "forceSleep", false);
|
||||
}
|
||||
|
||||
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||
{
|
||||
base.SpawnSetup(map, respawningAfterLoad);
|
||||
this.compMorphable = GetComp<CompMorphable>();
|
||||
this.compRefuelable = GetComp<CompRefuelableNutrition>();
|
||||
}
|
||||
|
||||
protected override void Tick()
|
||||
{
|
||||
base.Tick();
|
||||
|
||||
if (compMorphable?.StoredPawn == null)
|
||||
{
|
||||
StopResearchEffect();
|
||||
return;
|
||||
}
|
||||
|
||||
Pawn pawn = compMorphable.StoredPawn;
|
||||
|
||||
pawn.needs?.NeedsTrackerTickInterval(1);
|
||||
|
||||
var needs = pawn.needs;
|
||||
if (needs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 饮食逻辑 ---
|
||||
if (needs.food != null && compRefuelable != null)
|
||||
{
|
||||
if (compRefuelable.HasFuel)
|
||||
{
|
||||
needs.food.CurLevel += needs.food.FoodFallPerTick;
|
||||
compRefuelable.ConsumeFuel(needs.food.FoodFallPerTick);
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("PawnTransformer_OutOfFuel".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.NegativeEvent);
|
||||
this.Destroy(DestroyMode.Vanish);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 休息与研究逻辑 ---
|
||||
if (needs.rest != null)
|
||||
{
|
||||
if (needs.rest.CurLevel <= 0)
|
||||
{
|
||||
forceSleep = true;
|
||||
}
|
||||
if (forceSleep && needs.rest.CurLevel >= needs.rest.MaxLevel)
|
||||
{
|
||||
forceSleep = false;
|
||||
}
|
||||
}
|
||||
|
||||
TimeAssignmentDef assignment = pawn.timetable?.CurrentAssignment ?? TimeAssignmentDefOf.Anything;
|
||||
|
||||
if (forceSleep || (assignment != TimeAssignmentDefOf.Work && assignment != TimeAssignmentDefOf.Anything))
|
||||
{
|
||||
// 休眠期
|
||||
if (needs.rest != null)
|
||||
{
|
||||
needs.rest.CurLevel += Need_Rest.BaseRestGainPerTick * 2f;
|
||||
}
|
||||
StopResearchEffect();
|
||||
}
|
||||
else // 工作时间
|
||||
{
|
||||
ResearchProjectDef currentProj = Find.ResearchManager.GetProject();
|
||||
if (currentProj != null)
|
||||
{
|
||||
float researchSpeed = pawn.GetStatValue(StatDefOf.ResearchSpeed);
|
||||
researchSpeed *= this.GetStatValue(StatDefOf.ResearchSpeedFactor);
|
||||
Find.ResearchManager.ResearchPerformed(researchSpeed, pawn);
|
||||
pawn.skills.Learn(SkillDefOf.Intellectual, 0.1f, false);
|
||||
StartResearchEffect();
|
||||
}
|
||||
else
|
||||
{
|
||||
StopResearchEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void StartResearchEffect()
|
||||
{
|
||||
if (researchEffecter == null)
|
||||
{
|
||||
researchEffecter = EffecterDefOf.Research.Spawn();
|
||||
}
|
||||
researchEffecter.EffectTick(this, TargetInfo.Invalid);
|
||||
}
|
||||
|
||||
private void StopResearchEffect()
|
||||
{
|
||||
if (researchEffecter != null)
|
||||
{
|
||||
researchEffecter.Cleanup();
|
||||
researchEffecter = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string Label
|
||||
{
|
||||
get
|
||||
@@ -27,71 +141,102 @@ namespace ArachnaeSwarm
|
||||
|
||||
public override string GetInspectString()
|
||||
{
|
||||
string text = base.GetInspectString();
|
||||
if (compMorphable?.StoredPawn != null)
|
||||
List<string> inspectStrings = new List<string>();
|
||||
string baseString = base.GetInspectString();
|
||||
if (!baseString.NullOrEmpty())
|
||||
{
|
||||
if (!text.NullOrEmpty())
|
||||
{
|
||||
text += "\n";
|
||||
}
|
||||
text += "StoredPawn".Translate() + ": " + compMorphable.StoredPawn.LabelShort;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public override void PreApplyDamage(ref DamageInfo dinfo, out bool absorbed)
|
||||
{
|
||||
// 先让基类处理,我们不打断正常流程
|
||||
base.PreApplyDamage(ref dinfo, out absorbed);
|
||||
if (absorbed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果建筑即将被摧毁,则由Destroy方法处理,避免重复逻辑
|
||||
if (this.HitPoints - dinfo.Amount <= 0)
|
||||
{
|
||||
return;
|
||||
inspectStrings.Add(baseString);
|
||||
}
|
||||
|
||||
if (compMorphable?.StoredPawn != null)
|
||||
{
|
||||
Pawn pawn = compMorphable.StoredPawn;
|
||||
float damageProportion = dinfo.Amount / this.def.statBases.GetStatValueFromList(StatDefOf.MaxHitPoints, 500f);
|
||||
|
||||
// --- 立即强制解除变形 ---
|
||||
SkillRecord intellectualSkill = pawn.skills?.GetSkill(SkillDefOf.Intellectual);
|
||||
if (intellectualSkill != null)
|
||||
{
|
||||
inspectStrings.Add($"{SkillDefOf.Intellectual.LabelCap}: {intellectualSkill.Level} ({intellectualSkill.XpProgressPercent:P0})");
|
||||
}
|
||||
|
||||
Need_Rest restNeed = pawn.needs?.rest;
|
||||
if (restNeed != null)
|
||||
{
|
||||
inspectStrings.Add($"{restNeed.LabelCap}: {restNeed.CurLevelPercentage:P0}");
|
||||
}
|
||||
|
||||
TimeAssignmentDef assignment = pawn.timetable?.CurrentAssignment ?? TimeAssignmentDefOf.Anything;
|
||||
bool isWorkingTime = !forceSleep && (assignment == TimeAssignmentDefOf.Work || assignment == TimeAssignmentDefOf.Anything);
|
||||
|
||||
string activity;
|
||||
if (isWorkingTime)
|
||||
{
|
||||
ResearchProjectDef currentProj = Find.ResearchManager.GetProject();
|
||||
if (currentProj != null)
|
||||
{
|
||||
activity = "Activity".Translate() + ": " + "Researching".Translate() + $" ({currentProj.LabelCap})";
|
||||
}
|
||||
else
|
||||
{
|
||||
activity = "Activity".Translate() + ": " + "Idle".Translate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
activity = "Activity".Translate() + ": " + "Sleeping".Translate();
|
||||
}
|
||||
inspectStrings.Add(activity);
|
||||
}
|
||||
|
||||
return string.Join("\n", inspectStrings);
|
||||
}
|
||||
|
||||
public override void PostApplyDamage(DamageInfo dinfo, float totalDamageDealt)
|
||||
{
|
||||
base.PostApplyDamage(dinfo, totalDamageDealt);
|
||||
if (dinfo.Amount <= 0 || this.Destroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (compMorphable?.StoredPawn != null)
|
||||
{
|
||||
ForceRevert(dinfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceRevert(DamageInfo dinfo)
|
||||
{
|
||||
Pawn pawn = compMorphable.StoredPawn;
|
||||
Map map = this.Map;
|
||||
IntVec3 position = this.Position;
|
||||
|
||||
// 1. 将Pawn放回地图
|
||||
GenSpawn.Spawn(pawn, position, map, WipeMode.Vanish);
|
||||
PawnComponentsUtility.AddComponentsForSpawn(pawn);
|
||||
compMorphable.SetStoredPawn(null);
|
||||
|
||||
// 2. 对Pawn施加等比例伤害
|
||||
float damageProportion = dinfo.Amount / this.def.statBases.GetStatValueFromList(StatDefOf.MaxHitPoints, 1f);
|
||||
float pawnDamage = pawn.MaxHitPoints * damageProportion;
|
||||
DamageInfo pawnDinfo = new DamageInfo(dinfo.Def, pawnDamage, dinfo.ArmorPenetrationInt, dinfo.Angle, dinfo.Instigator, null, dinfo.Weapon, dinfo.Category, dinfo.IntendedTarget);
|
||||
|
||||
this.Destroy(DestroyMode.Vanish);
|
||||
|
||||
GenSpawn.Spawn(pawn, position, map, WipeMode.Vanish);
|
||||
PawnComponentsUtility.AddComponentsForSpawn(pawn);
|
||||
pawn.TakeDamage(pawnDinfo);
|
||||
|
||||
Messages.Message("PawnTransformer_ForcedRevert".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.NegativeEvent);
|
||||
|
||||
// 3. 移除建筑
|
||||
// 注意:这里不调用base.Destroy(),以避免循环和重复的恢复逻辑
|
||||
this.Destroy(DestroyMode.Vanish);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Destroy(DestroyMode mode)
|
||||
{
|
||||
// 只有在建筑还存在于地图上时,才执行恢复逻辑
|
||||
if (this.Spawned)
|
||||
if (researchEffecter != null)
|
||||
{
|
||||
if (compMorphable != null && compMorphable.StoredPawn != null)
|
||||
researchEffecter.Cleanup();
|
||||
researchEffecter = null;
|
||||
}
|
||||
|
||||
if (this.Spawned && compMorphable != null && compMorphable.StoredPawn != null)
|
||||
{
|
||||
Pawn pawn = compMorphable.StoredPawn;
|
||||
Map map = this.Map;
|
||||
IntVec3 position = this.Position;
|
||||
|
||||
GenSpawn.Spawn(pawn, position, map, WipeMode.Vanish);
|
||||
GenSpawn.Spawn(pawn, this.Position, this.Map, WipeMode.Vanish);
|
||||
PawnComponentsUtility.AddComponentsForSpawn(pawn);
|
||||
|
||||
if (mode == DestroyMode.KillFinalize)
|
||||
@@ -99,7 +244,6 @@ namespace ArachnaeSwarm
|
||||
Messages.Message("PawnTransformer_BuildingDestroyed".Translate(pawn.Named("PAWN"), this.Named("BUILDING")), pawn, MessageTypeDefOf.NegativeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
base.Destroy(mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,22 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
newMorphComp.SetStoredPawn(pawn);
|
||||
}
|
||||
|
||||
// 同步需求值
|
||||
var buildingMorphable = building as Building_Morphable;
|
||||
var refuelableComp = building.GetComp<CompRefuelableNutrition>();
|
||||
|
||||
if (buildingMorphable != null && refuelableComp != null && pawn.needs != null)
|
||||
{
|
||||
if(pawn.needs.food != null)
|
||||
{
|
||||
refuelableComp.Refuel(refuelableComp.Props.fuelCapacity * pawn.needs.food.CurLevelPercentage);
|
||||
}
|
||||
if(pawn.needs.rest != null)
|
||||
{
|
||||
buildingMorphable.virtualRest = buildingMorphable.VirtualRestMax * pawn.needs.rest.CurLevelPercentage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace ArachnaeSwarm
|
||||
public class CompMorphable : ThingComp
|
||||
{
|
||||
private Pawn storedPawn;
|
||||
public bool wasEjectedForFuel = false;
|
||||
public Pawn StoredPawn => storedPawn;
|
||||
|
||||
public void SetStoredPawn(Pawn pawn)
|
||||
@@ -40,6 +41,26 @@ namespace ArachnaeSwarm
|
||||
Building building = (Building)this.parent;
|
||||
Map map = building.Map;
|
||||
|
||||
// 同步燃料到食物
|
||||
var refuelableComp = building.GetComp<CompRefuelableNutrition>();
|
||||
var buildingMorphable = building as Building_Morphable;
|
||||
var needs = storedPawn.needs;
|
||||
|
||||
if (refuelableComp != null && needs?.food != null)
|
||||
{
|
||||
needs.food.CurLevelPercentage = refuelableComp.Fuel / refuelableComp.Props.fuelCapacity;
|
||||
}
|
||||
if (buildingMorphable != null && needs?.rest != null)
|
||||
{
|
||||
needs.rest.CurLevelPercentage = buildingMorphable.virtualRest / buildingMorphable.VirtualRestMax;
|
||||
}
|
||||
|
||||
// 如果是因为燃料耗尽而被弹出,清空Pawn的食物需求
|
||||
if(wasEjectedForFuel && needs?.food != null)
|
||||
{
|
||||
needs.food.CurLevel = 0;
|
||||
}
|
||||
|
||||
// 移除建筑
|
||||
building.DeSpawn(DestroyMode.Vanish);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user