杂七杂八

This commit is contained in:
Tourswen
2025-10-27 00:04:47 +08:00
parent 37f140be66
commit 4b5287037e
36 changed files with 969 additions and 255 deletions

View File

@@ -1,22 +1,18 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"WorkspaceRootPath": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_arachnaegravengine.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_arachnaegravengine.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_ejectorgans\\compabilityeffect_ejectorgans.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_ejectorgans\\compabilityeffect_ejectorgans.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\building_comps\\ara_building_refuelingvat\\building_refuelingvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_building_refuelingvat\\building_refuelingvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_genestealer\\compabilityeffect_extractgene.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_genestealer\\compabilityeffect_extractgene.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\hediffs\\ara_hediffterrainspawn\\comphediffterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_hediffterrainspawn\\comphediffterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\compabilityeffect_randomhediff.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\compabilityeffect_randomhediff.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_terrainrestriction\\compabilityeffect_terrainrestriction .cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_terrainrestriction\\compabilityeffect_terrainrestriction .cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -26,7 +22,7 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 2,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
@@ -34,54 +30,42 @@
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Building_RefuelingVat.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ViewState": "AgIAAEQAAAAAAAAAAAAAAEABAAAQAAAAAAAAAA==",
"DocumentIndex": 0,
"Title": "CompAbilityEffect_EjectOrgans.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"RelativeToolTip": "Abilities\\ARA_EjectOrgans\\CompAbilityEffect_EjectOrgans.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAC4AAAAoAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-24T06:16:14.743Z",
"WhenOpened": "2025-10-25T11:49:25.843Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Building_ArachnaeGravEngine.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ArachnaeGravEngine.cs",
"RelativeDocumentMoniker": "Buildings\\Building_ArachnaeGravEngine.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ArachnaeGravEngine.cs",
"RelativeToolTip": "Buildings\\Building_ArachnaeGravEngine.cs",
"ViewState": "AgIAAHwAAAAAAAAAAAAgwJcAAAAJAAAAAAAAAA==",
"DocumentIndex": 1,
"Title": "CompAbilityEffect_ExtractGene.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_Genestealer\\CompAbilityEffect_ExtractGene.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_Genestealer\\CompAbilityEffect_ExtractGene.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_Genestealer\\CompAbilityEffect_ExtractGene.cs",
"RelativeToolTip": "Abilities\\ARA_Genestealer\\CompAbilityEffect_ExtractGene.cs",
"ViewState": "AgIAABEAAAAAAAAAAAAAwCgAAAApAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-24T02:30:45.288Z",
"WhenOpened": "2025-10-25T11:49:07.979Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompHediffTerrainSpawn.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"RelativeDocumentMoniker": "Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"RelativeToolTip": "Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"ViewState": "AgIAAB0AAAAAAAAAAAAswE0AAABHAAAAAAAAAA==",
"Title": "CompAbilityEffect_TerrainRestriction .cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_TerrainRestriction\\CompAbilityEffect_TerrainRestriction .cs",
"RelativeDocumentMoniker": "Abilities\\ARA_TerrainRestriction\\CompAbilityEffect_TerrainRestriction .cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_TerrainRestriction\\CompAbilityEffect_TerrainRestriction .cs",
"RelativeToolTip": "Abilities\\ARA_TerrainRestriction\\CompAbilityEffect_TerrainRestriction .cs",
"ViewState": "AgIAAAAAAAAAAAAAAADwvwQAAAABAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-23T08:00:28.236Z",
"WhenOpened": "2025-10-25T11:48:39.275Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "CompAbilityEffect_RandomHediff.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_RandomHediff.cs",
"RelativeDocumentMoniker": "Abilities\\CompAbilityEffect_RandomHediff.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_RandomHediff.cs",
"RelativeToolTip": "Abilities\\CompAbilityEffect_RandomHediff.cs",
"ViewState": "AgIAALoAAAAAAAAAAAAqwNcAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-22T06:34:08.063Z"
}
]
}

