This commit is contained in:
2025-10-29 12:02:51 +08:00
parent 9118e6ee58
commit e8d96472ee
11 changed files with 1081 additions and 198 deletions

Binary file not shown.

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<!-- 能力定义 -->
<AbilityDef>
<defName>ARA_SpawnFlyOverTest</defName>
<label>召唤飞越物体</label>
<description>测试召唤不同类型的飞越物体</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</iconPath>
<cooldownTicksRange>1</cooldownTicksRange>
<hotKey>Misc12</hotKey>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<drawAimPie>false</drawAimPie>
<requireLineOfSight>false</requireLineOfSight>
<nonInterruptingSelfCast>true</nonInterruptingSelfCast>
<warmupTime>1</warmupTime>
<range>19.9</range>
<targetable>true</targetable>
<targetParams>
<canTargetSelf>True</canTargetSelf>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilitySpawnFlyOver">
<flyOverDef>ARA_HiveShip</flyOverDef>
<flyOverType>Standard</flyOverType>
<flightSpeed>0.01</flightSpeed>
<altitude>20</altitude>
<startPosition>MapEdge</startPosition>
<endPosition>OppositeMapEdge</endPosition>
<playFlyOverSound>true</playFlyOverSound>
</li>
</comps>
</AbilityDef>
<!-- 能力定义 -->
<AbilityDef>
<defName>ARA_Spawn_ARA_HiveCorvette_Rocket</defName>
<label>天巫攻击:酸烧炮扫射</label>
<description>召唤天巫种兽虫,高速掠过战场,使用其迅捷天灾炮对目标区域发起打击</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</iconPath>
<cooldownTicksRange>1</cooldownTicksRange>
<hotKey>Misc12</hotKey>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<drawAimPie>false</drawAimPie>
<requireLineOfSight>false</requireLineOfSight>
<warmupTime>1</warmupTime>
<range>120</range>
<targetable>true</targetable>
<targetParams>
<canTargetSelf>false</canTargetSelf>
<canTargetLocations>true</canTargetLocations>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilitySpawnFlyOver">
<flyOverDef>ARA_HiveCorvette</flyOverDef>
<flyOverType>GroundStrafing</flyOverType>
<flightSpeed>5</flightSpeed>
<altitude>20</altitude>
<playFlyOverSound>true</playFlyOverSound>
<approachType>Perpendicular</approachType>
<!-- 扫射参数 -->
<enableGroundStrafing>true</enableGroundStrafing>
<strafeWidth>3</strafeWidth>
<strafeLength>25</strafeLength>
<strafeProjectile>Proj_ARA_HiveCorvette</strafeProjectile>
<strafeFireChance>0.23</strafeFireChance>
<!-- 可视化 -->
<showStrafePreview>true</showStrafePreview>
<strafePreviewColor>(1.0,0.3,0.1,0.2)</strafePreviewColor>
</li>
</comps>
</AbilityDef>
<!-- 能力定义 -->
<AbilityDef>
<defName>ARA_Spawn_ARA_HiveCorvette_Bombardment</defName>
<label>天巫攻击:酸液轰炸</label>
<description>召唤天巫种兽虫,慢速掠过战场,对大范围目标区域进行酸团轰炸</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</iconPath>
<cooldownTicksRange>1</cooldownTicksRange>
<hotKey>Misc12</hotKey>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<drawAimPie>false</drawAimPie>
<requireLineOfSight>false</requireLineOfSight>
<warmupTime>1</warmupTime>
<range>120</range>
<targetable>true</targetable>
<targetParams>
<canTargetSelf>false</canTargetSelf>
<canTargetLocations>true</canTargetLocations>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilitySpawnFlyOver">
<flyOverDef>ARA_HiveCorvette_Bombardment</flyOverDef>
<flyOverType>GroundStrafing</flyOverType>
<flightSpeed>2</flightSpeed>
<altitude>20</altitude>
<playFlyOverSound>true</playFlyOverSound>
<approachType>Perpendicular</approachType>
<!-- 扫射参数 -->
<enableGroundStrafing>true</enableGroundStrafing>
<strafeWidth>6</strafeWidth>
<strafeLength>15</strafeLength>
<strafeProjectile>Bullet_ARA_RW_Acid_Mortar</strafeProjectile>
<strafeFireChance>0.3</strafeFireChance>
<!-- 可视化 -->
<showStrafePreview>true</showStrafePreview>
<strafePreviewColor>(1.0,0.3,0.1,0.2)</strafePreviewColor>
</li>
</comps>
</AbilityDef>
<!-- 能力定义 -->
<AbilityDef>
<defName>ARA_Spawn_ARA_HiveCorvette_Watch</defName>
<label>天巫巡航:血棘炮猎杀</label>
<description>召唤天巫种兽虫,中速掠过战场,使用血链棘刺炮监视并射击路径上的所有敌人。</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</iconPath>
<cooldownTicksRange>1</cooldownTicksRange>
<hotKey>Misc12</hotKey>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<drawAimPie>false</drawAimPie>
<requireLineOfSight>false</requireLineOfSight>
<warmupTime>1</warmupTime>
<range>120</range>
<targetable>true</targetable>
<targetParams>
<canTargetSelf>false</canTargetSelf>
<canTargetLocations>true</canTargetLocations>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilitySpawnFlyOver">
<flyOverDef>ARA_HiveCorvette</flyOverDef>
<flyOverType>SectorSurveillance</flyOverType>
<flightSpeed>3.5</flightSpeed>
<altitude>20</altitude>
<playFlyOverSound>true</playFlyOverSound>
<approachType>Perpendicular</approachType>
<!-- 只传递信号,不传递具体参数 -->
<enableSectorSurveillance>true</enableSectorSurveillance>
<!-- 预览配置 -->
<showSectorPreview>true</showSectorPreview>
<strafeWidth>4</strafeWidth> <!-- 用于预览的近似宽度 -->
<sectorPreviewColor>(0.3,0.7,1.0,0.3)</sectorPreviewColor>
</li>
</comps>
</AbilityDef>
</Defs>

View File

@@ -37,82 +37,4 @@
</li>
</comps>
</AbilityDef>
<!-- 能力定义 -->
<AbilityDef>
<defName>ARA_SpawnFlyOverTest</defName>
<label>召唤飞越物体</label>
<description>测试召唤不同类型的飞越物体</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</iconPath>
<cooldownTicksRange>1</cooldownTicksRange>
<hotKey>Misc12</hotKey>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<drawAimPie>false</drawAimPie>
<requireLineOfSight>false</requireLineOfSight>
<nonInterruptingSelfCast>true</nonInterruptingSelfCast>
<warmupTime>1</warmupTime>
<range>19.9</range>
<targetable>true</targetable>
<targetParams>
<canTargetSelf>True</canTargetSelf>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilitySpawnFlyOver">
<flyOverDef>ARA_HiveShip</flyOverDef>
<flyOverType>Standard</flyOverType>
<flightSpeed>0.01</flightSpeed>
<altitude>20</altitude>
<startPosition>MapEdge</startPosition>
<endPosition>OppositeMapEdge</endPosition>
<playFlyOverSound>true</playFlyOverSound>
</li>
</comps>
</AbilityDef>
<!-- 能力定义 -->
<AbilityDef>
<defName>ARA_SpawnFlyAttackerTest</defName>
<label>召唤攻击机物体</label>
<description>测试召唤不同类型的飞越物体</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</iconPath>
<cooldownTicksRange>1</cooldownTicksRange>
<hotKey>Misc12</hotKey>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<drawAimPie>false</drawAimPie>
<requireLineOfSight>false</requireLineOfSight>
<warmupTime>1</warmupTime>
<range>19.9</range>
<targetable>true</targetable>
<targetParams>
<canTargetSelf>false</canTargetSelf>
<canTargetLocations>true</canTargetLocations>
</targetParams>
</verbProperties>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AbilitySpawnFlyOver">
<flyOverDef>ARA_HiveCorvette</flyOverDef>
<flyOverType>GroundStrafing</flyOverType>
<flightSpeed>1</flightSpeed>
<altitude>20</altitude>
<playFlyOverSound>true</playFlyOverSound>
<approachType>Perpendicular</approachType>
<!-- 扫射参数 -->
<enableGroundStrafing>true</enableGroundStrafing>
<strafeWidth>0.5</strafeWidth>
<strafeLength>1</strafeLength>
<strafeProjectile>Bullet_Shell_AntigrainWarhead</strafeProjectile>
<strafeFireChance>1</strafeFireChance>
<!-- 可视化 -->
<showStrafePreview>true</showStrafePreview>
<strafePreviewColor>(1.0,0.3,0.1,0.2)</strafePreviewColor>
</li>
</comps>
</AbilityDef>
</Defs>

