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/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/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..484540cf07
--- /dev/null
+++ b/Source/CombatExtended/CombatExtended/Comps/CompProperties_OrbitalTurret.cs
@@ -0,0 +1,15 @@
+
+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/Gizmos/Command_ArtilleryTarget.cs b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs
index 08f0142ec4..c623e0f1b2 100755
--- a/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs
+++ b/Source/CombatExtended/CombatExtended/Gizmos/Command_ArtilleryTarget.cs
@@ -1,22 +1,39 @@
-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
{
+ #region Fields
+
public Building_TurretGunCE turret;
public List others = null;
+ #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;
@@ -28,6 +45,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 +65,227 @@ 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()
+ // Check additionnal condition
+ if (!AdditionnalTargettingCondition(targetInfo))
{
- canTargetLocations = true,
- canTargetBuildings = true,
- canTargetHumans = true
- }, (target) =>
+ return false;
+ }
+
+ IEnumerable turrets = SelectedTurrets;
+ Map map = Find.World.worldObjects.MapParentAt(targetInfo.Tile)?.Map ?? null;
+
+ if (!CanShootOtherLayers && targetInfo.Tile.Layer != turretTile.Layer)
{
- targetInfo.mapInt = map;
- targetInfo.tileInt = map.Tile;
- targetInfo.cellInt = target.cellInt;
- //targetInfo.thingInt = target.thingInt;
- TryAttack(turrets, targetInfo, target);
- }, highlightAction: (target) =>
+ 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))
{
- 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, CanShootOtherLayers);
+ }
}
- else
+ }
+ ShellingUtility.CachedDrawTurretRadiusRing(turretTile, radius, CanShootOtherLayers);
+ },
+ 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
- {
- 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)
+ return distanceMessage + "\n" + "CE_ArtilleryTarget_ClickToOrderAttack".Translate() + extra;
+ },
+ canSelectTarget: (targetInfo) =>
{
- 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()
{
- if (!targetInfo.HasWorldObject || targetInfo.Tile == turretTile ||
- (targetInfo.WorldObject as MapParent)?.Map == null &&
- targetInfo.WorldObject.GetComponent() == null)
+ 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) =>
+ {
+ 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);
}
+ #endregion
}
diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index e8d917f056..33d5130861 100755
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -1287,27 +1287,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;
@@ -1364,6 +1344,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..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
@@ -16,6 +14,90 @@ 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;
+ }
+
+ distanceCache.startingTile = startingTile;
+ distanceCache.destinationTile = destinationTile;
+
+ distanceCache.distance =
+ (int)(Find.WorldGrid.TraversalDistanceBetween(startingTile, destinationTile, true, maxDist, true) * 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, bool canShootOtherLayers = false)
+ {
+ PlanetTile realCenterTile;
+ int realRadius;
+ RadiusCache 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
+ {
+ // 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);
+ }
+
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 6f5b00781e..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,7 +154,20 @@ public CompFireModes CompFireModes
return compFireModes;
}
}
- private ProjectilePropertiesCE ProjectileProps => (ProjectilePropertiesCE)compAmmo?.CurAmmoProjectile?.projectile ?? null;
+
+ 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;
public bool FullMagazine => CompAmmo?.FullMagazine ?? false;
@@ -672,7 +686,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 +708,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();
@@ -740,7 +754,7 @@ public override IEnumerable GetGizmos() // Modified
yield return com;
}
}
- if (IsMortar && Active && Faction.IsPlayerSafe() && (compAmmo?.UseAmmo ?? false) && ProjectileProps?.shellingProps != null)
+ if (IsMortar && Active && Faction.IsPlayerSafe() && ProjectileProps?.shellingProps != null)
{
Command_ArtilleryTarget wt = new Command_ArtilleryTarget()
{
diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs
index 4081b39cee..30c2ee85df 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;
diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs
index f0433f3438..30132c6cca 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;
@@ -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;
diff --git a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShell.cs
index 8f82c4d961..16ece7b221 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
{
@@ -96,7 +99,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))
{
@@ -125,11 +128,7 @@ private bool TryShell(WorldObject worldObject)
Bounds mapBounds = new Bounds((mapSize / 2f).Yto0(), mapSize);
mapBounds.IntersectRay(ray, out float distanceToEdge);
IntVec3 sourceCell = ray.GetPoint(distanceToEdge * 0.75f).ToIntVec3();
- LaunchProjectile(
- sourceCell,
- targetCell,
- map: map,
- shotSpeed: 55f);
+ LaunchProjectile(sourceCell, targetCell, map);
}
WorldObjects.HostilityComp hostility = worldObject.GetComponent();
WorldObjects.HealthComp healthComp = worldObject.GetComponent();
@@ -148,20 +147,20 @@ private bool TryShell(WorldObject worldObject)
return shelled;
}
- private void LaunchProjectile(IntVec3 sourceCell, LocalTargetInfo target, Map map, float shotSpeed = 20, float shotHeight = 200)
+ private 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/TravelingShellProperties.cs b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShellProperties.cs
index 81a5583459..5252710f6c 100755
--- a/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShellProperties.cs
+++ b/Source/CombatExtended/CombatExtended/WorldObjects/TravelingShellProperties.cs
@@ -23,6 +23,11 @@ public class TravelingShellProperties
///
public string iconPath;
+ ///
+ /// Projectile arrive at the same height and same speed they're launched.
+ ///
+ public bool arrivedAtSameProps = false;
+
public Type workerClass = typeof(WorldObjectDamageWorker);
public WorldObjectDamageWorker Worker
{
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;
}
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/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 @@
-
+
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
-
+