Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/Verb/Verb_ShootWithOffset.cs
Tourswen 242866bada 好玩
2025-11-03 02:39:00 +08:00

262 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class PlaceWorker_ShowTurretWithOffsetRadius : PlaceWorker
{
public override AcceptanceReport AllowsPlacing(BuildableDef checkingDef, IntVec3 loc, Rot4 rot, Map map, Thing thingToIgnore = null, Thing thing = null)
{
VerbProperties verbProperties = ((ThingDef)checkingDef).building.turretGunDef.Verbs.Find((VerbProperties v) => v.verbClass == typeof(Verb_ShootWithOffset));
if (verbProperties.range > 0f)
{
GenDraw.DrawRadiusRing(loc, verbProperties.range);
}
if (verbProperties.minRange > 0f)
{
GenDraw.DrawRadiusRing(loc, verbProperties.minRange);
}
return true;
}
}
public class ModExtension_ShootWithOffset : DefModExtension
{
public Vector2 GetOffsetFor(int index)
{
Vector2 result;
if (this.offsets.NullOrEmpty<Vector2>())
{
result = Vector2.zero;
}
else
{
int index2 = index % this.offsets.Count;
result = this.offsets[index2];
}
return result;
}
public List<Vector2> offsets = new List<Vector2>();
}
public class Verb_ShootWithOffset : Verb_Shoot
{
public int offset = 0;
protected override bool TryCastShot()
{
bool num = BaseTryCastShot();
if (num && CasterIsPawn)
{
CasterPawn.records.Increment(RecordDefOf.ShotsFired);
}
return num;
}
protected bool BaseTryCastShot()
{
if (currentTarget.HasThing && currentTarget.Thing.Map != caster.Map)
{
return false;
}
ThingDef projectile = Projectile;
if (projectile == null)
{
return false;
}
ShootLine resultingLine;
bool flag = TryFindShootLineFromTo(caster.Position, currentTarget, out resultingLine);
if (verbProps.stopBurstWithoutLos && !flag)
{
return false;
}
if (base.EquipmentSource != null)
{
base.EquipmentSource.GetComp<CompChangeableProjectile>()?.Notify_ProjectileLaunched();
base.EquipmentSource.GetComp<CompApparelVerbOwner_Charged>()?.UsedOnce();
}
lastShotTick = Find.TickManager.TicksGame;
Thing manningPawn = caster;
Thing equipmentSource = base.EquipmentSource;
CompMannable compMannable = caster.TryGetComp<CompMannable>();
if (compMannable?.ManningPawn != null)
{
manningPawn = compMannable.ManningPawn;
equipmentSource = caster;
}
Vector3 drawPos = caster.DrawPos;
drawPos = ApplyProjectileOffset(drawPos, equipmentSource);
Projectile projectile2 = (Projectile)GenSpawn.Spawn(projectile, resultingLine.Source, caster.Map);
if (equipmentSource.TryGetComp(out CompUniqueWeapon comp))
{
foreach (WeaponTraitDef item in comp.TraitsListForReading)
{
if (item.damageDefOverride != null)
{
projectile2.damageDefOverride = item.damageDefOverride;
}
if (!item.extraDamages.NullOrEmpty())
{
Projectile projectile3 = projectile2;
if (projectile3.extraDamages == null)
{
projectile3.extraDamages = new List<ExtraDamage>();
}
projectile2.extraDamages.AddRange(item.extraDamages);
}
}
}
if (verbProps.ForcedMissRadius > 0.5f)
{
float num = verbProps.ForcedMissRadius;
if (manningPawn is Pawn pawn)
{
num *= verbProps.GetForceMissFactorFor(equipmentSource, pawn);
}
float num2 = VerbUtility.CalculateAdjustedForcedMiss(num, currentTarget.Cell - caster.Position);
if (num2 > 0.5f)
{
IntVec3 forcedMissTarget = GetForcedMissTarget(num2);
if (forcedMissTarget != currentTarget.Cell)
{
ProjectileHitFlags projectileHitFlags = ProjectileHitFlags.NonTargetWorld;
if (Rand.Chance(0.5f))
{
projectileHitFlags = ProjectileHitFlags.All;
}
if (!canHitNonTargetPawnsNow)
{
projectileHitFlags &= ~ProjectileHitFlags.NonTargetPawns;
}
projectile2.Launch(manningPawn, drawPos, forcedMissTarget, currentTarget, projectileHitFlags, preventFriendlyFire, equipmentSource);
return true;
}
}
}
ShotReport shotReport = ShotReport.HitReportFor(caster, this, currentTarget);
Thing randomCoverToMissInto = shotReport.GetRandomCoverToMissInto();
ThingDef targetCoverDef = randomCoverToMissInto?.def;
if (verbProps.canGoWild && !Rand.Chance(shotReport.AimOnTargetChance_IgnoringPosture))
{
bool flyOverhead = projectile2?.def?.projectile != null && projectile2.def.projectile.flyOverhead;
resultingLine.ChangeDestToMissWild(shotReport.AimOnTargetChance_StandardTarget, flyOverhead, caster.Map);
ProjectileHitFlags projectileHitFlags2 = ProjectileHitFlags.NonTargetWorld;
if (Rand.Chance(0.5f) && canHitNonTargetPawnsNow)
{
projectileHitFlags2 |= ProjectileHitFlags.NonTargetPawns;
}
projectile2.Launch(manningPawn, drawPos, resultingLine.Dest, currentTarget, projectileHitFlags2, preventFriendlyFire, equipmentSource, targetCoverDef);
return true;
}
if (currentTarget.Thing != null && currentTarget.Thing.def.CanBenefitFromCover && !Rand.Chance(shotReport.PassCoverChance))
{
ProjectileHitFlags projectileHitFlags3 = ProjectileHitFlags.NonTargetWorld;
if (canHitNonTargetPawnsNow)
{
projectileHitFlags3 |= ProjectileHitFlags.NonTargetPawns;
}
projectile2.Launch(manningPawn, drawPos, randomCoverToMissInto, currentTarget, projectileHitFlags3, preventFriendlyFire, equipmentSource, targetCoverDef);
return true;
}
ProjectileHitFlags projectileHitFlags4 = ProjectileHitFlags.IntendedTarget;
if (canHitNonTargetPawnsNow)
{
projectileHitFlags4 |= ProjectileHitFlags.NonTargetPawns;
}
if (!currentTarget.HasThing || currentTarget.Thing.def.Fillage == FillCategory.Full)
{
projectileHitFlags4 |= ProjectileHitFlags.NonTargetWorld;
}
if (currentTarget.Thing != null)
{
projectile2.Launch(manningPawn, drawPos, currentTarget, currentTarget, projectileHitFlags4, preventFriendlyFire, equipmentSource, targetCoverDef);
}
else
{
projectile2.Launch(manningPawn, drawPos, resultingLine.Dest, currentTarget, projectileHitFlags4, preventFriendlyFire, equipmentSource, targetCoverDef);
}
return true;
}
private Vector3 ApplyProjectileOffset(Vector3 originalDrawPos, Thing equipmentSource)
{
if (equipmentSource != null)
{
// 获取投射物偏移的模组扩展
ModExtension_ShootWithOffset offsetExtension =
equipmentSource.def.GetModExtension<ModExtension_ShootWithOffset>();
if (offsetExtension != null && offsetExtension.offsets != null && offsetExtension.offsets.Count > 0)
{
// 获取当前连发射击的剩余次数
int burstShotsLeft = GetBurstShotsLeft();
// 计算从发射者到目标的角度
Vector3 targetPos = currentTarget.CenterVector3;
Vector3 casterPos = caster.DrawPos;
float rimworldAngle = targetPos.AngleToFlat(casterPos);
// 将RimWorld角度转换为适合偏移计算的角度
float correctedAngle = ConvertRimWorldAngleToOffsetAngle(rimworldAngle);
// 应用偏移并旋转到正确方向
Vector2 offset = offsetExtension.GetOffsetFor(burstShotsLeft);
Vector2 rotatedOffset = offset.RotatedBy(correctedAngle);
// 将2D偏移转换为3D并应用到绘制位置
originalDrawPos += new Vector3(rotatedOffset.x, 0f, rotatedOffset.y);
}
}
return originalDrawPos;
}
/// <summary>
/// 获取当前连发射击剩余次数
/// </summary>
/// <returns>连发射击剩余次数</returns>
private int GetBurstShotsLeft()
{
if (burstShotsLeft >= 0)
{
return (int)burstShotsLeft;
}
return 0;
}
/// <summary>
/// 将RimWorld角度转换为偏移计算用的角度
/// RimWorld使用顺时针角度系统需要转换为标准的数学角度系统
/// </summary>
/// <param name="rimworldAngle">RimWorld角度</param>
/// <returns>转换后的角度</returns>
private float ConvertRimWorldAngleToOffsetAngle(float rimworldAngle)
{
// RimWorld角度0°=东90°=北180°=西270°=南
// 转换为0°=东90°=南180°=西270°=北
return -rimworldAngle - 90f;
}
}
}