View File

@@ -176,7 +176,7 @@
</ThingDef>
<ThingDef Parent="EtherealThingBase">
<defName>ARA_HiveCorvette</defName>
<label>天巫虫巢舰</label>
<label>天巫种兽虫(天灾炮)</label>
<thingClass>ArachnaeSwarm.FlyOver</thingClass>
<tickerType>Normal</tickerType>
<drawerType>RealtimeOnly</drawerType>
@@ -215,8 +215,61 @@
<altitudeLayer>MetaOverlays</altitudeLayer>
<comps>
<li Class="ArachnaeSwarm.CompProperties_GroundStrafing">
<projectileDef>Bullet_Shell_AntigrainWarhead</projectileDef>
<range>35</range>
<projectileDef>Proj_ARA_HiveCorvette</projectileDef>
<range>50</range>
</li>
<li Class="ArachnaeSwarm.CompProperties_SectorSurveillance">
<projectileDef>ARA_Bullet_SniperCannon</projectileDef>
<sectorAngle>90</sectorAngle> <!-- 扇形角度 -->
<sectorRange>50</sectorRange> <!-- 射程 -->
<shotCount>3</shotCount> <!-- 发射次数 -->
<shotInterval>0.3</shotInterval> <!-- 发射间隔 -->
</li>
</comps>
</ThingDef>
<ThingDef Parent="EtherealThingBase">
<defName>ARA_HiveCorvette_Bombardment</defName>
<label>天巫种兽虫(酸液轰炸)</label>
<thingClass>ArachnaeSwarm.FlyOver</thingClass>
<tickerType>Normal</tickerType>
<drawerType>RealtimeOnly</drawerType>
<graphicData>
<!-- <texPath>ArachnaeSwarm/Weapon/ARA_Weapon_Empty</texPath> -->
<texPath>ArachnaeSwarm/FlyOverThing/ARA_HiveCorvette_Shadow</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>TransparentPostLight</shaderType>
<drawSize>(20,30)</drawSize>
<color>(195,195,195,45)</color>
</graphicData>
<castEdgeShadows>false</castEdgeShadows>
<staticSunShadowHeight>0</staticSunShadowHeight>
<skyfaller>
<shadow>ArachnaeSwarm/Weapon/ARA_Weapon_Empty</shadow>
<shadowSize>(0, 0)</shadowSize>
<motesPerCell>0</motesPerCell>
<floatingSound>FlyOver/Flying</floatingSound>
<impactSound>FlyOver/Landing</impactSound>
</skyfaller>
<modExtensions>
<li Class="ArachnaeSwarm.FlyOverShadowExtension">
<customShadowPath>ArachnaeSwarm/Weapon/ARA_Weapon_Empty</customShadowPath>
<useCustomShadow>true</useCustomShadow>
<shadowIntensity>0.8</shadowIntensity>
<minShadowAlpha>0</minShadowAlpha>
<maxShadowAlpha>0</maxShadowAlpha>
<minShadowScale>0</minShadowScale>
<maxShadowScale>0</maxShadowScale>
</li>
</modExtensions>
<seeThroughFog>true</seeThroughFog>
<useHitPoints>false</useHitPoints>
<selectable>false</selectable>
<alwaysHaulable>false</alwaysHaulable>
<altitudeLayer>MetaOverlays</altitudeLayer>
<comps>
<li Class="ArachnaeSwarm.CompProperties_GroundStrafing">
<projectileDef>Bullet_ARA_RW_Acid_Mortar</projectileDef>
<range>15</range>
</li>
</comps>
</ThingDef>
@@ -266,7 +319,7 @@
<projectile>
<damageDef>Bomb</damageDef>
<explosionRadius>2.9</explosionRadius>
<speed>120</speed>
<speed>320</speed>
<filth>Filth_SpentAcid</filth>
<filthCount>2</filthCount>
<explosionSpawnsSingleFilth>true</explosionSpawnsSingleFilth>

View File

