diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index ec29884e5c..fb3be8f6c2 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -775,6 +775,13 @@ namespace OpenRA return CenterOfCell(cell); } + public WDist DistanceAboveTerrain(WPos pos) + { + var cell = CellContaining(pos); + var delta = pos - CenterOfCell(cell); + return new WDist(delta.Z); + } + public CPos CellContaining(WPos pos) { if (TileShape == TileShape.Rectangle) diff --git a/OpenRA.Mods.Common/ActorExts.cs b/OpenRA.Mods.Common/ActorExts.cs index 4d20e87e23..6ea703db00 100644 --- a/OpenRA.Mods.Common/ActorExts.cs +++ b/OpenRA.Mods.Common/ActorExts.cs @@ -18,6 +18,25 @@ namespace OpenRA.Mods.Common { public static class ActorExts { + public static bool IsAtGroundLevel(this Actor self) + { + if (self.IsDead) + return false; + + if (!self.HasTrait()) + return false; + + if (!self.IsInWorld) + return false; + + var map = self.World.Map; + + if (!map.Contains(self.Location)) + return false; + + return map.DistanceAboveTerrain(self.CenterPosition).Length == 0; + } + public static bool AppearsFriendlyTo(this Actor self, Actor toActor) { var stance = toActor.Owner.Stances[self.Owner]; diff --git a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs index 06e9375541..48b2bd31e9 100644 --- a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs @@ -104,8 +104,7 @@ namespace OpenRA.Mods.Common.Graphics public void Render(WorldRenderer wr) { - // TODO: This is a temporary workaround until we have a proper ramp-aware height calculation - var groundPos = wr.World.Map.CenterOfCell(wr.World.Map.CellContaining(voxel.pos)); + var groundPos = voxel.pos - new WVec(0, 0, wr.World.Map.DistanceAboveTerrain(voxel.pos).Length); var ts = Game.ModData.Manifest.TileSize; var groundZ = ts.Height * (groundPos.Z - voxel.pos.Z) / 1024f; diff --git a/OpenRA.Mods.Common/Traits/Cargo.cs b/OpenRA.Mods.Common/Traits/Cargo.cs index 598901f189..2dab3f15ad 100644 --- a/OpenRA.Mods.Common/Traits/Cargo.cs +++ b/OpenRA.Mods.Common/Traits/Cargo.cs @@ -171,7 +171,7 @@ namespace OpenRA.Mods.Common.Traits public bool CanLoad(Actor self, Actor a) { - return (reserves.Contains(a) || HasSpace(GetWeight(a))) && self.CenterPosition.Z == 0; + return (reserves.Contains(a) || HasSpace(GetWeight(a))) && self.IsAtGroundLevel(); } internal bool ReserveSpace(Actor a) diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index 0a5dcdbb98..d691e57d09 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -189,11 +189,8 @@ namespace OpenRA.Mods.Common.Traits public bool CrushableBy(string[] crushClasses, Player owner) { - // Crate can only be crushed if it is not in the air or underground - if (CenterPosition.Z != 0) - return false; - - return crushClasses.Contains(info.CrushClass); + // Crate can only be crushed if it is not in the air. + return self.IsAtGroundLevel() && crushClasses.Contains(info.CrushClass); } public void AddedToWorld(Actor self) diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index 41e90f6da8..44a5e84dc3 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -65,8 +65,8 @@ namespace OpenRA.Mods.Common.Traits public bool CrushableBy(string[] crushClasses, Player crushOwner) { - // Only make actor crushable if it is on the ground - if (self.CenterPosition.Z != 0) + // Only make actor crushable if it is on the ground. + if (!self.IsAtGroundLevel()) return false; if (!info.CrushedByFriendlies && crushOwner.IsAlliedWith(self.Owner)) diff --git a/OpenRA.Mods.Common/Traits/EjectOnDeath.cs b/OpenRA.Mods.Common/Traits/EjectOnDeath.cs index da94d94546..fd9f9bab40 100644 --- a/OpenRA.Mods.Common/Traits/EjectOnDeath.cs +++ b/OpenRA.Mods.Common/Traits/EjectOnDeath.cs @@ -65,7 +65,8 @@ namespace OpenRA.Mods.Common.Traits return; var cp = self.CenterPosition; - if ((cp.Z > 0 && !info.EjectInAir) || (cp.Z == 0 && !info.EjectOnGround)) + var inAir = !self.IsAtGroundLevel(); + if ((inAir && !info.EjectInAir) || (!inAir && !info.EjectOnGround)) return; var pilot = self.World.CreateActor(false, info.PilotActor.ToLowerInvariant(), @@ -73,7 +74,7 @@ namespace OpenRA.Mods.Common.Traits if (info.AllowUnsuitableCell || IsSuitableCell(self, pilot)) { - if (cp.Z > 0) + if (inAir) { self.World.AddFrameEndTask(w => { diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index d6efce25a8..038801cd8a 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -565,7 +565,7 @@ namespace OpenRA.Mods.Common.Traits public void FinishedMoving(Actor self) { // Only make actor crush if it is on the ground - if (self.CenterPosition.Z != 0) + if (!self.IsAtGroundLevel()) return; var crushables = self.World.ActorMap.GetUnitsAt(ToCell).Where(a => a != self)