diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index a0f17fb..91b8045 100644
Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ
diff --git a/1.6/1.6/Defs/AbilityDefs/Ability_Morph.xml b/1.6/1.6/Defs/AbilityDefs/Ability_Morph.xml
index 4e70f5e..0ff611f 100644
--- a/1.6/1.6/Defs/AbilityDefs/Ability_Morph.xml
+++ b/1.6/1.6/Defs/AbilityDefs/Ability_Morph.xml
@@ -6,14 +6,21 @@
ARA_Ability_Morph
将自己转换为一个坚固的静态建筑形态,或从建筑形态恢复。
- UI/Commands/Attack
+ UI/Commands/Attack
600
+ Misc12
+ false
+ false
Verb_CastAbility
- 1.5
- 0
+ false
+ false
+ true
+ 1
+ 19.9
+ false
- true
+ True
@@ -23,53 +30,56 @@
-
+
ARA_MorphableResearchBench
+ true
+ (0,0,-1)
- 一个供阿拉克涅虫族进行研究的活体结构,可以让虫群尽情地探索变异和进化方向。
+ 一个供阿拉克涅虫族进行研究的活体结构,可以让虫群尽情地探索变异和进化方向。其研究能力完全取决于内部的阿拉克涅织域种。
ArachnaeSwarm.Building_Morphable
+ Normal
(3,3)
-
- 0
-
-
-
- 50
-
ArachnaeSwarm/Building/ARA_ResearchBench
Graphic_Multi
CutoutComplex
(3,4.5)
- false
- 0
Building
- PassThroughOnly
+ Impassable
+ false
+ 0.8
+ 0
ARA_Creep
- 50
250
2800
1.0
+ 1.0
-
- PlaceWorker_PreventInteractionSpotOverlap
-
- 0.8
- (0,0,-1)
- true
2600
Item
Laboratory
0.8
+
ResearchSpeedFactor
-
+
+
+
+
+ Foods
+
+
+ 10.0
+ false
+ 营养
+ 没有营养
+
diff --git a/Source/ArachnaeSwarm/Morphable/Building_Morphable.cs b/Source/ArachnaeSwarm/Morphable/Building_Morphable.cs
index 3330862..c7cb295 100644
--- a/Source/ArachnaeSwarm/Morphable/Building_Morphable.cs
+++ b/Source/ArachnaeSwarm/Morphable/Building_Morphable.cs
@@ -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();
+ this.compRefuelable = GetComp();
}
+ 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 inspectStrings = new List();
+ 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);
diff --git a/Source/ArachnaeSwarm/Morphable/CompAbilityEffect_Transform.cs b/Source/ArachnaeSwarm/Morphable/CompAbilityEffect_Transform.cs
index a5614f5..0c13b61 100644
--- a/Source/ArachnaeSwarm/Morphable/CompAbilityEffect_Transform.cs
+++ b/Source/ArachnaeSwarm/Morphable/CompAbilityEffect_Transform.cs
@@ -38,6 +38,22 @@ namespace ArachnaeSwarm
{
newMorphComp.SetStoredPawn(pawn);
}
+
+ // 同步需求值
+ var buildingMorphable = building as Building_Morphable;
+ var refuelableComp = building.GetComp();
+
+ 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)
diff --git a/Source/ArachnaeSwarm/Morphable/CompMorphable.cs b/Source/ArachnaeSwarm/Morphable/CompMorphable.cs
index d1a35ae..61c7112 100644
--- a/Source/ArachnaeSwarm/Morphable/CompMorphable.cs
+++ b/Source/ArachnaeSwarm/Morphable/CompMorphable.cs
@@ -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();
+ 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);