View File

@@ -0,0 +1,158 @@
using RimWorld;
using System.Collections.Generic;
using Verse;
namespace ArachnaeSwarm
{
public class CompAbilityEffect_EjectOrgans : CompAbilityEffect
{
// 定义免疫的 Hediff 列表
private static readonly string[] ImmuneHediffs =
{
"ARA_HiveMindMaster",
"ARA_HiveMindDrone",
"ARA_HiveMindWorker"
};
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Pawn caster = parent.pawn;
Pawn targetPawn = target.Pawn;
if (caster == null || targetPawn == null || targetPawn.Dead)
return;
// 检查目标是否免疫
if (IsTargetImmune(targetPawn))
{
Messages.Message("ARA_TargetBlacklisted".Translate(), targetPawn, MessageTypeDefOf.RejectInput);
return;
}
List<BodyPartRecord> partsToEject = new List<BodyPartRecord>();
// 遍历所有身体部位
foreach (BodyPartRecord part in targetPawn.RaceProps.body.AllParts)
{
// 只检查有spawnThingOnRemoved且未缺失的部位
if (!targetPawn.health.hediffSet.PartIsMissing(part) &&
part.def.spawnThingOnRemoved != null)
{
partsToEject.Add(part);
}
}
if (partsToEject.Count == 0)
return;
// 执行弹出
foreach (BodyPartRecord part in partsToEject)
{
// 生成物品 - spawnThingOnRemoved 是 ThingDef 类型
ThingDef thingDef = part.def.spawnThingOnRemoved;
if (thingDef != null)
{
Thing thing = ThingMaker.MakeThing(thingDef);
GenSpawn.Spawn(thing, caster.Position, caster.Map);
}
// 标记部位缺失
targetPawn.health.AddHediff(HediffDefOf.MissingBodyPart, part);
}
Messages.Message("ARA_EjectedOrgans".Translate(targetPawn.LabelShort), MessageTypeDefOf.NeutralEvent);
}
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
Pawn targetPawn = target.Pawn;
if (targetPawn == null)
return false;
// 检查目标是否免疫
if (IsTargetImmune(targetPawn))
{
if (throwMessages)
Messages.Message("ARA_TargetBlacklisted".Translate(), targetPawn, MessageTypeDefOf.RejectInput, false);
return false;
}
// 检查是否有可弹出的器官
if (!HasEjectableOrgans(targetPawn))
{
if (throwMessages)
Messages.Message("ARA_NoEjectableOrgans".Translate(), targetPawn, MessageTypeDefOf.RejectInput, false);
return false;
}
return base.Valid(target, throwMessages);
}
public override bool GizmoDisabled(out string reason)
{
Pawn caster = parent.pawn;
if (caster == null || caster.Dead)
{
reason = "ARA_CasterDead".Translate();
return true;
}
reason = null;
return false;
}
/// <summary>
/// 检查目标是否免疫器官弹出
/// </summary>
private bool IsTargetImmune(Pawn pawn)
{
if (pawn?.health?.hediffSet?.hediffs == null)
return false;
foreach (Hediff hediff in pawn.health.hediffSet.hediffs)
{
if (hediff?.def != null)
{
string hediffDefName = hediff.def.defName;
foreach (string immuneHediff in ImmuneHediffs)
{
if (hediffDefName == immuneHediff)
return true;
}
}
}
return false;
}
/// <summary>
/// 检查目标是否有可弹出的器官
/// </summary>
private bool HasEjectableOrgans(Pawn pawn)
{
if (pawn?.RaceProps?.body?.AllParts == null)
return false;
foreach (BodyPartRecord part in pawn.RaceProps.body.AllParts)
{
if (!pawn.health.hediffSet.PartIsMissing(part) &&
part.def.spawnThingOnRemoved != null)
{
return true;
}
}
return false;
}
}
public class CompProperties_AbilityEjectOrgans : CompProperties_AbilityEffect
{
public CompProperties_AbilityEjectOrgans()
{
compClass = typeof(CompAbilityEffect_EjectOrgans);
}
}
}

