1
This commit is contained in:
Binary file not shown.
@@ -9,6 +9,7 @@
|
|||||||
<specialDesignatorClasses>
|
<specialDesignatorClasses>
|
||||||
<li>Designator_Cancel</li>
|
<li>Designator_Cancel</li>
|
||||||
<li>Designator_Deconstruct</li>
|
<li>Designator_Deconstruct</li>
|
||||||
|
<li>WulaFallenEmpire.Designator_CallSkyfallerInArea</li>
|
||||||
</specialDesignatorClasses>
|
</specialDesignatorClasses>
|
||||||
</DesignationCategoryDef>
|
</DesignationCategoryDef>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<minifiedDef>MinifiedThing</minifiedDef>
|
<minifiedDef>MinifiedThing</minifiedDef>
|
||||||
<tickerType>Normal</tickerType>
|
<tickerType>Normal</tickerType>
|
||||||
<tradeability>None</tradeability>
|
<tradeability>None</tradeability>
|
||||||
|
<replaceTags Inherit="False" IsNull="True" />
|
||||||
<descriptionHyperlinks>
|
<descriptionHyperlinks>
|
||||||
<ThingDef>WulaWall</ThingDef>
|
<ThingDef>WulaWall</ThingDef>
|
||||||
</descriptionHyperlinks>
|
</descriptionHyperlinks>
|
||||||
@@ -154,10 +155,6 @@
|
|||||||
<damageDef>Bomb</damageDef>
|
<damageDef>Bomb</damageDef>
|
||||||
<multiplier>0.01</multiplier>
|
<multiplier>0.01</multiplier>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<damageDef>Thump</damageDef>
|
|
||||||
<multiplier>0.1</multiplier>
|
|
||||||
</li>
|
|
||||||
</damageMultipliers>
|
</damageMultipliers>
|
||||||
<comps Inherit="False">
|
<comps Inherit="False">
|
||||||
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
|
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
|
||||||
@@ -347,15 +344,11 @@
|
|||||||
<allowNonPlayer>true</allowNonPlayer>
|
<allowNonPlayer>true</allowNonPlayer>
|
||||||
</li>
|
</li>
|
||||||
</comps>
|
</comps>
|
||||||
<damageMultipliers>
|
<damageMultipliers Inherit="False">
|
||||||
<li>
|
<li>
|
||||||
<damageDef>Bomb</damageDef>
|
<damageDef>Bomb</damageDef>
|
||||||
<multiplier>0.01</multiplier>
|
<multiplier>0.01</multiplier>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<damageDef>Thump</damageDef>
|
|
||||||
<multiplier>0.01</multiplier>
|
|
||||||
</li>
|
|
||||||
</damageMultipliers>
|
</damageMultipliers>
|
||||||
<building>
|
<building>
|
||||||
<paintable>true</paintable>
|
<paintable>true</paintable>
|
||||||
@@ -561,6 +554,19 @@
|
|||||||
<terrainAffordanceNeeded>Light</terrainAffordanceNeeded>
|
<terrainAffordanceNeeded>Light</terrainAffordanceNeeded>
|
||||||
<staticSunShadowHeight>0.20</staticSunShadowHeight>
|
<staticSunShadowHeight>0.20</staticSunShadowHeight>
|
||||||
<designationHotKey>Misc1</designationHotKey>
|
<designationHotKey>Misc1</designationHotKey>
|
||||||
|
<damageMultipliers Inherit="False">
|
||||||
|
<li>
|
||||||
|
<damageDef>Bomb</damageDef>
|
||||||
|
<multiplier>0.01</multiplier>
|
||||||
|
</li>
|
||||||
|
</damageMultipliers>
|
||||||
|
<comps>
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
|
||||||
|
<!-- <factionDef>Mechanoid</factionDef> 不写默认玩家派系-->
|
||||||
|
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
|
||||||
|
<overrideExistingFaction>false</overrideExistingFaction>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
</ThingDef>
|
</ThingDef>
|
||||||
|
|
||||||
<!-- 维护舱 -->
|
<!-- 维护舱 -->
|
||||||
|
|||||||
@@ -1,5 +1,192 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<Defs>
|
<Defs>
|
||||||
|
<!-- 地雷 -->
|
||||||
|
<ThingDef ParentName="BuildingBase">
|
||||||
|
<defName>Wula_Sonar_Mine_Cleanzone</defName>
|
||||||
|
<label>TAm-1"鹅卵石"感应地雷</label>
|
||||||
|
<description>清理出一块场地并准备好资源,使得乌拉帝国可以向此处投放建筑,建造好的信标可以收起或移至他处。\n\nTAm-1"鹅卵石"感应地雷是一种危险的地雷,它们通常由乌拉帝国的工程部队部署到战场上,拥有智能敌我识别能力,在检测到敌军活动时会将自身的战斗部直接向敌人的位置发射。</description>
|
||||||
|
<uiIconPath>Wula/Building/Wula_Base_ATGun_Turret</uiIconPath>
|
||||||
|
<minifiedDef>MinifiedThing</minifiedDef>
|
||||||
|
<tickerType>Normal</tickerType>
|
||||||
|
<tradeability>None</tradeability>
|
||||||
|
<descriptionHyperlinks>
|
||||||
|
<ThingDef>Wula_Sonar_Mine</ThingDef>
|
||||||
|
</descriptionHyperlinks>
|
||||||
|
<thingCategories Inherit="False">
|
||||||
|
<li>BuildingsMisc</li>
|
||||||
|
</thingCategories>
|
||||||
|
<graphicData>
|
||||||
|
<texPath>Wula/Building/WULA_Dropping_Building_Cleanzone</texPath>
|
||||||
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
|
<drawSize>(1,1)</drawSize>
|
||||||
|
<damageData>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</damageData>
|
||||||
|
</graphicData>
|
||||||
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
|
<passability>PassThroughOnly</passability>
|
||||||
|
<pathCost>0</pathCost>
|
||||||
|
<castEdgeShadows>false</castEdgeShadows>
|
||||||
|
<fillPercent>0.5</fillPercent>
|
||||||
|
<canOverlapZones>false</canOverlapZones>
|
||||||
|
<hasInteractionCell>false</hasInteractionCell>
|
||||||
|
<rotatable>false</rotatable>
|
||||||
|
<terrainAffordanceNeeded>Light</terrainAffordanceNeeded>
|
||||||
|
<researchPrerequisites Inherit="False">
|
||||||
|
<li>WULA_Turret_Base_AT_Technology</li>
|
||||||
|
</researchPrerequisites>
|
||||||
|
<statBases>
|
||||||
|
<MarketValue>0</MarketValue>
|
||||||
|
<MaxHitPoints>1</MaxHitPoints>
|
||||||
|
<WorkToBuild>0</WorkToBuild>
|
||||||
|
<Mass>1</Mass>
|
||||||
|
<Flammability>0</Flammability>
|
||||||
|
</statBases>
|
||||||
|
<size>(1,1)</size>
|
||||||
|
<constructionSkillPrerequisite>0</constructionSkillPrerequisite>
|
||||||
|
<resourcesFractionWhenDeconstructed>0</resourcesFractionWhenDeconstructed>
|
||||||
|
<costList Inherit="False">
|
||||||
|
<WULA_Alloy>2</WULA_Alloy>
|
||||||
|
<Chemfuel>5</Chemfuel>
|
||||||
|
</costList>
|
||||||
|
<building>
|
||||||
|
<destroySound>BuildingDestroyed_Metal_Small</destroySound>
|
||||||
|
</building>
|
||||||
|
<placeWorkers>
|
||||||
|
<li>WulaFallenEmpire.PlaceWorker_CustomRadius</li>
|
||||||
|
</placeWorkers>
|
||||||
|
<designationCategory>WULA_Buildings</designationCategory>
|
||||||
|
<comps>
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_CustomRadius">
|
||||||
|
<radius>15</radius> <!-- 半径大小 -->
|
||||||
|
<color>(1, 1, 1)</color> <!-- 绿色圆圈 -->
|
||||||
|
<radiusOffset>0</radiusOffset> <!-- 半径偏移 -->
|
||||||
|
<showInGUI>true</showInGUI>
|
||||||
|
<label>感应射程</label>
|
||||||
|
<description>在该建筑空降到指定地点时,地雷能够监测敌军动向的最大射程。</description>
|
||||||
|
<defaultVisible>true</defaultVisible>
|
||||||
|
</li>
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_SkyfallerCaller">
|
||||||
|
<skyfallerDef>Wula_Sonar_Mine_Incoming</skyfallerDef> <!-- 替换为您想要的Skyfaller类型 -->
|
||||||
|
<destroyBuilding>true</destroyBuilding>
|
||||||
|
<delayTicks>1</delayTicks>
|
||||||
|
<allowThinRoof>true</allowThinRoof>
|
||||||
|
<allowThickRoof>false</allowThickRoof>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
</ThingDef>
|
||||||
|
<ThingDef ParentName="SkyfallerBase">
|
||||||
|
<defName>Wula_Sonar_Mine_Incoming</defName>
|
||||||
|
<label>TAm-1"鹅卵石"感应地雷</label>
|
||||||
|
<size>(1,1)</size>
|
||||||
|
<graphicData>
|
||||||
|
<texPath>Wula/Building/Wula_Base_ATGun_Turret_Incoming</texPath>
|
||||||
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
|
<shaderType>CutoutFlying</shaderType>
|
||||||
|
<drawSize>(1,1)</drawSize>
|
||||||
|
</graphicData>
|
||||||
|
<skyfaller>
|
||||||
|
<movementType>Accelerate</movementType>
|
||||||
|
<shadow>Things/Skyfaller/SkyfallerShadowDropPod</shadow>
|
||||||
|
<shadowSize>(1, 1)</shadowSize>
|
||||||
|
<anticipationSound>DropPod_Fall</anticipationSound>
|
||||||
|
<anticipationSoundTicks>100</anticipationSoundTicks>
|
||||||
|
<impactSound>Explosion_Vaporize</impactSound>
|
||||||
|
<moteSpawnTime>0.05</moteSpawnTime>
|
||||||
|
<motesPerCell>1</motesPerCell>
|
||||||
|
<cameraShake>1</cameraShake>
|
||||||
|
<angleCurve>
|
||||||
|
<points>
|
||||||
|
<li>(0,0)</li>
|
||||||
|
<li>(1, 1)</li>
|
||||||
|
</points>
|
||||||
|
</angleCurve>
|
||||||
|
<spawnThing>Wula_Sonar_Mine</spawnThing>
|
||||||
|
</skyfaller>
|
||||||
|
<comps>
|
||||||
|
<li Class="CompProperties_Effecter">
|
||||||
|
<effecterDef>Smoke_Joint</effecterDef>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
</ThingDef>
|
||||||
|
<ThingDef ParentName="BuildingBase">
|
||||||
|
<defName>Wula_Sonar_Mine</defName>
|
||||||
|
<label>TAm-1"鹅卵石"感应地雷</label>
|
||||||
|
<description>一种危险的地雷,它们通常由乌拉帝国的工程部队部署到战场上,拥有智能敌我识别能力,在检测到敌军活动时会将自身的战斗部直接向敌人的位置发射。</description>
|
||||||
|
<graphicData>
|
||||||
|
<graphicClass>Graphic_Single</graphicClass>
|
||||||
|
<damageData>
|
||||||
|
<rect>(0.1,0,0.8,0.3)</rect>
|
||||||
|
</damageData>
|
||||||
|
</graphicData>
|
||||||
|
<uiIconOffset>(0,-0.14)</uiIconOffset>
|
||||||
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
|
<rotatable>false</rotatable>
|
||||||
|
<tickerType>Normal</tickerType>
|
||||||
|
<stealable>false</stealable>
|
||||||
|
<minifiedDef>MinifiedThing</minifiedDef>
|
||||||
|
<size>(1,1)</size>
|
||||||
|
<leaveResourcesWhenKilled>false</leaveResourcesWhenKilled>
|
||||||
|
<costList Inherit="False">
|
||||||
|
<WULA_Alloy>2</WULA_Alloy>
|
||||||
|
<Chemfuel>5</Chemfuel>
|
||||||
|
</costList>
|
||||||
|
<uiOrder>40</uiOrder>
|
||||||
|
<thingCategories>
|
||||||
|
<li>BuildingsSecurity</li>
|
||||||
|
</thingCategories>
|
||||||
|
<statBases>
|
||||||
|
<Mass>2</Mass>
|
||||||
|
<MaxHitPoints>40</MaxHitPoints>
|
||||||
|
<WorkToBuild>1400</WorkToBuild>
|
||||||
|
<Flammability>1</Flammability>
|
||||||
|
<Beauty>-4</Beauty>
|
||||||
|
<TrapSpringChance>0</TrapSpringChance>
|
||||||
|
</statBases>
|
||||||
|
<designationCategory>Security</designationCategory>
|
||||||
|
<building>
|
||||||
|
<isTrap>true</isTrap>
|
||||||
|
<expandHomeArea>false</expandHomeArea>
|
||||||
|
<ai_chillDestination>false</ai_chillDestination>
|
||||||
|
</building>
|
||||||
|
<placeWorkers>
|
||||||
|
<li>PlaceWorker_NeverAdjacentTrap</li>
|
||||||
|
</placeWorkers>
|
||||||
|
<comps>
|
||||||
|
<li Class="CompProperties_Explosive">
|
||||||
|
<explosiveRadius>3.9</explosiveRadius>
|
||||||
|
<explosiveDamageType>Bomb</explosiveDamageType>
|
||||||
|
<startWickHitPointsPercent>0.2</startWickHitPointsPercent>
|
||||||
|
<preExplosionSpawnSingleThingDef>Filth_BlastMark</preExplosionSpawnSingleThingDef>
|
||||||
|
<wickTicks>15</wickTicks>
|
||||||
|
<startWickOnDamageTaken>
|
||||||
|
<li>Bullet</li>
|
||||||
|
<li>Arrow</li>
|
||||||
|
<li>ArrowHighVelocity</li>
|
||||||
|
</startWickOnDamageTaken>
|
||||||
|
</li>
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_TrapLauncher">
|
||||||
|
<detectionRadius>15</detectionRadius> <!-- 检测半径,单位:格 -->
|
||||||
|
<scanIntervalTicks>60</scanIntervalTicks> <!-- 扫描间隔 -->
|
||||||
|
<projectileDef>Bullet_Wula_AI_Heavy_Panzer_Main_Weapon</projectileDef> <!-- 抛射体类型 -->
|
||||||
|
<requireLineOfSight>false</requireLineOfSight> <!-- 需要视线 -->
|
||||||
|
<showDetectionRadius>true</showDetectionRadius> <!-- 显示检测范围 -->
|
||||||
|
<burstCount>1</burstCount> <!-- 单次发射数量 -->
|
||||||
|
<canRetarget>false</canRetarget> <!-- 发射后是否可以重新锁定 -->
|
||||||
|
|
||||||
|
<!-- 音效定义 -->
|
||||||
|
<!-- <triggerSound>WULA_TrapTrigger</triggerSound>
|
||||||
|
<launchSound>WULA_ProjectileLaunch</launchSound>
|
||||||
|
<selfDestructSound>WULA_ExplosionSmall</selfDestructSound> -->
|
||||||
|
</li>
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
|
||||||
|
<!-- <factionDef>Mechanoid</factionDef> 不写默认玩家派系-->
|
||||||
|
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
|
||||||
|
<overrideExistingFaction>false</overrideExistingFaction>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
</ThingDef>
|
||||||
|
|
||||||
<!-- 猫猫地堡 -->
|
<!-- 猫猫地堡 -->
|
||||||
<ThingDef ParentName="BuildingBase">
|
<ThingDef ParentName="BuildingBase">
|
||||||
<defName>WULA_Cat_Bunker_Cleanzone</defName>
|
<defName>WULA_Cat_Bunker_Cleanzone</defName>
|
||||||
|
|||||||
@@ -551,7 +551,7 @@
|
|||||||
<defaultCooldownTime>12</defaultCooldownTime>
|
<defaultCooldownTime>12</defaultCooldownTime>
|
||||||
<burstShotCount>12</burstShotCount>
|
<burstShotCount>12</burstShotCount>
|
||||||
<ticksBetweenBurstShots>7</ticksBetweenBurstShots>
|
<ticksBetweenBurstShots>7</ticksBetweenBurstShots>
|
||||||
<requireLineOfSight>true</requireLineOfSight>
|
<requireLineOfSight>false</requireLineOfSight>
|
||||||
<forcedMissRadius>8</forcedMissRadius>
|
<forcedMissRadius>8</forcedMissRadius>
|
||||||
<soundCast>WULA_RW_Rocket_Shootingsound</soundCast>
|
<soundCast>WULA_RW_Rocket_Shootingsound</soundCast>
|
||||||
<soundCastTail>GunTail_Heavy</soundCastTail>
|
<soundCastTail>GunTail_Heavy</soundCastTail>
|
||||||
|
|||||||
@@ -412,4 +412,6 @@
|
|||||||
<WULA_SilverTransferred>已取出 {0} 白银,它们会随着下一次成品空投一起空投。</WULA_SilverTransferred>
|
<WULA_SilverTransferred>已取出 {0} 白银,它们会随着下一次成品空投一起空投。</WULA_SilverTransferred>
|
||||||
<WULA_TransferFailed>无法取出白银</WULA_TransferFailed>
|
<WULA_TransferFailed>无法取出白银</WULA_TransferFailed>
|
||||||
<WULA_TransferError>取出白银时发生错误</WULA_TransferError>
|
<WULA_TransferError>取出白银时发生错误</WULA_TransferError>
|
||||||
|
|
||||||
|
<WULA_TrapLauncherTriggered>感应地雷已启动!</WULA_TrapLauncherTriggered>
|
||||||
</LanguageData>
|
</LanguageData>
|
||||||
@@ -10,7 +10,7 @@ namespace WulaFallenEmpire
|
|||||||
{
|
{
|
||||||
public class CompSkyfallerCaller : ThingComp
|
public class CompSkyfallerCaller : ThingComp
|
||||||
{
|
{
|
||||||
protected CompProperties_SkyfallerCaller Props => (CompProperties_SkyfallerCaller)props;
|
public CompProperties_SkyfallerCaller Props => (CompProperties_SkyfallerCaller)props;
|
||||||
|
|
||||||
private WulaSkyfallerWorldComponent _worldComponent;
|
private WulaSkyfallerWorldComponent _worldComponent;
|
||||||
private WulaSkyfallerWorldComponent WorldComp
|
private WulaSkyfallerWorldComponent WorldComp
|
||||||
@@ -38,9 +38,9 @@ namespace WulaFallenEmpire
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool used = false;
|
public bool used = false;
|
||||||
private int callTick = -1;
|
private int callTick = -1;
|
||||||
private bool calling = false;
|
public bool calling = false;
|
||||||
private bool usedGlobalStorage = false;
|
private bool usedGlobalStorage = false;
|
||||||
public bool autoCallScheduled = false; // 新增:标记是否已安排自动呼叫
|
public bool autoCallScheduled = false; // 新增:标记是否已安排自动呼叫
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ namespace WulaFallenEmpire
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckRoofConditions
|
public bool CheckRoofConditions
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -620,7 +620,7 @@ namespace WulaFallenEmpire
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保留原有的 HasEnoughMaterials 方法用于 Gizmo 显示
|
// 保留原有的 HasEnoughMaterials 方法用于 Gizmo 显示
|
||||||
protected bool HasEnoughMaterials()
|
public bool HasEnoughMaterials()
|
||||||
{
|
{
|
||||||
if (DebugSettings.godMode) return true;
|
if (DebugSettings.godMode) return true;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class CompProperties_TrapLauncher : CompProperties
|
||||||
|
{
|
||||||
|
public float detectionRadius = 10f; // 检测半径
|
||||||
|
public int scanIntervalTicks = 60; // 扫描间隔(ticks)
|
||||||
|
public ThingDef projectileDef; // 抛射体定义
|
||||||
|
public bool ignoreNonHostilePawns = true; // 是否忽略非敌对Pawn
|
||||||
|
public bool requireLineOfSight = true; // 是否需要视线
|
||||||
|
public int maxTargets = 1; // 最大目标数量
|
||||||
|
public int warmupTicks = 30; // 发射前预热ticks
|
||||||
|
public bool showDetectionRadius = true; // 是否显示检测半径
|
||||||
|
|
||||||
|
// 触发时的音效
|
||||||
|
public SoundDef triggerSound;
|
||||||
|
public SoundDef launchSound;
|
||||||
|
public SoundDef selfDestructSound;
|
||||||
|
|
||||||
|
// 视觉效果
|
||||||
|
public EffecterDef triggerEffect;
|
||||||
|
public EffecterDef launchEffect;
|
||||||
|
public EffecterDef selfDestructEffect;
|
||||||
|
|
||||||
|
// 扩展选项
|
||||||
|
public bool canRetarget = false; // 发射后是否可以重新锁定新目标
|
||||||
|
public int burstCount = 1; // 连发数量
|
||||||
|
public float burstDelay = 0.1f; // 连发延迟
|
||||||
|
public bool targetBuildings = false; // 是否瞄准建筑
|
||||||
|
|
||||||
|
public CompProperties_TrapLauncher()
|
||||||
|
{
|
||||||
|
this.compClass = typeof(CompTrapLauncher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
|
||||||
|
{
|
||||||
|
foreach (var error in base.ConfigErrors(parentDef))
|
||||||
|
{
|
||||||
|
yield return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectileDef == null)
|
||||||
|
{
|
||||||
|
yield return $"CompProperties_TrapLauncher: projectileDef must be set for {parentDef.defName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,462 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
using Verse.Sound;
|
||||||
|
using static UnityEngine.GraphicsBuffer;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class CompTrapLauncher : ThingComp
|
||||||
|
{
|
||||||
|
public CompProperties_TrapLauncher Props => (CompProperties_TrapLauncher)props;
|
||||||
|
|
||||||
|
private int scanTickCounter = 0;
|
||||||
|
private bool hasTriggered = false;
|
||||||
|
private Pawn currentTarget = null;
|
||||||
|
private int warmupCounter = 0;
|
||||||
|
private bool isWarmingUp = false;
|
||||||
|
private int burstCounter = 0;
|
||||||
|
|
||||||
|
// 已检测过的目标(避免重复攻击)
|
||||||
|
private HashSet<Pawn> detectedTargets = new HashSet<Pawn>();
|
||||||
|
|
||||||
|
// 用于绘制检测范围
|
||||||
|
private Material cachedRadiusMat;
|
||||||
|
private Material RadiusMat
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (cachedRadiusMat == null)
|
||||||
|
{
|
||||||
|
cachedRadiusMat = SolidColorMaterials.SimpleSolidColorMaterial(
|
||||||
|
new Color(1f, 0.2f, 0.2f, 0.1f));
|
||||||
|
}
|
||||||
|
return cachedRadiusMat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void CompTick()
|
||||||
|
{
|
||||||
|
base.CompTick();
|
||||||
|
|
||||||
|
if (!parent.Spawned || hasTriggered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 预热计数
|
||||||
|
if (isWarmingUp)
|
||||||
|
{
|
||||||
|
warmupCounter++;
|
||||||
|
if (warmupCounter >= Props.warmupTicks)
|
||||||
|
{
|
||||||
|
LaunchProjectile();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扫描计数
|
||||||
|
scanTickCounter++;
|
||||||
|
if (scanTickCounter >= Props.scanIntervalTicks)
|
||||||
|
{
|
||||||
|
scanTickCounter = 0;
|
||||||
|
ScanForTargets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 扫描范围内的敌对目标
|
||||||
|
/// </summary>
|
||||||
|
private void ScanForTargets()
|
||||||
|
{
|
||||||
|
if (!parent.Spawned)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Map map = parent.Map;
|
||||||
|
IntVec3 center = parent.Position;
|
||||||
|
|
||||||
|
// 获取范围内的所有Pawn
|
||||||
|
List<Pawn> pawnsInRange = new List<Pawn>();
|
||||||
|
|
||||||
|
foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, Props.detectionRadius, true))
|
||||||
|
{
|
||||||
|
if (!cell.InBounds(map))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 检查视线(如果需要)
|
||||||
|
if (Props.requireLineOfSight)
|
||||||
|
{
|
||||||
|
if (!GenSight.LineOfSight(center, cell, map, skipFirstCell: true))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取格子上的所有Pawn
|
||||||
|
List<Thing> things = map.thingGrid.ThingsListAtFast(cell);
|
||||||
|
foreach (Thing thing in things)
|
||||||
|
{
|
||||||
|
if (thing is Pawn pawn && !detectedTargets.Contains(pawn))
|
||||||
|
{
|
||||||
|
// 检查是否敌对
|
||||||
|
if (IsValidTarget(pawn))
|
||||||
|
{
|
||||||
|
pawnsInRange.Add(pawn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有目标,直接返回
|
||||||
|
if (pawnsInRange.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 选择第一个目标
|
||||||
|
currentTarget = pawnsInRange[0];
|
||||||
|
detectedTargets.Add(currentTarget);
|
||||||
|
|
||||||
|
// 触发陷阱
|
||||||
|
TriggerTrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查是否为有效目标
|
||||||
|
/// </summary>
|
||||||
|
private bool IsValidTarget(Pawn pawn)
|
||||||
|
{
|
||||||
|
if (pawn == null || pawn.Dead || pawn.Downed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Props.ignoreNonHostilePawns)
|
||||||
|
{
|
||||||
|
// 检查是否为敌对派系
|
||||||
|
if (pawn.Faction == null || !pawn.Faction.HostileTo(Faction.OfPlayer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 检查是否为自然敌对生物
|
||||||
|
if (!pawn.Faction.IsPlayer && !pawn.Faction.HostileTo(Faction.OfPlayer))
|
||||||
|
{
|
||||||
|
// 检查是否为敌对性生物(如人形生物巢穴的敌人)
|
||||||
|
if (!pawn.RaceProps.Humanlike && !pawn.def.race.Animal)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为机械体(如果设定)
|
||||||
|
// 这里可以根据需要添加更多过滤条件
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发陷阱
|
||||||
|
/// </summary>
|
||||||
|
private void TriggerTrap()
|
||||||
|
{
|
||||||
|
if (hasTriggered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 播放触发音效
|
||||||
|
if (Props.triggerSound != null)
|
||||||
|
{
|
||||||
|
Props.triggerSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放触发特效
|
||||||
|
if (Props.triggerEffect != null)
|
||||||
|
{
|
||||||
|
Effecter effecter = Props.triggerEffect.Spawn();
|
||||||
|
effecter.Trigger(parent, parent);
|
||||||
|
effecter.Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
if (currentTarget != null)
|
||||||
|
{
|
||||||
|
Messages.Message("WULA_TrapLauncherTriggered".Translate(
|
||||||
|
parent.Label,
|
||||||
|
currentTarget.LabelShort
|
||||||
|
), parent, MessageTypeDefOf.ThreatBig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始预热
|
||||||
|
isWarmingUp = true;
|
||||||
|
warmupCounter = 0;
|
||||||
|
burstCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发射抛射体
|
||||||
|
/// </summary>
|
||||||
|
private void LaunchProjectile()
|
||||||
|
{
|
||||||
|
if (currentTarget == null || !currentTarget.Spawned)
|
||||||
|
{
|
||||||
|
// 如果目标无效,尝试寻找新目标
|
||||||
|
if (Props.canRetarget)
|
||||||
|
{
|
||||||
|
currentTarget = FindNewTarget();
|
||||||
|
if (currentTarget == null)
|
||||||
|
{
|
||||||
|
// 没有新目标,取消发射
|
||||||
|
isWarmingUp = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 直接自毁
|
||||||
|
SelfDestruct();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发射抛射体
|
||||||
|
for (int i = 0; i < Props.burstCount; i++)
|
||||||
|
{
|
||||||
|
if (burstCounter >= Props.maxTargets)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 创建抛射体
|
||||||
|
Projectile projectile = (Projectile)GenSpawn.Spawn(
|
||||||
|
Props.projectileDef,
|
||||||
|
parent.Position,
|
||||||
|
parent.Map
|
||||||
|
);
|
||||||
|
|
||||||
|
// 发射
|
||||||
|
projectile.Launch(parent, parent.DrawPos, currentTarget, currentTarget, ProjectileHitFlags.IntendedTarget, false);
|
||||||
|
|
||||||
|
// 连发延迟
|
||||||
|
if (i < Props.burstCount - 1 && Props.burstDelay > 0)
|
||||||
|
{
|
||||||
|
// 使用简单的延迟实现
|
||||||
|
// 在实际游戏中,可能需要更复杂的实现
|
||||||
|
// 这里我们简化处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放发射音效
|
||||||
|
if (Props.launchSound != null)
|
||||||
|
{
|
||||||
|
Props.launchSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放发射特效
|
||||||
|
if (Props.launchEffect != null)
|
||||||
|
{
|
||||||
|
Effecter effecter = Props.launchEffect.Spawn();
|
||||||
|
effecter.Trigger(parent, parent);
|
||||||
|
effecter.Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否需要继续攻击
|
||||||
|
if (burstCounter >= Props.maxTargets || !Props.canRetarget)
|
||||||
|
{
|
||||||
|
// 自毁
|
||||||
|
SelfDestruct();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 寻找新目标
|
||||||
|
currentTarget = FindNewTarget();
|
||||||
|
if (currentTarget == null)
|
||||||
|
{
|
||||||
|
SelfDestruct();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 重置预热,准备下一次发射
|
||||||
|
warmupCounter = 0;
|
||||||
|
burstCounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 寻找新目标
|
||||||
|
/// </summary>
|
||||||
|
private Pawn FindNewTarget()
|
||||||
|
{
|
||||||
|
Map map = parent.Map;
|
||||||
|
IntVec3 center = parent.Position;
|
||||||
|
|
||||||
|
foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, Props.detectionRadius, true))
|
||||||
|
{
|
||||||
|
if (!cell.InBounds(map))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Props.requireLineOfSight && !GenSight.LineOfSight(center, cell, map, skipFirstCell: true))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
List<Thing> things = map.thingGrid.ThingsListAtFast(cell);
|
||||||
|
foreach (Thing thing in things)
|
||||||
|
{
|
||||||
|
if (thing is Pawn pawn && !detectedTargets.Contains(pawn) && IsValidTarget(pawn))
|
||||||
|
{
|
||||||
|
detectedTargets.Add(pawn);
|
||||||
|
return pawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果目标建筑
|
||||||
|
if (Props.targetBuildings && thing is Building building &&
|
||||||
|
building.Faction != null && building.Faction.HostileTo(Faction.OfPlayer))
|
||||||
|
{
|
||||||
|
// 注意:这里需要处理建筑目标,但抛射体可能需要调整
|
||||||
|
// 为了简化,这里只处理Pawn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自毁
|
||||||
|
/// </summary>
|
||||||
|
private void SelfDestruct()
|
||||||
|
{
|
||||||
|
if (hasTriggered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hasTriggered = true;
|
||||||
|
|
||||||
|
// 播放自毁音效
|
||||||
|
if (Props.selfDestructSound != null)
|
||||||
|
{
|
||||||
|
Props.selfDestructSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放自毁特效
|
||||||
|
if (Props.selfDestructEffect != null)
|
||||||
|
{
|
||||||
|
Effecter effecter = Props.selfDestructEffect.Spawn();
|
||||||
|
effecter.Trigger(parent, parent);
|
||||||
|
effecter.Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁建筑
|
||||||
|
parent.Destroy(DestroyMode.Vanish);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制检测范围(仅在选定和调试模式下)
|
||||||
|
/// </summary>
|
||||||
|
public override void PostDrawExtraSelectionOverlays()
|
||||||
|
{
|
||||||
|
base.PostDrawExtraSelectionOverlays();
|
||||||
|
|
||||||
|
if (Props.showDetectionRadius && Props.detectionRadius > 0)
|
||||||
|
{
|
||||||
|
GenDraw.DrawRadiusRing(parent.Position, Props.detectionRadius, Color.red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取Gizmo按钮
|
||||||
|
/// </summary>
|
||||||
|
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||||
|
{
|
||||||
|
foreach (Gizmo gizmo in base.CompGetGizmosExtra())
|
||||||
|
{
|
||||||
|
yield return gizmo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasTriggered && DebugSettings.ShowDevGizmos)
|
||||||
|
{
|
||||||
|
// 调试:手动触发
|
||||||
|
Command_Action debugTrigger = new Command_Action();
|
||||||
|
debugTrigger.defaultLabel = "DEV: Trigger Trap";
|
||||||
|
debugTrigger.action = delegate
|
||||||
|
{
|
||||||
|
currentTarget = FindClosestHostilePawn();
|
||||||
|
if (currentTarget != null)
|
||||||
|
{
|
||||||
|
TriggerTrap();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Messages.Message("WULA_TrapLauncherNoTargetFound".Translate(),
|
||||||
|
parent, MessageTypeDefOf.RejectInput);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
yield return debugTrigger;
|
||||||
|
|
||||||
|
// 调试:立即自毁
|
||||||
|
Command_Action debugDestruct = new Command_Action();
|
||||||
|
debugDestruct.defaultLabel = "DEV: Self-Destruct";
|
||||||
|
debugDestruct.action = delegate
|
||||||
|
{
|
||||||
|
SelfDestruct();
|
||||||
|
};
|
||||||
|
yield return debugDestruct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找最近的敌对Pawn(调试用)
|
||||||
|
/// </summary>
|
||||||
|
private Pawn FindClosestHostilePawn()
|
||||||
|
{
|
||||||
|
if (!parent.Spawned)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Pawn closestPawn = null;
|
||||||
|
float closestDist = float.MaxValue;
|
||||||
|
|
||||||
|
foreach (Pawn pawn in parent.Map.mapPawns.AllPawnsSpawned)
|
||||||
|
{
|
||||||
|
if (IsValidTarget(pawn))
|
||||||
|
{
|
||||||
|
float dist = pawn.Position.DistanceTo(parent.Position);
|
||||||
|
if (dist <= Props.detectionRadius && dist < closestDist)
|
||||||
|
{
|
||||||
|
closestDist = dist;
|
||||||
|
closestPawn = pawn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestPawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
base.PostExposeData();
|
||||||
|
|
||||||
|
Scribe_Values.Look(ref scanTickCounter, "scanTickCounter", 0);
|
||||||
|
Scribe_Values.Look(ref hasTriggered, "hasTriggered", false);
|
||||||
|
Scribe_Values.Look(ref isWarmingUp, "isWarmingUp", false);
|
||||||
|
Scribe_Values.Look(ref warmupCounter, "warmupCounter", 0);
|
||||||
|
Scribe_Values.Look(ref burstCounter, "burstCounter", 0);
|
||||||
|
Scribe_References.Look(ref currentTarget, "currentTarget");
|
||||||
|
Scribe_Collections.Look(ref detectedTargets, "detectedTargets", LookMode.Reference);
|
||||||
|
|
||||||
|
if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
||||||
|
{
|
||||||
|
if (detectedTargets == null)
|
||||||
|
{
|
||||||
|
detectedTargets = new HashSet<Pawn>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除无效的目标引用
|
||||||
|
detectedTargets.RemoveWhere(pawn => pawn == null || pawn.Destroyed);
|
||||||
|
|
||||||
|
// 如果已经在预热但目标无效,尝试恢复或自毁
|
||||||
|
if (isWarmingUp && (currentTarget == null || !currentTarget.Spawned))
|
||||||
|
{
|
||||||
|
if (Props.canRetarget)
|
||||||
|
{
|
||||||
|
currentTarget = FindNewTarget();
|
||||||
|
if (currentTarget == null)
|
||||||
|
{
|
||||||
|
SelfDestruct();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelfDestruct();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class Designator_CallSkyfallerInArea : Designator
|
||||||
|
{
|
||||||
|
private readonly new Texture2D icon;
|
||||||
|
|
||||||
|
// 记录已经处理过的建筑(避免重复)
|
||||||
|
private HashSet<Thing> processedBuildings = new HashSet<Thing>();
|
||||||
|
|
||||||
|
public Designator_CallSkyfallerInArea()
|
||||||
|
{
|
||||||
|
defaultLabel = "WULA_Designator_CallSkyfallerInArea".Translate();
|
||||||
|
defaultDesc = "WULA_Designator_CallSkyfallerInAreaDesc".Translate();
|
||||||
|
icon = ContentFinder<Texture2D>.Get("Wula/UI/Designators/WULA_AreaSkyfaller");
|
||||||
|
soundDragSustain = SoundDefOf.Designate_DragStandard;
|
||||||
|
soundDragChanged = SoundDefOf.Designate_DragStandard_Changed;
|
||||||
|
useMouseIcon = true;
|
||||||
|
soundSucceeded = SoundDefOf.Designate_Claim;
|
||||||
|
hotKey = KeyBindingDefOf.Misc12;
|
||||||
|
tutorTag = "CallSkyfallerInArea";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override AcceptanceReport CanDesignateCell(IntVec3 c)
|
||||||
|
{
|
||||||
|
if (!c.InBounds(Map))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 允许在迷雾格上选择(就像拆除和索赔设计器一样)
|
||||||
|
if (c.Fogged(Map))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 只要单元格内有玩家建筑,就允许选择
|
||||||
|
var things = Map.thingGrid.ThingsListAt(c);
|
||||||
|
foreach (var thing in things)
|
||||||
|
{
|
||||||
|
if (thing.def.category == ThingCategory.Building &&
|
||||||
|
thing.Faction == Faction.OfPlayer &&
|
||||||
|
thing.TryGetComp<CompSkyfallerCaller>() != null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 即使单元格内没有符合条件的建筑,也允许选择(这样用户可以拖动区域)
|
||||||
|
// 但返回一个友好的提示
|
||||||
|
return "WULA_NoCallableBuildingsInCell".Translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DesignateSingleCell(IntVec3 c)
|
||||||
|
{
|
||||||
|
// 处理单个单元格内的所有建筑
|
||||||
|
ProcessCell(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DesignateMultiCell(IEnumerable<IntVec3> cells)
|
||||||
|
{
|
||||||
|
// 清除已处理的建筑记录
|
||||||
|
processedBuildings.Clear();
|
||||||
|
|
||||||
|
int totalBuildings = 0;
|
||||||
|
|
||||||
|
// 处理所有选中的单元格
|
||||||
|
foreach (var cell in cells)
|
||||||
|
{
|
||||||
|
if (cell.InBounds(Map))
|
||||||
|
{
|
||||||
|
// 统计该单元格处理的建筑数量
|
||||||
|
int cellCount = processedBuildings.Count;
|
||||||
|
ProcessCell(cell);
|
||||||
|
int newBuildings = processedBuildings.Count - cellCount;
|
||||||
|
totalBuildings += newBuildings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算成功和失败的数量
|
||||||
|
// 这里需要跟踪每个建筑的调用结果
|
||||||
|
// 由于我们直接调用CallSkyfaller,需要知道哪些失败了
|
||||||
|
// 简化处理:在ProcessCell中统计
|
||||||
|
|
||||||
|
// 显示简单的结果消息
|
||||||
|
if (totalBuildings > 0)
|
||||||
|
{
|
||||||
|
Messages.Message("WULA_AreaCallInitiated".Translate(totalBuildings),
|
||||||
|
MessageTypeDefOf.PositiveEvent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Messages.Message("WULA_NoCallableBuildingsInArea".Translate(),
|
||||||
|
MessageTypeDefOf.NeutralEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCell(IntVec3 cell)
|
||||||
|
{
|
||||||
|
var things = Map.thingGrid.ThingsListAt(cell);
|
||||||
|
|
||||||
|
foreach (var thing in things)
|
||||||
|
{
|
||||||
|
// 跳过非建筑
|
||||||
|
if (thing.def.category != ThingCategory.Building)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 跳过非玩家建筑
|
||||||
|
if (thing.Faction != Faction.OfPlayer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 检查是否已经处理过(避免重复处理同一个建筑)
|
||||||
|
if (processedBuildings.Contains(thing))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 获取空投组件
|
||||||
|
var comp = thing.TryGetComp<CompSkyfallerCaller>();
|
||||||
|
if (comp == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 尝试呼叫空投
|
||||||
|
if (comp.CanCallSkyfaller)
|
||||||
|
{
|
||||||
|
comp.CallSkyfaller(false);
|
||||||
|
processedBuildings.Add(thing);
|
||||||
|
}
|
||||||
|
// 即使不能呼叫,也添加到已处理列表,避免重复尝试
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processedBuildings.Add(thing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override AcceptanceReport CanDesignateThing(Thing t)
|
||||||
|
{
|
||||||
|
// 这里提供单个建筑的反向设计器支持(右键菜单)
|
||||||
|
if (t.def.category != ThingCategory.Building)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (t.Faction != Faction.OfPlayer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var comp = t.TryGetComp<CompSkyfallerCaller>();
|
||||||
|
if (comp == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!comp.CanCallSkyfaller)
|
||||||
|
return GetFailureReason(t, comp);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFailureReason(Thing building, CompSkyfallerCaller comp)
|
||||||
|
{
|
||||||
|
if (!comp.HasRequiredFlyOver && comp.Props.requireFlyOver)
|
||||||
|
return "WULA_NoBuildingDropperFlyOver".Translate();
|
||||||
|
|
||||||
|
if (!comp.CheckRoofConditions)
|
||||||
|
{
|
||||||
|
var roof = building.Position.GetRoof(building.Map);
|
||||||
|
if (roof?.isThickRoof == true)
|
||||||
|
return "WULA_ThickRoofBlocking".Translate();
|
||||||
|
else
|
||||||
|
return "WULA_RoofBlocking".Translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!comp.HasEnoughMaterials())
|
||||||
|
return "WULA_InsufficientMaterials".Translate();
|
||||||
|
|
||||||
|
if (comp.used)
|
||||||
|
return "WULA_AlreadyUsed".Translate();
|
||||||
|
|
||||||
|
if (comp.calling)
|
||||||
|
return "WULA_AlreadyCalling".Translate();
|
||||||
|
|
||||||
|
return "WULA_CannotCallSkyfaller".Translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DesignateThing(Thing t)
|
||||||
|
{
|
||||||
|
// 用于反向设计器(右键菜单)
|
||||||
|
var comp = t.TryGetComp<CompSkyfallerCaller>();
|
||||||
|
if (comp != null && comp.CanCallSkyfaller)
|
||||||
|
{
|
||||||
|
comp.CallSkyfaller(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SelectedUpdate()
|
||||||
|
{
|
||||||
|
// 参考Designator_Deconstruct,只绘制鼠标悬停方框
|
||||||
|
GenUI.RenderMouseoverBracket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,10 @@ namespace WulaFallenEmpire
|
|||||||
|
|
||||||
// 新增:绘制相关变量
|
// 新增:绘制相关变量
|
||||||
private float currentArcHeight;
|
private float currentArcHeight;
|
||||||
private const float DRAW_ALTITUDE_OFFSET = 15f; // 增加绘制高度偏移
|
private const float DRAW_ALTITUDE_OFFSET = 0.5f; // 减少偏移量,避免裁剪问题
|
||||||
|
|
||||||
|
// 新增:用于保存真实计算的位置(仅XZ平面)
|
||||||
|
private Vector3 horizontalPosition;
|
||||||
|
|
||||||
public TrackingBulletDef TrackingDef
|
public TrackingBulletDef TrackingDef
|
||||||
{
|
{
|
||||||
@@ -45,7 +48,7 @@ namespace WulaFallenEmpire
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改:重写绘制位置,确保在正确的高度
|
// 修改:简化ExactPosition计算
|
||||||
public override Vector3 ExactPosition
|
public override Vector3 ExactPosition
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -53,15 +56,31 @@ namespace WulaFallenEmpire
|
|||||||
if (!initialized)
|
if (!initialized)
|
||||||
return base.ExactPosition;
|
return base.ExactPosition;
|
||||||
|
|
||||||
// 返回计算的位置,但保持Y轴为绘制高度
|
// 返回水平位置,保持Y轴为定义的高度
|
||||||
Vector3 pos = exactPositionInt;
|
// RimWorld使用Y轴作为高度层,不应该随意改变
|
||||||
pos.y = def.Altitude + currentArcHeight + DRAW_ALTITUDE_OFFSET;
|
return new Vector3(horizontalPosition.x, def.Altitude, horizontalPosition.z);
|
||||||
return pos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改:重写ExactRotation以考虑高度变化
|
||||||
public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection());
|
public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection());
|
||||||
|
|
||||||
|
// 新增:获取带高度的位置(用于特效绘制)
|
||||||
|
public Vector3 PositionWithHeight
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!initialized)
|
||||||
|
return ExactPosition;
|
||||||
|
|
||||||
|
return new Vector3(
|
||||||
|
horizontalPosition.x,
|
||||||
|
def.Altitude + currentArcHeight * 0.3f, // 适当缩放高度
|
||||||
|
horizontalPosition.z
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void ExposeData()
|
public override void ExposeData()
|
||||||
{
|
{
|
||||||
base.ExposeData();
|
base.ExposeData();
|
||||||
@@ -75,6 +94,7 @@ namespace WulaFallenEmpire
|
|||||||
Scribe_Values.Look(ref exactPositionInt, "exactPositionInt", Vector3.zero);
|
Scribe_Values.Look(ref exactPositionInt, "exactPositionInt", Vector3.zero);
|
||||||
Scribe_Values.Look(ref curveSteepness, "curveSteepness", 1f);
|
Scribe_Values.Look(ref curveSteepness, "curveSteepness", 1f);
|
||||||
Scribe_Values.Look(ref currentArcHeight, "currentArcHeight", 0f);
|
Scribe_Values.Look(ref currentArcHeight, "currentArcHeight", 0f);
|
||||||
|
Scribe_Values.Look(ref horizontalPosition, "horizontalPosition", Vector3.zero);
|
||||||
|
|
||||||
Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
|
Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
|
||||||
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
|
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
|
||||||
@@ -115,6 +135,7 @@ namespace WulaFallenEmpire
|
|||||||
bezierControlPoint = 2f * apexPoint - midPoint;
|
bezierControlPoint = 2f * apexPoint - midPoint;
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
horizontalPosition = origin;
|
||||||
exactPositionInt = origin;
|
exactPositionInt = origin;
|
||||||
lastTickPosition = origin;
|
lastTickPosition = origin;
|
||||||
currentArcHeight = 0f;
|
currentArcHeight = 0f;
|
||||||
@@ -147,7 +168,7 @@ namespace WulaFallenEmpire
|
|||||||
|
|
||||||
// 垂直高度 (抛物线)
|
// 垂直高度 (抛物线)
|
||||||
currentArcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
|
currentArcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
|
||||||
nextPos.y = 0f; // 水平位置不包含高度
|
horizontalPosition = nextPos; // 保存水平位置
|
||||||
|
|
||||||
if (!nextPos.ToIntVec3().InBounds(base.Map))
|
if (!nextPos.ToIntVec3().InBounds(base.Map))
|
||||||
{
|
{
|
||||||
@@ -172,7 +193,8 @@ namespace WulaFallenEmpire
|
|||||||
if (map != null)
|
if (map != null)
|
||||||
{
|
{
|
||||||
int count = TrackingDef.fleckMakeFleckNum.RandomInRange;
|
int count = TrackingDef.fleckMakeFleckNum.RandomInRange;
|
||||||
Vector3 currentPosition = this.ExactPosition; // 使用重写后的ExactPosition
|
// 使用带高度的位置生成尾迹
|
||||||
|
Vector3 currentPosition = PositionWithHeight;
|
||||||
Vector3 previousPosition = lastTickPosition;
|
Vector3 previousPosition = lastTickPosition;
|
||||||
|
|
||||||
if ((currentPosition - previousPosition).MagnitudeHorizontalSquared() > 0.0001f)
|
if ((currentPosition - previousPosition).MagnitudeHorizontalSquared() > 0.0001f)
|
||||||
@@ -195,7 +217,7 @@ namespace WulaFallenEmpire
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTickPosition = ExactPosition; // 使用重写后的ExactPosition
|
lastTickPosition = PositionWithHeight;
|
||||||
|
|
||||||
if (ticksFlying >= totalTicks)
|
if (ticksFlying >= totalTicks)
|
||||||
{
|
{
|
||||||
@@ -204,7 +226,7 @@ namespace WulaFallenEmpire
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改:重写绘制方法,确保正确绘制
|
// 修改:重写绘制方法
|
||||||
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
|
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
|
||||||
{
|
{
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
@@ -213,13 +235,18 @@ namespace WulaFallenEmpire
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用我们计算的位置进行绘制
|
// 使用固定的绘制位置,但考虑高度偏移
|
||||||
Vector3 finalDrawPos = ExactPosition;
|
Vector3 finalDrawPos = ExactPosition;
|
||||||
|
|
||||||
|
// 调整绘制位置以考虑抛物线高度
|
||||||
|
// 但保持Y轴在合理范围内,避免被裁剪
|
||||||
|
float heightAdjustment = currentArcHeight * 0.2f; // 缩放高度影响
|
||||||
|
finalDrawPos.y += Mathf.Clamp(heightAdjustment, -0.5f, 2f);
|
||||||
|
|
||||||
// 绘制阴影
|
// 绘制阴影
|
||||||
if (def.projectile.shadowSize > 0f)
|
if (def.projectile.shadowSize > 0f)
|
||||||
{
|
{
|
||||||
DrawShadow(finalDrawPos, currentArcHeight);
|
DrawShadow(finalDrawPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Quaternion rotation = ExactRotation;
|
Quaternion rotation = ExactRotation;
|
||||||
@@ -232,7 +259,14 @@ namespace WulaFallenEmpire
|
|||||||
// 使用正确的绘制方法
|
// 使用正确的绘制方法
|
||||||
if (def.projectile.useGraphicClass)
|
if (def.projectile.useGraphicClass)
|
||||||
{
|
{
|
||||||
Graphic.Draw(finalDrawPos, base.Rotation, this, rotation.eulerAngles.y);
|
// 确保图形缩放合适
|
||||||
|
float scaleFactor = 1f + currentArcHeight * 0.1f; // 轻微缩放模拟远近
|
||||||
|
Matrix4x4 matrix = Matrix4x4.TRS(
|
||||||
|
finalDrawPos,
|
||||||
|
rotation,
|
||||||
|
new Vector3(scaleFactor, 1f, scaleFactor)
|
||||||
|
);
|
||||||
|
Graphics.DrawMesh(MeshPool.GridPlane(def.graphicData.drawSize), matrix, DrawMat, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -242,26 +276,32 @@ namespace WulaFallenEmpire
|
|||||||
Comps_PostDraw();
|
Comps_PostDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改:重写阴影绘制,使用正确的高度
|
// 修改:简化阴影绘制
|
||||||
private void DrawShadow(Vector3 drawLoc, float height)
|
private void DrawShadow(Vector3 drawLoc)
|
||||||
{
|
{
|
||||||
|
if (def.projectile.shadowSize <= 0f)
|
||||||
|
return;
|
||||||
|
|
||||||
Material shadowMat = MaterialPool.MatFrom("Things/Skyfaller/SkyfallerShadowCircle", ShaderDatabase.Transparent);
|
Material shadowMat = MaterialPool.MatFrom("Things/Skyfaller/SkyfallerShadowCircle", ShaderDatabase.Transparent);
|
||||||
if (shadowMat == null) return;
|
if (shadowMat == null) return;
|
||||||
|
|
||||||
float shadowSize = def.projectile.shadowSize * Mathf.Lerp(1f, 0.6f, height / (def.projectile.arcHeightFactor + 1f));
|
// 根据当前高度调整阴影大小
|
||||||
|
float normalizedHeight = Mathf.Clamp01(currentArcHeight / (def.projectile.arcHeightFactor + 0.01f));
|
||||||
|
float shadowSize = def.projectile.shadowSize * Mathf.Lerp(1f, 0.4f, normalizedHeight);
|
||||||
|
|
||||||
Vector3 scale = new Vector3(shadowSize, 1f, shadowSize);
|
Vector3 scale = new Vector3(shadowSize, 1f, shadowSize);
|
||||||
Vector3 shadowOffset = new Vector3(0f, -0.01f, 0f);
|
Vector3 shadowOffset = new Vector3(0f, -0.05f, 0f); // 稍微降低阴影位置
|
||||||
|
|
||||||
Matrix4x4 matrix = Matrix4x4.TRS(drawLoc + shadowOffset, Quaternion.identity, scale);
|
Matrix4x4 matrix = Matrix4x4.TRS(drawLoc + shadowOffset, Quaternion.identity, scale);
|
||||||
Graphics.DrawMesh(MeshPool.plane10, matrix, shadowMat, 0);
|
Graphics.DrawMesh(MeshPool.plane10, matrix, shadowMat, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算当前位置的切线方向
|
// 计算当前位置的切线方向(考虑高度变化)
|
||||||
private Vector3 GetCurrentDirection()
|
private Vector3 GetCurrentDirection()
|
||||||
{
|
{
|
||||||
if (!initialized || totalTicks <= 0)
|
if (!initialized || totalTicks <= 0)
|
||||||
{
|
{
|
||||||
return destinationPos - originPos;
|
return (destinationPos - originPos).normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
float t = (float)ticksFlying / (float)totalTicks;
|
float t = (float)ticksFlying / (float)totalTicks;
|
||||||
@@ -275,12 +315,26 @@ namespace WulaFallenEmpire
|
|||||||
return (destinationPos - originPos).normalized;
|
return (destinationPos - originPos).normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tangent.normalized;
|
// 添加轻微的上/下方向以模拟抛物线
|
||||||
|
float verticalComponent = GenMath.InverseParabola(t) * def.projectile.arcHeightFactor * 0.3f;
|
||||||
|
return (tangent.normalized + new Vector3(0, verticalComponent, 0)).normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Impact(Thing hitThing, bool blockedByShield = false)
|
protected override void Impact(Thing hitThing, bool blockedByShield = false)
|
||||||
{
|
{
|
||||||
base.Impact(hitThing, blockedByShield);
|
base.Impact(hitThing, blockedByShield);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:确保在保存时位置正确
|
||||||
|
public override void PostMapInit()
|
||||||
|
{
|
||||||
|
base.PostMapInit();
|
||||||
|
|
||||||
|
// 确保位置数据有效
|
||||||
|
if (initialized && horizontalPosition == Vector3.zero)
|
||||||
|
{
|
||||||
|
horizontalPosition = originPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,9 @@
|
|||||||
<Compile Include="BuildingComp\WULA_TransformAtFullCapacity\CompTransformAtFullCapacity.cs" />
|
<Compile Include="BuildingComp\WULA_TransformAtFullCapacity\CompTransformAtFullCapacity.cs" />
|
||||||
<Compile Include="BuildingComp\WULA_TransformAtFullCapacity\CompTransformIntoBuilding.cs" />
|
<Compile Include="BuildingComp\WULA_TransformAtFullCapacity\CompTransformIntoBuilding.cs" />
|
||||||
<Compile Include="BuildingComp\WULA_TransformAtFullCapacity\TransformValidationUtility.cs" />
|
<Compile Include="BuildingComp\WULA_TransformAtFullCapacity\TransformValidationUtility.cs" />
|
||||||
|
<Compile Include="BuildingComp\WULA_TrapLauncher\CompProperties_TrapLauncher.cs" />
|
||||||
|
<Compile Include="BuildingComp\WULA_TrapLauncher\CompTrapLauncher.cs" />
|
||||||
|
<Compile Include="Designator\Designator_CallSkyfallerInArea.cs" />
|
||||||
<Compile Include="EventSystem\CompOpenCustomUI.cs" />
|
<Compile Include="EventSystem\CompOpenCustomUI.cs" />
|
||||||
<Compile Include="EventSystem\Condition\ConditionBase.cs" />
|
<Compile Include="EventSystem\Condition\ConditionBase.cs" />
|
||||||
<Compile Include="EventSystem\Condition\Condition_FlagExists.cs" />
|
<Compile Include="EventSystem\Condition\Condition_FlagExists.cs" />
|
||||||
@@ -256,9 +259,6 @@
|
|||||||
<Compile Include="Pawn\WULA_Maintenance\Need_Maintenance.cs" />
|
<Compile Include="Pawn\WULA_Maintenance\Need_Maintenance.cs" />
|
||||||
<Compile Include="Pawn\WULA_Maintenance\WorkGiver_DoMaintenance.cs" />
|
<Compile Include="Pawn\WULA_Maintenance\WorkGiver_DoMaintenance.cs" />
|
||||||
<Compile Include="Placeworker\CompProperties_CustomRadius.cs" />
|
<Compile Include="Placeworker\CompProperties_CustomRadius.cs" />
|
||||||
<Compile Include="Projectiles\WULA_BouncingMine\BouncingMineFlying.cs" />
|
|
||||||
<Compile Include="Projectiles\WULA_BouncingMine\CompBouncingMine.cs" />
|
|
||||||
<Compile Include="Projectiles\WULA_BouncingMine\CompFlyingObject.cs" />
|
|
||||||
<Compile Include="QuestNodes\QuestNode_AddInspectionJob.cs" />
|
<Compile Include="QuestNodes\QuestNode_AddInspectionJob.cs" />
|
||||||
<Compile Include="QuestNodes\QuestNode_CheckGlobalResource.cs" />
|
<Compile Include="QuestNodes\QuestNode_CheckGlobalResource.cs" />
|
||||||
<Compile Include="QuestNodes\QuestNode_GeneratePawnWithCustomization.cs" />
|
<Compile Include="QuestNodes\QuestNode_GeneratePawnWithCustomization.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user