@@ -1,37 +1,41 @@
{
"Version": 1,
"WorkspaceRootPath": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\flyover\\ara_groundstrafing\\compgroundstrafing.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_groundstrafing\\compgroundstrafing.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\\flyover\\ara_sectorsurveillance\\compsectorsurveillance.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_sectorsurveillance\\compsectorsurveillance.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\\flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.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\\flyover\\ara_spawnflyover\\compproperties_abilityspawnflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_spawnflyover\\compproperties_abilityspawnflyover.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\\flyover\\thingclassflyover.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\\flyover\\thingclassflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\thingclassflyover.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\\flyover\\ara_flyoverescort\\compflyoverescort.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\\flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_spawnflyover\\compabilityeffect_spawnflyover.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\\flyover\\ara_spawnflyover\\compproperties_abilityspawnflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_spawnflyover\\compproperties_abilityspawnflyover.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\\flyover\\ara_groundstrafing\\compgroundstrafing.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_groundstrafing\\compgroundstrafing.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\\flyover\\ara_flyoverescort\\compflyoverescort.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_flyoverescort\\compflyoverescort.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\\flyover\\ara_flyoverescort\\compproperties_flyoverescort.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\\flyover\\ara_flyoverescort\\compproperties_flyoverescort.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_flyoverescort\\compproperties_flyoverescort.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\\flyover\\ara_shipartillery\\compshipartillery.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\\flyover\\ara_shipartillery\\compshipartillery.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_shipartillery\\compshipartillery.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\\flyover\\ara_shipartillery\\compproperties_shipartillery.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\\flyover\\ara_shipartillery\\compproperties_shipartillery.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_shipartillery\\compproperties_shipartillery.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
@@ -51,62 +55,74 @@
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "CompGroundStrafing.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"RelativeToolTip": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"ViewState": "AgIAAB0AAAAAAAAAAAAQwCkAAAAIAAAAAAAAAA==",
"Title": "CompSectorSurveillance.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"RelativeToolTip": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs",
"ViewState": "AgIAACMBAAAAAAAAAAAqwDkBAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-28T16:19:23.118Z",
"WhenOpened": "2025-10-29T02:30:42.063Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"DocumentIndex": 4,
"Title": "CompGroundStrafing.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"RelativeToolTip": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs",
"ViewState": "AgIAAB0AAAAAAAAAAAAQwCYAAABFAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-28T16:19:23.118Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompAbilityEffect_SpawnFlyOver.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs*",
"RelativeToolTip": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs*",
"ViewState": "AgIAAPEAAAAAAAAAAAAawAsBAAAWAAAAAAAAAA==",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"RelativeToolTip": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABYAAABOAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-28T14:51:14.836Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"DocumentIndex": 3,
"Title": "CompProperties_AbilitySpawnFlyOver.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompProperties_AbilitySpawnFlyOver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompProperties_AbilitySpawnFlyOver.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_SpawnFlyOver\\CompProperties_AbilitySpawnFlyOver.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompProperties_AbilitySpawnFlyOver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompProperties_AbilitySpawnFlyOver.cs",
"RelativeToolTip": "Flyover\\ARA_SpawnFlyOver\\CompProperties_AbilitySpawnFlyOver.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAACsAAAAJAAAAAAAAAA==",
"ViewState": "AgIAABgAAAAAAAAAAAAswDQAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-28T13:51:12.201Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"DocumentIndex": 1,
"Title": "ThingclassFlyOver.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
"RelativeDocumentMoniker": "Flyover\\ThingclassFlyOver.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
"RelativeToolTip": "Flyover\\ThingclassFlyOver.cs",
"ViewState": "AgIAALAAAAAAAAAAAAAWwNQAAAAlAAAAAAAAAA==",
"ViewState": "AgIAAAkCAAAAAAAAAAAqwCYCAAB/AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-28T09:09:22.03Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"DocumentIndex": 5,
"Title": "CompFlyOverEscort.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompFlyOverEscort.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompFlyOverEscort.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_FlyOverEscort\\CompFlyOverEscort.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompFlyOverEscort.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompFlyOverEscort.cs",
"RelativeToolTip": "Flyover\\ARA_FlyOverEscort\\CompFlyOverEscort.cs",
"ViewState": "AgIAAKYBAAAAAAAAAAAWwNEBAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
@@ -114,11 +130,11 @@
},
{
"$type": "Document",
"DocumentIndex": 5,
"DocumentIndex": 6,
"Title": "CompProperties_FlyOverEscort.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"RelativeToolTip": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABgAAAAPAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
@@ -126,11 +142,11 @@
},
{
"$type": "Document",
"DocumentIndex": 7,
"DocumentIndex": 8,
"Title": "CompProperties_ShipArtillery.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompProperties_ShipArtillery.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompProperties_ShipArtillery.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_ShipArtillery\\CompProperties_ShipArtillery.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompProperties_ShipArtillery.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompProperties_ShipArtillery.cs",
"RelativeToolTip": "Flyover\\ARA_ShipArtillery\\CompProperties_ShipArtillery.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAA8AAAAhAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
@@ -138,11 +154,11 @@
},
{
"$type": "Document",
"DocumentIndex": 6,
"DocumentIndex": 7,
"Title": "CompShipArtillery.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompShipArtillery.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompShipArtillery.cs",
"RelativeDocumentMoniker": "Flyover\\ARA_ShipArtillery\\CompShipArtillery.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompShipArtillery.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompShipArtillery.cs",
"RelativeToolTip": "Flyover\\ARA_ShipArtillery\\CompShipArtillery.cs",
"ViewState": "AgIAALEAAAAAAAAAAAAywNEAAABcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",

View File

@@ -141,6 +141,7 @@
<Compile Include="Flyover\ARA_FlyOverEscort\CompFlyOverEscort.cs" />
<Compile Include="Flyover\ARA_FlyOverEscort\CompProperties_FlyOverEscort.cs" />
<Compile Include="Flyover\ARA_GroundStrafing\CompGroundStrafing.cs" />
<Compile Include="Flyover\ARA_SectorSurveillance\CompSectorSurveillance.cs" />
<Compile Include="Flyover\ARA_SendLetterAfterTicks\CompProperties_SendLetterAfterTicks.cs" />
<Compile Include="Flyover\ARA_SendLetterAfterTicks\CompSendLetterAfterTicks.cs" />
<Compile Include="Flyover\ARA_ShipArtillery\CompProperties_ShipArtillery.cs" />

View File

@@ -0,0 +1,565 @@
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class CompSectorSurveillance : ThingComp
{
public CompProperties_SectorSurveillance Props => (CompProperties_SectorSurveillance)props;
// 监视状态
private HashSet<Pawn> attackedPawns = new HashSet<Pawn>();
private Dictionary<Pawn, int> activeTargets = new Dictionary<Pawn, int>();
private Dictionary<Pawn, int> shotCooldowns = new Dictionary<Pawn, int>();
// 性能优化
private int checkInterval = 10;
private int lastCheckTick = 0;
// 调试状态
private int totalFramesProcessed = 0;
private int totalTargetsFound = 0;
private int totalShotsFired = 0;
// 派系缓存
private Faction cachedFaction = null;
private bool factionInitialized = false;
// 新增:射弹数量跟踪
private int remainingProjectiles = -1; // -1 表示无限
private bool ammoExhausted = false;
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// 初始化射弹数量
if (!respawningAfterLoad)
{
remainingProjectiles = Props.maxProjectiles;
ammoExhausted = false;
}
Log.Message($"SectorSurveillance: Initialized - Angle: {Props.sectorAngle}°, Range: {Props.sectorRange}, Shots: {Props.shotCount}, Interval: {Props.shotInterval}s");
Log.Message($"SectorSurveillance: ProjectileDef = {Props.projectileDef?.defName ?? "NULL"}");
Log.Message($"SectorSurveillance: Parent = {parent?.def?.defName ?? "NULL"} at {parent?.Position.ToString() ?? "NULL"}");
Log.Message($"SectorSurveillance: Max Projectiles = {Props.maxProjectiles}, Remaining = {remainingProjectiles}");
InitializeFactionCache();
}
private void InitializeFactionCache()
{
Log.Message($"SectorSurveillance: Initializing faction cache...");
if (parent.Faction != null)
{
cachedFaction = parent.Faction;
Log.Message($"SectorSurveillance: Using parent.Faction: {cachedFaction?.Name ?? "NULL"}");
}
else
{
FlyOver flyOver = parent as FlyOver;
if (flyOver?.caster != null && flyOver.caster.Faction != null)
{
cachedFaction = flyOver.caster.Faction;
Log.Message($"SectorSurveillance: Using caster.Faction: {cachedFaction?.Name ?? "NULL"}");
}
else if (flyOver?.faction != null)
{
cachedFaction = flyOver.faction;
Log.Message($"SectorSurveillance: Using flyOver.faction: {cachedFaction?.Name ?? "NULL"}");
}
else
{
Log.Error($"SectorSurveillance: CRITICAL - No faction found!");
}
}
factionInitialized = true;
Log.Message($"SectorSurveillance: Faction cache initialized: {cachedFaction?.Name ?? "NULL"}");
}
private Faction GetEffectiveFaction()
{
if (!factionInitialized)
{
InitializeFactionCache();
}
if (cachedFaction == null)
{
Log.Warning("SectorSurveillance: Cached faction is null, reinitializing...");
InitializeFactionCache();
}
return cachedFaction;
}
public override void CompTick()
{
base.CompTick();
totalFramesProcessed++;
// 每60帧输出一次状态摘要
if (Find.TickManager.TicksGame % 60 == 0)
{
Faction currentFaction = GetEffectiveFaction();
Log.Message($"SectorSurveillance Status: Frames={totalFramesProcessed}, TargetsFound={totalTargetsFound}, ShotsFired={totalShotsFired}, ActiveTargets={activeTargets.Count}, Cooldowns={shotCooldowns.Count}, Faction={currentFaction?.Name ?? "NULL"}, RemainingProjectiles={remainingProjectiles}, AmmoExhausted={ammoExhausted}");
}
UpdateShotCooldowns();
if (Find.TickManager.TicksGame - lastCheckTick >= checkInterval)
{
CheckSectorForTargets();
lastCheckTick = Find.TickManager.TicksGame;
}
ExecuteAttacks();
}
private void UpdateShotCooldowns()
{
List<Pawn> toRemove = new List<Pawn>();
// 关键修复:创建键的副本
List<Pawn> cooldownKeys = new List<Pawn>(shotCooldowns.Keys);
foreach (Pawn pawn in cooldownKeys)
{
// 检查pawn是否仍然有效可能已被爆炸杀死
if (pawn == null || pawn.Destroyed || pawn.Dead || !pawn.Spawned)
{
toRemove.Add(pawn);
continue;
}
// 检查键是否仍在字典中
if (!shotCooldowns.ContainsKey(pawn))
{
continue;
}
shotCooldowns[pawn]--;
if (shotCooldowns[pawn] <= 0)
{
toRemove.Add(pawn);
}
}
foreach (Pawn pawn in toRemove)
{
shotCooldowns.Remove(pawn);
Log.Message($"SectorSurveillance: Cooldown finished for {pawn?.Label ?? "NULL"}");
}
}
private void CheckSectorForTargets()
{
// 如果弹药耗尽,不再检查新目标
if (ammoExhausted)
{
return;
}
List<Pawn> enemiesInSector = GetEnemiesInSector();
Log.Message($"SectorSurveillance: Found {enemiesInSector.Count} enemies in sector");
if (enemiesInSector.Count > 0)
{
Log.Message($"SectorSurveillance: Enemies in sector: {string.Join(", ", enemiesInSector.ConvertAll(p => p.Label))}");
}
foreach (Pawn enemy in enemiesInSector)
{
totalTargetsFound++;
if (!attackedPawns.Contains(enemy) &&
!activeTargets.ContainsKey(enemy) &&
!shotCooldowns.ContainsKey(enemy))
{
activeTargets[enemy] = Props.shotCount;
Log.Message($"SectorSurveillance: Starting attack sequence on {enemy.Label} at {enemy.Position} - {Props.shotCount} shots");
}
}
}
private void ExecuteAttacks()
{
// 如果弹药耗尽,不再执行攻击
if (ammoExhausted)
{
return;
}
List<Pawn> completedTargets = new List<Pawn>();
// 关键修复:在枚举之前创建键的副本
List<Pawn> targetsToProcess = new List<Pawn>(activeTargets.Keys);
foreach (Pawn enemy in targetsToProcess)
{
// 检查目标是否仍然有效(可能已被爆炸杀死)
if (enemy == null || enemy.Destroyed || enemy.Dead || !enemy.Spawned)
{
completedTargets.Add(enemy);
continue;
}
// 检查目标是否仍在字典中
if (!activeTargets.ContainsKey(enemy))
{
continue;
}
int remainingShots = activeTargets[enemy];
if (!IsInSector(enemy.Position))
{
Log.Message($"SectorSurveillance: Target {enemy.Label} left sector, cancelling attack");
completedTargets.Add(enemy);
continue;
}
if (shotCooldowns.ContainsKey(enemy))
{
Log.Message($"SectorSurveillance: Target {enemy.Label} in cooldown, skipping this frame");
continue;
}
// 新增:检查剩余射弹数量
if (remainingProjectiles == 0)
{
Log.Message($"SectorSurveillance: Ammo exhausted, cannot fire at {enemy.Label}");
ammoExhausted = true;
break; // 跳出循环,不再发射任何射弹
}
Log.Message($"SectorSurveillance: Attempting to fire at {enemy.Label}, remaining shots: {remainingShots}, remaining projectiles: {remainingProjectiles}");
if (LaunchProjectileAt(enemy))
{
totalShotsFired++;
remainingShots--;
activeTargets[enemy] = remainingShots;
// 新增:减少剩余射弹数量(如果不是无限)
if (remainingProjectiles > 0)
{
remainingProjectiles--;
Log.Message($"SectorSurveillance: Remaining projectiles: {remainingProjectiles}");
// 检查是否耗尽弹药
if (remainingProjectiles == 0)
{
ammoExhausted = true;
Log.Message($"SectorSurveillance: AMMO EXHAUSTED - No more projectiles available");
}
}
int cooldownTicks = Mathf.RoundToInt(Props.shotInterval * 60f);
shotCooldowns[enemy] = cooldownTicks;
Log.Message($"SectorSurveillance: Successfully fired at {enemy.Label}, {remainingShots} shots remaining, cooldown: {cooldownTicks} ticks");
if (remainingShots <= 0)
{
attackedPawns.Add(enemy);
completedTargets.Add(enemy);
Log.Message($"SectorSurveillance: Completed attack sequence on {enemy.Label}");
}
}
else
{
Log.Error($"SectorSurveillance: Failed to fire projectile at {enemy.Label}");
}
}
// 清理已完成的目标
foreach (Pawn enemy in completedTargets)
{
// 再次检查目标是否有效
if (enemy != null)
{
activeTargets.Remove(enemy);
Log.Message($"SectorSurveillance: Removed {enemy.Label} from active targets");
}
else
{
// 如果目标已不存在,直接从字典中移除对应的键
activeTargets.Remove(enemy);
Log.Message($"SectorSurveillance: Removed null target from active targets");
}
}
}
private List<Pawn> GetEnemiesInSector()
{
List<Pawn> enemies = new List<Pawn>();
Map map = parent.Map;
if (map == null)
{
Log.Error("SectorSurveillance: Map is null!");
return enemies;
}
FlyOver flyOver = parent as FlyOver;
if (flyOver == null)
{
Log.Error("SectorSurveillance: Parent is not a FlyOver!");
return enemies;
}
Vector3 center = parent.DrawPos;
Vector3 flightDirection = flyOver.MovementDirection;
float range = Props.sectorRange;
float halfAngle = Props.sectorAngle * 0.5f;
Log.Message($"SectorSurveillance: Checking sector - Center: {center}, Direction: {flightDirection}, Range: {range}, HalfAngle: {halfAngle}");
int totalEnemiesChecked = 0;
// 关键修复创建pawn列表的副本避免在枚举时集合被修改
List<Pawn> allPawns = new List<Pawn>(map.mapPawns.AllPawnsSpawned);
foreach (Pawn pawn in allPawns)
{
totalEnemiesChecked++;
if (IsValidTarget(pawn))
{
bool inSector = IsInSector(pawn.Position);
if (inSector)
{
enemies.Add(pawn);
Log.Message($"SectorSurveillance: Valid target found - {pawn.Label} at {pawn.Position}, in sector: {inSector}");
}
}
}
Log.Message($"SectorSurveillance: Checked {totalEnemiesChecked} pawns, found {enemies.Count} valid targets in sector");
return enemies;
}
private bool IsValidTarget(Pawn pawn)
{
if (pawn == null)
{
Log.Message("SectorSurveillance: IsValidTarget - pawn is null");
return false;
}
// 关键修复检查pawn是否已被销毁或死亡
if (pawn.Destroyed || pawn.Dead || !pawn.Spawned)
{
Log.Message($"SectorSurveillance: IsValidTarget - {pawn.Label} is destroyed/dead/unspawned");
return false;
}
if (pawn.Downed)
{
Log.Message($"SectorSurveillance: IsValidTarget - {pawn.Label} is downed");
return false;
}
Faction effectiveFaction = GetEffectiveFaction();
if (effectiveFaction == null)
{
Log.Error($"SectorSurveillance: IsValidTarget - No effective faction found for {pawn.Label}");
return false;
}
bool hostile = pawn.HostileTo(effectiveFaction);
Log.Message($"SectorSurveillance: IsValidTarget - {pawn.Label} from {pawn.Faction?.Name ?? "NULL"} is hostile to {effectiveFaction.Name}: {hostile}");
return hostile;
}
private bool IsInSector(IntVec3 targetPos)
{
FlyOver flyOver = parent as FlyOver;
if (flyOver == null)
{
Log.Error("SectorSurveillance: IsInSector - Parent is not a FlyOver!");
return false;
}
Vector3 flyOverPos = parent.DrawPos;
Vector3 targetVector = targetPos.ToVector3() - flyOverPos;
targetVector.y = 0;
float distance = targetVector.magnitude;
if (distance > Props.sectorRange)
{
Log.Message($"SectorSurveillance: IsInSector - Target at {targetPos} is out of range: {distance:F1} > {Props.sectorRange}");
return false;
}
Vector3 flightDirection = flyOver.MovementDirection;
float angle = Vector3.Angle(flightDirection, targetVector);
bool inAngle = angle <= Props.sectorAngle * 0.5f;
Log.Message($"SectorSurveillance: IsInSector - Target at {targetPos}, distance: {distance:F1}, angle: {angle:F1}°, inAngle: {inAngle}");
return inAngle;
}
private bool LaunchProjectileAt(Pawn target)
{
if (Props.projectileDef == null)
{
Log.Error("SectorSurveillance: No projectile defined for sector surveillance");
return false;
}
Log.Message($"SectorSurveillance: LaunchProjectileAt - Starting launch for target {target?.Label ?? "NULL"}");
try
{
Vector3 spawnPos = parent.DrawPos;
IntVec3 spawnCell = spawnPos.ToIntVec3();
Log.Message($"SectorSurveillance: Spawn position - World: {spawnPos}, Cell: {spawnCell}");
if (parent.Map == null)
{
Log.Error("SectorSurveillance: Map is null during projectile launch");
return false;
}
if (!spawnCell.InBounds(parent.Map))
{
Log.Error($"SectorSurveillance: Spawn cell {spawnCell} is out of bounds");
return false;
}
Log.Message($"SectorSurveillance: Attempting to spawn projectile: {Props.projectileDef.defName}");
Projectile projectile = (Projectile)GenSpawn.Spawn(Props.projectileDef, spawnCell, parent.Map);
if (projectile != null)
{
Log.Message($"SectorSurveillance: Projectile spawned successfully: {projectile}");
Thing launcher = GetLauncher();
Vector3 launchPos = parent.DrawPos;
LocalTargetInfo targetInfo = new LocalTargetInfo(target);
Log.Message($"SectorSurveillance: Launching projectile - Launcher: {launcher?.def?.defName ?? "NULL"}, LaunchPos: {launchPos}, Target: {targetInfo.Cell}");
projectile.Launch(
launcher,
launchPos,
targetInfo,
targetInfo,
ProjectileHitFlags.IntendedTarget,
false
);
Log.Message($"SectorSurveillance: Projectile launched successfully");
return true;
}
else
{
Log.Error("SectorSurveillance: Failed to spawn projectile - GenSpawn.Spawn returned null");
return false;
}
}
catch (System.Exception ex)
{
Log.Error($"SectorSurveillance: Exception launching projectile: {ex}");
Log.Error($"SectorSurveillance: Stack trace: {ex.StackTrace}");
return false;
}
}
private Thing GetLauncher()
{
FlyOver flyOver = parent as FlyOver;
if (flyOver != null && flyOver.caster != null)
{
Log.Message($"SectorSurveillance: Using caster as launcher: {flyOver.caster.Label}");
return flyOver.caster;
}
Log.Message($"SectorSurveillance: Using parent as launcher: {parent.Label}");
return parent;
}
// 新增获取剩余射弹数量的方法用于UI显示等
public int GetRemainingProjectiles()
{
return remainingProjectiles;
}
// 新增:检查是否还有弹药
public bool HasAmmo()
{
return !ammoExhausted;
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Collections.Look(ref attackedPawns, "attackedPawns", LookMode.Reference);
Scribe_Collections.Look(ref activeTargets, "activeTargets", LookMode.Reference, LookMode.Value);
Scribe_Collections.Look(ref shotCooldowns, "shotCooldowns", LookMode.Reference, LookMode.Value);
Scribe_Values.Look(ref lastCheckTick, "lastCheckTick", 0);
Scribe_Values.Look(ref totalFramesProcessed, "totalFramesProcessed", 0);
Scribe_Values.Look(ref totalTargetsFound, "totalTargetsFound", 0);
Scribe_Values.Look(ref totalShotsFired, "totalShotsFired", 0);
Scribe_References.Look(ref cachedFaction, "cachedFaction");
Scribe_Values.Look(ref factionInitialized, "factionInitialized", false);
// 新增:保存和加载射弹数量状态
Scribe_Values.Look(ref remainingProjectiles, "remainingProjectiles", -1);
Scribe_Values.Look(ref ammoExhausted, "ammoExhausted", false);
}
public override string CompInspectStringExtra()
{
string baseString = base.CompInspectStringExtra();
string ammoString = "";
if (Props.maxProjectiles == -1)
{
ammoString = "Ammo: Unlimited";
}
else
{
ammoString = $"Ammo: {remainingProjectiles}/{Props.maxProjectiles}";
if (ammoExhausted)
{
ammoString += " (EXHAUSTED)";
}
}
if (!string.IsNullOrEmpty(baseString))
{
return baseString + "\n" + ammoString;
}
return ammoString;
}
}
public class CompProperties_SectorSurveillance : CompProperties
{
public ThingDef projectileDef;
public float sectorAngle = 90f;
public float sectorRange = 25f;
public int shotCount = 3;
public float shotInterval = 0.3f;
// 新增:最大射弹数量限制
public int maxProjectiles = -1; // -1 表示无限开火
public CompProperties_SectorSurveillance()
{
compClass = typeof(CompSectorSurveillance);
}
}
}