View File

@@ -78,6 +78,7 @@
<Compile Include="Abilities\ARA_AddExtraExp\CompProperties_AddExtraExp.cs" />
<Compile Include="Abilities\ARA_DestroyOwnBodyPart\CompAbilityEffect_DestroyOwnBodyPart.cs" />
<Compile Include="Abilities\ARA_DestroyOwnBodyPart\CompProperties_AbilityDestroyOwnBodyPart.cs" />
<Compile Include="Abilities\ARA_EjectOrgans\CompAbilityEffect_EjectOrgans.cs" />
<Compile Include="Abilities\ARA_Genestealer\CompAbilityEffect_ExtractGene.cs" />
<Compile Include="Abilities\ARA_Genestealer\CompAbilityEffect_InjectGenes.cs" />
<Compile Include="Abilities\ARA_Genestealer\CompProperties_AbilityInjectGenes.cs" />
@@ -115,8 +116,12 @@
<Compile Include="Building_Comps\ARA_Building_RefuelingVat\CompRefuelingVat.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\CompResearchProducer.cs" />
<Compile Include="Building_Comps\ARA_CompInteractiveProducer\JobDriver_StartResearchProduction.cs" />
<Compile Include="Building_Comps\ARA_ForwardClearance\CompForwardClearance.cs" />
<Compile Include="Building_Comps\ARA_ForwardClearance\CompProperties_ForwardClearance.cs" />
<Compile Include="Building_Comps\ARA_ForwardClearance\PlaceWorker_ForwardClearance.cs" />
<Compile Include="Building_Comps\ARA_ProductStorage\CompProductStorage.cs" />
<Compile Include="Building_Comps\ARA_ProductStorage\CompProperties_ProductStorage.cs" />
<Compile Include="Building_Comps\CompBreakdownDisabler.cs" />
<Compile Include="EventSystem\CompOpenCustomUI.cs" />
<Compile Include="EventSystem\Condition.cs" />
<Compile Include="EventSystem\DebugActions.cs" />

View File

