@@ -38,6 +38,10 @@ public class SmudgeLayerInfo : TraitInfo
 		[Desc("Chance of smoke rising from the ground")]
 		public readonly int SmokeChance = 0;
 
+		[Desc("By how much (in each direction) can the smoke appearance offset stray from the center of the cell?",
+			"Note: Limit this to half a cell for square and 1/3 a cell for isometric cells to avoid straying into neighbour cells.")]
+		public readonly WDist MaxSmokeOffsetDistance = WDist.Zero;
+
 		[Desc("Smoke sprite image name")]
 		public readonly string SmokeImage = null;
 
@@ -152,8 +156,19 @@ public void AddSmudge(CPos loc)
 				return;
 
 			if (hasSmoke && Game.CosmeticRandom.Next(0, 100) <= Info.SmokeChance)
+			{
+				var maxOffsetDistance = Info.MaxSmokeOffsetDistance.Length;
+
+				// TODO: With offset added height is not accurate for TS maps
+				var position = world.Map.CenterOfCell(loc);
+				if (maxOffsetDistance != 0)
+					position += new WVec(Game.CosmeticRandom.Next(-maxOffsetDistance, maxOffsetDistance),
+										 Game.CosmeticRandom.Next(-maxOffsetDistance, maxOffsetDistance),
+										 0);
+
 				world.AddFrameEndTask(w => w.Add(new SpriteEffect(
-					w.Map.CenterOfCell(loc), w, Info.SmokeImage, Info.SmokeSequences.Random(w.SharedRandom), Info.SmokePalette)));
+					position, w, Info.SmokeImage, Info.SmokeSequences.Random(Game.CosmeticRandom), Info.SmokePalette)));
+			}
 
 			// A null Sequence indicates a deleted smudge.
 			if ((!dirty.ContainsKey(loc) || dirty[loc].Sequence == null) && !tiles.ContainsKey(loc))