This commit is contained in:
2025-11-21 16:03:12 +08:00
parent 2ea2c33059
commit 9c5293550a
4 changed files with 481 additions and 0 deletions

View File

@@ -0,0 +1,289 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<!-- 迫击炮塔空投信标 -->
<ThingDef ParentName="BuildingBase">
<defName>Wula_Base_Mortar_Turret_Cleanzone</defName>
<label>MTt-8"深渊"迫击炮塔</label>
<description>清理出一块场地并准备好资源,使得乌拉帝国母舰可以向此处投放建筑。建造好的信标可以收起或移至他处,但是必须要有母舰或者工程舰在上空才能投送建筑。\n\nMTt-8"深渊"迫击炮塔是一种需要通电才能运转的中型防御炮塔,发射高爆迫击炮弹。这种炮台具有高伤害范围攻击能力,适合对付集群敌人和建筑。</description>
<uiIconPath>Wula/Building/Wula_Base_Mortar_Turret</uiIconPath>
<minifiedDef>MinifiedThing</minifiedDef>
<tickerType>Normal</tickerType>
<descriptionHyperlinks>
<ThingDef>Wula_Base_Mortar_Turret</ThingDef>
<ThingDef>Wula_Base_Mortar_Turret_Weapon</ThingDef>
</descriptionHyperlinks>
<thingCategories Inherit="False">
<li>BuildingsMisc</li>
</thingCategories>
<graphicData>
<texPath>Wula/Building/WULA_Dropping_Building_Cleanzone</texPath>
<graphicClass>Graphic_Multi</graphicClass>
<drawSize>(3,3)</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>
<researchPrerequisites Inherit="False">
<li>WULA_Turret_Base_Mortar_Technology</li>
</researchPrerequisites>
<statBases>
<MaxHitPoints>1</MaxHitPoints>
<WorkToBuild>1</WorkToBuild>
<Mass>1</Mass>
<Flammability>0</Flammability>
</statBases>
<size>(3,3)</size>
<constructionSkillPrerequisite>0</constructionSkillPrerequisite>
<resourcesFractionWhenDeconstructed>1</resourcesFractionWhenDeconstructed>
<costList Inherit="False">
<WULA_Alloy>100</WULA_Alloy>
<WULA_Charge_Cube>6</WULA_Charge_Cube>
</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>75</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_Base_Mortar_Turret_Incoming</skyfallerDef> <!-- 迫击炮塔空投类型 -->
<destroyBuilding>true</destroyBuilding>
<delayTicks>1</delayTicks>
<allowThinRoof>true</allowThinRoof>
<allowThickRoof>false</allowThickRoof>
</li>
</comps>
</ThingDef>
<!-- 迫击炮塔空投中 -->
<ThingDef ParentName="SkyfallerBase">
<defName>Wula_Base_Mortar_Turret_Incoming</defName>
<label>MTt-8"深渊"迫击炮塔(空投中)</label>
<size>(3,3)</size>
<graphicData>
<texPath>Wula/Building/Wula_Base_ATGun_Turret_Incoming</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutFlying</shaderType>
<drawSize>(3,3.75)</drawSize>
</graphicData>
<skyfaller>
<movementType>Accelerate</movementType>
<shadow>Things/Skyfaller/SkyfallerShadowDropPod</shadow>
<shadowSize>(2, 2)</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_Base_Mortar_Turret</spawnThing>
</skyfaller>
<comps>
<li Class="CompProperties_Effecter">
<effecterDef>Smoke_Joint</effecterDef>
</li>
</comps>
</ThingDef>
<!-- 迫击炮塔 -->
<ThingDef ParentName="BuildingBase">
<defName>Wula_Base_Mortar_Turret</defName>
<label>MTt-8"深渊"迫击炮塔</label>
<description>MTt-8"深渊"迫击炮塔是一种需要通电才能运转的中型防御炮塔,发射高爆迫击炮弹。这种炮台具有高伤害范围攻击能力,适合对付集群敌人和建筑。</description>
<thingClass>WulaFallenEmpire.Building_TurretGunHasSpeed</thingClass>
<drawerType>MapMeshAndRealTime</drawerType>
<modExtensions>
<li Class="WulaFallenEmpire.ModExt_HasSpeedTurret">
<speed>0.2</speed> <!-- 旋转速度 (度/Tick)。迫击炮旋转较慢。 -->
</li>
</modExtensions>
<graphicData>
<texPath>Wula/Building/WULA_Turret_Component</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>(3,3)</drawSize>
<!-- <drawOffset>(0,0,-0.16)</drawOffset> -->
<damageData>
<rect>(0.3,0.3,1.4,1.4)</rect>
</damageData>
<shadowData>
<volume>(0.5,0.35,0.75)</volume>
<offset>(0,0,-0.05)</offset>
</shadowData>
</graphicData>
<receivesSignals>true</receivesSignals>
<uiIconPath>Wula/Building/Wula_Base_Mortar_Turret</uiIconPath>
<uiIconScale>1.0</uiIconScale>
<altitudeLayer>Building</altitudeLayer>
<stealable>false</stealable>
<rotatable>false</rotatable>
<size>(3,3)</size>
<statBases>
<MaxHitPoints>1500</MaxHitPoints>
<Flammability>0</Flammability>
<WorkToBuild>3200</WorkToBuild>
<Mass>80</Mass>
<Beauty>0</Beauty>
<ShootingAccuracyTurret>0.80</ShootingAccuracyTurret>
</statBases>
<costList Inherit="False">
<WULA_Alloy>100</WULA_Alloy>
<WULA_Charge_Cube>6</WULA_Charge_Cube>
</costList>
<leaveResourcesWhenKilled>false</leaveResourcesWhenKilled>
<tickerType>Normal</tickerType>
<passability>PassThroughOnly</passability>
<pathCost>50</pathCost>
<fillPercent>0.5</fillPercent>
<hasTooltip>true</hasTooltip>
<building>
<combatPower>450</combatPower>
<ai_combatDangerous>true</ai_combatDangerous>
<turretGunDef>Wula_Base_Mortar_Turret_Weapon</turretGunDef>
<turretBurstCooldownTime>4</turretBurstCooldownTime>
<turretTopOffset>(-0.04, 0)</turretTopOffset>
<turretTopDrawSize>5.0</turretTopDrawSize>
</building>
<placeWorkers>
<li>PlaceWorker_TurretTop</li>
<li>PlaceWorker_ShowTurretRadius</li>
</placeWorkers>
<comps>
<li Class="CompProperties_CanBeDormant" />
<li Class="CompProperties_Initiatable" />
<li Class="CompProperties_WakeUpDormant">
<wakeUpSound>MechanoidsWakeUp</wakeUpSound>
</li>
<li Class="CompProperties_Stunnable">
<affectedDamageDefs>
<li>EMP</li>
</affectedDamageDefs>
</li>
<li Class="CompProperties_AmbientSound">
<sound>MechTurretBig_Call</sound>
</li>
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
<!-- <factionDef>Mechanoid</factionDef> 不写默认玩家派系-->
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
<overrideExistingFaction>false</overrideExistingFaction>
</li>
<li Class="CompProperties_Power">
<compClass>CompPowerTrader</compClass>
<basePowerConsumption>200</basePowerConsumption> <!-- 电力消耗较低 -->
</li>
<li Class="CompProperties_Glower">
<glowRadius>6</glowRadius>
<glowColor>(252,240,120,0)</glowColor> <!-- 黄色光晕 -->
</li>
<li Class="WulaFallenEmpire.CompProperties_ForceTargetable" />
</comps>
</ThingDef>
<!-- 迫击炮塔武器 -->
<ThingDef ParentName="BaseWeaponTurret">
<defName>Wula_Base_Mortar_Turret_Weapon</defName>
<label>MTt-8"深渊"</label>
<description>迫击炮塔,可以从远距离上对敌方集群造成巨大伤害。</description>
<tradeability>None</tradeability>
<destroyOnDrop>true</destroyOnDrop>
<techLevel>Ultra</techLevel>
<graphicData>
<texPath>Wula/Building/Wula_Base_ATGun_Turret_Weapon</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>3</drawSize>
</graphicData>
<uiIconScale>0.33</uiIconScale>
<statBases>
<Mass>150</Mass>
<AccuracyTouch>0.3</AccuracyTouch>
<AccuracyShort>0.7</AccuracyShort>
<AccuracyMedium>0.85</AccuracyMedium>
<AccuracyLong>0.8</AccuracyLong>
</statBases>
<verbs>
<li>
<verbClass>Verb_Shoot</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<defaultProjectile>Bullet_WULA_WM_Mortar_Turret</defaultProjectile>
<warmupTime>2.5</warmupTime>
<defaultCooldownTime>5</defaultCooldownTime>
<minRange>10</minRange>
<range>75</range>
<burstShotCount>1</burstShotCount>
<forcedMissRadius>1.9</forcedMissRadius>
<soundCast>WULA_MW_Mass_Drivers_Shootingsound</soundCast>
<soundCastTail>GunTail_Heavy</soundCastTail>
<muzzleFlashScale>18</muzzleFlashScale>
<targetParams>
<canTargetLocations>true</canTargetLocations>
</targetParams>
</li>
</verbs>
</ThingDef>
<!-- 迫击炮弹 -->
<ThingDef ParentName="BaseBullet">
<defName>Bullet_WULA_WM_Mortar_Turret</defName>
<label>深渊迫击炮弹</label>
<thingClass>WulaFallenEmpire.Projectile_NorthArcTrail</thingClass>
<tickerType>Normal</tickerType>
<neverMultiSelect>True</neverMultiSelect>
<graphicData>
<texPath>Wula/Projectile/WULA_Bullet_ChargeLanceShot_Red</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>TransparentPostLight</shaderType>
<drawSize>5</drawSize>
</graphicData>
<projectile>
<damageDef>Bomb</damageDef>
<damageAmountBase>220</damageAmountBase>
<speed>20</speed>
<armorPenetrationBase>0.8</armorPenetrationBase>
<explosionRadius>5.0</explosionRadius>
<soundHitThickRoof>Artillery_HitThickRoof</soundHitThickRoof>
<soundExplode>MortarBomb_Explode</soundExplode>
<soundImpactAnticipate>MortarRound_PreImpact</soundImpactAnticipate>
<soundAmbient>MortarRound_Ambient</soundAmbient>
<stoppingPower>5</stoppingPower>
<flyOverhead>true</flyOverhead>
<arcHeightFactor>150</arcHeightFactor>
</projectile>
<modExtensions>
<li Class="WulaFallenEmpire.TrackingBulletDef">
<tailFleckDef>WULA_GunTail_Lighting</tailFleckDef>
<fleckMakeFleckTickMax>3</fleckMakeFleckTickMax>
<fleckDelayTicks>1</fleckDelayTicks>
<fleckMakeFleckNum>1~2</fleckMakeFleckNum>
<fleckScale>0.5~1.0</fleckScale>
<fleckSpeed>0.1~0.3</fleckSpeed>
<fleckAngle>-30~30</fleckAngle>
</li>
</modExtensions>
</ThingDef>
</Defs>

