From c9938b326f064245bb3a0eb811e91e06d3be9796 Mon Sep 17 00:00:00 2001 From: Haecriver Date: Mon, 23 Feb 2026 18:50:29 +0100 Subject: [PATCH 01/10] Update nuget rimworld packages --- Source/BetterTurretsCompat/BetterTurretsCompat.csproj | 2 +- Source/CombatExtended/CombatExtended.csproj | 2 +- Source/MiscTurretsCompat/MiscTurretsCompat.csproj | 2 +- Source/MultiplayerCompat/MultiplayerCompat.csproj | 2 +- Source/PsyblastersCompat/PsyblastersCompat.csproj | 2 +- Source/SOS2Compat/SOS2Compat.csproj | 2 +- Source/SRTSCompat/SRTSCompat.csproj | 2 +- Source/VehiclesCompat/VehiclesCompat.csproj | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/BetterTurretsCompat/BetterTurretsCompat.csproj b/Source/BetterTurretsCompat/BetterTurretsCompat.csproj index 4f85faa002..329e7019eb 100644 --- a/Source/BetterTurretsCompat/BetterTurretsCompat.csproj +++ b/Source/BetterTurretsCompat/BetterTurretsCompat.csproj @@ -48,7 +48,7 @@ - + diff --git a/Source/CombatExtended/CombatExtended.csproj b/Source/CombatExtended/CombatExtended.csproj index 57f60f9a1c..27823e2ec8 100644 --- a/Source/CombatExtended/CombatExtended.csproj +++ b/Source/CombatExtended/CombatExtended.csproj @@ -118,7 +118,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Source/MiscTurretsCompat/MiscTurretsCompat.csproj b/Source/MiscTurretsCompat/MiscTurretsCompat.csproj index 377d15e481..011a1dc66a 100644 --- a/Source/MiscTurretsCompat/MiscTurretsCompat.csproj +++ b/Source/MiscTurretsCompat/MiscTurretsCompat.csproj @@ -48,7 +48,7 @@ - + diff --git a/Source/MultiplayerCompat/MultiplayerCompat.csproj b/Source/MultiplayerCompat/MultiplayerCompat.csproj index 30617b2d77..de4f91e545 100644 --- a/Source/MultiplayerCompat/MultiplayerCompat.csproj +++ b/Source/MultiplayerCompat/MultiplayerCompat.csproj @@ -40,7 +40,7 @@ - + diff --git a/Source/PsyblastersCompat/PsyblastersCompat.csproj b/Source/PsyblastersCompat/PsyblastersCompat.csproj index c92dd2ea12..d039aca777 100644 --- a/Source/PsyblastersCompat/PsyblastersCompat.csproj +++ b/Source/PsyblastersCompat/PsyblastersCompat.csproj @@ -41,7 +41,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Source/SOS2Compat/SOS2Compat.csproj b/Source/SOS2Compat/SOS2Compat.csproj index f42b714d23..487ceadbae 100644 --- a/Source/SOS2Compat/SOS2Compat.csproj +++ b/Source/SOS2Compat/SOS2Compat.csproj @@ -61,7 +61,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Source/SRTSCompat/SRTSCompat.csproj b/Source/SRTSCompat/SRTSCompat.csproj index bc5a3457a5..c441ec6c87 100644 --- a/Source/SRTSCompat/SRTSCompat.csproj +++ b/Source/SRTSCompat/SRTSCompat.csproj @@ -46,7 +46,7 @@ - + diff --git a/Source/VehiclesCompat/VehiclesCompat.csproj b/Source/VehiclesCompat/VehiclesCompat.csproj index c1749e127b..60448d09fd 100644 --- a/Source/VehiclesCompat/VehiclesCompat.csproj +++ b/Source/VehiclesCompat/VehiclesCompat.csproj @@ -55,7 +55,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 2a0e75d4431a575bcde785637bafc859015b3583 Mon Sep 17 00:00:00 2001 From: Haecriver Date: Mon, 23 Feb 2026 18:57:16 +0100 Subject: [PATCH 02/10] Add code --- .../Gizmos/Command_ArtilleryTarget.cs | 317 +++++++++++------- .../Projectiles/ProjectileCE.cs | 52 +-- .../CombatExtended/ShellingUtility.cs | 60 ++++ .../Things/Building_TurretGunCE.cs | 6 +- .../Verbs/Verb_ShootMortarCE.cs | 6 +- .../WorldObjects/TravelingShell.cs | 13 +- .../WorldObjects/TravelingThing.cs | 24 +- 7 files changed, 312 insertions(+), 166 deletions(-) diff --git a/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs index 08f0142ec4..6aeeae1772 100755 --- a/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs +++ b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs @@ -1,18 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; using RimWorld; using RimWorld.Planet; +using System.Collections.Generic; +using System.Linq; using UnityEngine; using Verse; namespace CombatExtended; + public class Command_ArtilleryTarget : Command { public Building_TurretGunCE turret; public List others = null; + /// + /// When firing on orbital targets, it can be tricky to use binoculars ... + /// This disables the need of target mark. + /// + public bool mandatoryMarkToFireOutBounds = true; + public IEnumerable SelectedTurrets => others?.Select(o => o.turret) ?? new List() { turret }; public override bool GroupsWith(Gizmo other) => other is Command_ArtilleryTarget; @@ -28,6 +34,11 @@ public override void MergeWith(Gizmo other) others.Add(order); } + protected void CommandProcessInput(Event ev) + { + base.ProcessInput(ev); + } + public override void ProcessInput(Event ev) { CameraJumper.TryJump(CameraJumper.GetWorldTarget(turret)); @@ -43,160 +54,220 @@ public override void ProcessInput(Event ev) Log.Error("Command_ArtilleryTarget selected turrets collection is invalid"); return; } - int turretTile = turret.Map.Tile; - int radius = (int)turret.MaxWorldRange; - Find.WorldTargeter.BeginTargeting((targetInfo) => - { - IEnumerable turrets = SelectedTurrets; - Map map = Find.World.worldObjects.MapParentAt(targetInfo.Tile)?.Map ?? null; - // We only want player to target world object when there's no colonist in the map - if (map != null && map.mapPawns.AnyPawnBlockingMapRemoval) + PlanetTile turretTile = turret.Map.Tile; + int radius = Mathf.FloorToInt(turret.MaxWorldRange); + + ShellingUtility.ClearRadiusCache(); + + Find.WorldTargeter.BeginTargeting( + action: (GlobalTargetInfo targetInfo) => { - IntVec3 selectedCell = IntVec3.Invalid; - Find.WorldTargeter.StopTargeting(); - CameraJumper.TryJumpInternal(new IntVec3((int)map.Size.x / 2, 0, (int)map.Size.z / 2), map, CameraJumper.MovementMode.Pan); - Find.Targeter.BeginTargeting(new TargetingParameters() - { - canTargetLocations = true, - canTargetBuildings = true, - canTargetHumans = true - }, (target) => + // Check additionnal condition + if (!AdditionnalTargettingCondition(targetInfo)) { - targetInfo.mapInt = map; - targetInfo.tileInt = map.Tile; - targetInfo.cellInt = target.cellInt; - //targetInfo.thingInt = target.thingInt; - TryAttack(turrets, targetInfo, target); - }, highlightAction: (target) => + return false; + } + + IEnumerable turrets = SelectedTurrets; + Map map = Find.World.worldObjects.MapParentAt(targetInfo.Tile)?.Map ?? null; + + // We only want player to target world object when there's no colonist in the map + // Only if mark is needed + if (map != null && (!mandatoryMarkToFireOutBounds || map.mapPawns.AnyPawnBlockingMapRemoval)) { - GenDraw.DrawTargetHighlight(target); - }, targetValidator: (target) => + return AttackWorldTile(turrets, targetInfo, map); + } + + return AttackWorldObject(turrets, targetInfo); + }, + canTargetTiles: true, + closeWorldTabWhenFinished: true, + onUpdate: () => + { + if (others != null) { - RoofDef roof = map.roofGrid.RoofAt(target.Cell); - if ((roof == null || roof == RoofDefOf.RoofConstructed) && - target.Cell.GetFirstThing(map) != null) + foreach (var t in SelectedTurrets) { - return true; + int radius2 = Mathf.FloorToInt(t.MaxWorldRange); + if (radius2 != radius) + { + ShellingUtility.CachedDrawTurretRadiusRing(t.Tile, radius2); + } } - else + } + ShellingUtility.CachedDrawTurretRadiusRing(turretTile, radius); + }, + extraLabelGetter: (targetInfo) => + { + // Remove label when bad layer + if (PlanetLayer.Selected != targetInfo.Tile.Layer) + { + return ""; + } + int distanceToTarget = ShellingUtility.GetDistancePlanetTiles(turretTile, targetInfo.Tile); + float maxWorldRange = turret.MaxWorldRange; + string distanceMessage = null; + if (others != null) + { + int inRangeCount = 0; + int count = 0; + foreach (var t in SelectedTurrets) { - Messages.Message("CE_ArtilleryTarget_MustTargetMark".Translate(), MessageTypeDefOf.RejectInput); - return false; + count++; + if (t.MaxWorldRange >= distanceToTarget) + { + inRangeCount++; + } } - }); - return false; - } - if (targetInfo.WorldObject.Destroyed || targetInfo.WorldObject is DestroyedSettlement || targetInfo.WorldObject.def == WorldObjectDefOf.DestroyedSettlement) - { - Messages.Message("CE_ArtilleryTarget_AlreadyDestroyed".Translate(), MessageTypeDefOf.CautionInput); - return false; - } - if (targetInfo.WorldObject.Faction != null) - { - Faction targetFaction = targetInfo.WorldObject.Faction; - FactionRelation relation = targetFaction.RelationWith(turret.Faction, true); - if (relation == null) + distanceMessage = "CE_ArtilleryTarget_Distance_Selections".Translate().Formatted(distanceToTarget, inRangeCount, count); + } + else { - targetFaction.TryMakeInitialRelationsWith(turret.Faction); + distanceMessage = "CE_ArtilleryTarget_Distance".Translate().Formatted(distanceToTarget, maxWorldRange); } - if (!targetFaction.HostileTo(turret.Faction) && !targetFaction.Hidden) + if (maxWorldRange > 0 && distanceToTarget > maxWorldRange) { - Find.WindowStack.Add( - new Dialog_MessageBox( - "CE_ArtilleryTarget_AttackingAllies".Translate().Formatted(targetInfo.WorldObject.Label, targetFaction.Name), - "CE_Yes".Translate(), - delegate - { - TryAttack(turrets, targetInfo, LocalTargetInfo.Invalid); - Find.WorldTargeter.StopTargeting(); - }, - "CE_No".Translate(), - delegate - { - Find.WorldTargeter.StopTargeting(); - }, buttonADestructive: true)); - return false; + GUI.color = ColorLibrary.RedReadable; + return distanceMessage + "\n" + "CE_ArtilleryTarget_DestinationBeyondMaximumRange".Translate(); } - } - return TryAttack(turrets, targetInfo, LocalTargetInfo.Invalid); - }, true, closeWorldTabWhenFinished: true, onUpdate: () => - { - if (others != null) - { - foreach (var t in SelectedTurrets) + if (!targetInfo.HasWorldObject || targetInfo.WorldObject is Caravan) { - if (t.MaxWorldRange != radius) - { - GenDraw.DrawWorldRadiusRing(turretTile, (int)t.MaxWorldRange); - } + GUI.color = ColorLibrary.RedReadable; + return distanceMessage + "\n" + "CE_ArtilleryTarget_InvalidTarget".Translate(); } - } - GenDraw.DrawWorldRadiusRing(turretTile, radius); - }, extraLabelGetter: (targetInfo) => - { - int distanceToTarget = Find.WorldGrid.TraversalDistanceBetween(turretTile, targetInfo.Tile, true); - string distanceMessage = null; - if (others != null) - { - int inRangeCount = 0; - int count = 0; - foreach (var t in SelectedTurrets) + string extra = ""; + if (targetInfo.WorldObject is Settlement settlement) { - count++; - if (t.MaxWorldRange >= distanceToTarget) + extra = $" {settlement.Name}"; + if (settlement.Faction != null && !settlement.Faction.name.NullOrEmpty()) { - inRangeCount++; + extra += $" ({settlement.Faction.name})"; } } - distanceMessage = "CE_ArtilleryTarget_Distance_Selections".Translate().Formatted(distanceToTarget, inRangeCount, count); - } - else + return distanceMessage + "\n" + "CE_ArtilleryTarget_ClickToOrderAttack".Translate() + extra; + }, + canSelectTarget: (targetInfo) => { - distanceMessage = "CE_ArtilleryTarget_Distance".Translate().Formatted(distanceToTarget, radius); - } - if (turret.MaxWorldRange > 0 && distanceToTarget > turret.MaxWorldRange) - { - GUI.color = ColorLibrary.RedReadable; - return distanceMessage + "\n" + "CE_ArtilleryTarget_DestinationBeyondMaximumRange".Translate(); - } - if (!targetInfo.HasWorldObject || targetInfo.WorldObject is Caravan) - { - GUI.color = ColorLibrary.RedReadable; - return distanceMessage + "\n" + "CE_ArtilleryTarget_InvalidTarget".Translate(); - } - string extra = ""; - if (targetInfo.WorldObject is Settlement settlement) - { - extra = $" {settlement.Name}"; - if (settlement.Faction != null && !settlement.Faction.name.NullOrEmpty()) + if ( + // Is unvalid + !targetInfo.HasWorldObject + // Fire on its own tile + || targetInfo.Tile == turretTile + // World object has neither a Map nor a HealthComp (ennemy faction) + || (targetInfo.WorldObject as MapParent)?.Map == null && + targetInfo.WorldObject.GetComponent() == null) { - extra += $" ({settlement.Faction.name})"; + return false; } + return true; + }); + CommandProcessInput(ev); + } + + /// + /// Return false to stop the targeting process, true to continue. + /// + protected virtual bool AdditionnalTargettingCondition(GlobalTargetInfo targetInfo) + { + return true; + } + + protected bool TryAttack(IEnumerable turrets, GlobalTargetInfo targetInfo, LocalTargetInfo localTargetInfo) + { + bool attackStarted = false; + foreach (var t in turrets) + { + if (t.Active && t.TryAttackWorldTarget(targetInfo, localTargetInfo)) + { + attackStarted = attackStarted || true; } - return distanceMessage + "\n" + "CE_ArtilleryTarget_ClickToOrderAttack".Translate() + extra; - }, canSelectTarget: (targetInfo) => + } + return attackStarted; + } + + private bool AttackWorldTile(IEnumerable turrets, GlobalTargetInfo targetInfo, Map map) + { + IntVec3 selectedCell = IntVec3.Invalid; + Find.WorldTargeter.StopTargeting(); + CameraJumper.TryJumpInternal(new IntVec3((int)map.Size.x / 2, 0, (int)map.Size.z / 2), map, CameraJumper.MovementMode.Pan); + Find.Targeter.BeginTargeting(new TargetingParameters() + { + canTargetLocations = true, + canTargetBuildings = true, + canTargetHumans = true + }, (target) => + { + targetInfo.mapInt = map; + targetInfo.tileInt = map.Tile; + targetInfo.cellInt = target.cellInt; + //targetInfo.thingInt = target.thingInt; + TryAttack(turrets, targetInfo, target); + }, highlightAction: (target) => { - if (!targetInfo.HasWorldObject || targetInfo.Tile == turretTile || - (targetInfo.WorldObject as MapParent)?.Map == null && - targetInfo.WorldObject.GetComponent() == null) + GenDraw.DrawTargetHighlight(target); + }, targetValidator: (target) => + { + // Cannot fire through mountain roof + RoofDef roof = map.roofGrid.RoofAt(target.Cell); + if (roof != null && roof != RoofDefOf.RoofConstructed) + { + Messages.Message("CE_ArtilleryTarget_NoThickRoof".Translate(), MessageTypeDefOf.RejectInput); + return false; + } + + // Marker condition + if (mandatoryMarkToFireOutBounds && target.Cell.GetFirstThing(map) == null) { + Messages.Message("CE_ArtilleryTarget_MustTargetMark".Translate(), MessageTypeDefOf.RejectInput); return false; } + return true; }); - base.ProcessInput(ev); + return false; } - private bool TryAttack(IEnumerable turrets, GlobalTargetInfo targetInfo, LocalTargetInfo localTargetInfo) + private bool AttackWorldObject(IEnumerable turrets, GlobalTargetInfo targetInfo) { - bool attackStarted = false; - foreach (var t in turrets) + if (targetInfo.WorldObject.Destroyed || targetInfo.WorldObject is DestroyedSettlement || targetInfo.WorldObject.def == WorldObjectDefOf.DestroyedSettlement) { - if (t.Active && t.TryAttackWorldTarget(targetInfo, localTargetInfo)) + Messages.Message("CE_ArtilleryTarget_AlreadyDestroyed".Translate(), MessageTypeDefOf.CautionInput); + return false; + } + + if (targetInfo.WorldObject.Faction != null) + { + if (targetInfo.WorldObject.Faction == Faction.OfPlayer) { - attackStarted = attackStarted || true; + // We should not be able to target our own faction + return false; + } + + Faction targetFaction = targetInfo.WorldObject.Faction; + FactionRelation relation = targetFaction.RelationWith(turret.Faction, true); + if (relation == null) + { + targetFaction.TryMakeInitialRelationsWith(turret.Faction); + } + if (!targetFaction.HostileTo(turret.Faction) && !targetFaction.Hidden) + { + Find.WindowStack.Add( + new Dialog_MessageBox( + "CE_ArtilleryTarget_AttackingAllies".Translate().Formatted(targetInfo.WorldObject.Label, targetFaction.Name), + "CE_Yes".Translate(), + delegate + { + TryAttack(turrets, targetInfo, LocalTargetInfo.Invalid); + Find.WorldTargeter.StopTargeting(); + }, + "CE_No".Translate(), + delegate + { + Find.WorldTargeter.StopTargeting(); + }, buttonADestructive: true)); + return false; } } - return attackStarted; + return TryAttack(turrets, targetInfo, LocalTargetInfo.Invalid); } } diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs index 064ca57327..c53874eac5 100755 --- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs +++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs @@ -1392,27 +1392,7 @@ public override void Tick() { if (globalTargetInfo.IsValid) { - TravelingShell shell = (TravelingShell)WorldObjectMaker.MakeWorldObject(CE_WorldObjectDefOf.TravelingShell); - if (launcher?.Faction != null) - { - shell.SetFaction(launcher.Faction); - } - shell.Tile = Map.Tile; - shell.SpawnSetup(); - Find.World.worldObjects.Add(shell); - shell.launcher = launcher; - shell.equipmentDef = equipmentDef; - shell.globalSource = new GlobalTargetInfo(OriginIV3, Map); - shell.globalSource.tileInt = Map.Tile; - shell.globalSource.mapInt = Map; - shell.globalSource.worldObjectInt = Map.Parent; - shell.shellDef = def; - shell.globalTarget = globalTargetInfo; - if (!shell.TryTravel(Map.Tile, globalTargetInfo.Tile)) - { - Log.Error($"CE: Travling shell {this.def} failed to launch!"); - shell.Destroy(); - } + CreateShellWorldObject(); } Destroy(); return; @@ -1469,6 +1449,36 @@ public override void Tick() } } + protected void CreateShellWorldObject() + { + TravelingShell shell = (TravelingShell)WorldObjectMaker.MakeWorldObject(CE_WorldObjectDefOf.TravelingShell); + if (launcher?.Faction != null) + { + shell.SetFaction(launcher.Faction); + } + shell.Tile = Map.Tile; + shell.SpawnSetup(); + Find.World.worldObjects.Add(shell); + shell.launcher = launcher; + shell.equipmentDef = equipmentDef; + shell.globalSource = new GlobalTargetInfo(OriginIV3, Map); + shell.globalSource.tileInt = Map.Tile; + shell.globalSource.mapInt = Map; + shell.globalSource.worldObjectInt = Map.Parent; + shell.shellDef = def; + shell.globalTarget = globalTargetInfo; + if (Props.shellingProps?.arrivedAtSameProps ?? false) + { + shell.arrivedShotHeight = shotHeight; + shell.arrivedShotSpeed = shotSpeed; + } + if (!shell.TryTravel(Map.Tile, globalTargetInfo.Tile)) + { + Log.Error($"CE: Travling shell {this.def} failed to launch!"); + shell.Destroy(); + } + } + /// /// Draws projectile if at least a tick away from caster (or always if no caster) /// diff --git a/Source/CombatExtended/CombatExtended/ShellingUtility.cs b/Source/CombatExtended/CombatExtended/ShellingUtility.cs index e22c294f6c..0347b9dd1e 100755 --- a/Source/CombatExtended/CombatExtended/ShellingUtility.cs +++ b/Source/CombatExtended/CombatExtended/ShellingUtility.cs @@ -16,6 +16,66 @@ public static class ShellingUtility private static ProjectilePropertiesCE props; private static DamageDef projectileDamageDef; + private struct DistanceCache + { + public PlanetTile startingTile; + public PlanetTile destinationTile; + public int distance; + } + private static DistanceCache distanceCache = new DistanceCache(); + + public static int GetDistancePlanetTiles(PlanetTile startingTile, PlanetTile destinationTile, int maxDist = int.MaxValue) + { + if (distanceCache.startingTile == startingTile && distanceCache.destinationTile == destinationTile) + { + return distanceCache.distance; + } + + if (startingTile.layerId != destinationTile.layerId) + { + startingTile = destinationTile.Layer.GetClosestTile_NewTemp(startingTile); + } + distanceCache.startingTile = startingTile; + distanceCache.destinationTile = destinationTile; + + distanceCache.distance = + (int)(Find.WorldGrid.TraversalDistanceBetween(startingTile, destinationTile, true, maxDist) * destinationTile.LayerDef.rangeDistanceFactor); + return distanceCache.distance; + } + + private struct RadiusCache + { + public PlanetTile realCenterTile; + public int radius; + } + private static Dictionary radiusCache = new Dictionary(); + + public static void ClearRadiusCache() + { + radiusCache.Clear(); + } + + public static void CachedDrawTurretRadiusRing(PlanetTile center, int radius) + { + PlanetTile realCenterTile; + int realRadius; + RadiusCache cache; + // We cache these operations, because there is no need to overcharge the update. + if (radiusCache.TryGetValue(center.tileId, out cache)) + { + realCenterTile = cache.realCenterTile; + realRadius = cache.radius; + } + else + { + realCenterTile = PlanetLayer.Selected.GetClosestTile_NewTemp(center); + realRadius = Mathf.FloorToInt(radius / PlanetLayer.Selected.Def.rangeDistanceFactor); + // Add cache + radiusCache.Add(center.tileId, new RadiusCache() { realCenterTile = center, radius = realRadius }); + } + GenDraw.DrawWorldRadiusRing(realCenterTile, realRadius); + } + public static IntVec3 FindRandomImpactCell(Map map, ThingDef shellDef = null) { ShellingUtility.map = map; diff --git a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs index 126eb411e4..8b4cbf7010 100755 --- a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs +++ b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs @@ -672,7 +672,7 @@ public bool TryAttackWorldTarget(GlobalTargetInfo targetInfo, LocalTargetInfo lo { ResetCurrentTarget(); ResetForcedTarget(); - int distanceToTarget = Find.WorldGrid.TraversalDistanceBetween(Map.Tile, targetInfo.Tile, true, maxDist: (int)(this.MaxWorldRange * 1.5f)); + int distanceToTarget = ShellingUtility.GetDistancePlanetTiles(Map.Tile, targetInfo.Tile, (int)(MaxWorldRange * 1.5f)); if (distanceToTarget > MaxWorldRange) { return false; @@ -694,8 +694,8 @@ public bool TryAttackWorldTarget(GlobalTargetInfo targetInfo, LocalTargetInfo lo public virtual void TryOrderAttackWorldTile(GlobalTargetInfo targetInf, IntVec3? cell = null) { - int startingTile = Map.Tile; - int destinationTile = targetInf.Tile; + PlanetTile startingTile = Map.Tile; + PlanetTile destinationTile = targetInf.Tile; Vector3 direction = (Find.WorldGrid.GetTileCenter(startingTile) - Find.WorldGrid.GetTileCenter(destinationTile)).normalized; Vector3 shotPos = DrawPos.Yto0(); diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs index f086e952ee..b201887a3b 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs @@ -29,12 +29,12 @@ public class Verb_ShootMortarCE : Verb_ShootCE /// /// Wether the target is marked /// - private bool targetHasMarker = false; + protected bool targetHasMarker = false; // for global target only // - private int startingTile; - private int destinationTile; + private PlanetTile startingTile; + private PlanetTile destinationTile; private int globalDistance; private Vector3 direction; private new int numShotsFired; diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs index 004e489244..6da6d52114 100755 --- a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs +++ b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs @@ -17,6 +17,8 @@ public class TravelingShell : TravelingThing public ThingDef equipmentDef; public ThingDef shellDef; public Thing launcher; + public float arrivedShotHeight = 200f; + public float arrivedShotSpeed = 55f; private Texture2D expandingIcon; public override Texture2D ExpandingIcon { @@ -39,6 +41,7 @@ public override float TilesPerTick { get => (shellDef.projectile as ProjectilePropertiesCE).shellingProps.tilesPerTick; } + public bool IsInstant => (shellDef.projectile as ProjectilePropertiesCE).isInstant; public override bool ExpandingIconFlipHorizontal { @@ -97,7 +100,7 @@ public override string GetDescription() protected override void Arrived() { int tile = Tile; - foreach (WorldObject worldObject in Find.World.worldObjects.ObjectsAt(tile)) + foreach (WorldObject worldObject in Find.World.worldObjects.ObjectsAt(Tile)) { if (TryShell(worldObject)) { @@ -145,20 +148,20 @@ private bool TryShell(WorldObject worldObject) return shelled; } - protected virtual void LaunchProjectile(IntVec3 sourceCell, LocalTargetInfo target, Map map, float shotSpeed = 20, float shotHeight = 200) + protected virtual void LaunchProjectile(IntVec3 sourceCell, LocalTargetInfo target, Map map) { - Vector3 source = new Vector3(sourceCell.x, shotHeight, sourceCell.z); + Vector3 source = new Vector3(sourceCell.x, arrivedShotHeight, sourceCell.z); Vector3 targetPos = target.Cell.ToVector3Shifted(); ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(shellDef); ProjectilePropertiesCE pprops = projectile.def.projectile as ProjectilePropertiesCE; float shotRotation = pprops.TrajectoryWorker.ShotRotation(pprops, source, targetPos); - float shotAngle = pprops.TrajectoryWorker.ShotAngle(pprops, source, targetPos, shotSpeed); + float shotAngle = pprops.TrajectoryWorker.ShotAngle(pprops, source, targetPos, arrivedShotSpeed); projectile.canTargetSelf = false; projectile.Position = sourceCell; projectile.SpawnSetup(map, false); - projectile.Launch(launcher, new Vector2(source.x, source.z), shotAngle, shotRotation, shotHeight, shotSpeed); + projectile.Launch(launcher, new Vector2(source.x, source.z), shotAngle, shotRotation, arrivedShotHeight, arrivedShotSpeed); //projectile.cameraShakingInit = Rand.Range(0f, 2f); } diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingThing.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingThing.cs index 68aea2ae08..a748aec40d 100755 --- a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingThing.cs +++ b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingThing.cs @@ -8,8 +8,8 @@ namespace CombatExtended; public abstract class TravelingThing : WorldObject { - private int startingTile; - private int destinationTile; + private PlanetTile startingTile; + private PlanetTile destinationTile; private int distanceInTiles; private float tilesPerTick; @@ -31,13 +31,13 @@ public virtual float TilesPerTick { get => 0.03f; } - public int StartTile + public PlanetTile StartTile { get => startingTile; } - public int DestinationTile + public PlanetTile DestinationTile { - get => startingTile; + get => destinationTile; } public float TraveledPtc => this.distanceTraveled / this.distanceInTiles; @@ -47,22 +47,24 @@ public TravelingThing() { } - public virtual bool TryTravel(int startingTile, int destinationTile) + public virtual bool TryTravel(PlanetTile startingTile, PlanetTile destinationTile) { if (startingTile <= -1 || destinationTile <= -1 || startingTile == destinationTile) { Log.Warning($"CE: TryTravel in thing {this} got {startingTile} {destinationTile}"); return false; } + this.startingTile = this.Tile = startingTile; this.destinationTile = destinationTile; this.tilesPerTick = TilesPerTick; - Vector3 start = Find.WorldGrid.GetTileCenter(startingTile); - Vector3 end = Find.WorldGrid.GetTileCenter(destinationTile); - - this.distance = GenMath.SphericalDistance(start.normalized, end.normalized); - this.distanceInTiles = (int)Find.World.grid.ApproxDistanceInTiles(this.distance); + PlanetLayer layerToUse = Find.WorldGrid.PlanetLayers.TryGetValue(0); // gound layer + if (startingTile.Layer == destinationTile.Layer) + { + layerToUse = startingTile.Layer; + } + this.distanceInTiles = (int)layerToUse.ApproxDistanceInTiles(startingTile, destinationTile); return true; } From 6cb6912f505b8270e64433b1fe15122cf55718cc Mon Sep 17 00:00:00 2001 From: Haecriver Date: Mon, 2 Mar 2026 17:50:41 +0100 Subject: [PATCH 03/10] update vfe nuget package --- Source/VFESecurityCompat/VFESecurityCompat.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VFESecurityCompat/VFESecurityCompat.csproj b/Source/VFESecurityCompat/VFESecurityCompat.csproj index 57deaa1738..0cdfbe80ac 100644 --- a/Source/VFESecurityCompat/VFESecurityCompat.csproj +++ b/Source/VFESecurityCompat/VFESecurityCompat.csproj @@ -44,7 +44,7 @@ - + From 7c421055c36f5c34ab1c76dc1ce49064cbea6fab Mon Sep 17 00:00:00 2001 From: Haecriver Date: Mon, 2 Mar 2026 21:01:49 +0100 Subject: [PATCH 04/10] Add a comp to control orbital artillery --- Languages/English/Keyed/Messages.xml | 2 + .../CombatExtended/Comps/CompOrbitalTurret.cs | 8 ++++ .../Comps/CompProperties_OrbitalTurret.cs | 16 +++++++ .../Gizmos/Command_ArtilleryTarget.cs | 36 +++++++++++---- .../CombatExtended/ShellingUtility.cs | 46 +++++++++++++++---- .../Things/Building_TurretGunCE.cs | 14 ++++++ .../Verbs/Verb_LaunchProjectileCE.cs | 2 +- 7 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs create mode 100644 Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs diff --git a/Languages/English/Keyed/Messages.xml b/Languages/English/Keyed/Messages.xml index d906df7b51..da165942ad 100644 --- a/Languages/English/Keyed/Messages.xml +++ b/Languages/English/Keyed/Messages.xml @@ -14,4 +14,6 @@ Artillery fire incoming from {0} ({1}). A group from {0} is planning to raid {1} at {2} soon in retaliation for recent events. + Cannot fire on different layer tile with this piece of artillery. + \ No newline at end of file diff --git a/Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs b/Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs new file mode 100644 index 0000000000..b4ad17c756 --- /dev/null +++ b/Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs @@ -0,0 +1,8 @@ +using Verse; + +namespace CombatExtended; + +public class CompOrbitalTurret : ThingComp +{ + public CompProperties_OrbitalTurret Props => (CompProperties_OrbitalTurret)props; +} diff --git a/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs new file mode 100644 index 0000000000..e5861ce196 --- /dev/null +++ b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs @@ -0,0 +1,16 @@ + +using Verse; + +namespace CombatExtended; + +public class CompProperties_OrbitalTurret: CompProperties +{ + public float precisionBonusFactor = 20; + public float rangeBonusFactor = 20; + public bool isMarkMandatory = false; + + public CompProperties_OrbitalTurret() + { + compClass = typeof(CompOrbitalTurret); + } +} diff --git a/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs index 6aeeae1772..c623e0f1b2 100755 --- a/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs +++ b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs @@ -9,20 +9,31 @@ namespace CombatExtended; public class Command_ArtilleryTarget : Command { + #region Fields + public Building_TurretGunCE turret; public List others = null; - /// - /// When firing on orbital targets, it can be tricky to use binoculars ... - /// This disables the need of target mark. - /// - public bool mandatoryMarkToFireOutBounds = true; + #endregion + + #region Properties + + bool CanShootOtherLayers => turret.CompOrbitalTurret != null; + ///// + ///// When firing on orbital targets, it can be tricky to use binoculars ... + ///// This disables the need of target mark. + ///// + bool MandatoryMarkToFireOutBounds => turret.CompOrbitalTurret?.Props.isMarkMandatory ?? true; public IEnumerable SelectedTurrets => others?.Select(o => o.turret) ?? new List() { turret }; public override bool GroupsWith(Gizmo other) => other is Command_ArtilleryTarget; + #endregion + + #region Methods + public override void MergeWith(Gizmo other) { var order = other as Command_ArtilleryTarget; @@ -71,9 +82,15 @@ public override void ProcessInput(Event ev) IEnumerable turrets = SelectedTurrets; Map map = Find.World.worldObjects.MapParentAt(targetInfo.Tile)?.Map ?? null; + if (!CanShootOtherLayers && targetInfo.Tile.Layer != turretTile.Layer) + { + Messages.Message("CE_Message_ArtilleryBadLayer".Translate(), MessageTypeDefOf.RejectInput, false); + return false; + } + // We only want player to target world object when there's no colonist in the map // Only if mark is needed - if (map != null && (!mandatoryMarkToFireOutBounds || map.mapPawns.AnyPawnBlockingMapRemoval)) + if (map != null && (!MandatoryMarkToFireOutBounds || map.mapPawns.AnyPawnBlockingMapRemoval)) { return AttackWorldTile(turrets, targetInfo, map); } @@ -91,11 +108,11 @@ public override void ProcessInput(Event ev) int radius2 = Mathf.FloorToInt(t.MaxWorldRange); if (radius2 != radius) { - ShellingUtility.CachedDrawTurretRadiusRing(t.Tile, radius2); + ShellingUtility.CachedDrawTurretRadiusRing(t.Tile, radius2, CanShootOtherLayers); } } } - ShellingUtility.CachedDrawTurretRadiusRing(turretTile, radius); + ShellingUtility.CachedDrawTurretRadiusRing(turretTile, radius, CanShootOtherLayers); }, extraLabelGetter: (targetInfo) => { @@ -216,7 +233,7 @@ private bool AttackWorldTile(IEnumerable turrets, GlobalTa } // Marker condition - if (mandatoryMarkToFireOutBounds && target.Cell.GetFirstThing(map) == null) + if (MandatoryMarkToFireOutBounds && target.Cell.GetFirstThing(map) == null) { Messages.Message("CE_ArtilleryTarget_MustTargetMark".Translate(), MessageTypeDefOf.RejectInput); return false; @@ -270,4 +287,5 @@ private bool AttackWorldObject(IEnumerable turrets, Global } return TryAttack(turrets, targetInfo, LocalTargetInfo.Invalid); } + #endregion } diff --git a/Source/CombatExtended/CombatExtended/ShellingUtility.cs b/Source/CombatExtended/CombatExtended/ShellingUtility.cs index 0347b9dd1e..0155be97b4 100755 --- a/Source/CombatExtended/CombatExtended/ShellingUtility.cs +++ b/Source/CombatExtended/CombatExtended/ShellingUtility.cs @@ -33,13 +33,14 @@ public static int GetDistancePlanetTiles(PlanetTile startingTile, PlanetTile des if (startingTile.layerId != destinationTile.layerId) { - startingTile = destinationTile.Layer.GetClosestTile_NewTemp(startingTile); + // startingTile = destinationTile.Layer.GetClosestTile_NewTemp(startingTile); } distanceCache.startingTile = startingTile; distanceCache.destinationTile = destinationTile; distanceCache.distance = - (int)(Find.WorldGrid.TraversalDistanceBetween(startingTile, destinationTile, true, maxDist) * destinationTile.LayerDef.rangeDistanceFactor); + (int)(Find.WorldGrid.TraversalDistanceBetween(startingTile, destinationTile, true, maxDist, true) * destinationTile.LayerDef.rangeDistanceFactor); + return distanceCache.distance; } @@ -55,23 +56,50 @@ public static void ClearRadiusCache() radiusCache.Clear(); } - public static void CachedDrawTurretRadiusRing(PlanetTile center, int radius) + public static void CachedDrawTurretRadiusRing(PlanetTile center, int radius, bool canShootOtherLayers = false) { PlanetTile realCenterTile; int realRadius; RadiusCache cache; - // We cache these operations, because there is no need to overcharge the update. - if (radiusCache.TryGetValue(center.tileId, out cache)) + + // Try to find cache. + bool cacheFound = radiusCache.TryGetValue(center.tileId, out cache); + + // If the result is not on the current layer, we need to recalculate it + if (cacheFound && cache.realCenterTile.Layer != PlanetLayer.Selected) + { + if (!canShootOtherLayers) + { + // Don't display radius + return; + } + + cacheFound = false; + radiusCache.Remove(center.tileId); + } + + // Use cached values + if (cacheFound) { realCenterTile = cache.realCenterTile; realRadius = cache.radius; } else { - realCenterTile = PlanetLayer.Selected.GetClosestTile_NewTemp(center); - realRadius = Mathf.FloorToInt(radius / PlanetLayer.Selected.Def.rangeDistanceFactor); - // Add cache - radiusCache.Add(center.tileId, new RadiusCache() { realCenterTile = center, radius = realRadius }); + // We cache these operations, because there is no need to overcharge the update. + realCenterTile = center; + float rangeDistanceFactor = PlanetLayer.Selected.Def.rangeDistanceFactor; + if (center.Layer != PlanetLayer.Selected) + { + realCenterTile = PlanetLayer.Selected.GetClosestTile_NewTemp(center); + } + realRadius = Mathf.FloorToInt(radius / rangeDistanceFactor); + + // Add result to cache + radiusCache.Add(center.tileId, new RadiusCache() { + realCenterTile = realCenterTile, + radius = realRadius + }); } GenDraw.DrawWorldRadiusRing(realCenterTile, realRadius); } diff --git a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs index 8b4cbf7010..86e9ad2ca8 100755 --- a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs +++ b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs @@ -47,6 +47,7 @@ public class Building_TurretGunCE : Building_Turret private CompAmmoUser compAmmo = null; private CompFireModes compFireModes = null; private CompChangeableProjectile compChangeable = null; + private CompOrbitalTurret compOrbitalTurret = null; public bool isReloading = false; private int ticksUntilAutoReload = 0; private bool everSpawned = false; @@ -153,6 +154,19 @@ public CompFireModes CompFireModes return compFireModes; } } + + public CompOrbitalTurret CompOrbitalTurret + { + get + { + if (compOrbitalTurret == null && Gun != null) + { + compOrbitalTurret = Gun.TryGetComp(); + } + return compOrbitalTurret; + } + } + private ProjectilePropertiesCE ProjectileProps => (ProjectilePropertiesCE)Projectile?.projectile; public float MaxWorldRange => ProjectileProps?.shellingProps.range ?? -1f; public bool EmptyMagazine => CompAmmo?.EmptyMagazine ?? false; diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs index 4f52200a90..f0905c321d 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs @@ -704,7 +704,7 @@ public virtual ShiftVecReport ShiftVecReportFor(GlobalTargetInfo target) return null; } // multiplie by 250 to emulate cells - int distanceToTarget = Find.WorldGrid.TraversalDistanceBetween(target.Tile, caster.Map.Tile, true); + int distanceToTarget = Find.WorldGrid.TraversalDistanceBetween(target.Tile, caster.Map.Tile, true, int.MaxValue, true); LocalTargetInfo localTarget = new LocalTargetInfo(); localTarget.cellInt = target.Cell; From aa98f67e24e55fc29595708059561e2ebf0eb104 Mon Sep 17 00:00:00 2001 From: Haecriver Date: Sat, 21 Mar 2026 16:22:57 +0100 Subject: [PATCH 05/10] add a factor when shelling between layers --- .../Comps/CompProperties_OrbitalTurret.cs | 3 +-- .../CombatExtended/ShellingUtility.cs | 8 +------- .../CombatExtended/Verbs/Verb_ShootMortarCE.cs | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs index e5861ce196..484540cf07 100644 --- a/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs +++ b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs @@ -5,8 +5,7 @@ namespace CombatExtended; public class CompProperties_OrbitalTurret: CompProperties { - public float precisionBonusFactor = 20; - public float rangeBonusFactor = 20; + public float interLayerPrecisionBonusFactor = 1; public bool isMarkMandatory = false; public CompProperties_OrbitalTurret() diff --git a/Source/CombatExtended/CombatExtended/ShellingUtility.cs b/Source/CombatExtended/CombatExtended/ShellingUtility.cs index 0155be97b4..0f2c2a0b8d 100755 --- a/Source/CombatExtended/CombatExtended/ShellingUtility.cs +++ b/Source/CombatExtended/CombatExtended/ShellingUtility.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using RimWorld; using RimWorld.Planet; using UnityEngine; using Verse; -using CombatExtended.WorldObjects; namespace CombatExtended; public static class ShellingUtility @@ -31,10 +29,6 @@ public static int GetDistancePlanetTiles(PlanetTile startingTile, PlanetTile des return distanceCache.distance; } - if (startingTile.layerId != destinationTile.layerId) - { - // startingTile = destinationTile.Layer.GetClosestTile_NewTemp(startingTile); - } distanceCache.startingTile = startingTile; distanceCache.destinationTile = destinationTile; diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs index b201887a3b..8b3ebb550b 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs @@ -112,7 +112,20 @@ public override ShiftVecReport ShiftVecReportFor(GlobalTargetInfo target) return null; } ShiftVecReport report = base.ShiftVecReportFor(target); - report.circularMissRadius = GetGlobalMissRadiusForDist(report.shotDist); + + float shotDist = report.shotDist; + + // Shelling across layers + if (globalSourceInfo.Tile.Layer != globalTargetInfo.Tile.Layer) + { + CompOrbitalTurret compOrbitalTurret = caster.TryGetComp(); + if (compOrbitalTurret != null && compOrbitalTurret.Props.interLayerPrecisionBonusFactor != 0) + { + shotDist = shotDist / compOrbitalTurret.Props.interLayerPrecisionBonusFactor; + } + } + + report.circularMissRadius = GetGlobalMissRadiusForDist(shotDist); report.weatherShift = (1f - globalTargetInfo.Map.weatherManager.CurWeatherAccuracyMultiplier) * 1.5f + (1 - globalSourceInfo.Map.weatherManager.CurWeatherAccuracyMultiplier) * 0.5f; ArtilleryMarker marker = null; From 4ed72d9c9a97f94c029c935d865d90627b244d91 Mon Sep 17 00:00:00 2001 From: Haecriver Date: Fri, 8 May 2026 14:56:49 +0200 Subject: [PATCH 06/10] fix sln --- Source/CombatExtended.sln | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/CombatExtended.sln b/Source/CombatExtended.sln index 44b005dce1..f4d1d1bec1 100644 --- a/Source/CombatExtended.sln +++ b/Source/CombatExtended.sln @@ -180,8 +180,7 @@ Global {6B518120-4315-4017-B4E6-7DA8ABCCFF84}.Release|x64.Build.0 = Release|Any CPU {6B518120-4315-4017-B4E6-7DA8ABCCFF84}.Release|x86.ActiveCfg = Release|Any CPU {6B518120-4315-4017-B4E6-7DA8ABCCFF84}.Release|x86.Build.0 = Release|Any CPU - - {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Debug|x64.ActiveCfg = Debug|Any CPU {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Debug|x64.Build.0 = Debug|Any CPU @@ -199,7 +198,6 @@ Global {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Release|x64.Build.0 = Release|Any CPU {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Release|x86.ActiveCfg = Release|Any CPU {3A259010-BDE3-35B1-1369-2A44C34FE8F0}.Release|x86.Build.0 = Release|Any CPU - {181DADED-1C60-457A-92FC-FA8482D81117}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {181DADED-1C60-457A-92FC-FA8482D81117}.Debug|Any CPU.Build.0 = Debug|Any CPU {181DADED-1C60-457A-92FC-FA8482D81117}.Debug|x64.ActiveCfg = Debug|Any CPU From b9e7c26622aee630cb984a9fbfea18d392c275c2 Mon Sep 17 00:00:00 2001 From: Haecriver Date: Fri, 8 May 2026 15:10:41 +0200 Subject: [PATCH 07/10] Fix merge --- .../CombatExtended/WorldObjects/TravelingRaycast.cs | 12 ++++++------ .../CombatExtended/WorldObjects/TravelingShell.cs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingRaycast.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingRaycast.cs index 11deaeefdb..bd28350833 100644 --- a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingRaycast.cs +++ b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingRaycast.cs @@ -12,15 +12,15 @@ public class TravelingRaycast : TravelingShell public float aperatureSize; public Thing equipement; - protected override void LaunchProjectile(IntVec3 sourceCell, LocalTargetInfo target, Map map, float shotSpeed = 20, float shotHeight = 200) + protected override void LaunchProjectile(IntVec3 sourceCell, LocalTargetInfo target, Map map) { - Vector3 source = new Vector3(sourceCell.x, shotHeight, sourceCell.z); + Vector3 source = new Vector3(sourceCell.x, arrivedShotHeight, sourceCell.z); Vector3 targetPos = target.Cell.ToVector3Shifted(); ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(shellDef); ProjectilePropertiesCE pprops = projectile.def.projectile as ProjectilePropertiesCE; float shotRotation = pprops.TrajectoryWorker.ShotRotation(pprops, source, targetPos); - float shotAngle = pprops.TrajectoryWorker.ShotAngle(pprops, source, targetPos, shotSpeed); + float shotAngle = pprops.TrajectoryWorker.ShotAngle(pprops, source, targetPos, arrivedShotSpeed); projectile.canTargetSelf = false; projectile.Position = sourceCell; @@ -34,14 +34,14 @@ protected override void LaunchProjectile(IntVec3 sourceCell, LocalTargetInfo tar return; } - float tsa = verbToUse.AdjustShotHeight(launcher, target, ref shotHeight); + float tsa = verbToUse.AdjustShotHeight(launcher, target, ref arrivedShotHeight); projectile.RayCast(launcher, verbToUse.verbProps, new Vector2(source.x, source.z), shotAngle, shotRotation, - shotHeight + tsa, - shotSpeed, + arrivedShotHeight + tsa, + arrivedShotSpeed, spreadDegrees, aperatureSize, null, diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs index 6da6d52114..2bbb08ccd1 100755 --- a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs +++ b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs @@ -41,12 +41,12 @@ public override float TilesPerTick { get => (shellDef.projectile as ProjectilePropertiesCE).shellingProps.tilesPerTick; } - public bool IsInstant => (shellDef.projectile as ProjectilePropertiesCE).isInstant; - + public override bool ExpandingIconFlipHorizontal { get => GenWorldUI.WorldToUIPosition(Start).x > GenWorldUI.WorldToUIPosition(End).x; } + public bool IsInstant => (shellDef.projectile as ProjectilePropertiesCE).isInstant; public override float ExpandingIconRotation From 35fffa006f203dd39830ebfc1002c00a166f781e Mon Sep 17 00:00:00 2001 From: Haecriver Date: Fri, 8 May 2026 15:15:02 +0200 Subject: [PATCH 08/10] run dotnet format --- .../Comps/CompProperties_OrbitalTurret.cs | 2 +- .../CombatExtended/CombatExtended/ShellingUtility.cs | 11 ++++++----- .../CombatExtended/Verbs/Verb_ShootMortarCE.cs | 2 +- .../CombatExtended/WorldObjects/TravelingShell.cs | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs index 484540cf07..96fecd4bbb 100644 --- a/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs +++ b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs @@ -3,7 +3,7 @@ namespace CombatExtended; -public class CompProperties_OrbitalTurret: CompProperties +public class CompProperties_OrbitalTurret : CompProperties { public float interLayerPrecisionBonusFactor = 1; public bool isMarkMandatory = false; diff --git a/Source/CombatExtended/CombatExtended/ShellingUtility.cs b/Source/CombatExtended/CombatExtended/ShellingUtility.cs index 0f2c2a0b8d..08eaaad68d 100755 --- a/Source/CombatExtended/CombatExtended/ShellingUtility.cs +++ b/Source/CombatExtended/CombatExtended/ShellingUtility.cs @@ -34,7 +34,7 @@ public static int GetDistancePlanetTiles(PlanetTile startingTile, PlanetTile des distanceCache.distance = (int)(Find.WorldGrid.TraversalDistanceBetween(startingTile, destinationTile, true, maxDist, true) * destinationTile.LayerDef.rangeDistanceFactor); - + return distanceCache.distance; } @@ -58,7 +58,7 @@ public static void CachedDrawTurretRadiusRing(PlanetTile center, int radius, boo // Try to find cache. bool cacheFound = radiusCache.TryGetValue(center.tileId, out cache); - + // If the result is not on the current layer, we need to recalculate it if (cacheFound && cache.realCenterTile.Layer != PlanetLayer.Selected) { @@ -84,13 +84,14 @@ public static void CachedDrawTurretRadiusRing(PlanetTile center, int radius, boo realCenterTile = center; float rangeDistanceFactor = PlanetLayer.Selected.Def.rangeDistanceFactor; if (center.Layer != PlanetLayer.Selected) - { + { realCenterTile = PlanetLayer.Selected.GetClosestTile_NewTemp(center); } realRadius = Mathf.FloorToInt(radius / rangeDistanceFactor); - + // Add result to cache - radiusCache.Add(center.tileId, new RadiusCache() { + radiusCache.Add(center.tileId, new RadiusCache() + { realCenterTile = realCenterTile, radius = realRadius }); diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs index 8b3ebb550b..ccffdf3d77 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs @@ -116,7 +116,7 @@ public override ShiftVecReport ShiftVecReportFor(GlobalTargetInfo target) float shotDist = report.shotDist; // Shelling across layers - if (globalSourceInfo.Tile.Layer != globalTargetInfo.Tile.Layer) + if (globalSourceInfo.Tile.Layer != globalTargetInfo.Tile.Layer) { CompOrbitalTurret compOrbitalTurret = caster.TryGetComp(); if (compOrbitalTurret != null && compOrbitalTurret.Props.interLayerPrecisionBonusFactor != 0) diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs index 2bbb08ccd1..d2c52a1fa7 100755 --- a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs +++ b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs @@ -41,12 +41,12 @@ public override float TilesPerTick { get => (shellDef.projectile as ProjectilePropertiesCE).shellingProps.tilesPerTick; } - + public override bool ExpandingIconFlipHorizontal { get => GenWorldUI.WorldToUIPosition(Start).x > GenWorldUI.WorldToUIPosition(End).x; } - + public bool IsInstant => (shellDef.projectile as ProjectilePropertiesCE).isInstant; public override float ExpandingIconRotation From c5812565658a2c28ce0dc8de7d610f6dc321ccc3 Mon Sep 17 00:00:00 2001 From: ViralReaction <136116069+ViralReaction@users.noreply.github.com> Date: Mon, 1 Jun 2026 19:41:55 -0600 Subject: [PATCH 09/10] Update VEFCompat Package Reference --- Source/VEFCompat/VEFCompat.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VEFCompat/VEFCompat.csproj b/Source/VEFCompat/VEFCompat.csproj index 48af831681..40a8fdfdc1 100644 --- a/Source/VEFCompat/VEFCompat.csproj +++ b/Source/VEFCompat/VEFCompat.csproj @@ -49,7 +49,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 4479c82b40e8cb25c9256b729ab736e554b0ed2d Mon Sep 17 00:00:00 2001 From: Haecriver Date: Thu, 4 Jun 2026 19:10:05 +0200 Subject: [PATCH 10/10] Change CompOrbitalTurret for a OrbitalTurretExtension --- .../CombatExtended/Comps/CompOrbitalTurret.cs | 8 -------- .../Comps/CompProperties_OrbitalTurret.cs | 15 --------------- .../CombatExtended/Defs/OrbitalTurretExtension.cs | 9 +++++++++ .../Gizmos/Command_ArtilleryTarget.cs | 4 ++-- .../CombatExtended/Things/Building_TurretGunCE.cs | 10 +++++----- .../CombatExtended/Verbs/Verb_ShootMortarCE.cs | 7 ++++--- 6 files changed, 20 insertions(+), 33 deletions(-) delete mode 100644 Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs delete mode 100644 Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs create mode 100644 Source/CombatExtended/CombatExtended/Defs/OrbitalTurretExtension.cs diff --git a/Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs b/Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs deleted file mode 100644 index b4ad17c756..0000000000 --- a/Source/CombatExtended/CombatExtended/Comps/CompOrbitalTurret.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Verse; - -namespace CombatExtended; - -public class CompOrbitalTurret : ThingComp -{ - public CompProperties_OrbitalTurret Props => (CompProperties_OrbitalTurret)props; -} diff --git a/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs deleted file mode 100644 index 96fecd4bbb..0000000000 --- a/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs +++ /dev/null @@ -1,15 +0,0 @@ - -using Verse; - -namespace CombatExtended; - -public class CompProperties_OrbitalTurret : CompProperties -{ - public float interLayerPrecisionBonusFactor = 1; - public bool isMarkMandatory = false; - - public CompProperties_OrbitalTurret() - { - compClass = typeof(CompOrbitalTurret); - } -} diff --git a/Source/CombatExtended/CombatExtended/Defs/OrbitalTurretExtension.cs b/Source/CombatExtended/CombatExtended/Defs/OrbitalTurretExtension.cs new file mode 100644 index 0000000000..e3107933c7 --- /dev/null +++ b/Source/CombatExtended/CombatExtended/Defs/OrbitalTurretExtension.cs @@ -0,0 +1,9 @@ +using Verse; + +namespace CombatExtended; + +public class OrbitalTurretExtension : DefModExtension +{ + public float interLayerPrecisionBonusFactor = 1; + public bool isMarkMandatory = false; +} diff --git a/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs index c623e0f1b2..7200c233c7 100755 --- a/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs +++ b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs @@ -19,12 +19,12 @@ public class Command_ArtilleryTarget : Command #region Properties - bool CanShootOtherLayers => turret.CompOrbitalTurret != null; + bool CanShootOtherLayers => turret.OrbitalTurretExtension != null; ///// ///// When firing on orbital targets, it can be tricky to use binoculars ... ///// This disables the need of target mark. ///// - bool MandatoryMarkToFireOutBounds => turret.CompOrbitalTurret?.Props.isMarkMandatory ?? true; + bool MandatoryMarkToFireOutBounds => turret.OrbitalTurretExtension?.isMarkMandatory ?? true; public IEnumerable SelectedTurrets => others?.Select(o => o.turret) ?? new List() { turret }; diff --git a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs index f8c38ab8de..5589735be0 100755 --- a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs +++ b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs @@ -47,7 +47,7 @@ public class Building_TurretGunCE : Building_Turret private CompAmmoUser compAmmo = null; private CompFireModes compFireModes = null; private CompChangeableProjectile compChangeable = null; - private CompOrbitalTurret compOrbitalTurret = null; + private OrbitalTurretExtension orbitalTurretExtension = null; public bool isReloading = false; private int ticksUntilAutoReload = 0; private bool everSpawned = false; @@ -158,15 +158,15 @@ public CompFireModes CompFireModes } } - public CompOrbitalTurret CompOrbitalTurret + public OrbitalTurretExtension OrbitalTurretExtension { get { - if (compOrbitalTurret == null && Gun != null) + if (orbitalTurretExtension == null && Gun != null) { - compOrbitalTurret = Gun.TryGetComp(); + orbitalTurretExtension = Gun.def.GetModExtension(); } - return compOrbitalTurret; + return orbitalTurretExtension; } } diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs index ccffdf3d77..643b08df32 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs @@ -2,6 +2,7 @@ using RimWorld; using RimWorld.Planet; using UnityEngine; +using VEF; using Verse; using Verse.Sound; @@ -118,10 +119,10 @@ public override ShiftVecReport ShiftVecReportFor(GlobalTargetInfo target) // Shelling across layers if (globalSourceInfo.Tile.Layer != globalTargetInfo.Tile.Layer) { - CompOrbitalTurret compOrbitalTurret = caster.TryGetComp(); - if (compOrbitalTurret != null && compOrbitalTurret.Props.interLayerPrecisionBonusFactor != 0) + OrbitalTurretExtension orbitalTurretExtension = caster.def.GetModExtension(); + if (orbitalTurretExtension != null && orbitalTurretExtension.interLayerPrecisionBonusFactor != 0) { - shotDist = shotDist / compOrbitalTurret.Props.interLayerPrecisionBonusFactor; + shotDist = shotDist / orbitalTurretExtension.interLayerPrecisionBonusFactor; } }