View File

@@ -73,6 +73,9 @@ namespace ArachnaeSwarm
case FlyOverType.GroundStrafing:
CreateGroundStrafingFlyOver(startPos, endPos);
break;
case FlyOverType.SectorSurveillance:
CreateSectorSurveillanceFlyOver(startPos, endPos);
break;
}
// 显示效果消息
@@ -84,13 +87,12 @@ namespace ArachnaeSwarm
}
}
// 新增:在目标选择时显示扫射范围预览
// 新增:在目标选择时显示预览
public override void DrawEffectPreview(LocalTargetInfo target)
{
base.DrawEffectPreview(target);
if (Props.enableGroundStrafing && Props.showStrafePreview && parent.pawn != null && parent.pawn.Map != null)
if (parent.pawn != null && parent.pawn.Map != null)
{
// 计算飞行路径
IntVec3 startPos, endPos;
@@ -104,12 +106,19 @@ namespace ArachnaeSwarm
endPos = CalculateEndPosition(target, startPos);
}
// 绘制扫射区域预览
DrawStrafingAreaPreview(startPos, endPos);
// 根据不同类型显示不同的预览
if (Props.enableGroundStrafing && Props.showStrafePreview)
{
DrawStrafingAreaPreview(startPos, endPos);
}
else if (Props.enableSectorSurveillance && Props.showSectorPreview)
{
DrawSectorAreaPreview(startPos, endPos);
}
}
}
// 新增:绘制扫射区域预览
// 绘制地面扫射预览
private void DrawStrafingAreaPreview(IntVec3 startPos, IntVec3 endPos)
{
Map map = parent.pawn.Map;
@@ -120,7 +129,8 @@ namespace ArachnaeSwarm
{
flightDirection = Vector3.forward;
}
// 只计算扫射影响区域的单元格(不是整个飞行路径)
// 只计算扫射影响区域的单元格
List<IntVec3> strafeImpactCells = CalculateStrafingImpactCells(startPos, endPos, flightDirection);
// 绘制扫射影响区域的预览单元格
@@ -128,94 +138,187 @@ namespace ArachnaeSwarm
{
if (cell.InBounds(map))
{
// 使用更明显的预览效果
GenDraw.DrawFieldEdges(new List<IntVec3> { cell }, Props.strafePreviewColor, 0.5f);
}
}
// 绘制飞行路径线(只显示线条,不显示格子)
// 绘制飞行路径线
GenDraw.DrawLineBetween(startPos.ToVector3Shifted(), endPos.ToVector3Shifted(), SimpleColor.Red, 0.2f);
// 绘制扫射范围边界
DrawStrafingBoundaries(startPos, endPos, flightDirection);
}
// 新增:计算扫射影响区域的单元格(只计算扫射实际影响的区域,不是整个路径)
// 计算扫射影响区域的单元格
private List<IntVec3> CalculateStrafingImpactCells(IntVec3 startPos, IntVec3 endPos, Vector3 flightDirection)
{
List<IntVec3> cells = new List<IntVec3>();
Map map = parent.pawn.Map;
// 计算垂直于飞行方向的方向
Vector3 perpendicular = new Vector3(-flightDirection.z, 0f, flightDirection.x).normalized;
// 计算飞行路径的总长度
float totalPathLength = startPos.DistanceTo(endPos);
// 计算扫射区域的中心点(目标位置附近)
Vector3 targetCenter = Vector3.Lerp(startPos.ToVector3(), endPos.ToVector3(), 0.5f);
// 计算扫射区域的起始和结束位置(基于扫射长度)
float strafeHalfLength = Props.strafeLength * 0.5f;
Vector3 strafeStart = targetCenter - flightDirection * strafeHalfLength;
Vector3 strafeEnd = targetCenter + flightDirection * strafeHalfLength;
// 沿着扫射区域计算单元格(不是整个飞行路径)
// 沿着扫射区域计算单元格
int steps = Mathf.CeilToInt(Props.strafeLength / 1f);
for (int i = 0; i <= steps; i++)
{
float progress = (float)i / steps;
Vector3 centerPoint = Vector3.Lerp(strafeStart, strafeEnd, progress);
// 在垂直方向扩展扫射宽度
for (int w = -Props.strafeWidth; w <= Props.strafeWidth; w++)
{
Vector3 offset = perpendicular * w;
Vector3 cellPos = centerPoint + offset;
IntVec3 cell = new IntVec3((int)cellPos.x, (int)cellPos.y, (int)cellPos.z);
if (cell.InBounds(map))
if (cell.InBounds(map) && !cells.Contains(cell))
{
if (!cells.Contains(cell))
{
cells.Add(cell);
}
cells.Add(cell);
}
}
}
// 只输出最终结果
Log.Message($"Strafing Area: Calculated {cells.Count} impact cells ({Props.strafeWidth * 2 + 1}x{Props.strafeLength})");
return cells;
}
// 新增:绘制扫射范围边界
// 绘制扫射范围边界
private void DrawStrafingBoundaries(IntVec3 startPos, IntVec3 endPos, Vector3 flightDirection)
{
Map map = parent.pawn.Map;
Vector3 perpendicular = new Vector3(-flightDirection.z, 0f, flightDirection.x).normalized;
// 计算飞行路径的总长度
float totalPathLength = startPos.DistanceTo(endPos);
// 计算扫射区域的中心点(目标位置附近)
// 计算扫射区域的中心点
Vector3 targetCenter = Vector3.Lerp(startPos.ToVector3(), endPos.ToVector3(), 0.5f);
// 计算扫射区域的起始和结束位置(基于扫射长度)
// 计算扫射区域的起始和结束位置
float strafeHalfLength = Props.strafeLength * 0.5f;
Vector3 strafeStart = targetCenter - flightDirection * strafeHalfLength;
Vector3 strafeEnd = targetCenter + flightDirection * strafeHalfLength;
// 计算扫射区域的四个角
Vector3 startLeft = strafeStart + perpendicular * Props.strafeWidth;
Vector3 startRight = strafeStart - perpendicular * Props.strafeWidth;
Vector3 endLeft = strafeEnd + perpendicular * Props.strafeWidth;
Vector3 endRight = strafeEnd - perpendicular * Props.strafeWidth;
// 转换为 IntVec3 来使用 ToVector3Shifted
// 转换为 IntVec3
IntVec3 startLeftCell = new IntVec3((int)startLeft.x, (int)startLeft.y, (int)startLeft.z);
IntVec3 startRightCell = new IntVec3((int)startRight.x, (int)startRight.y, (int)startRight.z);
IntVec3 endLeftCell = new IntVec3((int)endLeft.x, (int)endLeft.y, (int)endLeft.z);
IntVec3 endRightCell = new IntVec3((int)endRight.x, (int)endRight.y, (int)endRight.z);
// 使用带颜色的绘制方法
// 绘制边界线
GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), endLeftCell.ToVector3Shifted(), SimpleColor.Red, 0.2f);
GenDraw.DrawLineBetween(startRightCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Red, 0.2f);
// 绘制起始和结束边界
GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), startRightCell.ToVector3Shifted(), SimpleColor.Red, 0.2f);
GenDraw.DrawLineBetween(endLeftCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Red, 0.2f);
}
// 新增:预处理扫射目标单元格
// 新增:绘制扇形区域预览 - 使用strafeWidth来近似扇形扫过的区域宽度
private void DrawSectorAreaPreview(IntVec3 startPos, IntVec3 endPos)
{
Map map = parent.pawn.Map;
// 计算飞行方向
Vector3 flightDirection = (endPos.ToVector3() - startPos.ToVector3()).normalized;
if (flightDirection == Vector3.zero)
{
flightDirection = Vector3.forward;
}
// 计算垂直于飞行方向的方向
Vector3 perpendicular = new Vector3(-flightDirection.z, 0f, flightDirection.x).normalized;
// 使用strafeWidth来近似扇形扫过的区域宽度
List<IntVec3> previewCells = CalculateRectangularPreviewArea(startPos, endPos, flightDirection, perpendicular);
// 绘制预览区域
foreach (IntVec3 cell in previewCells)
{
if (cell.InBounds(map))
{
GenDraw.DrawFieldEdges(new List<IntVec3> { cell }, Props.sectorPreviewColor, 0.3f);
}
}
// 绘制飞行路径线
GenDraw.DrawLineBetween(startPos.ToVector3Shifted(), endPos.ToVector3Shifted(), SimpleColor.Blue, 0.2f);
// 绘制预览区域边界
DrawRectangularPreviewBoundaries(startPos, endPos, flightDirection, perpendicular);
}
// 计算矩形预览区域(近似扇形扫过的区域)
private List<IntVec3> CalculateRectangularPreviewArea(IntVec3 startPos, IntVec3 endPos, Vector3 flightDirection, Vector3 perpendicular)
{
List<IntVec3> cells = new List<IntVec3>();
Map map = parent.pawn.Map;
// 计算飞行路径的总长度
float totalPathLength = startPos.DistanceTo(endPos);
// 沿着飞行路径计算预览单元格
int steps = Mathf.CeilToInt(totalPathLength / 1f);
for (int i = 0; i <= steps; i++)
{
float progress = (float)i / steps;
Vector3 centerPoint = Vector3.Lerp(startPos.ToVector3(), endPos.ToVector3(), progress);
// 在垂直方向扩展预览宽度使用strafeWidth
for (int w = -Props.strafeWidth; w <= Props.strafeWidth; w++)
{
Vector3 offset = perpendicular * w;
Vector3 cellPos = centerPoint + offset;
IntVec3 cell = new IntVec3((int)cellPos.x, (int)cellPos.y, (int)cellPos.z);
if (cell.InBounds(map) && !cells.Contains(cell))
{
cells.Add(cell);
}
}
}
return cells;
}
// 绘制矩形预览边界
private void DrawRectangularPreviewBoundaries(IntVec3 startPos, IntVec3 endPos, Vector3 flightDirection, Vector3 perpendicular)
{
Map map = parent.pawn.Map;
// 计算预览区域的四个角
Vector3 startLeft = startPos.ToVector3() + perpendicular * Props.strafeWidth;
Vector3 startRight = startPos.ToVector3() - perpendicular * Props.strafeWidth;
Vector3 endLeft = endPos.ToVector3() + perpendicular * Props.strafeWidth;
Vector3 endRight = endPos.ToVector3() - perpendicular * Props.strafeWidth;
// 转换为 IntVec3
IntVec3 startLeftCell = new IntVec3((int)startLeft.x, (int)startLeft.y, (int)startLeft.z);
IntVec3 startRightCell = new IntVec3((int)startRight.x, (int)startRight.y, (int)startRight.z);
IntVec3 endLeftCell = new IntVec3((int)endLeft.x, (int)endLeft.y, (int)endLeft.z);
IntVec3 endRightCell = new IntVec3((int)endRight.x, (int)endRight.y, (int)endRight.z);
// 绘制边界线
GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), endLeftCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f);
GenDraw.DrawLineBetween(startRightCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f);
GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), startRightCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f);
GenDraw.DrawLineBetween(endLeftCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f);
}
// 预处理扫射目标单元格
private List<IntVec3> PreprocessStrafingTargets(List<IntVec3> potentialTargets, float fireChance)
{
List<IntVec3> confirmedTargets = new List<IntVec3>();
@@ -227,11 +330,12 @@ namespace ArachnaeSwarm
confirmedTargets.Add(cell);
}
}
// 只输出预处理结果
Log.Message($"Strafing Preprocess: {confirmedTargets.Count}/{potentialTargets.Count} cells confirmed ({fireChance:P0} chance)");
return confirmedTargets;
}
// 修改后的创建地面扫射飞越方法
// 创建地面扫射飞越
private void CreateGroundStrafingFlyOver(IntVec3 startPos, IntVec3 endPos)
{
ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase<ThingDef>.GetNamedSilentFail("ARA_HiveCorvette");
@@ -240,6 +344,7 @@ namespace ArachnaeSwarm
Log.Warning("No fly over def specified for ground strafing fly over");
return;
}
FlyOver flyOver = FlyOver.MakeFlyOver(
flyOverDef,
startPos,
@@ -249,9 +354,11 @@ namespace ArachnaeSwarm
Props.altitude,
casterPawn: parent.pawn
);
// 设置基本属性
flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact;
flyOver.playFlyOverSound = Props.playFlyOverSound;
// 获取扫射组件并设置预处理后的目标单元格
CompGroundStrafing strafingComp = flyOver.GetComp<CompGroundStrafing>();
if (strafingComp != null)
@@ -259,6 +366,7 @@ namespace ArachnaeSwarm
// 计算扫射区域的所有单元格
Vector3 flightDirection = (endPos.ToVector3() - startPos.ToVector3()).normalized;
List<IntVec3> potentialTargetCells = CalculateStrafingImpactCells(startPos, endPos, flightDirection);
if (potentialTargetCells.Count > 0)
{
// 预处理:根据概率筛选实际会被射击的单元格
@@ -266,6 +374,7 @@ namespace ArachnaeSwarm
potentialTargetCells,
Props.strafeFireChance
);
if (confirmedTargetCells.Count > 0)
{
strafingComp.SetConfirmedTargets(confirmedTargetCells);
@@ -286,23 +395,37 @@ namespace ArachnaeSwarm
}
}
// 新增:获取扫射区域描述(用于技能提示)
public override string ExtraLabelMouseAttachment(LocalTargetInfo target)
// 新增:创建扇形监视飞越
private void CreateSectorSurveillanceFlyOver(IntVec3 startPos, IntVec3 endPos)
{
if (Props.enableGroundStrafing)
ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase<ThingDef>.GetNamedSilentFail("ARA_HiveCorvette");
if (flyOverDef == null)
{
string projectileInfo = Props.strafeProjectile != null ?
$"抛射体: {Props.strafeProjectile.label}" :
"抛射体: 无";
return $"扫射区域: {Props.strafeWidth * 2 + 1}x{Props.strafeLength} 单元格\n{projectileInfo}";
Log.Warning("No fly over def specified for sector surveillance fly over");
return;
}
return base.ExtraLabelMouseAttachment(target);
FlyOver flyOver = FlyOver.MakeFlyOver(
flyOverDef,
startPos,
endPos,
parent.pawn.Map,
Props.flightSpeed,
Props.altitude,
casterPawn: parent.pawn
);
// 设置基本属性
flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact;
flyOver.playFlyOverSound = Props.playFlyOverSound;
Log.Message($"SectorSurveillance FlyOver created: {flyOver} from {startPos} to {endPos}");
// 注意扇形监视的具体参数角度、射程、发射次数等在FlyOver的CompProperties_SectorSurveillance中定义
// 这里只负责创建FlyOver不传递具体参数
}
// 原有的其他方法保持不变...
// 计算垂直线进场路径
private void CalculatePerpendicularPath(LocalTargetInfo target, out IntVec3 startPos, out IntVec3 endPos)
{
Map map = parent.pawn.Map;
@@ -345,6 +468,7 @@ namespace ArachnaeSwarm
Log.Message($"Perpendicular path: {startPos} -> {targetPos} -> {endPos}");
}
// 在指定方向上找到地图边缘
private IntVec3 FindMapEdgeInDirection(Map map, IntVec3 fromPos, Vector3 direction)
{
// 计算最大搜索距离(地图对角线的一半)
@@ -372,6 +496,7 @@ namespace ArachnaeSwarm
return GetRandomMapEdgePosition(map);
}
// 原有的位置计算方法
private IntVec3 CalculateStartPosition(LocalTargetInfo target)
{
Map map = parent.pawn.Map;
@@ -431,7 +556,7 @@ namespace ArachnaeSwarm
return endPos;
}
// 原有的辅助方法保持不变...
// 原有的辅助方法保持不变
private IntVec3 GetOppositeMapEdgeThroughCenter(Map map, IntVec3 startPos)
{
IntVec3 center = map.Center;
@@ -626,12 +751,29 @@ namespace ArachnaeSwarm
return "ReconnaissanceFlyOver".Translate(parent.pawn.LabelShort);
case FlyOverType.GroundStrafing:
return "GroundStrafingIncoming".Translate(parent.pawn.LabelShort);
case FlyOverType.SectorSurveillance:
return "SectorSurveillanceActive".Translate(parent.pawn.LabelShort);
case FlyOverType.Standard:
default:
return "FlyOverInitiated".Translate(parent.pawn.LabelShort);
}
}
// 更新技能提示信息
public override string ExtraLabelMouseAttachment(LocalTargetInfo target)
{
if (Props.enableGroundStrafing)
{
return $"扫射区域: {Props.strafeWidth * 2 + 1}格宽度";
}
else if (Props.enableSectorSurveillance)
{
return $"扇形监视: 约{Props.strafeWidth * 2 + 1}格宽度\n(具体参数在飞行物定义中)";
}
return base.ExtraLabelMouseAttachment(target);
}
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
return base.Valid(target, throwMessages) &&