@@ -0,0 +1,175 @@
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class CompForwardClearance : ThingComp
{
private bool isBlocked = false;
private int lastCheckedTick = -99999;
private const int CheckInterval = 60; // 每60tick检查一次
public CompProperties_ForwardClearance Props => (CompProperties_ForwardClearance)props;
public bool IsBlocked
{
get
{
if (Find.TickManager.TicksGame - lastCheckedTick > CheckInterval)
{
CheckClearance();
}
return isBlocked;
}
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
CheckClearance();
}
public override void CompTick()
{
base.CompTick();
if (Find.TickManager.TicksGame % CheckInterval == 0)
{
bool previousState = isBlocked;
CheckClearance();
// 状态变化时发送信号
if (isBlocked != previousState)
{
SendStateSignal();
}
}
}
public override void CompTickRare()
{
base.CompTickRare();
CheckClearance();
}
/// <summary>
/// 检查前方是否有遮挡
/// </summary>
public void CheckClearance()
{
lastCheckedTick = Find.TickManager.TicksGame;
bool newBlockedState = IsForwardBlocked(parent.Position, parent.Rotation, parent.Map, Props);
if (newBlockedState != isBlocked)
{
isBlocked = newBlockedState;
SendStateSignal();
}
}
/// <summary>
/// 发送状态信号
/// </summary>
private void SendStateSignal()
{
if (isBlocked)
{
parent.BroadcastCompSignal(Props.blockedSignal);
}
else
{
parent.BroadcastCompSignal(Props.clearSignal);
}
}
/// <summary>
/// 静态方法:检查指定位置和朝向是否有遮挡
/// </summary>
public static bool IsForwardBlocked(IntVec3 position, Rot4 rotation, Map map, CompProperties_ForwardClearance props)
{
if (!position.InBounds(map))
return false;
// 获取前方检查的单元格
List<IntVec3> checkCells = GetForwardCells(position, rotation, props.checkDistance);
foreach (IntVec3 cell in checkCells)
{
if (!cell.InBounds(map))
continue;
// 检查屋顶
if (props.checkRoof && map.roofGrid.Roofed(cell))
return true;
// 检查建筑
if (props.checkBuildings)
{
foreach (Thing thing in map.thingGrid.ThingsListAt(cell))
{
if (thing.def.passability == Traversability.Impassable &&
!thing.def.IsFrame && !thing.def.IsBlueprint)
{
return true;
}
}
}
// 检查植物
if (props.checkPlants)
{
Plant plant = cell.GetPlant(map);
if (plant != null && plant.def.passability == Traversability.Impassable)
return true;
}
// 检查地形
if (props.checkTerrain)
{
TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
if (terrain != null && terrain.passability == Traversability.Impassable)
return true;
}
}
return false;
}
/// <summary>
/// 获取前方需要检查的单元格
/// </summary>
public static List<IntVec3> GetForwardCells(IntVec3 position, Rot4 rotation, int distance)
{
List<IntVec3> cells = new List<IntVec3>();
// 根据朝向确定检查方向
IntVec3 direction = rotation.FacingCell;
for (int i = 1; i <= distance; i++)
{
IntVec3 checkCell = position + direction * i;
cells.Add(checkCell);
}
return cells;
}
public override string CompInspectStringExtra()
{
if (IsBlocked)
{
return "ForwardClearanceBlocked".Translate(Props.checkDistance);
}
return "ForwardClearanceClear".Translate(Props.checkDistance);
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref isBlocked, "isBlocked", false);
Scribe_Values.Look(ref lastCheckedTick, "lastCheckedTick", -99999);
}
}
}

View File

@@ -0,0 +1,23 @@
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_ForwardClearance : CompProperties
{
public int checkDistance = 5; // 检查距离(格数)
public bool checkRoof = true; // 是否检查屋顶
public bool checkBuildings = true; // 是否检查建筑
public bool checkPlants = false; // 是否检查植物
public bool checkTerrain = false; // 是否检查地形
// 信号定义
public string blockedSignal = "ForwardBlocked";
public string clearSignal = "ForwardClear";
public CompProperties_ForwardClearance()
{
compClass = typeof(CompForwardClearance);
}
}
}

View File

