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