View File

@@ -27,16 +27,23 @@ namespace ArachnaeSwarm
public IntVec3 customEndOffset = IntVec3.Zero;
public int flyOverDistance = 30; // 飞越距离(当终点为自定义时)
// 新增:简化的地面扫射配置
// 地面扫射配置
public bool enableGroundStrafing = false; // 是否启用地面扫射
public int strafeWidth = 3; // 扫射宽度(垂直于飞行方向的单元格数
public int strafeLength = 15; // 扫射长度(沿着飞行方向的单元格数)
public float strafeFireChance = 0.7f; // 扫射发射概率(用于预处理)
public ThingDef strafeProjectile; // 抛射体定义(用于后续攻击)
public int strafeWidth = 3; // 扫射宽度(用于预览
public int strafeLength = 15; // 扫射长度
public float strafeFireChance = 0.7f; // 扫射发射概率
public ThingDef strafeProjectile; // 抛射体定义
// 新增:扫射可视化
// 地面扫射可视化
public bool showStrafePreview = true; // 是否显示扫射预览
public Color strafePreviewColor = new Color(1f, 0.3f, 0.3f, 0.3f); // 扫射预览颜色
public Color strafePreviewColor = new Color(1f, 0.3f, 0.3f, 0.3f);
// 扇形监视配置 - 只传递信号,不传递具体参数
public bool enableSectorSurveillance = false; // 是否启用扇形区域监视
// 扇形监视可视化 - 使用strafeWidth来近似预览区域宽度
public bool showSectorPreview = true; // 是否显示扇形预览
public Color sectorPreviewColor = new Color(0.3f, 0.7f, 1f, 0.3f);
public CompProperties_AbilitySpawnFlyOver()
{
@@ -52,7 +59,8 @@ namespace ArachnaeSwarm
CargoDrop, // 货运飞越
BombingRun, // 轰炸飞越
Reconnaissance, // 侦察飞越
GroundStrafing // 地面扫射
GroundStrafing, // 地面扫射
SectorSurveillance // 扇形区域监视
}
// 进场类型枚举