@@ -0,0 +1,112 @@
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class PlaceWorker_ForwardClearance : PlaceWorker
{
private static List<IntVec3> forwardCells = new List<IntVec3>();
public override void DrawGhost(ThingDef def, IntVec3 center, Rot4 rot, Color ghostCol, Thing thing = null)
{
// 获取清除检查组件属性
CompProperties_ForwardClearance compProps = def.GetCompProperties<CompProperties_ForwardClearance>();
if (compProps == null) return;
// 获取前方检查的单元格
forwardCells.Clear();
forwardCells = CompForwardClearance.GetForwardCells(center, rot, compProps.checkDistance);
// 绘制检查区域
Color cellColor = GetBlockedColor(center, rot, Find.CurrentMap, compProps);
GenDraw.DrawFieldEdges(forwardCells, cellColor);
// 绘制方向指示器
DrawDirectionIndicator(center, rot, cellColor);
}
public override AcceptanceReport AllowsPlacing(BuildableDef def, IntVec3 center, Rot4 rot, Map map, Thing thingToIgnore = null, Thing thing = null)
{
// 将 BuildableDef 转换为 ThingDef 来获取组件属性
ThingDef thingDef = def as ThingDef;
if (thingDef == null) return true;
CompProperties_ForwardClearance compProps = thingDef.GetCompProperties<CompProperties_ForwardClearance>();
if (compProps == null) return true;
// 检查前方是否有遮挡
if (CompForwardClearance.IsForwardBlocked(center, rot, map, compProps))
{
return new AcceptanceReport("ForwardClearanceBlockedPlacement".Translate(compProps.checkDistance));
}
return true;
}
public override void PostPlace(Map map, BuildableDef def, IntVec3 loc, Rot4 rot)
{
// 将 BuildableDef 转换为 ThingDef 来获取组件属性
ThingDef thingDef = def as ThingDef;
if (thingDef == null) return;
CompProperties_ForwardClearance compProps = thingDef.GetCompProperties<CompProperties_ForwardClearance>();
if (compProps == null) return;
// 放置后检查并发送警告
if (CompForwardClearance.IsForwardBlocked(loc, rot, map, compProps))
{
Messages.Message("WarningForwardClearanceBlocked".Translate(def.LabelCap, compProps.checkDistance),
MessageTypeDefOf.CautionInput, historical: false);
}
}
/// <summary>
/// 根据遮挡状态获取颜色
/// </summary>
private Color GetBlockedColor(IntVec3 center, Rot4 rot, Map map, CompProperties_ForwardClearance props)
{
if (CompForwardClearance.IsForwardBlocked(center, rot, map, props))
{
return Color.red;
}
return Color.green;
}
/// <summary>
/// 绘制方向指示器
/// </summary>
private void DrawDirectionIndicator(IntVec3 center, Rot4 rot, Color color)
{
Vector3 startPos = center.ToVector3Shifted();
Vector3 direction = rot.FacingCell.ToVector3();
Vector3 endPos = startPos + direction * 2f; // 2格长的方向线
// 使用正确的绘制方法
Material lineMaterial = SolidColorMaterials.SimpleSolidColorMaterial(color);
// 绘制方向线
GenDraw.DrawLineBetween(startPos, endPos, lineMaterial, 0.2f);
// 绘制方向箭头
DrawDirectionArrow(endPos, direction, lineMaterial);
}
/// <summary>
/// 绘制方向箭头
/// </summary>
private void DrawDirectionArrow(Vector3 pos, Vector3 direction, Material material)
{
float arrowSize = 0.5f;
Vector3 perpendicular = new Vector3(-direction.z, 0f, direction.x);
Vector3 arrowTip = pos + direction * arrowSize;
Vector3 arrowLeft = pos - direction * arrowSize * 0.5f + perpendicular * arrowSize * 0.5f;
Vector3 arrowRight = pos - direction * arrowSize * 0.5f - perpendicular * arrowSize * 0.5f;
GenDraw.DrawLineBetween(arrowTip, arrowLeft, material, 0.15f);
GenDraw.DrawLineBetween(arrowTip, arrowRight, material, 0.15f);
}
}
}

View File

