This commit is contained in:
2025-09-11 20:57:42 +08:00
parent 6c1e7743dc
commit f55952befc
5 changed files with 272 additions and 81 deletions

View File

@@ -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,77 +141,107 @@ 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);
// --- 立即强制解除变形 ---
Map map = this.Map;
IntVec3 position = this.Position;
// 1. 将Pawn放回地图
GenSpawn.Spawn(pawn, position, map, WipeMode.Vanish);
PawnComponentsUtility.AddComponentsForSpawn(pawn);
// 2. 对Pawn施加等比例伤害
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);
pawn.TakeDamage(pawnDinfo);
Messages.Message("PawnTransformer_ForcedRevert".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.NegativeEvent);
// 3. 移除建筑
// 注意这里不调用base.Destroy(),以避免循环和重复的恢复逻辑
this.Destroy(DestroyMode.Vanish);
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;
compMorphable.SetStoredPawn(null);
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);
}
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;
GenSpawn.Spawn(pawn, this.Position, this.Map, WipeMode.Vanish);
PawnComponentsUtility.AddComponentsForSpawn(pawn);
if (mode == DestroyMode.KillFinalize)
{
Pawn pawn = compMorphable.StoredPawn;
Map map = this.Map;
IntVec3 position = this.Position;
GenSpawn.Spawn(pawn, position, map, WipeMode.Vanish);
PawnComponentsUtility.AddComponentsForSpawn(pawn);
if (mode == DestroyMode.KillFinalize)
{
Messages.Message("PawnTransformer_BuildingDestroyed".Translate(pawn.Named("PAWN"), this.Named("BUILDING")), pawn, MessageTypeDefOf.NegativeEvent);
}
Messages.Message("PawnTransformer_BuildingDestroyed".Translate(pawn.Named("PAWN"), this.Named("BUILDING")), pawn, MessageTypeDefOf.NegativeEvent);
}
}
base.Destroy(mode);

View File

@@ -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)

View File

@@ -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);