View File

@@ -16,6 +16,7 @@ namespace ArachnaeSwarm
public float flightSpeed = 1f; // 飞行速度
public float currentProgress = 0f; // 当前进度 (0-1)
public float altitude = 10f; // 飞行高度
public Faction faction; // 派系引用
// 淡入效果相关
public float fadeInDuration = 1.5f; // 淡入持续时间(秒)
@@ -29,7 +30,7 @@ namespace ArachnaeSwarm
public bool fadeOutCompleted = false; // 淡出是否完成
public float fadeOutStartProgress = 0.7f; // 开始淡出的进度阈值0-1
public float defaultFadeOutDuration = 1.5f; // 默认淡出持续时间(仅用于销毁)
// 伴飞相关
public float escortScale = 1f; // 伴飞缩放比例
public bool isEscort = false; // 是否是伴飞
@@ -50,7 +51,7 @@ namespace ArachnaeSwarm
public bool spawnContentsOnImpact = false; // 是否在结束时生成内容物
public bool playFlyOverSound = true; // 是否播放飞越音效
public bool createShadow = true; // 是否创建阴影
public Pawn caster; // 施法者引用
// 属性
@@ -165,10 +166,10 @@ namespace ArachnaeSwarm
// 计算剩余飞行时间
float remainingTime = RemainingFlightTime;
// 使用剩余时间的一部分作为淡出持续时间
float dynamicDuration = remainingTime * fadeOutDistanceFactor;
// 限制在最小和最大范围内
return Mathf.Clamp(dynamicDuration, minFadeOutDuration, maxFadeOutDuration);
}
@@ -193,7 +194,7 @@ namespace ArachnaeSwarm
Scribe_Values.Look(ref fadeInDuration, "fadeInDuration", 1.5f);
Scribe_Values.Look(ref currentFadeInTime, "currentFadeInTime", 0f);
Scribe_Values.Look(ref fadeInCompleted, "fadeInCompleted", false);
// 淡出效果数据保存
Scribe_Values.Look(ref fadeOutDuration, "fadeOutDuration", 0f);
Scribe_Values.Look(ref currentFadeOutTime, "currentFadeOutTime", 0f);
@@ -203,6 +204,7 @@ namespace ArachnaeSwarm
Scribe_Values.Look(ref defaultFadeOutDuration, "defaultFadeOutDuration", 1.5f);
Scribe_References.Look(ref caster, "caster");
Scribe_References.Look(ref faction, "faction");
}
public override void SpawnSetup(Map map, bool respawningAfterLoad)
@@ -213,15 +215,15 @@ namespace ArachnaeSwarm
if (!respawningAfterLoad)
{
Log.Message($"FlyOver Direction - Vector: {MovementDirection}, Rotation: {ExactRotation.eulerAngles}");
// 设置初始位置
base.Position = startPosition;
hasStarted = true;
// 重置淡入状态
currentFadeInTime = 0f;
fadeInCompleted = false;
// 重置淡出状态
currentFadeOutTime = 0f;
fadeOutStarted = false;
@@ -297,10 +299,10 @@ namespace ArachnaeSwarm
private void StartFadeOut()
{
fadeOutStarted = true;
// 基于剩余距离动态计算淡出持续时间
fadeOutDuration = CalculateDynamicFadeOutDuration();
Log.Message($"FlyOver started fade out at progress {currentProgress:F2}, duration: {fadeOutDuration:F2}s, remaining time: {RemainingFlightTime:F2}s");
}
@@ -362,7 +364,7 @@ namespace ArachnaeSwarm
fadeOutDuration = defaultFadeOutDuration;
Log.Message($"FlyOver emergency destroy with default fade out: {defaultFadeOutDuration}s");
}
// 设置标记,下一帧会处理淡出
hasCompleted = true;
}
@@ -386,7 +388,7 @@ namespace ArachnaeSwarm
{
Vector3 effectPos = DrawPos;
effectPos.y = AltitudeLayer.MoteOverhead.AltitudeFor();
// 淡出时减少粒子效果强度
float effectIntensity = fadeOutStarted ? FadeOutAlpha : 1f;
FleckMaker.ThrowSmoke(effectPos, base.Map, 1f * effectIntensity);
@@ -526,7 +528,7 @@ namespace ArachnaeSwarm
// 工具方法:创建飞越物体
public static FlyOver MakeFlyOver(ThingDef flyOverDef, IntVec3 start, IntVec3 end, Map map,
float speed = 1f, float height = 10f, ThingOwner contents = null,
float speed = 1f, float height = 10f, ThingOwner contents = null,
float fadeInDuration = 1.5f, float defaultFadeOutDuration = 1.5f, Pawn casterPawn = null)
{
FlyOver flyOver = (FlyOver)ThingMaker.MakeThing(flyOverDef);
@@ -538,16 +540,28 @@ namespace ArachnaeSwarm
flyOver.defaultFadeOutDuration = defaultFadeOutDuration;
flyOver.caster = casterPawn;
// 简化派系设置 - 直接设置 faction 字段
if (casterPawn != null && casterPawn.Faction != null)
{
flyOver.faction = casterPawn.Faction;
Log.Message($"FlyOver faction set to: {casterPawn.Faction.Name}");
}
else
{
Log.Warning($"FlyOver: Cannot set faction - casterPawn: {casterPawn?.Label ?? "NULL"}, casterFaction: {casterPawn?.Faction?.Name ?? "NULL"}");
}
if (contents != null)
{
flyOver.innerContainer.TryAddRangeOrTransfer(contents);
}
GenSpawn.Spawn(flyOver, start, map);
Log.Message($"FlyOver created: {flyOver} from {start} to {end} at altitude {height}");
Log.Message($"FlyOver created: {flyOver} from {start} to {end} at altitude {height}, Faction: {flyOver.faction?.Name ?? "NULL"}");
return flyOver;
}
}
// ModExtension 配置