@@ -0,0 +1,162 @@
using System.Collections.Generic;
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class CompBreakdownDisabler : ThingComp
{
private CompBreakdownable breakdownable;
private CompPowerTrader powerTrader;
private CompRefuelable refuelable;
private bool wasBrokenDown = false;
public CompProperties_BreakdownDisabler Props => (CompProperties_BreakdownDisabler)props;
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// 获取相关的组件
breakdownable = parent.GetComp<CompBreakdownable>();
powerTrader = parent.GetComp<CompPowerTrader>();
refuelable = parent.GetComp<CompRefuelable>();
// 立即修复任何现有的损坏
if (breakdownable != null && breakdownable.BrokenDown)
{
FixBreakdownImmediately();
}
}
public override void CompTick()
{
base.CompTick();
// 每 tick 检查是否需要修复
if (breakdownable != null && breakdownable.BrokenDown && !wasBrokenDown)
{
FixBreakdownImmediately();
}
wasBrokenDown = breakdownable?.BrokenDown ?? false;
}
public override void CompTickRare()
{
base.CompTickRare();
// 每 250 ticks 检查一次(更高效)
if (breakdownable != null && breakdownable.BrokenDown && !wasBrokenDown)
{
FixBreakdownImmediately();
}
wasBrokenDown = breakdownable?.BrokenDown ?? false;
}
public override void ReceiveCompSignal(string signal)
{
base.ReceiveCompSignal(signal);
// 监听损坏信号并立即修复
if (signal == "Breakdown" && breakdownable != null)
{
FixBreakdownImmediately();
}
}
/// <summary>
/// 立即修复损坏并消除所有影响
/// </summary>
private void FixBreakdownImmediately()
{
if (breakdownable == null) return;
// 记录修复前的状态
bool wasPoweredOn = powerTrader?.PowerOn ?? true;
float fuelAmount = refuelable?.Fuel ?? 0f;
// 调用修复方法
breakdownable.Notify_Repaired();
// 恢复电力状态
if (powerTrader != null)
{
// 确保电力交易器恢复正常
if (wasPoweredOn && !powerTrader.PowerOn)
{
powerTrader.PowerOn = true;
}
}
// 恢复燃料状态
if (refuelable != null)
{
refuelable.ConsumeFuel(0f); // 触发更新
}
// 发送修复完成的信号
parent.BroadcastCompSignal("Repaired");
// 记录日志(可选)
if (Props.logRepairs)
{
Log.Message($"[BreakdownDisabler] Automatically repaired {parent.Label} at {parent.Position}");
}
}
public override void PostDraw()
{
base.PostDraw();
// 可选:在修复时显示视觉反馈
if (breakdownable != null && breakdownable.BrokenDown)
{
// 立即在下一次绘制前修复
if (Find.TickManager.TicksGame % 10 == 0) // 每10 tick尝试修复一次
{
FixBreakdownImmediately();
}
}
}
public override string CompInspectStringExtra()
{
if (breakdownable != null && breakdownable.BrokenDown)
{
return "AutoRepairSystemActive".Translate();
}
return null;
}
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
if (breakdownable != null && breakdownable.BrokenDown)
{
yield return new Command_Action
{
defaultLabel = "ForceAutoRepair".Translate(),
defaultDesc = "ForceAutoRepairDesc".Translate(),
icon = TexCommand.DesirePower,
action = delegate
{
FixBreakdownImmediately();
Messages.Message("ForcedAutoRepairComplete".Translate(parent.Label), parent, MessageTypeDefOf.PositiveEvent);
}
};
}
}
}
public class CompProperties_BreakdownDisabler : CompProperties
{
public bool logRepairs = false; // 是否在控制台记录修复日志
public bool showVisualFeedback = true; // 是否显示视觉反馈
public CompProperties_BreakdownDisabler()
{
compClass = typeof(CompBreakdownDisabler);
}
}
}

View File

