伴飞系统,技能飞行物垂直入场
This commit is contained in:
Binary file not shown.
@@ -34,8 +34,9 @@
|
||||
<useHitPoints>false</useHitPoints>
|
||||
<selectable>false</selectable>
|
||||
<alwaysHaulable>false</alwaysHaulable>
|
||||
<altitudeLayer>LightingOverlay</altitudeLayer>
|
||||
<altitudeLayer>FogOfWar</altitudeLayer>
|
||||
<comps>
|
||||
<!-- 入场信封信息 -->
|
||||
<li Class="ArachnaeSwarm.CompProperties_SendLetterAfterTicks">
|
||||
<ticksDelay>60</ticksDelay> <!-- 2秒后发送 -->
|
||||
<letterLabel>虫巢母舰</letterLabel>
|
||||
@@ -45,6 +46,7 @@
|
||||
<requireOnMap>true</requireOnMap>
|
||||
<destroyAfterSending>false</destroyAfterSending> <!-- 发送后销毁flyover -->
|
||||
</li>
|
||||
<!-- 空投 -->
|
||||
<li Class="ArachnaeSwarm.CompProperties_FlyOverDropPods">
|
||||
<!-- <dropProgress>0.5</dropProgress> -->
|
||||
<useCyclicDrops>true</useCyclicDrops>
|
||||
@@ -102,13 +104,13 @@
|
||||
<joinPlayer>false</joinPlayer>
|
||||
<makePrisoners>false</makePrisoners>
|
||||
</li>
|
||||
<!-- 炮击支援 -->
|
||||
<li Class="ArachnaeSwarm.CompProperties_ShipArtillery">
|
||||
<ticksBetweenAttacks>600</ticksBetweenAttacks>
|
||||
<attackDurationTicks>600</attackDurationTicks>
|
||||
<warmupTicks>60</warmupTicks>
|
||||
<attackRadius>60</attackRadius>
|
||||
<shellsPerVolley>3</shellsPerVolley>
|
||||
<scatterRadius>60</scatterRadius>
|
||||
<ignoreProtectionChance>0.1</ignoreProtectionChance>
|
||||
|
||||
<skyfallerDef>ARA_HiveShip_Fire_Incoming</skyfallerDef>
|
||||
@@ -120,13 +122,78 @@
|
||||
<avoidPlayerAssets>true</avoidPlayerAssets>
|
||||
<playerAssetAvoidanceRadius>10</playerAssetAvoidanceRadius>
|
||||
|
||||
<sendAttackLetter>true</sendAttackLetter>
|
||||
<sendAttackLetter>false</sendAttackLetter>
|
||||
<customLetterLabel>战舰炮击警告</customLetterLabel>
|
||||
<customLetterText>一艘敌方战舰正在对殖民地进行炮击!立即寻找掩护!</customLetterText>
|
||||
<letterDef>ThreatBig</letterDef>
|
||||
</li>
|
||||
<!-- 伴飞 -->
|
||||
<li Class="ArachnaeSwarm.CompProperties_FlyOverEscort">
|
||||
<escortFlyOverDef>ARA_HiveCorvette</escortFlyOverDef>
|
||||
|
||||
<!-- 生成配置 -->
|
||||
<spawnIntervalTicks>250</spawnIntervalTicks> <!-- 5秒 -->
|
||||
<maxEscorts>60</maxEscorts>
|
||||
<spawnCount>1</spawnCount>
|
||||
|
||||
<!-- 位置配置 -->
|
||||
<spawnDistance>15</spawnDistance>
|
||||
<lateralOffset>200</lateralOffset>
|
||||
<verticalOffset>5</verticalOffset>
|
||||
<useRandomOffset>true</useRandomOffset>
|
||||
|
||||
<!-- 飞行配置 -->
|
||||
<escortSpeedMultiplier>20</escortSpeedMultiplier> <!-- 比主舰稍快 -->
|
||||
<escortAltitudeOffset>10</escortAltitudeOffset> <!-- 比主舰稍高 -->
|
||||
<mirrorMovement>false</mirrorMovement>
|
||||
|
||||
<!-- 行为配置 -->
|
||||
<spawnOnStart>true</spawnOnStart>
|
||||
<destroyWithParent>false</destroyWithParent>
|
||||
<continuousSpawning>true</continuousSpawning>
|
||||
|
||||
<!-- 外观配置 -->
|
||||
<escortScale>0.6</escortScale>
|
||||
<useParentRotation>true</useParentRotation>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
<ThingDef Parent="EtherealThingBase">
|
||||
<defName>ARA_HiveCorvette</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_HiveShip_Shadow</texPath>
|
||||
<graphicClass>Graphic_Single</graphicClass>
|
||||
<shaderType>TransparentPostLight</shaderType>
|
||||
<drawSize>(20,30)</drawSize>
|
||||
<color>(195,195,195,45)</color>
|
||||
</graphicData>
|
||||
<skyfaller>
|
||||
<shadowSize>(0, 0)</shadowSize>
|
||||
<motesPerCell>0</motesPerCell>
|
||||
<floatingSound>FlyOver/Flying</floatingSound>
|
||||
<impactSound>FlyOver/Landing</impactSound>
|
||||
</skyfaller>
|
||||
<modExtensions>
|
||||
<li Class="ArachnaeSwarm.FlyOverShadowExtension">
|
||||
<customShadowPath>ArachnaeSwarm/FlyOverThing/ARA_HiveShip_Shadow</customShadowPath>
|
||||
<useCustomShadow>false</useCustomShadow>
|
||||
<!-- <shadowIntensity>0.8</shadowIntensity>
|
||||
<minShadowAlpha>0</minShadowAlpha>
|
||||
<maxShadowAlpha>0</maxShadowAlpha>
|
||||
<minShadowScale>15</minShadowScale>
|
||||
<maxShadowScale>15</maxShadowScale> -->
|
||||
</li>
|
||||
</modExtensions>
|
||||
<useHitPoints>false</useHitPoints>
|
||||
<selectable>false</selectable>
|
||||
<alwaysHaulable>false</alwaysHaulable>
|
||||
<altitudeLayer>FogOfWar</altitudeLayer>
|
||||
</ThingDef>
|
||||
|
||||
<ThingDef ParentName="SkyfallerBase">
|
||||
<defName>ARA_HiveShip_Fire_Incoming</defName>
|
||||
@@ -140,6 +207,12 @@
|
||||
<explosionDamage>ARA_AcidBurn</explosionDamage>
|
||||
<explosionDamageFactor>0.5</explosionDamageFactor>
|
||||
<cameraShake>1</cameraShake>
|
||||
<angleCurve>
|
||||
<points>
|
||||
<li>(0,0)</li>
|
||||
<li>(1,-1)</li>
|
||||
</points>
|
||||
</angleCurve>
|
||||
</skyfaller>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
Binary file not shown.
@@ -1,7 +1,28 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
|
||||
"Documents": [],
|
||||
"Documents": [
|
||||
{
|
||||
"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|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|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|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|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}"
|
||||
}
|
||||
],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
@@ -9,11 +30,76 @@
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": -1,
|
||||
"SelectedChildIndex": 1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "ThingclassFlyOver.cs",
|
||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
|
||||
"RelativeDocumentMoniker": "Flyover\\ThingclassFlyOver.cs",
|
||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs",
|
||||
"RelativeToolTip": "Flyover\\ThingclassFlyOver.cs",
|
||||
"ViewState": "AgIAAAMCAAAAAAAAAAAAABcCAAAeAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2025-10-28T09:09:22.03Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "CompFlyOverEscort.cs",
|
||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompFlyOverEscort.cs",
|
||||
"RelativeDocumentMoniker": "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": "AgIAAAAAAAAAAAAAAAAAAAMAAAAMAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2025-10-28T07:30:55.268Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "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": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
|
||||
"RelativeToolTip": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs",
|
||||
"ViewState": "AgIAAAMAAAAAAAAAAAAAABUAAAAjAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2025-10-28T07:30:47.27Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 4,
|
||||
"Title": "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": "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|",
|
||||
"WhenOpened": "2025-10-28T06:21:06.271Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 3,
|
||||
"Title": "CompShipArtillery.cs",
|
||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_ShipArtillery\\CompShipArtillery.cs",
|
||||
"RelativeDocumentMoniker": "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|",
|
||||
"WhenOpened": "2025-10-28T06:21:04.222Z",
|
||||
"EditorCaption": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -138,6 +138,8 @@
|
||||
<Compile Include="EventSystem\EventVariableManager.cs" />
|
||||
<Compile Include="EventSystem\Letter_EventChoice.cs" />
|
||||
<Compile Include="EventSystem\QuestNode_Root_EventLetter.cs" />
|
||||
<Compile Include="Flyover\ARA_FlyOverEscort\CompFlyOverEscort.cs" />
|
||||
<Compile Include="Flyover\ARA_FlyOverEscort\CompProperties_FlyOverEscort.cs" />
|
||||
<Compile Include="Flyover\ARA_SendLetterAfterTicks\CompProperties_SendLetterAfterTicks.cs" />
|
||||
<Compile Include="Flyover\ARA_SendLetterAfterTicks\CompSendLetterAfterTicks.cs" />
|
||||
<Compile Include="Flyover\ARA_ShipArtillery\CompProperties_ShipArtillery.cs" />
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompFlyOverEscort : ThingComp
|
||||
{
|
||||
public CompProperties_FlyOverEscort Props => (CompProperties_FlyOverEscort)props;
|
||||
|
||||
// 状态变量
|
||||
private float ticksUntilNextSpawn = 0f;
|
||||
private List<FlyOver> activeEscorts = new List<FlyOver>();
|
||||
private bool hasInitialized = false;
|
||||
|
||||
public override void Initialize(CompProperties props)
|
||||
{
|
||||
base.Initialize(props);
|
||||
|
||||
if (Props.spawnOnStart)
|
||||
{
|
||||
ticksUntilNextSpawn = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
ticksUntilNextSpawn = Props.spawnIntervalTicks;
|
||||
}
|
||||
|
||||
Log.Message($"FlyOver Escort initialized: {Props.spawnIntervalTicks} ticks interval, max {Props.maxEscorts} escorts");
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
|
||||
if (parent is not FlyOver mainFlyOver || !mainFlyOver.Spawned || mainFlyOver.Map == null)
|
||||
return;
|
||||
|
||||
// 初始化检查
|
||||
if (!hasInitialized && mainFlyOver.hasStarted)
|
||||
{
|
||||
hasInitialized = true;
|
||||
Log.Message($"FlyOver Escort: Main FlyOver started at {mainFlyOver.startPosition}");
|
||||
}
|
||||
|
||||
// 清理已销毁的伴飞
|
||||
activeEscorts.RemoveAll(escort => escort == null || escort.Destroyed || !escort.Spawned);
|
||||
|
||||
// 检查是否应该生成新伴飞
|
||||
if (ShouldSpawnEscort(mainFlyOver))
|
||||
{
|
||||
ticksUntilNextSpawn -= 1f;
|
||||
|
||||
if (ticksUntilNextSpawn <= 0f)
|
||||
{
|
||||
SpawnEscorts(mainFlyOver);
|
||||
ticksUntilNextSpawn = Props.spawnIntervalTicks;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新现有伴飞的位置(如果需要)
|
||||
UpdateEscortPositions(mainFlyOver);
|
||||
}
|
||||
|
||||
private bool ShouldSpawnEscort(FlyOver mainFlyOver)
|
||||
{
|
||||
if (!mainFlyOver.hasStarted || mainFlyOver.hasCompleted)
|
||||
return false;
|
||||
|
||||
if (!Props.continuousSpawning && activeEscorts.Count >= Props.spawnCount)
|
||||
return false;
|
||||
|
||||
if (activeEscorts.Count >= Props.maxEscorts)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SpawnEscorts(FlyOver mainFlyOver)
|
||||
{
|
||||
int escortsToSpawn = Mathf.Min(Props.spawnCount, Props.maxEscorts - activeEscorts.Count);
|
||||
|
||||
for (int i = 0; i < escortsToSpawn; i++)
|
||||
{
|
||||
FlyOver escort = CreateEscort(mainFlyOver);
|
||||
if (escort != null)
|
||||
{
|
||||
activeEscorts.Add(escort);
|
||||
Log.Message($"Spawned escort #{i+1} for FlyOver at {mainFlyOver.DrawPos}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FlyOver CreateEscort(FlyOver mainFlyOver)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 选择伴飞定义
|
||||
ThingDef escortDef = SelectEscortDef();
|
||||
if (escortDef == null)
|
||||
{
|
||||
Log.Error("FlyOver Escort: No valid escort def found");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算伴飞的起点和终点
|
||||
IntVec3 escortStart = CalculateEscortStart(mainFlyOver);
|
||||
IntVec3 escortEnd = CalculateEscortEnd(mainFlyOver, escortStart);
|
||||
|
||||
if (!escortStart.InBounds(mainFlyOver.Map) || !escortEnd.InBounds(mainFlyOver.Map))
|
||||
{
|
||||
Log.Warning("FlyOver Escort: Escort start or end position out of bounds");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算伴飞参数
|
||||
float escortSpeed = mainFlyOver.flightSpeed * Props.escortSpeedMultiplier;
|
||||
float escortAltitude = mainFlyOver.altitude + Props.escortAltitudeOffset;
|
||||
|
||||
// 创建伴飞
|
||||
FlyOver escort = FlyOver.MakeFlyOver(
|
||||
escortDef,
|
||||
escortStart,
|
||||
escortEnd,
|
||||
mainFlyOver.Map,
|
||||
escortSpeed,
|
||||
escortAltitude,
|
||||
null, // 没有内容物
|
||||
mainFlyOver.fadeInDuration
|
||||
);
|
||||
|
||||
// 设置伴飞属性
|
||||
SetupEscortProperties(escort, mainFlyOver);
|
||||
|
||||
Log.Message($"Created escort: {escortStart} -> {escortEnd}, speed: {escortSpeed}, altitude: {escortAltitude}");
|
||||
|
||||
return escort;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"Error creating FlyOver escort: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ThingDef SelectEscortDef()
|
||||
{
|
||||
if (Props.escortFlyOverDefs != null && Props.escortFlyOverDefs.Count > 0)
|
||||
{
|
||||
return Props.escortFlyOverDefs.RandomElement();
|
||||
}
|
||||
|
||||
return Props.escortFlyOverDef;
|
||||
}
|
||||
|
||||
private IntVec3 CalculateEscortStart(FlyOver mainFlyOver)
|
||||
{
|
||||
Vector3 mainDirection = mainFlyOver.MovementDirection;
|
||||
Vector3 mainPosition = mainFlyOver.DrawPos;
|
||||
|
||||
// 计算横向偏移方向(垂直于飞行方向)
|
||||
Vector3 lateralDirection = GetLateralOffsetDirection(mainDirection);
|
||||
|
||||
// 计算偏移量
|
||||
float lateralOffset = Props.useRandomOffset ?
|
||||
Rand.Range(-Props.lateralOffset, Props.lateralOffset) :
|
||||
Props.lateralOffset;
|
||||
|
||||
float spawnDistance = Props.useRandomOffset ?
|
||||
Rand.Range(Props.spawnDistance * 0.5f, Props.spawnDistance * 1.5f) :
|
||||
Props.spawnDistance;
|
||||
|
||||
// 计算起点位置(从主FlyOver后方偏移)
|
||||
Vector3 offset = (-mainDirection * spawnDistance) + (lateralDirection * lateralOffset);
|
||||
Vector3 escortStartPos = mainPosition + offset;
|
||||
|
||||
// 确保位置在地图边界内
|
||||
IntVec3 escortStart = escortStartPos.ToIntVec3();
|
||||
if (!escortStart.InBounds(mainFlyOver.Map))
|
||||
{
|
||||
// 如果超出边界,调整到边界内
|
||||
escortStart = ClampToMap(escortStart, mainFlyOver.Map);
|
||||
}
|
||||
|
||||
return escortStart;
|
||||
}
|
||||
|
||||
private IntVec3 CalculateEscortEnd(FlyOver mainFlyOver, IntVec3 escortStart)
|
||||
{
|
||||
Vector3 mainDirection = mainFlyOver.MovementDirection;
|
||||
Vector3 mainEndPos = mainFlyOver.endPosition.ToVector3();
|
||||
|
||||
// 如果镜像移动,使用相反方向
|
||||
if (Props.mirrorMovement)
|
||||
{
|
||||
mainDirection = -mainDirection;
|
||||
}
|
||||
|
||||
// 计算从起点沿飞行方向延伸的终点
|
||||
float flightDistance = mainFlyOver.startPosition.DistanceTo(mainFlyOver.endPosition);
|
||||
Vector3 escortEndPos = escortStart.ToVector3() + (mainDirection * flightDistance);
|
||||
|
||||
// 确保终点在地图边界内
|
||||
IntVec3 escortEnd = escortEndPos.ToIntVec3();
|
||||
if (!escortEnd.InBounds(mainFlyOver.Map))
|
||||
{
|
||||
escortEnd = ClampToMap(escortEnd, mainFlyOver.Map);
|
||||
}
|
||||
|
||||
return escortEnd;
|
||||
}
|
||||
|
||||
private Vector3 GetLateralOffsetDirection(Vector3 mainDirection)
|
||||
{
|
||||
// 获取垂直于飞行方向的向量(随机选择左侧或右侧)
|
||||
Vector3 lateral = new Vector3(-mainDirection.z, 0f, mainDirection.x);
|
||||
|
||||
// 随机选择方向
|
||||
if (Rand.Value > 0.5f)
|
||||
{
|
||||
lateral = -lateral;
|
||||
}
|
||||
|
||||
return lateral.normalized;
|
||||
}
|
||||
|
||||
private IntVec3 ClampToMap(IntVec3 position, Map map)
|
||||
{
|
||||
CellRect mapRect = CellRect.WholeMap(map);
|
||||
return new IntVec3(
|
||||
Mathf.Clamp(position.x, mapRect.minX, mapRect.maxX),
|
||||
0,
|
||||
Mathf.Clamp(position.z, mapRect.minZ, mapRect.maxZ)
|
||||
);
|
||||
}
|
||||
|
||||
private void SetupEscortProperties(FlyOver escort, FlyOver mainFlyOver)
|
||||
{
|
||||
// 设置缩放
|
||||
if (Props.escortScale != 1f)
|
||||
{
|
||||
// 这里可能需要通过ModExtension或其他方式设置缩放
|
||||
}
|
||||
|
||||
// 禁用阴影(如果需要)
|
||||
if (!mainFlyOver.createShadow)
|
||||
{
|
||||
escort.createShadow = false;
|
||||
}
|
||||
|
||||
// 禁用音效(如果需要)
|
||||
if (!mainFlyOver.playFlyOverSound)
|
||||
{
|
||||
escort.playFlyOverSound = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEscortPositions(FlyOver mainFlyOver)
|
||||
{
|
||||
// 如果需要实时更新伴飞位置,可以在这里实现
|
||||
// 目前伴飞会按照自己的路径飞行
|
||||
}
|
||||
|
||||
public override void PostDestroy(DestroyMode mode, Map previousMap)
|
||||
{
|
||||
base.PostDestroy(mode, previousMap);
|
||||
|
||||
// 销毁所有伴飞
|
||||
if (Props.destroyWithParent)
|
||||
{
|
||||
foreach (FlyOver escort in activeEscorts)
|
||||
{
|
||||
if (escort != null && escort.Spawned)
|
||||
{
|
||||
escort.Destroy();
|
||||
}
|
||||
}
|
||||
activeEscorts.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Values.Look(ref ticksUntilNextSpawn, "ticksUntilNextSpawn", 0f);
|
||||
Scribe_Collections.Look(ref activeEscorts, "activeEscorts", LookMode.Reference);
|
||||
Scribe_Values.Look(ref hasInitialized, "hasInitialized", false);
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||
{
|
||||
if (DebugSettings.ShowDevGizmos && parent is FlyOver)
|
||||
{
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "Dev: Spawn Escort",
|
||||
action = () => SpawnEscorts(parent as FlyOver)
|
||||
};
|
||||
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = $"Dev: Escort Status - Active: {activeEscorts.Count}/{Props.maxEscorts}",
|
||||
action = () => {}
|
||||
};
|
||||
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "Dev: Clear All Escorts",
|
||||
action = () =>
|
||||
{
|
||||
foreach (var escort in activeEscorts)
|
||||
{
|
||||
if (escort != null && escort.Spawned)
|
||||
escort.Destroy();
|
||||
}
|
||||
activeEscorts.Clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 公共方法:强制生成伴飞
|
||||
public void SpawnEscortNow()
|
||||
{
|
||||
if (parent is FlyOver flyOver)
|
||||
{
|
||||
SpawnEscorts(flyOver);
|
||||
}
|
||||
}
|
||||
|
||||
// 公共方法:获取活跃伴飞数量
|
||||
public int GetActiveEscortCount()
|
||||
{
|
||||
return activeEscorts.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_FlyOverEscort : CompProperties
|
||||
{
|
||||
// 伴飞配置
|
||||
public ThingDef escortFlyOverDef; // 伴飞FlyOver定义
|
||||
public List<ThingDef> escortFlyOverDefs; // 多个伴飞定义(随机选择)
|
||||
|
||||
// 生成配置
|
||||
public float spawnIntervalTicks = 600f; // 生成间隔(tick)
|
||||
public int maxEscorts = 3; // 最大伴飞数量
|
||||
public int spawnCount = 1; // 每次生成的伴飞数量
|
||||
|
||||
// 位置配置
|
||||
public float spawnDistance = 10f; // 生成距离(从主FlyOver)
|
||||
public float lateralOffset = 5f; // 横向偏移量
|
||||
public float verticalOffset = 2f; // 垂直偏移量(高度差)
|
||||
public bool useRandomOffset = true; // 是否使用随机偏移
|
||||
|
||||
// 飞行配置
|
||||
public float escortSpeedMultiplier = 1f; // 速度乘数(相对于主FlyOver)
|
||||
public float escortAltitudeOffset = 0f; // 高度偏移
|
||||
public bool mirrorMovement = false; // 是否镜像移动(相反方向)
|
||||
|
||||
// 行为配置
|
||||
public bool spawnOnStart = true; // 开始时立即生成
|
||||
public bool continuousSpawning = true; // 是否持续生成
|
||||
public bool destroyWithParent = true; // 是否随父级销毁
|
||||
|
||||
// 外观配置
|
||||
public float escortScale = 1f; // 缩放比例
|
||||
public bool useParentRotation = true; // 使用父级旋转
|
||||
|
||||
public CompProperties_FlyOverEscort()
|
||||
{
|
||||
compClass = typeof(CompFlyOverEscort);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ namespace ArachnaeSwarm
|
||||
public ThingDef skyfallerDef; // 使用的 Skyfaller 定义
|
||||
public List<ThingDef> skyfallerDefs; // 多个 Skyfaller 定义(随机选择)
|
||||
public int shellsPerVolley = 1; // 每轮齐射的炮弹数量
|
||||
public float scatterRadius = 3f; // 散布半径
|
||||
public bool useDifferentShells = false; // 是否使用不同类型的炮弹
|
||||
|
||||
// 音效配置
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
@@ -29,7 +30,7 @@ namespace ArachnaeSwarm
|
||||
|
||||
ticksUntilNextAttack = Props.ticksBetweenAttacks;
|
||||
|
||||
Log.Message($"Ship Artillery initialized: {Props.ticksBetweenAttacks} ticks between attacks, {Props.attackRadius} radius, Scatter: {Props.scatterRadius}");
|
||||
Log.Message($"Ship Artillery initialized: {Props.ticksBetweenAttacks} ticks between attacks, {Props.attackRadius} radius");
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
@@ -132,7 +133,7 @@ namespace ArachnaeSwarm
|
||||
attackEffecter = Props.attackEffect.Spawn();
|
||||
}
|
||||
|
||||
Log.Message($"Ship Artillery started firing at area {currentTarget} (scatter radius: {Props.scatterRadius})");
|
||||
Log.Message($"Ship Artillery started firing at area {currentTarget}");
|
||||
|
||||
// 发送攻击通知
|
||||
if (Props.sendAttackLetter)
|
||||
@@ -194,17 +195,20 @@ namespace ArachnaeSwarm
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算散布位置 - 使用新的散射逻辑
|
||||
IntVec3 shellTarget = GetScatteredTarget(flyOver);
|
||||
// 直接选择随机目标
|
||||
IntVec3 shellTarget = SelectRandomTarget(flyOver);
|
||||
|
||||
// 创建 Skyfaller
|
||||
Skyfaller shell = (Skyfaller)ThingMaker.MakeThing(shellDef);
|
||||
|
||||
// 生成炮弹
|
||||
GenSpawn.Spawn(shell, GetLaunchPosition(flyOver), flyOver.Map);
|
||||
// 关键修复:使用 SkyfallerMaker 创建并立即生成 Skyfaller
|
||||
SkyfallerMaker.SpawnSkyfaller(shellDef, shellTarget, flyOver.Map);
|
||||
|
||||
float distanceFromCenter = shellTarget.DistanceTo(currentTarget);
|
||||
Log.Message($"Ship Artillery fired shell at {shellTarget} (distance from center: {distanceFromCenter:F1}, scatter radius: {Props.scatterRadius})");
|
||||
Log.Message($"Ship Artillery fired shell at {shellTarget} (distance from center: {distanceFromCenter:F1})");
|
||||
|
||||
// 播放音效
|
||||
if (Props.attackSound != null)
|
||||
{
|
||||
Props.attackSound.PlayOneShot(new TargetInfo(shellTarget, flyOver.Map));
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
@@ -243,45 +247,11 @@ namespace ArachnaeSwarm
|
||||
return launchPos;
|
||||
}
|
||||
|
||||
// 新的散射逻辑 - 基于 FlyOverDropPods 的实现方式
|
||||
private IntVec3 GetScatteredTarget(FlyOver flyOver)
|
||||
// 简化的目标选择 - 每次直接随机选择目标
|
||||
private IntVec3 SelectRandomTarget(FlyOver flyOver)
|
||||
{
|
||||
// 基础目标(攻击区域中心)
|
||||
IntVec3 baseTarget = currentTarget;
|
||||
|
||||
// 如果散射半径为0,直接返回基础目标
|
||||
if (Props.scatterRadius <= 0)
|
||||
return baseTarget;
|
||||
|
||||
// 使用类似 FlyOverDropPods 的散射逻辑
|
||||
for (int i = 0; i < 20; i++) // 尝试20次找到有效位置
|
||||
{
|
||||
// 在散射半径内随机选择位置
|
||||
IntVec3 scatteredPos = baseTarget;
|
||||
|
||||
// 使用圆形散射而不是方形
|
||||
float angle = Rand.Range(0f, 360f);
|
||||
float distance = Rand.Range(0f, Props.scatterRadius);
|
||||
|
||||
scatteredPos.x += Mathf.RoundToInt(Mathf.Cos(angle * Mathf.Deg2Rad) * distance);
|
||||
scatteredPos.z += Mathf.RoundToInt(Mathf.Sin(angle * Mathf.Deg2Rad) * distance);
|
||||
|
||||
// 检查是否无视保护机制
|
||||
bool ignoreProtectionThisShot = Rand.Value < Props.ignoreProtectionChance;
|
||||
|
||||
if (scatteredPos.InBounds(flyOver.Map) && IsValidTarget(scatteredPos, flyOver.Map, ignoreProtectionThisShot))
|
||||
{
|
||||
if (ignoreProtectionThisShot)
|
||||
{
|
||||
Log.Warning($"Ship Artillery: Protection ignored! Shell may hit player assets at {scatteredPos}");
|
||||
}
|
||||
return scatteredPos;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找不到有效位置,使用基础目标
|
||||
Log.Warning($"Ship Artillery: Could not find valid scattered target after 20 attempts, using base target");
|
||||
return baseTarget;
|
||||
IntVec3 center = GetFlyOverPosition(flyOver) + Props.targetOffset;
|
||||
return FindRandomTargetInRadius(center, flyOver.Map, Props.attackRadius);
|
||||
}
|
||||
|
||||
private IntVec3 SelectTarget(FlyOver flyOver)
|
||||
@@ -316,7 +286,7 @@ namespace ArachnaeSwarm
|
||||
return result;
|
||||
}
|
||||
|
||||
// 新的目标查找逻辑 - 基于攻击半径
|
||||
// 目标查找逻辑 - 基于攻击半径
|
||||
private IntVec3 FindRandomTargetInRadius(IntVec3 center, Map map, float radius)
|
||||
{
|
||||
Log.Message($"Finding target around {center} with radius {radius}");
|
||||
|
||||
@@ -22,8 +22,18 @@ namespace ArachnaeSwarm
|
||||
Log.Message($"Target cell: {target.Cell}, Dest: {dest.Cell}");
|
||||
|
||||
// 计算起始和结束位置
|
||||
IntVec3 startPos = CalculateStartPosition(target);
|
||||
IntVec3 endPos = CalculateEndPosition(target, startPos);
|
||||
IntVec3 startPos, endPos;
|
||||
|
||||
// 根据进场类型选择不同的计算方法
|
||||
if (Props.approachType == ApproachType.Perpendicular)
|
||||
{
|
||||
CalculatePerpendicularPath(target, out startPos, out endPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
startPos = CalculateStartPosition(target);
|
||||
endPos = CalculateEndPosition(target, startPos);
|
||||
}
|
||||
|
||||
Log.Message($"Final positions - Start: {startPos}, End: {endPos}");
|
||||
|
||||
@@ -40,6 +50,18 @@ namespace ArachnaeSwarm
|
||||
endPos = parent.pawn.Map.Center;
|
||||
}
|
||||
|
||||
// 确保起点和终点不同
|
||||
if (startPos == endPos)
|
||||
{
|
||||
Log.Warning($"FlyOver start and end positions are the same: {startPos}. Adjusting end position.");
|
||||
IntVec3 randomOffset = new IntVec3(Rand.Range(-10, 11), 0, Rand.Range(-10, 11));
|
||||
endPos += randomOffset;
|
||||
if (!endPos.InBounds(parent.pawn.Map))
|
||||
{
|
||||
endPos = parent.pawn.Map.Center;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据类型创建不同的飞越物体
|
||||
switch (Props.flyOverType)
|
||||
{
|
||||
@@ -58,6 +80,78 @@ namespace ArachnaeSwarm
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:计算垂直线进场路径
|
||||
private void CalculatePerpendicularPath(LocalTargetInfo target, out IntVec3 startPos, out IntVec3 endPos)
|
||||
{
|
||||
Map map = parent.pawn.Map;
|
||||
IntVec3 casterPos = parent.pawn.Position;
|
||||
IntVec3 targetPos = target.Cell;
|
||||
|
||||
Log.Message($"Calculating perpendicular path: Caster={casterPos}, Target={targetPos}");
|
||||
|
||||
// 计算施法者到目标的方向向量
|
||||
Vector3 directionToTarget = (targetPos.ToVector3() - casterPos.ToVector3()).normalized;
|
||||
|
||||
// 如果方向为零向量,使用随机方向
|
||||
if (directionToTarget == Vector3.zero)
|
||||
{
|
||||
directionToTarget = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
||||
Log.Message($"Using random direction: {directionToTarget}");
|
||||
}
|
||||
|
||||
// 计算垂直于施法者-目标连线的方向(旋转90度)
|
||||
Vector3 perpendicularDirection = new Vector3(-directionToTarget.z, 0, directionToTarget.x).normalized;
|
||||
|
||||
Log.Message($"Perpendicular direction: {perpendicularDirection}");
|
||||
|
||||
// 从目标点出发,向垂直方向的两侧延伸找到地图边缘
|
||||
IntVec3 edge1 = FindMapEdgeInDirection(map, targetPos, perpendicularDirection);
|
||||
IntVec3 edge2 = FindMapEdgeInDirection(map, targetPos, -perpendicularDirection);
|
||||
|
||||
// 随机选择起点和终点(确保目标点在路径上)
|
||||
if (Rand.Value < 0.5f)
|
||||
{
|
||||
startPos = edge1;
|
||||
endPos = edge2;
|
||||
}
|
||||
else
|
||||
{
|
||||
startPos = edge2;
|
||||
endPos = edge1;
|
||||
}
|
||||
|
||||
Log.Message($"Perpendicular path: {startPos} -> {targetPos} -> {endPos}");
|
||||
}
|
||||
|
||||
// 新增:在指定方向上找到地图边缘
|
||||
private IntVec3 FindMapEdgeInDirection(Map map, IntVec3 fromPos, Vector3 direction)
|
||||
{
|
||||
// 计算最大搜索距离(地图对角线的一半)
|
||||
float maxDistance = Mathf.Sqrt(map.Size.x * map.Size.x + map.Size.z * map.Size.z) * 0.6f;
|
||||
|
||||
// 沿着方向逐步搜索,直到找到地图边界
|
||||
for (float distance = 10f; distance <= maxDistance; distance += 5f)
|
||||
{
|
||||
IntVec3 testPos = fromPos + new IntVec3(
|
||||
Mathf.RoundToInt(direction.x * distance),
|
||||
0,
|
||||
Mathf.RoundToInt(direction.z * distance));
|
||||
|
||||
if (!testPos.InBounds(map))
|
||||
{
|
||||
// 找到边界,返回最近的有效位置
|
||||
IntVec3 edgePos = FindClosestValidPosition(testPos, map);
|
||||
Log.Message($"Found map edge at {edgePos} (direction: {direction}, distance: {distance})");
|
||||
return edgePos;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没找到边界,使用随机边缘位置
|
||||
Log.Warning($"Could not find map edge in direction {direction}, using random edge");
|
||||
return GetRandomMapEdgePosition(map);
|
||||
}
|
||||
|
||||
// 原有的位置计算方法
|
||||
private IntVec3 CalculateStartPosition(LocalTargetInfo target)
|
||||
{
|
||||
Map map = parent.pawn.Map;
|
||||
@@ -114,60 +208,33 @@ namespace ArachnaeSwarm
|
||||
break;
|
||||
}
|
||||
|
||||
// 关键修复:确保起点和终点不同
|
||||
if (startPos == endPos)
|
||||
{
|
||||
Log.Warning($"FlyOver start and end positions are the same: {startPos}. Adjusting end position.");
|
||||
|
||||
// 如果相同,将终点向随机方向移动
|
||||
IntVec3 randomOffset = new IntVec3(Rand.Range(-10, 11), 0, Rand.Range(-10, 11));
|
||||
endPos += randomOffset;
|
||||
|
||||
// 确保新位置在地图内
|
||||
if (!endPos.InBounds(map))
|
||||
{
|
||||
endPos = map.Center;
|
||||
}
|
||||
}
|
||||
|
||||
Log.Message($"Calculated positions - Start: {startPos}, End: {endPos}, Distance: {startPos.DistanceTo(endPos)}");
|
||||
return endPos;
|
||||
}
|
||||
|
||||
// 修复的 OppositeMapEdge 逻辑:确保穿过地图中心
|
||||
// 原有的辅助方法保持不变
|
||||
private IntVec3 GetOppositeMapEdgeThroughCenter(Map map, IntVec3 startPos)
|
||||
{
|
||||
IntVec3 center = map.Center;
|
||||
|
||||
// 计算从起点到中心的方向
|
||||
Vector3 toCenter = (center.ToVector3() - startPos.ToVector3()).normalized;
|
||||
|
||||
// 如果方向为零向量,使用随机方向
|
||||
if (toCenter == Vector3.zero)
|
||||
{
|
||||
toCenter = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
||||
Log.Message($"Using random direction to center: {toCenter}");
|
||||
}
|
||||
|
||||
// 计算从中心到对面边缘的方向(与起点到中心的方向相同)
|
||||
Vector3 fromCenter = toCenter;
|
||||
|
||||
Log.Message($"Calculating opposite edge: Start={startPos}, Center={center}, Direction={fromCenter}");
|
||||
|
||||
// 从中心出发,沿着相同方向找到对面的地图边缘
|
||||
IntVec3 oppositeEdge = GetMapEdgePositionFromCenter(map, fromCenter);
|
||||
|
||||
Log.Message($"Found opposite edge through center: {oppositeEdge}");
|
||||
return oppositeEdge;
|
||||
}
|
||||
|
||||
// 从地图中心出发找到指定方向的地图边缘
|
||||
private IntVec3 GetMapEdgePositionFromCenter(Map map, Vector3 direction)
|
||||
{
|
||||
IntVec3 center = map.Center;
|
||||
float maxDist = Mathf.Max(map.Size.x, map.Size.z) * 0.6f;
|
||||
|
||||
// 从中心向指定方向延伸
|
||||
for (int i = 1; i <= maxDist; i++)
|
||||
{
|
||||
IntVec3 testPos = center + new IntVec3(
|
||||
@@ -177,14 +244,12 @@ namespace ArachnaeSwarm
|
||||
|
||||
if (!testPos.InBounds(map))
|
||||
{
|
||||
// 找到边界位置
|
||||
IntVec3 edgePos = FindClosestValidPosition(testPos, map);
|
||||
Log.Message($"Found map edge from center: {edgePos} (direction: {direction}, distance: {i})");
|
||||
return edgePos;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没找到边界,使用随机边缘位置
|
||||
Log.Warning("Could not find map edge from center, using random edge");
|
||||
return GetRandomMapEdgePosition(map);
|
||||
}
|
||||
@@ -200,7 +265,6 @@ namespace ArachnaeSwarm
|
||||
IntVec3 center = map.Center;
|
||||
float maxDist = Mathf.Max(map.Size.x, map.Size.z) * 0.6f;
|
||||
|
||||
// 从中心向指定方向延伸
|
||||
for (int i = 1; i <= maxDist; i++)
|
||||
{
|
||||
IntVec3 testPos = center + new IntVec3(
|
||||
@@ -210,21 +274,18 @@ namespace ArachnaeSwarm
|
||||
|
||||
if (!testPos.InBounds(map))
|
||||
{
|
||||
// 找到边界位置
|
||||
IntVec3 edgePos = FindClosestValidPosition(testPos, map);
|
||||
Log.Message($"Found map edge position: {edgePos} (direction: {direction}, distance: {i})");
|
||||
return edgePos;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没找到边界,使用随机边缘位置
|
||||
Log.Warning("Could not find map edge in direction, using random edge");
|
||||
return GetRandomMapEdgePosition(map);
|
||||
}
|
||||
|
||||
private IntVec3 FindClosestValidPosition(IntVec3 invalidPos, Map map)
|
||||
{
|
||||
// 在无效位置周围寻找有效的边界位置
|
||||
for (int radius = 1; radius <= 5; radius++)
|
||||
{
|
||||
foreach (IntVec3 pos in GenRadial.RadialPatternInRadius(radius))
|
||||
@@ -240,26 +301,8 @@ namespace ArachnaeSwarm
|
||||
return map.Center;
|
||||
}
|
||||
|
||||
// 旧的 OppositeMapEdge 逻辑(保留作为参考)
|
||||
private IntVec3 GetOppositeMapEdgePosition(Map map, IntVec3 startPos)
|
||||
{
|
||||
// 计算从起点指向地图中心的方向,然后取反
|
||||
Vector3 toCenter = (map.Center.ToVector3() - startPos.ToVector3()).normalized;
|
||||
Vector3 oppositeDirection = -toCenter;
|
||||
|
||||
// 如果方向为零向量,使用随机方向
|
||||
if (oppositeDirection == Vector3.zero)
|
||||
{
|
||||
oppositeDirection = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
||||
}
|
||||
|
||||
Log.Message($"Opposite direction from {startPos}: {oppositeDirection}");
|
||||
return GetMapEdgePosition(map, oppositeDirection);
|
||||
}
|
||||
|
||||
private IntVec3 GetRandomMapEdgePosition(Map map)
|
||||
{
|
||||
// 随机选择一个地图边缘位置
|
||||
int edge = Rand.Range(0, 4);
|
||||
int x, z;
|
||||
|
||||
@@ -305,7 +348,6 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
Vector3 direction = (target.Cell.ToVector3() - parent.pawn.Position.ToVector3()).normalized;
|
||||
|
||||
// 如果方向为零向量,使用随机方向
|
||||
if (direction == Vector3.zero)
|
||||
{
|
||||
direction = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
||||
@@ -317,7 +359,6 @@ namespace ArachnaeSwarm
|
||||
|
||||
private void CreateStandardFlyOver(IntVec3 startPos, IntVec3 endPos)
|
||||
{
|
||||
// 确保有默认的飞越定义
|
||||
ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase<ThingDef>.GetNamedSilentFail("ARA_HiveShip");
|
||||
if (flyOverDef == null)
|
||||
{
|
||||
@@ -334,14 +375,12 @@ namespace ArachnaeSwarm
|
||||
Props.altitude
|
||||
);
|
||||
|
||||
// 设置属性
|
||||
flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact;
|
||||
flyOver.playFlyOverSound = Props.playFlyOverSound;
|
||||
|
||||
// 应用自定义音效
|
||||
if (Props.customSound != null)
|
||||
{
|
||||
// 这里需要更复杂的逻辑来替换音效
|
||||
// 自定义音效逻辑
|
||||
}
|
||||
|
||||
Log.Message($"Standard FlyOver created: {flyOver} from {startPos} to {endPos}");
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
public ThingDef flyOverDef; // 飞越物体的 ThingDef
|
||||
public FlyOverType flyOverType = FlyOverType.Standard; // 飞越类型
|
||||
public ApproachType approachType = ApproachType.Standard; // 进场类型
|
||||
public float flightSpeed = 1f; // 飞行速度
|
||||
public float altitude = 15f; // 飞行高度
|
||||
public bool spawnContents = false; // 是否生成内容物
|
||||
@@ -16,11 +17,11 @@ namespace ArachnaeSwarm
|
||||
public SoundDef customSound; // 自定义音效
|
||||
public bool playFlyOverSound = true; // 是否播放飞越音效
|
||||
|
||||
// 起始位置选项
|
||||
// 起始位置选项(当approachType为Standard时使用)
|
||||
public StartPosition startPosition = StartPosition.Caster;
|
||||
public IntVec3 customStartOffset = IntVec3.Zero;
|
||||
|
||||
// 终点位置选项
|
||||
// 终点位置选项(当approachType为Standard时使用)
|
||||
public EndPosition endPosition = EndPosition.TargetCell;
|
||||
public IntVec3 customEndOffset = IntVec3.Zero;
|
||||
public int flyOverDistance = 30; // 飞越距离(当终点为自定义时)
|
||||
@@ -41,6 +42,13 @@ namespace ArachnaeSwarm
|
||||
Reconnaissance // 侦察飞越
|
||||
}
|
||||
|
||||
// 新增:进场类型枚举
|
||||
public enum ApproachType
|
||||
{
|
||||
Standard, // 标准进场(使用原有的位置计算)
|
||||
Perpendicular // 垂直线进场(垂直于施法者-目标连线)
|
||||
}
|
||||
|
||||
// 起始位置枚举
|
||||
public enum StartPosition
|
||||
{
|
||||
@@ -56,7 +64,7 @@ namespace ArachnaeSwarm
|
||||
TargetCell, // 目标单元格
|
||||
OppositeMapEdge, // 对面地图边缘
|
||||
CustomOffset, // 自定义偏移
|
||||
FixedDistance, // 固定距离
|
||||
FixedDistance, // 固定距离
|
||||
RandomMapEdge
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,14 @@ namespace ArachnaeSwarm
|
||||
public float currentFadeInTime = 0f; // 当前淡入时间
|
||||
public bool fadeInCompleted = false; // 淡入是否完成
|
||||
|
||||
// 淡出效果相关
|
||||
public float fadeOutDuration = 0f; // 动态计算的淡出持续时间
|
||||
public float currentFadeOutTime = 0f; // 当前淡出时间
|
||||
public bool fadeOutStarted = false; // 淡出是否开始
|
||||
public bool fadeOutCompleted = false; // 淡出是否完成
|
||||
public float fadeOutStartProgress = 0.7f; // 开始淡出的进度阈值(0-1)
|
||||
public float defaultFadeOutDuration = 1.5f; // 默认淡出持续时间(仅用于销毁)
|
||||
|
||||
// 状态标志
|
||||
public bool hasStarted = false;
|
||||
public bool hasCompleted = false;
|
||||
@@ -110,6 +118,55 @@ namespace ArachnaeSwarm
|
||||
}
|
||||
}
|
||||
|
||||
// 淡出透明度(0-1)
|
||||
public float FadeOutAlpha
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!fadeOutStarted) return 1f;
|
||||
if (fadeOutCompleted) return 0f;
|
||||
return Mathf.Clamp01(1f - (currentFadeOutTime / fadeOutDuration));
|
||||
}
|
||||
}
|
||||
|
||||
// 总体透明度(淡入 * 淡出)
|
||||
public float OverallAlpha
|
||||
{
|
||||
get
|
||||
{
|
||||
return FadeInAlpha * FadeOutAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:计算剩余飞行时间(秒)
|
||||
public float RemainingFlightTime
|
||||
{
|
||||
get
|
||||
{
|
||||
float remainingProgress = 1f - currentProgress;
|
||||
return remainingProgress / (flightSpeed * 0.001f) * (1f / 60f);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:计算基于剩余距离的淡出持续时间
|
||||
private float CalculateDynamicFadeOutDuration()
|
||||
{
|
||||
// 获取 ModExtension 配置
|
||||
var shadowExtension = def.GetModExtension<FlyOverShadowExtension>();
|
||||
float minFadeOutDuration = shadowExtension?.minFadeOutDuration ?? 0.5f;
|
||||
float maxFadeOutDuration = shadowExtension?.maxFadeOutDuration ?? 3f;
|
||||
float fadeOutDistanceFactor = shadowExtension?.fadeOutDistanceFactor ?? 0.3f;
|
||||
|
||||
// 计算剩余飞行时间
|
||||
float remainingTime = RemainingFlightTime;
|
||||
|
||||
// 使用剩余时间的一部分作为淡出持续时间
|
||||
float dynamicDuration = remainingTime * fadeOutDistanceFactor;
|
||||
|
||||
// 限制在最小和最大范围内
|
||||
return Mathf.Clamp(dynamicDuration, minFadeOutDuration, maxFadeOutDuration);
|
||||
}
|
||||
|
||||
public FlyOver()
|
||||
{
|
||||
innerContainer = new ThingOwner<Thing>(this);
|
||||
@@ -130,6 +187,14 @@ 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);
|
||||
Scribe_Values.Look(ref fadeOutStarted, "fadeOutStarted", false);
|
||||
Scribe_Values.Look(ref fadeOutCompleted, "fadeOutCompleted", false);
|
||||
Scribe_Values.Look(ref fadeOutStartProgress, "fadeOutStartProgress", 0.7f);
|
||||
Scribe_Values.Look(ref defaultFadeOutDuration, "defaultFadeOutDuration", 1.5f);
|
||||
}
|
||||
|
||||
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||
@@ -148,6 +213,12 @@ namespace ArachnaeSwarm
|
||||
// 重置淡入状态
|
||||
currentFadeInTime = 0f;
|
||||
fadeInCompleted = false;
|
||||
|
||||
// 重置淡出状态
|
||||
currentFadeOutTime = 0f;
|
||||
fadeOutStarted = false;
|
||||
fadeOutCompleted = false;
|
||||
fadeOutDuration = 0f;
|
||||
|
||||
// 开始飞行音效
|
||||
if (playFlyOverSound && def.skyfaller?.floatingSound != null)
|
||||
@@ -169,7 +240,7 @@ namespace ArachnaeSwarm
|
||||
// 更新淡入效果
|
||||
if (!fadeInCompleted)
|
||||
{
|
||||
currentFadeInTime += 1f / 60f; // 假设 60 FPS
|
||||
currentFadeInTime += 1f / 60f;
|
||||
if (currentFadeInTime >= fadeInDuration)
|
||||
{
|
||||
fadeInCompleted = true;
|
||||
@@ -180,11 +251,29 @@ namespace ArachnaeSwarm
|
||||
// 更新飞行进度
|
||||
currentProgress += flightSpeed * 0.001f;
|
||||
|
||||
// 更新当前位置(用于碰撞检测等)
|
||||
// 检查是否应该开始淡出(基于剩余距离动态计算)
|
||||
if (!fadeOutStarted && currentProgress >= fadeOutStartProgress)
|
||||
{
|
||||
StartFadeOut();
|
||||
}
|
||||
|
||||
// 更新淡出效果
|
||||
if (fadeOutStarted && !fadeOutCompleted)
|
||||
{
|
||||
currentFadeOutTime += 1f / 60f;
|
||||
if (currentFadeOutTime >= fadeOutDuration)
|
||||
{
|
||||
fadeOutCompleted = true;
|
||||
currentFadeOutTime = fadeOutDuration;
|
||||
Log.Message("FlyOver fade out completed");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新当前位置
|
||||
UpdatePosition();
|
||||
|
||||
// 维持飞行音效
|
||||
flightSoundPlaying?.Maintain();
|
||||
// 维持飞行音效(在淡出时逐渐降低音量)
|
||||
UpdateFlightSound();
|
||||
|
||||
// 检查是否到达终点
|
||||
if (currentProgress >= 1f)
|
||||
@@ -192,16 +281,23 @@ namespace ArachnaeSwarm
|
||||
CompleteFlyOver();
|
||||
}
|
||||
|
||||
// 可选:生成飞行轨迹特效
|
||||
if (Rand.MTBEventOccurs(0.5f, 1f, 1f) && def.skyfaller?.motesPerCell > 0)
|
||||
{
|
||||
CreateFlightEffects();
|
||||
}
|
||||
// 生成飞行轨迹特效(在淡出时减少特效)
|
||||
CreateFlightEffects();
|
||||
}
|
||||
|
||||
// 新增:开始淡出效果
|
||||
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");
|
||||
}
|
||||
|
||||
private void UpdatePosition()
|
||||
{
|
||||
// 更新物体的网格位置(用于碰撞检测等)
|
||||
Vector3 currentWorldPos = Vector3.Lerp(startPosition.ToVector3(), endPosition.ToVector3(), currentProgress);
|
||||
IntVec3 newPos = currentWorldPos.ToIntVec3();
|
||||
|
||||
@@ -211,6 +307,19 @@ namespace ArachnaeSwarm
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFlightSound()
|
||||
{
|
||||
if (flightSoundPlaying != null)
|
||||
{
|
||||
if (fadeOutStarted)
|
||||
{
|
||||
// 淡出时逐渐降低音效音量
|
||||
flightSoundPlaying.externalParams["VolumeFactor"] = FadeOutAlpha;
|
||||
}
|
||||
flightSoundPlaying?.Maintain();
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteFlyOver()
|
||||
{
|
||||
hasCompleted = true;
|
||||
@@ -235,6 +344,21 @@ namespace ArachnaeSwarm
|
||||
Destroy();
|
||||
}
|
||||
|
||||
// 新增:紧急销毁方法(使用默认淡出时间)
|
||||
public void EmergencyDestroy()
|
||||
{
|
||||
if (!fadeOutStarted)
|
||||
{
|
||||
// 如果还没有开始淡出,使用默认淡出时间
|
||||
fadeOutStarted = true;
|
||||
fadeOutDuration = defaultFadeOutDuration;
|
||||
Log.Message($"FlyOver emergency destroy with default fade out: {defaultFadeOutDuration}s");
|
||||
}
|
||||
|
||||
// 设置标记,下一帧会处理淡出
|
||||
hasCompleted = true;
|
||||
}
|
||||
|
||||
private void SpawnContents()
|
||||
{
|
||||
foreach (Thing thing in innerContainer)
|
||||
@@ -250,33 +374,36 @@ namespace ArachnaeSwarm
|
||||
private void CreateFlightEffects()
|
||||
{
|
||||
// 在飞行轨迹上生成粒子效果
|
||||
Vector3 effectPos = DrawPos;
|
||||
effectPos.y = AltitudeLayer.MoteOverhead.AltitudeFor();
|
||||
FleckMaker.ThrowSmoke(effectPos, base.Map, 1f);
|
||||
|
||||
// 可选:根据速度生成更多效果
|
||||
if (flightSpeed > 2f)
|
||||
if (Rand.MTBEventOccurs(0.5f, 1f, 1f) && def.skyfaller?.motesPerCell > 0 && !fadeOutCompleted)
|
||||
{
|
||||
FleckMaker.ThrowAirPuffUp(effectPos, base.Map);
|
||||
Vector3 effectPos = DrawPos;
|
||||
effectPos.y = AltitudeLayer.MoteOverhead.AltitudeFor();
|
||||
|
||||
// 淡出时减少粒子效果强度
|
||||
float effectIntensity = fadeOutStarted ? FadeOutAlpha : 1f;
|
||||
FleckMaker.ThrowSmoke(effectPos, base.Map, 1f * effectIntensity);
|
||||
|
||||
// 可选:根据速度生成更多效果
|
||||
if (flightSpeed > 2f && !fadeOutStarted)
|
||||
{
|
||||
FleckMaker.ThrowAirPuffUp(effectPos, base.Map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关键修改:添加淡入效果的绘制方法
|
||||
// 绘制方法保持不变,使用OverallAlpha
|
||||
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
|
||||
{
|
||||
Vector3 finalDrawPos = drawLoc;
|
||||
|
||||
// 绘制阴影
|
||||
if (createShadow)
|
||||
{
|
||||
DrawFlightShadow();
|
||||
}
|
||||
|
||||
// 绘制主体 - 使用带淡入效果的绘制方法
|
||||
DrawFlyOverWithFade(finalDrawPos);
|
||||
}
|
||||
|
||||
// 带淡入效果的主体绘制方法
|
||||
protected virtual void DrawFlyOverWithFade(Vector3 drawPos)
|
||||
{
|
||||
Thing thingForGraphic = GetThingForGraphic();
|
||||
@@ -285,41 +412,41 @@ namespace ArachnaeSwarm
|
||||
if (graphic == null)
|
||||
return;
|
||||
|
||||
// 获取原始材质
|
||||
Material material = graphic.MatSingle;
|
||||
if (material == null)
|
||||
return;
|
||||
|
||||
// 计算淡入透明度
|
||||
float alpha = FadeInAlpha;
|
||||
float alpha = OverallAlpha;
|
||||
|
||||
// 如果已经完全淡入,使用原版绘制方法以获得最佳性能
|
||||
if (alpha >= 0.999f)
|
||||
if (alpha <= 0.001f)
|
||||
return;
|
||||
|
||||
if (fadeInCompleted && !fadeOutStarted && alpha >= 0.999f)
|
||||
{
|
||||
graphic.Draw(drawPos, Rot4.North, thingForGraphic, ExactRotation.eulerAngles.y);
|
||||
Vector3 highAltitudePos = drawPos;
|
||||
highAltitudePos.y = AltitudeLayer.MetaOverlays.AltitudeFor();
|
||||
graphic.Draw(highAltitudePos, Rot4.North, thingForGraphic, ExactRotation.eulerAngles.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建带透明度的材质属性块
|
||||
fadePropertyBlock.SetColor(ShaderPropertyIDs.Color,
|
||||
fadePropertyBlock.SetColor(ShaderPropertyIDs.Color,
|
||||
new Color(graphic.Color.r, graphic.Color.g, graphic.Color.b, graphic.Color.a * alpha));
|
||||
|
||||
// 计算缩放
|
||||
Vector3 scale = Vector3.one;
|
||||
if (def.graphicData != null)
|
||||
{
|
||||
scale = new Vector3(def.graphicData.drawSize.x, 1f, def.graphicData.drawSize.y);
|
||||
}
|
||||
|
||||
// 使用自定义绘制实现淡入效果
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(drawPos, ExactRotation, scale);
|
||||
Vector3 highPos = drawPos;
|
||||
highPos.y = AltitudeLayer.MetaOverlays.AltitudeFor();
|
||||
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(highPos, ExactRotation, scale);
|
||||
Graphics.DrawMesh(MeshPool.plane10, matrix, material, 0, null, 0, fadePropertyBlock);
|
||||
}
|
||||
|
||||
// 简化的阴影绘制方法 - 完全移除旋转(模仿原版)
|
||||
protected virtual void DrawFlightShadow()
|
||||
{
|
||||
// 检查是否有 ModExtension
|
||||
var shadowExtension = def.GetModExtension<FlyOverShadowExtension>();
|
||||
|
||||
Material shadowMaterial;
|
||||
@@ -338,7 +465,6 @@ namespace ArachnaeSwarm
|
||||
Vector3 shadowPos = DrawPos;
|
||||
shadowPos.y = AltitudeLayer.Shadows.AltitudeFor();
|
||||
|
||||
// 使用 ModExtension 参数或默认值
|
||||
float shadowIntensity = shadowExtension?.shadowIntensity ?? 1f;
|
||||
float minAlpha = shadowExtension?.minShadowAlpha ?? 0.3f;
|
||||
float maxAlpha = shadowExtension?.maxShadowAlpha ?? 1f;
|
||||
@@ -348,10 +474,11 @@ namespace ArachnaeSwarm
|
||||
float shadowAlpha = Mathf.Lerp(minAlpha, maxAlpha, currentProgress) * shadowIntensity;
|
||||
float shadowScale = Mathf.Lerp(minScale, maxScale, currentProgress);
|
||||
|
||||
// 阴影也应用淡入效果
|
||||
shadowAlpha *= FadeInAlpha;
|
||||
shadowAlpha *= OverallAlpha;
|
||||
|
||||
if (shadowAlpha <= 0.001f)
|
||||
return;
|
||||
|
||||
// 完全移除阴影旋转 - 始终使用默认朝向(模仿原版 Projectile)
|
||||
Vector3 s = new Vector3(shadowScale, 1f, shadowScale);
|
||||
Vector3 vector = new Vector3(0f, -0.01f, 0f);
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(shadowPos + vector, Quaternion.identity, s);
|
||||
@@ -359,7 +486,7 @@ namespace ArachnaeSwarm
|
||||
Graphics.DrawMesh(MeshPool.plane10, matrix, shadowMaterial, 0);
|
||||
}
|
||||
|
||||
// IThingHolder 接口实现
|
||||
// IThingHolder 接口实现和其他方法保持不变
|
||||
public ThingOwner GetDirectlyHeldThings()
|
||||
{
|
||||
return innerContainer;
|
||||
@@ -381,7 +508,8 @@ namespace ArachnaeSwarm
|
||||
|
||||
// 工具方法:创建飞越物体
|
||||
public static FlyOver MakeFlyOver(ThingDef flyOverDef, IntVec3 start, IntVec3 end, Map map,
|
||||
float speed = 1f, float height = 10f, ThingOwner contents = null, float fadeInDuration = 1.5f)
|
||||
float speed = 1f, float height = 10f, ThingOwner contents = null,
|
||||
float fadeInDuration = 1.5f, float defaultFadeOutDuration = 1.5f)
|
||||
{
|
||||
FlyOver flyOver = (FlyOver)ThingMaker.MakeThing(flyOverDef);
|
||||
flyOver.startPosition = start;
|
||||
@@ -389,6 +517,7 @@ namespace ArachnaeSwarm
|
||||
flyOver.flightSpeed = speed;
|
||||
flyOver.altitude = height;
|
||||
flyOver.fadeInDuration = fadeInDuration;
|
||||
flyOver.defaultFadeOutDuration = defaultFadeOutDuration;
|
||||
|
||||
if (contents != null)
|
||||
{
|
||||
@@ -397,51 +526,32 @@ namespace ArachnaeSwarm
|
||||
|
||||
GenSpawn.Spawn(flyOver, start, map);
|
||||
|
||||
Log.Message($"FlyOver created: {flyOver} from {start} to {end} at altitude {height} with {fadeInDuration}s fade-in");
|
||||
Log.Message($"FlyOver created: {flyOver} from {start} to {end} at altitude {height}");
|
||||
return flyOver;
|
||||
}
|
||||
|
||||
// 高空版本的工具方法
|
||||
public static FlyOver MakeHighAltitudeFlyOver(ThingDef flyOverDef, IntVec3 start, IntVec3 end, Map map,
|
||||
float speed = 2f, float height = 50f, ThingOwner contents = null, float fadeInDuration = 1.5f)
|
||||
{
|
||||
FlyOver flyOver = MakeFlyOver(flyOverDef, start, end, map, speed, height, contents, fadeInDuration);
|
||||
|
||||
// 高空特有的设置
|
||||
flyOver.playFlyOverSound = false;
|
||||
flyOver.createShadow = true;
|
||||
flyOver.spawnContentsOnImpact = false;
|
||||
|
||||
Log.Message($"HighAltitude FlyOver created: {flyOver} from {start} to {end} at altitude {height}");
|
||||
return flyOver;
|
||||
}
|
||||
|
||||
// 快速淡入版本的工具方法
|
||||
public static FlyOver MakeQuickFlyOver(ThingDef flyOverDef, IntVec3 start, IntVec3 end, Map map,
|
||||
float speed = 1f, float height = 10f, ThingOwner contents = null)
|
||||
{
|
||||
return MakeFlyOver(flyOverDef, start, end, map, speed, height, contents, 0.5f);
|
||||
}
|
||||
|
||||
// 慢速淡入版本的工具方法(适合非常大的模型)
|
||||
public static FlyOver MakeSlowFlyOver(ThingDef flyOverDef, IntVec3 start, IntVec3 end, Map map,
|
||||
float speed = 1f, float height = 10f, ThingOwner contents = null)
|
||||
{
|
||||
return MakeFlyOver(flyOverDef, start, end, map, speed, height, contents, 3f);
|
||||
}
|
||||
// 其他工具方法...
|
||||
}
|
||||
|
||||
// 更新的 ModExtension,添加淡入配置
|
||||
// 更新的 ModExtension,添加淡出配置
|
||||
public class FlyOverShadowExtension : DefModExtension
|
||||
{
|
||||
public string customShadowPath; // 自定义阴影材质路径
|
||||
public float shadowIntensity = 0.6f; // 阴影强度
|
||||
public bool useCustomShadow = false; // 是否使用自定义阴影
|
||||
public float minShadowAlpha = 0.05f; // 最小阴影透明度
|
||||
public float maxShadowAlpha = 0.2f; // 最大阴影透明度
|
||||
public float minShadowScale = 0.5f; // 最小阴影大小
|
||||
public float maxShadowScale = 1.0f; // 最大阴影大小
|
||||
public float defaultFadeInDuration = 1.5f; // 默认淡入持续时间
|
||||
public float ActuallyHeight = 150f; // 实际高度
|
||||
public string customShadowPath;
|
||||
public float shadowIntensity = 0.6f;
|
||||
public bool useCustomShadow = false;
|
||||
public float minShadowAlpha = 0.05f;
|
||||
public float maxShadowAlpha = 0.2f;
|
||||
public float minShadowScale = 0.5f;
|
||||
public float maxShadowScale = 1.0f;
|
||||
public float defaultFadeInDuration = 1.5f;
|
||||
public float defaultFadeOutDuration = 1.5f; // 默认淡出持续时间(用于紧急销毁)
|
||||
public float fadeOutStartProgress = 0.98f;
|
||||
|
||||
// 新增:动态淡出配置
|
||||
public float minFadeOutDuration = 0.5f; // 最小淡出持续时间
|
||||
public float maxFadeOutDuration = 3f; // 最大淡出持续时间
|
||||
public float fadeOutDistanceFactor = 0.3f; // 淡出距离因子(剩余时间的百分比)
|
||||
|
||||
public float ActuallyHeight = 150f;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user