View File

@@ -0,0 +1,191 @@
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class Projectile_NorthArcTrail : Projectile_Explosive
{
// --- 弹道部分变量 ---
// 定义向北偏移的高度格数建议在XML中通过 <projectile> 参数无法直接传参时硬编码或扩展 ThingDef
// 这里为了方便,设定默认值为 10你可以根据需要修改
public float northOffsetDistance = 10f;
private Vector3 originPos;
private Vector3 destinationPos;
private Vector3 bezierControlPoint;
private int ticksFlying;
private int totalTicks;
private bool initialized = false;
// --- 尾迹部分变量 ---
private TrackingBulletDef trackingDefInt;
private int Fleck_MakeFleckTick;
private Vector3 lastTickPosition; // 记录上一帧位置用于计算拖尾方向
// 获取 XML 中的扩展数据
public TrackingBulletDef TrackingDef
{
get
{
if (trackingDefInt == null)
{
trackingDefInt = def.GetModExtension<TrackingBulletDef>();
if (trackingDefInt == null)
{
// 如果没配置,给一个空的默认值防止报错,或者只报错一次
trackingDefInt = new TrackingBulletDef();
}
}
return trackingDefInt;
}
}
public override void ExposeData()
{
base.ExposeData();
// 保存弹道数据
Scribe_Values.Look(ref originPos, "originPos");
Scribe_Values.Look(ref destinationPos, "destinationPos");
Scribe_Values.Look(ref bezierControlPoint, "bezierControlPoint");
Scribe_Values.Look(ref ticksFlying, "ticksFlying", 0);
Scribe_Values.Look(ref totalTicks, "totalTicks", 0);
Scribe_Values.Look(ref initialized, "initialized", false);
Scribe_Values.Look(ref northOffsetDistance, "northOffsetDistance", 10f);
// 保存尾迹数据
Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
}
public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null)
{
base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef);
// --- 初始化弹道 ---
originPos = origin;
destinationPos = usedTarget.CenterVector3;
float speed = def.projectile.speed;
if (speed <= 0) speed = 1f;
// 计算直线距离估算时间
float distance = (originPos - destinationPos).MagnitudeHorizontal();
totalTicks = Mathf.CeilToInt(distance / speed * 100f);
if (totalTicks < 1) totalTicks = 1;
ticksFlying = 0;
// 贝塞尔曲线计算:
// 中点
Vector3 midPoint = (originPos + destinationPos) / 2f;
// 顶点 (中点向北偏移 X 格)
Vector3 apexPoint = midPoint + new Vector3(0, 0, northOffsetDistance);
// 控制点 P1 = 2 * 顶点 - 中点
bezierControlPoint = 2f * apexPoint - midPoint;
initialized = true;
// --- 初始化尾迹 ---
lastTickPosition = origin;
}
protected override void Tick()
{
// 注意:这里不直接调用 base.Tick() 的物理位移部分,因为我们要自己控制位置
// 但我们需要 Projectile_Explosive 的倒计时逻辑,所以我们在方法末尾调用 TickInterval
if (!initialized)
{
base.Tick();
return;
}
ticksFlying++;
// 1. 计算当前帧的新位置 (贝塞尔曲线 + 高度)
float t = (float)ticksFlying / (float)totalTicks;
if (t > 1f) t = 1f;
float u = 1 - t;
// 水平位移 (贝塞尔)
Vector3 nextPos = (u * u * originPos) + (2 * u * t * bezierControlPoint) + (t * t * destinationPos);
// 垂直高度 (抛物线)
float arcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
nextPos.y = arcHeight;
// 设置物体确切位置
this.Position = nextPos.ToIntVec3();
// 2. 处理拖尾特效 (合并的代码)
// 只有当这一帧移动了,且配置了 DefModExtension 时才生成
if (TrackingDef != null && TrackingDef.tailFleckDef != null)
{
Fleck_MakeFleckTick++;
// 检查生成间隔
if (Fleck_MakeFleckTick >= TrackingDef.fleckDelayTicks)
{
// 简单的循环计时重置逻辑
if (Fleck_MakeFleckTick >= (TrackingDef.fleckDelayTicks + TrackingDef.fleckMakeFleckTickMax))
{
Fleck_MakeFleckTick = TrackingDef.fleckDelayTicks;
}
Map map = base.Map;
// 只有当在地图内时才生成
if (map != null)
{
int count = TrackingDef.fleckMakeFleckNum.RandomInRange;
Vector3 currentPosition = this.ExactPosition;
Vector3 previousPosition = lastTickPosition;
// 仅当有位移时才计算角度,防止原地鬼畜
if ((currentPosition - previousPosition).MagnitudeHorizontalSquared() > 0.0001f)
{
float moveAngle = (currentPosition - previousPosition).AngleFlat();
for (int i = 0; i < count; i++)
{
// 这里的逻辑完全照搬原来的 BulletWithTrail
float velocityAngle = TrackingDef.fleckAngle.RandomInRange + moveAngle;
FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, TrackingDef.tailFleckDef, TrackingDef.fleckScale.RandomInRange);
dataStatic.rotation = moveAngle; // 粒子朝向跟随移动方向
dataStatic.rotationRate = TrackingDef.fleckRotation.RandomInRange;
dataStatic.velocityAngle = velocityAngle;
dataStatic.velocitySpeed = TrackingDef.fleckSpeed.RandomInRange;
map.flecks.CreateFleck(dataStatic);
}
}
}
}
}
// 3. 更新旋转角度 (Visual) 和 记录上一帧位置
if (lastTickPosition != nextPos)
{
if ((nextPos - lastTickPosition).MagnitudeHorizontalSquared() > 0.001f)
{
this.Rotation = Rot4.FromAngleFlat((nextPos - lastTickPosition).AngleFlat());
}
}
lastTickPosition = nextPos;
// 4. 判定到达目标或倒计时爆炸
if (ticksFlying >= totalTicks)
{
Impact(null);
return;
}
// 5. 处理 Projectile_Explosive 的内部倒计时 (如果 ticksToDetonation 被设置了)
base.TickInterval(1);
}
protected override void Impact(Thing hitThing, bool blockedByShield = false)
{
base.Impact(hitThing, blockedByShield);
}
}
}

View File

@@ -294,6 +294,7 @@
<Compile Include="Verb\MeleeAttack_MultiStrike\Verb_MeleeAttack_MultiStrike.cs" />
<Compile Include="Projectiles\Projectile_ExplosiveWithTrail.cs" />
<Compile Include="Projectiles\BulletWithTrail.cs" />
<Compile Include="Projectiles\Projectile_NorthArcTrail.cs" />
<Compile Include="HarmonyPatches\Patch_DropCellFinder_SkyfallerCanLandAt.cs" />
<Compile Include="HarmonyPatches\MechWeapon\FloatMenuProvider_Mech.cs" />
<Compile Include="HarmonyPatches\MechWeapon\Patch_MissingWeapon.cs" />