@@ -15,13 +15,19 @@ namespace ArachnaeSwarm
// 自定义图形组件
private Graphic customBaseGraphic; // 基座图形 - 始终静止
private Graphic customOrbGraphic; // 球体图形 - 正常状态下
private Graphic customOrbGraphic; // 球体图形 - 正常状态下
private Graphic customCooldownGraphic; // 冷却球体图形 - 冷却状态下静止
// 渲染相关的变量
private float orbFloatOffset = 0f;
// 搏动相关的变量
private float orbPulseScale = 1f; // 球体缩放比例
private float orbPulseIntensity = 0f; // 搏动强度
private int lastTick = -1;
// 记录初始缩放值
private Vector2 baseInitialScale;
private Vector2 orbInitialScale;
private Vector2 cooldownInitialScale;
public ArachnaeGravEngineExtension ModExtension
{
get
@@ -42,17 +48,18 @@ namespace ArachnaeSwarm
if (customBaseGraphic == null)
{
string basePath = ModExtension?.baseTexPath ?? "ArachnaeSwarm/Building/ARA_HiveShip_Heart_Pedestal";
baseInitialScale = ModExtension?.baseScale ?? Vector2.one * 3f;
customBaseGraphic = GraphicDatabase.Get<Graphic_Single>(
basePath,
ShaderDatabase.Cutout,
def.graphicData?.drawSize ?? Vector2.one * 3f,
baseInitialScale,
Color.white);
}
return customBaseGraphic;
}
}
// 球体图形 - 正常状态下
// 球体图形 - 正常状态下
private Graphic CustomOrbGraphic
{
get
@@ -60,10 +67,11 @@ namespace ArachnaeSwarm
if (customOrbGraphic == null)
{
string orbPath = ModExtension?.orbTexPath ?? "ArachnaeSwarm/Building/ARA_HiveShip_Heart";
orbInitialScale = ModExtension?.orbScale ?? Vector2.one * 2f;
customOrbGraphic = GraphicDatabase.Get<Graphic_Single>(
orbPath,
ShaderDatabase.Cutout,
Vector2.one * 2f,
orbInitialScale,
Color.white);
}
return customOrbGraphic;
@@ -78,10 +86,11 @@ namespace ArachnaeSwarm
if (customCooldownGraphic == null)
{
string cooldownPath = ModExtension?.cooldownTexPath ?? "ArachnaeSwarm/Building/ARA_HiveShip_Heart_Cooldown";
cooldownInitialScale = ModExtension?.cooldownScale ?? Vector2.one * 2f;
customCooldownGraphic = GraphicDatabase.Get<Graphic_Single>(
cooldownPath,
ShaderDatabase.Cutout,
Vector2.one * 2f, // 注意:这里应该和球体大小一致,而不是基座大小
cooldownInitialScale,
Color.white);
}
return customCooldownGraphic;
@@ -110,19 +119,6 @@ namespace ArachnaeSwarm
}
yield return gizmo;
}
// 可以添加虫群特有的 gizmo
if (DebugSettings.ShowDevGizmos)
{
yield return new Command_Action
{
defaultLabel = "DEV: Spawn Arachnae Swarm",
action = delegate
{
SpawnArachnaeSwarm();
}
};
}
}
// 重写 GetFloatMenuOptions 方法,移除检查选项
@@ -162,29 +158,16 @@ namespace ArachnaeSwarm
text = text.Replace("GravEngineNotInspected".Translate() + "\n", "");
}
// 添加虫群特有的信息
if (!string.IsNullOrEmpty(text))
{
text += "\n";
}
text += "ArachnaeSwarmControl".Translate();
return text;
}
// 虫群特有的方法
private void SpawnArachnaeSwarm()
{
Messages.Message("ArachnaeSwarmActivated".Translate(), this, MessageTypeDefOf.PositiveEvent);
}
// 完全重写 DrawAt 方法,实现自定义渲染系统
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
{
// 不调用基类的 DrawAt完全自定义渲染
// 更新动效果
UpdateFloatOffsets();
// 更新动效果
UpdatePulseEffect();
// 基础位置 - 底座始终静止
Vector3 baseDrawPos = drawLoc;
@@ -192,49 +175,75 @@ namespace ArachnaeSwarm
// 球体位置 - 根据状态决定
Vector3 orbDrawPos = drawLoc;
orbDrawPos.y += 0.03658537f; // 基础高度偏移
orbDrawPos.z += 0.05f; // 球体稍微向前偏移,避免与底座重叠
orbDrawPos.y += 0.05f; // 球体稍微向前偏移,避免与底座重叠
orbDrawPos.z += 0.05f;
// 始终绘制底座
CustomBaseGraphic.Draw(baseDrawPos, flip ? base.Rotation.Opposite : base.Rotation, this, 0f);
// 根据状态绘制球体
if (Find.TickManager.TicksGame >= cooldownCompleteTick)
{
// 正常状态:球体浮动
orbDrawPos.y += orbFloatOffset;
CustomOrbGraphic.Draw(orbDrawPos, flip ? base.Rotation.Opposite : base.Rotation, this, 0f);
}
else
{
// 冷却状态:球体静止,使用冷却贴图
CustomCooldownGraphic.Draw(orbDrawPos, flip ? base.Rotation.Opposite : base.Rotation, this, 0f);
}
DrawPulsingOrb(orbDrawPos, flip ? base.Rotation.Opposite : base.Rotation, CustomOrbGraphic);
}
// 更新浮动效果 - 只更新球体浮动
private void UpdateFloatOffsets()
// 绘制搏动的球体
private void DrawPulsingOrb(Vector3 drawPos, Rot4 rotation, Graphic graphic)
{
// 计算搏动缩放,基于初始缩放
Vector3 baseScale = new Vector3(orbInitialScale.x, 1f, orbInitialScale.y);
Vector3 pulseScale = Vector3.one * orbPulseScale;
Vector3 finalScale = Vector3.Scale(baseScale, pulseScale);
// 创建缩放矩阵
Matrix4x4 matrix = Matrix4x4.TRS(drawPos, rotation.AsQuat, finalScale);
// 获取材质
Material material = graphic.MatSingle;
// 绘制缩放后的球体
Graphics.DrawMesh(MeshPool.plane10, matrix, material, 0);
}
// 更新搏动效果 - 类似心脏搏动的放大缩小
private void UpdatePulseEffect()
{
int currentTick = Find.TickManager.TicksGame;
if (currentTick != lastTick)
{
// 只在非冷却状态下更新球体
// 只在非冷却状态下更新球体
if (Find.TickManager.TicksGame >= cooldownCompleteTick)
{
// 球体浮动 - 上下浮动
orbFloatOffset = Mathf.Sin((float)currentTick / 100f) * 0.1f;
// 心脏搏动效果
float time = (float)currentTick / 60f; // 控制搏动速度
// 使用更自然的搏动曲线
// 快速收缩然后缓慢舒张,类似真实心跳
float pulse = Mathf.Sin(time * 3f) * 0.5f + 0.5f; // 0到1的范围
// 应用非线性变换,使搏动更自然
pulse = EaseInOutCubic(pulse);
// 计算缩放比例 (0.9到1.1的范围)
orbPulseScale = 0.9f + pulse * 0.2f;
// 搏动强度用于可能的其他效果
orbPulseIntensity = pulse;
}
else
{
// 冷却状态下球体不浮动
orbFloatOffset = 0f;
// 冷却状态下球体不搏动,保持正常大小
orbPulseScale = 1f;
orbPulseIntensity = 0f;
}
lastTick = currentTick;
}
}
// 缓动函数 - 使搏动更自然
private float EaseInOutCubic(float x)
{
return x < 0.5f ? 4f * x * x * x : 1f - Mathf.Pow(-2f * x + 2f, 3f) / 2f;
}
// 重写额外的绘制阶段(如果需要)
public override void DrawExtraSelectionOverlays()
{
@@ -264,11 +273,16 @@ namespace ArachnaeSwarm
}
}
// 扩展 ModExtension 定义,添加基座纹理路径
// 扩展 ModExtension 定义,添加基座纹理路径和缩放参数
public class ArachnaeGravEngineExtension : DefModExtension
{
public string baseTexPath; // 基座纹理路径 - 始终静止
public string orbTexPath; // 球体纹理路径 - 正常状态下
public string orbTexPath; // 球体纹理路径 - 正常状态下
public string cooldownTexPath; // 冷却球体纹理路径 - 冷却状态下静止
// 新增:各部分的初始缩放比例
public Vector2 baseScale = Vector2.one * 3f; // 基座默认缩放
public Vector2 orbScale = Vector2.one * 2f; // 球体默认缩放
public Vector2 cooldownScale = Vector2.one * 2f; // 冷却球体默认缩放
}
}