Decompiled source of LivingMap v0.19.2

LivingMap.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace LivingMap;

[BepInPlugin("j1ga.livingmap", "Living Map", "0.19.2")]
public class LivingMapPlugin : BaseUnityPlugin
{
	private struct PieceRec
	{
		public float X0;

		public float Z0;

		public float X1;

		public float Z1;

		public float Y;

		public byte Mat;

		public float CX => (X0 + X1) * 0.5f;

		public float CZ => (Z0 + Z1) * 0.5f;
	}

	private class DTile
	{
		public int Level;

		public int TX;

		public int TZ;

		public long Key;

		public Texture2D Tex;

		public RawImage Img;

		public bool Ready;

		public bool Queued;

		public bool Failed;

		public long LightSig;

		public byte[] Pending;

		public byte[] Pending2;

		public long PendingLight;

		public Texture2D Tex2;

		public RawImage Img2;

		public long Sig;

		public float RenderedAt;

		public float LastUsed;
	}

	private class HeightZone
	{
		public float Cx;

		public float Cz;

		public float Scale;

		public int Pitch;

		public int Half;

		public float[] D;
	}

	private struct DTree
	{
		public float X;

		public float Z;

		public float R;

		public bool Rock;
	}

	private struct DBox
	{
		public float X;

		public float Z;

		public float HX;

		public float HZ;

		public float Cos;

		public float Sin;

		public float Top;

		public byte Mat;

		public bool Roof;

		public bool Ridge;

		public float NX;

		public float NY;

		public float NZ;
	}

	private class DJob
	{
		public DTile Tile;

		public float X0;

		public float Z0;

		public float Mpp;

		public float Priority;

		public float WaterLevel;

		public float Relief;

		public float ForestShade;

		public float ZoneSize;

		public Color32 WaterShallow;

		public Color32 WaterDeep;

		public Color32 Tree;

		public Color32 Rock;

		public Color32[] Biome;

		public float[] Fog;

		public int FogX0;

		public int FogZ0;

		public int FogW;

		public int FogH;

		public float PixelSize;

		public int TexSize;

		public sbyte[] ForestFix;

		public List<PieceRec> Pieces;

		public List<DBox> Boxes;

		public List<DTree> Trees;

		public HashSet<long> LiveZones;

		public Dictionary<long, HeightZone> Heights;

		public Color32[] MatColors;

		public Color32 MatUnknown;

		public Color32 Outline;

		public bool DrawOutline;

		public List<long> TerrKeys;

		public List<byte> TerrKinds;

		public float Grid;

		public float PathWidth;

		public Color32 Paved;

		public Color32 Dirt;

		public Color32 Cultivated;

		public Color32 Cleared;

		public long Sig;

		public PortJob Port;

		public long LightSig;

		public bool Relight;

		public byte[] Pixels;

		public byte[] Pixels2;

		public double Ms;
	}

	private sealed class HostTile
	{
		public long Hash;

		public byte[] Data;
	}

	private sealed class TileData
	{
		public readonly List<PieceRec> Pieces = new List<PieceRec>();

		public readonly List<KeyValuePair<long, byte>> Terrain = new List<KeyValuePair<long, byte>>();

		public readonly List<int> Cleared = new List<int>();

		public readonly List<int> Planted = new List<int>();
	}

	private sealed class MirrorTile
	{
		public long Hash;

		public TileData Data;

		public bool Hidden;

		public bool Pending;
	}

	private class PortTex
	{
		public int Levels;

		public int[] W;

		public int[] H;

		public float[][] R;

		public float[][] G;

		public float[][] B;

		public float[][] A;

		public void Sample(float u, float v, float lod, out float r, out float g, out float b, out float a)
		{
			int num = Mathf.Clamp((int)(lod + 0.5f), 0, Levels - 1);
			int num2 = W[num];
			int num3 = H[num];
			float num4 = u * (float)num2 - 0.5f;
			float num5 = v * (float)num3 - 0.5f;
			int num6 = Mathf.FloorToInt(num4);
			int num7 = Mathf.FloorToInt(num5);
			float num8 = num4 - (float)num6;
			float num9 = num5 - (float)num7;
			num6 %= num2;
			if (num6 < 0)
			{
				num6 += num2;
			}
			num7 %= num3;
			if (num7 < 0)
			{
				num7 += num3;
			}
			int num10 = ((num6 + 1 != num2) ? (num6 + 1) : 0);
			int num11 = ((num7 + 1 != num3) ? (num7 + 1) : 0);
			int num12 = num7 * num2 + num6;
			int num13 = num7 * num2 + num10;
			int num14 = num11 * num2 + num6;
			int num15 = num11 * num2 + num10;
			float num16 = (1f - num8) * (1f - num9);
			float num17 = num8 * (1f - num9);
			float num18 = (1f - num8) * num9;
			float num19 = num8 * num9;
			float[] array = R[num];
			float[] array2 = G[num];
			float[] array3 = B[num];
			float[] array4 = A[num];
			r = array[num12] * num16 + array[num13] * num17 + array[num14] * num18 + array[num15] * num19;
			g = array2[num12] * num16 + array2[num13] * num17 + array2[num14] * num18 + array2[num15] * num19;
			b = array3[num12] * num16 + array3[num13] * num17 + array3[num14] * num18 + array3[num15] * num19;
			a = array4[num12] * num16 + array4[num13] * num17 + array4[num14] * num18 + array4[num15] * num19;
		}

		public float Lod(float repeat, float mpp, float world)
		{
			float num = repeat * (float)W[0] * mpp / world;
			if (!(num <= 1f))
			{
				return Mathf.Log(num, 2f);
			}
			return 0f;
		}
	}

	private class PortAssets
	{
		public PortTex Background;

		public PortTex FogLayer;

		public PortTex Water;

		public PortTex Lava;

		public PortTex Mountain;

		public PortTex Cloud;

		public PortTex Forest;

		public Texture2D CloudTex;
	}

	private class PortJob
	{
		public PortAssets A;

		public float World;

		public int TexSize;

		public Color Forest;

		public Color Water;

		public Color WaterDeep;

		public Color WaterAsh;

		public Color WaterAshDeep;

		public Color SunFog;

		public Color Light;

		public Color Ambient;

		public Color Sun;

		public Color AmbientG;

		public Color Lava1;

		public Color Lava2;

		public Vector3 LightDir;

		public float NormalWidth;

		public float NormalIntensity;

		public float Zoom;

		public float SharedFade;

		public float CloudX;

		public float CloudZ;

		public float TimeX;

		public float TimeY;

		public float Cells;

		public bool CellPerPixel;

		public bool Clouds;

		public Color32[] Mat;

		public Color32 MatUnknown;

		public Color32 Outline;

		public Color32 Paved;

		public Color32 Dirt;

		public Color32 Cultivated;

		public Color32 Cleared;

		public bool DrawOutline;

		public bool Styled;

		public int X0;

		public int Z0;

		public int W;

		public int H;

		public float[] Height;

		public float[] MainR;

		public float[] MainG;

		public float[] MainB;

		public float[] MaskX;

		public float[] MaskY;

		public float[] MaskZ;

		public float[] FogX;

		public float[] FogY;

		public int MainScale = 1;

		public int MainX0;

		public int MainZ0;

		public int MainW;

		public int MainH;

		public bool Classic;
	}

	private class PortDetail
	{
		public byte[] Main;

		public float[] MaskX;

		public byte[] Ground;

		public float[] H;

		public int Gs;

		public float Half;

		public byte[] Kind;

		public short[] Box;
	}

	private struct PrefabInfo
	{
		public bool IsPiece;

		public bool IsTree;

		public bool IsRock;

		public bool IsTerrain;

		public byte Mat;

		public Vector3 Min;

		public Vector3 Max;

		public bool Roof;

		public bool Ridge;

		public Vector3 RoofN;
	}

	private const byte LayerBuildings = 1;

	private const byte LayerPaths = 2;

	private const byte LayerForest = 4;

	private const float DefaultResetRadius = 100f;

	private const string SecGeneral = "01 General";

	private const string SecLayers = "02 Layers";

	private const string SecScanning = "03 Scanning";

	private const string SecRendering = "04 Rendering";

	private const string SecColors = "05 Colors";

	private const string SecAdvanced = "06 Advanced";

	private const string SecDebug = "07 Debug";

	private const int GameMatCount = 9;

	private const byte MatRoofThatch = 9;

	private const byte MatRoofDarkwood = 10;

	private const byte MatRoofTurf = 11;

	private const byte MatRoofSlate = 12;

	private const string UiLayers = "1. Map layers";

	private const string UiDetail = "2. Close-up detail";

	private const string UiColours = "3. Colours";

	private const string UiShare = "4. Multiplayer";

	private const string UiAdvanced = "5. Advanced";

	private const string UiDebug = "6. Debug";

	public const string Guid = "j1ga.livingmap";

	public const string Name = "Living Map";

	public const string Version = "0.19.2";

	private const byte MatNone = byte.MaxValue;

	private const byte TerrainNone = 0;

	private const byte TerrainCultivated = 1;

	private const byte TerrainPaved = 2;

	private const byte TerrainDirt = 3;

	private const byte TerrainCleared = 4;

	private const float RefogInterval = 15f;

	private const float MaxFootprint = 32f;

	private const int MaxErrors = 25;

	private const int MaxTerrainErrors = 10;

	internal const float FlatPieceDamp = 0.85f;

	private const string OldGuid = "j1ga.mapoverlay";

	private const string OldFolder = "MapOverlay";

	private const string SecDetail = "08 Detail";

	private const float LegacyRelief = 1f;

	private const float LegacyForestShade = 0.35f;

	private const float LegacyToneDesaturate = 0.5f;

	private const float LegacySharedOpacity = 0.5f;

	private const int DTileSize = 256;

	private const int DVisibleLimit = 64;

	private const int DUploadsPerFrame = 2;

	private const int DEnqueuesPerFrame = 2;

	private const float DRedrawMinAge = 5f;

	private const float DObjectsMaxMpp = 2.9f;

	private const float DPrefetchPriority = 1E+09f;

	private const float DPrefetchAge = 30f;

	private const int DStep = 2;

	private const int ForestZonesPerFrame = 150;

	private const int BiomeLookupsPerFrame = 600;

	private const int MaxIncrementalPieces = 1500;

	private const float MapRebuildInterval = 0.5f;

	private const float HeightEditMin = 0.5f;

	private const int HeightSamples = 3;

	private const string HighOff = "Off";

	private const string HighMinimap = "Minimap";

	private const string HighBoth = "Both";

	private const float HighGroundFrom = 70f;

	private const float WaterHidesBelow = 29.5f;

	private const string NoticeVersion = "0.16.0";

	private const float NoticeFontIncrease = 2f;

	private const BindingFlags MmFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

	private const float ScanMoveDelta = 8f;

	private const float ScanInterval = 1f;

	private const float IdleRescanInterval = 10f;

	private const int MaxColliders = 8192;

	private const string SecShare = "09 Multiplayer";

	private const int ShareProto = 1;

	private const float ShareTile = 128f;

	private const float ShareRefresh = 60f;

	private const float ShareReapply = 20f;

	private const int ShareReplyBytes = 204800;

	private const int ShareAskTiles = 128;

	private const int ShareTilesPerFrame = 64;

	private const string RpcHello = "LivingMap_Hello";

	private const string RpcIndex = "LivingMap_Index";

	private const string RpcGet = "LivingMap_Get";

	private const string RpcTiles = "LivingMap_Tiles";

	private const int MaxShareErrors = 4;

	private const float SaveInterval = 60f;

	private const int TerrainZonesPerFrame = 4;

	private const int MaxTcErrors = 10;

	private const string CloudsSmooth = "Smooth";

	private const string CloudsExact = "Exact";

	private const string CloudsOff = "Off";

	private const int MaxZdoErrors = 10;

	private ConfigEntry<bool> _cfgEnabled;

	private ConfigEntry<bool> _cfgPersist;

	private ConfigEntry<bool> _cfgShowBuildings;

	private ConfigEntry<bool> _cfgOnlyPlayerBuilt;

	private ConfigEntry<bool> _cfgShowPaths;

	private ConfigEntry<bool> _cfgShowCleared;

	private ConfigEntry<float> _cfgPathWidth;

	private ConfigEntry<bool> _cfgPathsClearForest;

	private ConfigEntry<bool> _cfgShowForest;

	private ConfigEntry<float> _cfgForestRadius;

	private ConfigEntry<int> _cfgForestMaxTrees;

	private ConfigEntry<bool> _cfgShowPlanted;

	private ConfigEntry<int> _cfgPlantedMinTrees;

	private ConfigEntry<bool> _cfgPlantedAnyBiome;

	private ConfigEntry<bool> _cfgRespectFog;

	private ConfigEntry<string> _cfgScanSource;

	private ConfigEntry<string> _cfgPathSource;

	private ConfigEntry<float> _cfgZdoInterval;

	private ConfigEntry<int> _cfgMapScale;

	private ConfigEntry<float> _cfgPieceSize;

	private ConfigEntry<bool> _cfgOutline;

	private ConfigEntry<float> _cfgOutlineWidth;

	private ConfigEntry<float> _cfgTerrainGrid;

	private ConfigEntry<Color>[] _cfgMatColor;

	private ConfigEntry<float> _cfgPaletteStrength;

	private ConfigEntry<Color> _cfgMatUnknownColor;

	private ConfigEntry<Color> _cfgCultivatedColor;

	private ConfigEntry<Color> _cfgPavedColor;

	private ConfigEntry<Color> _cfgDirtColor;

	private ConfigEntry<Color> _cfgClearedColor;

	private ConfigEntry<Color> _cfgOutlineColor;

	private ConfigEntry<int> _cfgZdoPerFrame;

	private ConfigEntry<float> _cfgScanRadius;

	private ConfigEntry<int> _cfgMaxPieces;

	private ConfigEntry<int> _cfgMaxTerrain;

	private ConfigEntry<bool> _cfgDebug;

	private ConfigEntry<bool> _cfgDebugMarker;

	private ConfigEntry<bool> _cfgIncremental;

	private ConfigEntry<bool> _cfgLinearFix;

	private ConfigEntry<bool> _cfgMapFlipY;

	private static readonly string[] MatNames = new string[13]
	{
		"Wood", "Stone", "Iron", "HardWood", "Marble", "Ashstone", "Ancient", "Ice", "Timberwood", "Roof_Thatch",
		"Roof_Darkwood", "Roof_Turf", "Roof_Slate"
	};

	private static readonly string[] DroppedSettings = new string[16]
	{
		"MapRebuildInterval", "MoveDelta", "ScanInterval", "IdleRescanInterval", "SaveInterval", "MaxColliders", "PaintMapTexture", "DetailedOverlay", "MinimapOverlay", "FlushInterval",
		"PixelsPerFlush", "OverlayOpacity", "SmoothOverlay", "MinimapOverlayResolution", "DetailedOverlayResolution", "DetailedOverlayInterval"
	};

	private readonly Dictionary<int, List<PieceRec>> _pieces = new Dictionary<int, List<PieceRec>>();

	private int _pieceCount;

	private readonly Dictionary<long, byte> _terrain = new Dictionary<long, byte>();

	private readonly List<int> _circleScratch = new List<int>();

	private readonly Dictionary<int, List<PieceRec>> _scanBuckets = new Dictionary<int, List<PieceRec>>();

	private readonly List<long> _terrainScratch = new List<long>();

	private Minimap _mm;

	private Texture2D _vanillaTex;

	private Texture _origSmallTex;

	private Texture _origLargeTex;

	private int _texSize;

	private float _pixelSize;

	private bool _ready;

	private Vector3 _lastScanPos;

	private float _nextScanTime;

	private float _lastScanTime;

	private float _nextSaveTime;

	private float _nextRefogTime;

	private FieldInfo _fiExplored;

	private FieldInfo _fiExploredOthers;

	private int _errorCount;

	private int _terrainErrorCount;

	private bool _disabledByErrors;

	private bool _terrainDisabled;

	private readonly HashSet<string> _loggedErrors = new HashSet<string>();

	private readonly HashSet<int> _pathForest = new HashSet<int>();

	private long _drawSig = -1L;

	private long _maskCfgSig = -1L;

	private bool _highWaterOn;

	private bool _heightOn;

	private float _nextSigCheck;

	private float _paletteApplied = 1f;

	private bool _oldPluginChecked;

	private ConfigEntry<bool> _cfgDetail;

	private ConfigEntry<float> _cfgDetailStartSpan;

	private ConfigEntry<float> _cfgDetailFinest;

	private ConfigEntry<bool> _cfgDetailObjects;

	private ConfigEntry<int> _cfgDetailWorkers;

	private ConfigEntry<int> _cfgDetailCache;

	private ConfigEntry<bool> _cfgDetailPrefetch;

	private static readonly Color LegacyWaterShallow = new Color(0.27f, 0.28f, 0.33f, 1f);

	private static readonly Color LegacyWaterDeep = new Color(0.22f, 0.24f, 0.3f, 1f);

	private static readonly Color LegacyTree = new Color(0.55f, 0.62f, 0.45f, 1f);

	private static readonly Color LegacyRock = new Color(0.5f, 0.5f, 0.48f, 1f);

	private static readonly Color LegacyToneTint = new Color(0.38f, 0.48f, 0.44f, 1f);

	private static float[] DLevels = new float[3] { 4f, 2f, 1f };

	private readonly Dictionary<long, DTile> _dTiles = new Dictionary<long, DTile>();

	private readonly HashSet<long> _dEverDrawn = new HashSet<long>();

	private int _dRedraws;

	private readonly List<DJob> _dPending = new List<DJob>();

	private readonly List<DJob> _dDone = new List<DJob>();

	private readonly object _dLock = new object();

	private Thread[] _dWorkers;

	private volatile bool _dStop;

	private bool _dBroken;

	private RectTransform _dRoot;

	private RectTransform _dBack;

	private RectTransform _dFront;

	private RectTransform _dFogBack;

	private RectTransform _dFogFront;

	private CanvasGroup _dGroup;

	private int _dRecheckCursor;

	private int _dEnqueuedThisFrame;

	private readonly List<DTile> _dScratch = new List<DTile>();

	private FieldInfo _fiMistColor;

	private FieldInfo _fiObjectsBySector;

	private int _dRendered;

	private double _dMsTotal;

	private double _dMsMax;

	private int _dSnaps;

	private double _dSnapMsTotal;

	private double _dSnapMsMax;

	private readonly Dictionary<long, HeightZone> _dHeights = new Dictionary<long, HeightZone>();

	private readonly Dictionary<long, int> _dZoneRev = new Dictionary<long, int>();

	private long _dHeightsWorld;

	private readonly Dictionary<int, byte> _dKind = new Dictionary<int, byte>();

	private RawImage _dClouds;

	private int _dShownLevel = -1;

	private bool _dViewCovered;

	private float _dShownNeed;

	private float _dShownAt = -100f;

	private long _dLight;

	private float _dLightNext;

	private FieldInfo _fiGeneratedZones;

	private BitArray _forestMask;

	private BitArray _pixelTrusted;

	private byte[] _treeCount;

	private readonly HashSet<int> _clearedForest = new HashSet<int>();

	private readonly HashSet<int> _trustedZones = new HashSet<int>();

	private readonly List<int> _trustedZoneScratch = new List<int>();

	private readonly List<int> _forestSample = new List<int>();

	private bool _forestDisabled;

	private int _forestCursor = -1;

	private bool _forestChanged;

	private int _forestAdded;

	private int _forestRemoved;

	private readonly HashSet<int> _plantedForest = new HashSet<int>();

	private readonly Dictionary<int, int> _outsideCounts = new Dictionary<int, int>();

	private readonly Dictionary<int, bool> _forestBiome = new Dictionary<int, bool>();

	private int _biomeLookupsThisFrame;

	private RenderTexture _rtMask;

	private Material _glMatMask;

	private Material _glMatMaskAdd;

	private Texture2D _vanillaMask;

	private Texture _origSmallMask;

	private Texture _origLargeMask;

	private RenderTexture _rt;

	private Material _glMat;

	private bool _gpuUnavailable;

	private bool _rtDirty;

	private bool _maskDirty;

	private readonly List<PieceRec> _addPieces = new List<PieceRec>();

	private readonly List<long> _addTerrain = new List<long>();

	private readonly List<PieceRec> _fogPieces = new List<PieceRec>();

	private readonly List<long> _fogTerrain = new List<long>();

	private readonly List<int> _fogForest = new List<int>();

	private readonly List<PieceRec> _groupScratch = new List<PieceRec>();

	private readonly List<PieceRec> _fillScratch = new List<PieceRec>();

	private int _rtIncrements;

	private bool _collectedCleared;

	private float _nextRtRebuild;

	private int _rtScale = 1;

	private int _rtRebuilds;

	private static readonly Comparison<PieceRec> ByTop = (PieceRec a, PieceRec b) => a.Y.CompareTo(b.Y);

	private static float MapPixelSize;

	private static bool LinearColors;

	private ConfigEntry<bool> _cfgHeightFromTerrain;

	private Texture2D _heightTex;

	private Texture _origSmallHeight;

	private Texture _origLargeHeight;

	private bool _heightDirty;

	private bool _heightUnavailable;

	private readonly Dictionary<int, float> _heightApplied = new Dictionary<int, float>();

	private ConfigEntry<string> _cfgHighOverlay;

	private ConfigEntry<float> _cfgHighStrength;

	private ConfigEntry<bool> _cfgHighWater;

	private RenderTexture _rtHigh;

	private Material _glMatHigh;

	private bool _highDirty;

	private bool _highUnavailable;

	private BitArray _highPixels;

	private BitArray _highMist;

	private BitArray _highWater;

	private RawImage _highSmall;

	private RawImage _highLarge;

	private Sprite _highCircle;

	private Color _highLight = Color.white;

	private int _highPieceCount;

	private int _highCellCount;

	private long _highSig = -1L;

	private readonly List<PieceRec> _highScratch = new List<PieceRec>();

	private readonly List<long> _highCells = new List<long>();

	private ConfigEntry<string> _cfgNoticeSeen;

	private bool _noticeDone;

	private TMP_Text _noticeBody;

	private float _noticeFont;

	private float _noticeFontMax;

	private static readonly FieldInfo s_fiPopupInstance = typeof(UnifiedPopup).GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo s_fiPopupBody = typeof(UnifiedPopup).GetField("bodyText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly string[] PortTestGlobals = new string[3] { "_SunColor", "_AmbientColor", "_SunFogColor" };

	private static readonly string[] ProbeTexNames = new string[4] { "_MainTex", "_HeightTex", "_MaskTex", "_FogTex" };

	private RawImage _probeImg;

	private Material _probeMat;

	private RenderTexture[] _probeRt;

	private Texture[] _probeSrc;

	private Rect _probeUv;

	private bool _probeCrop;

	private int _probeSize;

	private Collider[] _colliderBuf;

	private int _pieceLayerMask;

	private ConfigEntry<bool> _cfgShare;

	private ConfigEntry<bool> _cfgUseHostMap;

	private ZRoutedRpc _rpcRegistered;

	private int _storeRev;

	private int _sharedRev = -1;

	private int _shareErrors;

	private bool _shareOff;

	private readonly HashSet<long> _hostPeers = new HashSet<long>();

	private Dictionary<int, HostTile> _hostTiles = new Dictionary<int, HostTile>();

	private bool _hostBuilding;

	private float _nextShareCheck;

	private int _shareSentTiles;

	private long _shareSentBytes;

	private readonly Dictionary<int, MirrorTile> _mirror = new Dictionary<int, MirrorTile>();

	private readonly Dictionary<int, List<long>> _ownTerrainByTile = new Dictionary<int, List<long>>();

	private readonly Queue<int> _wantQueue = new Queue<int>();

	private readonly Dictionary<int, long> _indexHashes = new Dictionary<int, long>();

	private readonly List<int> _asked = new List<int>();

	private bool _helloSent;

	private bool _waiting;

	private float _waitingSince;

	private float _nextReapply;

	private bool _hostPieces;

	private bool _hostTerrain;

	private bool _hostForest;

	private int _shareGotTiles;

	private long _shareGotBytes;

	private long _worldUid;

	private bool _storeChanged;

	private readonly List<ZDO> _tcZdos = new List<ZDO>();

	private readonly Dictionary<ZDOID, uint> _tcRevision = new Dictionary<ZDOID, uint>();

	private int _tcCursor = -1;

	private int _tcParsed;

	private int _tcSkipped;

	private int _tcAdded;

	private int _tcRemoved;

	private int _tcChanged;

	private int _tcErrorCount;

	private int _tcPasses;

	private double _tcMs;

	private bool _tcDisabled;

	private bool[] _tcHeightScratch;

	private byte[] _tcKindScratch;

	private ConfigEntry<string> _cfgDetailStyle;

	private ConfigEntry<float> _cfgDetailPixel;

	private ConfigEntry<string> _cfgDetailCloudStyle;

	private ConfigEntry<bool> _cfgDetailStyled;

	private ConfigEntry<bool> _cfgPortClassicFirst;

	internal static int s_portCoastFrom = 0;

	private Texture _origCloudTex;

	private Texture _clearCloudTex;

	private Material _cloudMatLarge;

	private Material _cloudMatSmall;

	private PortAssets _port;

	private Material _portMat;

	private bool _portFailed;

	private static float[] s_srgbToLinear;

	private static readonly FieldInfo s_fiHeightTex = typeof(Minimap).GetField("m_heightTexture", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo s_fiForestTex = typeof(Minimap).GetField("m_forestMaskTexture", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo s_fiFogTex = typeof(Minimap).GetField("m_fogTexture", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	internal static bool s_portLinMaterial = false;

	internal static bool s_portLinGlobals = false;

	internal static bool s_portLinTextures = true;

	internal static bool s_portSrgbOut = true;

	internal static int s_portLightMode = 1;

	internal static bool s_portMaskFromMaterial = false;

	private int _zdoPieceLayer = -1;

	private readonly Dictionary<int, PrefabInfo> _prefabCache = new Dictionary<int, PrefabInfo>();

	private List<ZDO>[] _zdoSectors;

	private FieldInfo _fiPortalObjects;

	private readonly HashSet<int> _portalHashes = new HashSet<int>();

	private readonly HashSet<ZDO> _portalsSeen = new HashSet<ZDO>();

	private int _zdoSectorCursor;

	private readonly List<ZDO> _zdoSnapshot = new List<ZDO>();

	private int _zdoSnapshotCursor;

	private readonly Dictionary<int, List<PieceRec>> _zdoBuckets = new Dictionary<int, List<PieceRec>>();

	private readonly Stack<List<PieceRec>> _listPool = new Stack<List<PieceRec>>();

	private readonly List<int> _removeScratch = new List<int>();

	private int _zdoCursor = -1;

	private float _nextZdoPass;

	private Vector3 _zdoPassOrigin;

	private Vector3 _zdoPrevOrigin;

	private bool _zdoHavePrevOrigin;

	private bool _zdoAuthoritative;

	private int _zdoPasses;

	private int _zdoPassFrames;

	private int _zdoErrorCount;

	private readonly Stopwatch _sw = new Stopwatch();

	private double _zdoPassMs;

	private double _forestMs;

	private bool _zdoDisabled;

	private bool _zdoPassPieces;

	private bool _piecesOnlyPlayerBuilt;

	private float PaletteStrength
	{
		get
		{
			if (_cfgPaletteStrength == null)
			{
				return 1f;
			}
			return Mathf.Clamp(_cfgPaletteStrength.Value, 0.5f, 1f);
		}
	}

	private static bool WeakCpu => SystemInfo.processorCount <= 4;

	private int DetailThreads
	{
		get
		{
			int num = ((_cfgDetailWorkers != null) ? _cfgDetailWorkers.Value : 0);
			if (num > 0)
			{
				return Mathf.Clamp(num, 1, 8);
			}
			return Mathf.Clamp(SystemInfo.processorCount - 2, 1, 4);
		}
	}

	private int DetailCacheLimit
	{
		get
		{
			if (_cfgDetailCache == null)
			{
				return 96;
			}
			return Mathf.Clamp(_cfgDetailCache.Value, 32, 512);
		}
	}

	private bool ForestWanted
	{
		get
		{
			if ((_cfgShowForest.Value || _cfgShowPlanted.Value) && !_forestDisabled)
			{
				return UseZdoScan;
			}
			return false;
		}
	}

	private int MapScale
	{
		get
		{
			int num = ((_cfgMapScale != null) ? _cfgMapScale.Value : 0);
			if (num > 0)
			{
				return Mathf.Clamp(num, 1, 8);
			}
			int graphicsMemorySize = SystemInfo.graphicsMemorySize;
			if (graphicsMemorySize <= 0)
			{
				return 2;
			}
			if (graphicsMemorySize >= 3072)
			{
				if (graphicsMemorySize >= 7168)
				{
					return 4;
				}
				return 2;
			}
			return 1;
		}
	}

	private bool HeightsWanted
	{
		get
		{
			if (_cfgHeightFromTerrain != null)
			{
				return _cfgHeightFromTerrain.Value;
			}
			return true;
		}
	}

	private string HighMode
	{
		get
		{
			if (_cfgHighOverlay == null)
			{
				return "Off";
			}
			return _cfgHighOverlay.Value;
		}
	}

	private bool HighWaterWanted
	{
		get
		{
			if (_cfgHighWater != null)
			{
				return _cfgHighWater.Value;
			}
			return false;
		}
	}

	private float HighStrength
	{
		get
		{
			if (_cfgHighStrength == null)
			{
				return 0.7f;
			}
			return Mathf.Clamp(_cfgHighStrength.Value, 0.2f, 1f);
		}
	}

	private bool HighWanted
	{
		get
		{
			if (!_highUnavailable && HighMode != "Off" && _cfgEnabled != null)
			{
				return _cfgEnabled.Value;
			}
			return false;
		}
	}

	private bool ShareHost
	{
		get
		{
			if (!_shareOff && _cfgShare.Value && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && !ZNet.instance.IsDedicated() && _zdoAuthoritative)
			{
				return _zdoPasses > 0;
			}
			return false;
		}
	}

	private bool ShareClient
	{
		get
		{
			if (!_shareOff && _cfgUseHostMap.Value && (Object)(object)ZNet.instance != (Object)null)
			{
				return !ZNet.instance.IsServer();
			}
			return false;
		}
	}

	private bool UseZdoPaths
	{
		get
		{
			if (UseZdoScan && !_tcDisabled)
			{
				return string.Equals(_cfgPathSource.Value, "ZDO", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}
	}

	private bool PortWanted
	{
		get
		{
			if (_cfgDetailStyle != null)
			{
				return _cfgDetailStyle.Value == "Vanilla";
			}
			return false;
		}
	}

	private bool PortClassicFirst
	{
		get
		{
			if (_cfgPortClassicFirst != null)
			{
				return _cfgPortClassicFirst.Value;
			}
			return false;
		}
	}

	private string CloudStyle
	{
		get
		{
			if (_cfgDetailCloudStyle == null)
			{
				return "Smooth";
			}
			return _cfgDetailCloudStyle.Value;
		}
	}

	private bool UseZdoScan
	{
		get
		{
			if (!_zdoDisabled && _fiObjectsBySector != null)
			{
				return string.Equals(_cfgScanSource.Value, "ZDO", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}
	}

	private void RegisterCommands()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		new ConsoleCommand("livingmap", "Living Map. 'livingmap status' shows what the map remembers; 'livingmap reset [buildings|paths|forest] [metres|all]' forgets it around you (100 m by default) and collects it again", (ConsoleEvent)delegate(ConsoleEventArgs args)
		{
			RunCommand(args);
		}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static void Say(ConsoleEventArgs args, string text)
	{
		if (args != null && (Object)(object)args.Context != (Object)null)
		{
			args.Context.AddString(text);
		}
	}

	private void RunCommand(ConsoleEventArgs args)
	{
		//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			string text = ((args.Args.Length > 1) ? args.Args[1].ToLowerInvariant() : "");
			switch (text)
			{
			case "status":
				Say(args, StatusText());
				return;
			case "probe":
				Say(args, ProbeCommand(args.Args));
				return;
			case "port":
				Say(args, PortTest());
				return;
			}
			if (text != "reset")
			{
				Say(args, "livingmap status | livingmap reset [buildings|paths|forest] [metres|all]");
				return;
			}
			byte b = 0;
			float radius = 100f;
			bool world = false;
			for (int i = 2; i < args.Args.Length; i++)
			{
				string text2 = args.Args[i].ToLowerInvariant();
				switch (text2)
				{
				case "buildings":
				case "pieces":
					b |= 1;
					continue;
				case "paths":
				case "terrain":
					b |= 2;
					continue;
				case "forest":
				case "trees":
					b |= 4;
					continue;
				case "all":
				case "world":
					world = true;
					continue;
				}
				if (float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result > 0f)
				{
					radius = result;
					continue;
				}
				Say(args, "livingmap: unknown word '" + args.Args[i] + "'");
				return;
			}
			if (b == 0)
			{
				b = 7;
			}
			if (!_ready || (Object)(object)_mm == (Object)null)
			{
				Say(args, "livingmap: not attached to a map right now");
			}
			else if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				Say(args, "livingmap: no player");
			}
			else
			{
				Say(args, ResetData(b, ((Component)Player.m_localPlayer).transform.position, radius, world));
			}
		}
		catch (Exception e)
		{
			HandleError("console command", e);
			Say(args, "livingmap: failed, see the BepInEx log");
		}
	}

	private string StatusText()
	{
		if (!_ready || (Object)(object)_mm == (Object)null)
		{
			return "Living Map 0.19.2: not attached to a map";
		}
		return string.Format("Living Map {0}: {1} pieces in {2} map pixels, {3} path cells, forest cleared {4} / planted {5}; buildings via {6}, paths via {7}; map {8}px ({9:0.##} m/px); last pass {10} frames, {11:0.0} ms", "0.19.2", _pieceCount, _pieces.Count, _terrain.Count, _clearedForest.Count, _plantedForest.Count, (!UseZdoScan) ? "physics" : (_zdoAuthoritative ? "ZDO (host, whole world)" : "ZDO (client)"), UseZdoPaths ? "terrain records" : "loaded heightmaps", (((Object)(object)_rt != (Object)null) ? ((Texture)_rt).width : 0) + ((_cfgMapScale.Value > 0) ? "" : " auto"), ((Object)(object)_rt != (Object)null) ? (_pixelSize / (float)_rtScale) : 0f, _zdoPassFrames, _zdoPassMs) + "; " + DetailStatus() + "; " + ShareStatus() + "; " + HighStatus() + "; " + HeightStatus() + "\n" + MaterialText();
	}

	private string MaterialText()
	{
		int[] array = new int[MatNames.Length];
		int num = 0;
		foreach (KeyValuePair<int, List<PieceRec>> piece in _pieces)
		{
			List<PieceRec> value = piece.Value;
			for (int i = 0; i < value.Count; i++)
			{
				byte mat = value[i].Mat;
				if (mat < array.Length)
				{
					array[mat]++;
				}
				else
				{
					num++;
				}
			}
		}
		StringBuilder stringBuilder = new StringBuilder("pieces by material: ");
		int num2 = 0;
		for (int j = 0; j < array.Length; j++)
		{
			if (array[j] != 0)
			{
				if (num2++ > 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(MatNames[j]).Append(' ').Append(array[j]);
			}
		}
		if (num > 0)
		{
			stringBuilder.Append((num2++ > 0) ? ", " : "").Append("unknown ").Append(num);
		}
		if (num2 == 0)
		{
			stringBuilder.Append("none stored yet");
		}
		return stringBuilder.ToString();
	}

	private string ResetData(byte layers, Vector3 center, float radius, bool world)
	{
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		float half = (float)_texSize * 0.5f;
		float num = radius * radius;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		if ((layers & 1) != 0)
		{
			_removeScratch.Clear();
			foreach (KeyValuePair<int, List<PieceRec>> piece in _pieces)
			{
				if (world || PixelWithin(piece.Key, center, num, half))
				{
					_removeScratch.Add(piece.Key);
				}
			}
			for (int i = 0; i < _removeScratch.Count; i++)
			{
				List<PieceRec> list = _pieces[_removeScratch[i]];
				num2 += list.Count;
				_pieceCount -= list.Count;
				ReturnList(list);
				_pieces.Remove(_removeScratch[i]);
			}
			_removeScratch.Clear();
			_addPieces.Clear();
			_fogPieces.Clear();
		}
		if ((layers & 2) != 0)
		{
			float num5 = Mathf.Max(0.5f, _cfgTerrainGrid.Value);
			List<long> list2 = new List<long>();
			foreach (KeyValuePair<long, byte> item in _terrain)
			{
				if (!world)
				{
					float num6 = ((float)(int)(item.Key >> 32) + 0.5f) * num5 - center.x;
					float num7 = ((float)(int)(item.Key & 0xFFFFFFFFu) + 0.5f) * num5 - center.z;
					if (num6 * num6 + num7 * num7 > num)
					{
						continue;
					}
				}
				list2.Add(item.Key);
			}
			for (int j = 0; j < list2.Count; j++)
			{
				_terrain.Remove(list2[j]);
			}
			num3 = list2.Count;
			_addTerrain.Clear();
			_fogTerrain.Clear();
			_tcRevision.Clear();
			_lastScanPos = new Vector3(float.MinValue, 0f, float.MinValue);
			_nextScanTime = 0f;
		}
		if ((layers & 4) != 0)
		{
			num4 += RemovePixels(_clearedForest, center, num, half, world);
			num4 += RemovePixels(_plantedForest, center, num, half, world);
			_trustedZones.Clear();
			_fogForest.Clear();
			_maskDirty = true;
		}
		ResetZdoPass();
		_nextZdoPass = 0f;
		_rtDirty = true;
		MarkStoreChanged();
		string text = (world ? "the whole world" : $"{radius:0} m around you");
		string text2 = $"Living Map: forgot {num2} pieces, {num3} path cells, {num4} forest pixels in {text}; collecting again";
		((BaseUnityPlugin)this).Logger.LogInfo((object)text2);
		return text2;
	}

	private bool PixelWithin(int idx, Vector3 center, float r2, float half)
	{
		float num = ((float)(idx % _texSize) + 0.5f - half) * _pixelSize - center.x;
		float num2 = ((float)(idx / _texSize) + 0.5f - half) * _pixelSize - center.z;
		return num * num + num2 * num2 <= r2;
	}

	private int RemovePixels(HashSet<int> pixels, Vector3 center, float r2, float half, bool world)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		if (world)
		{
			int count = pixels.Count;
			pixels.Clear();
			return count;
		}
		_removeScratch.Clear();
		foreach (int pixel in pixels)
		{
			if (PixelWithin(pixel, center, r2, half))
			{
				_removeScratch.Add(pixel);
			}
		}
		for (int i = 0; i < _removeScratch.Count; i++)
		{
			pixels.Remove(_removeScratch[i]);
		}
		int count2 = _removeScratch.Count;
		_removeScratch.Clear();
		return count2;
	}

	private void BindConfig()
	{
		//IL_05b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_05bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_05dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_05e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0603: Unknown result type (might be due to invalid IL or missing references)
		//IL_0608: Unknown result type (might be due to invalid IL or missing references)
		//IL_0629: Unknown result type (might be due to invalid IL or missing references)
		//IL_062e: Unknown result type (might be due to invalid IL or missing references)
		//IL_064f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0654: Unknown result type (might be due to invalid IL or missing references)
		//IL_0675: Unknown result type (might be due to invalid IL or missing references)
		//IL_067a: Unknown result type (might be due to invalid IL or missing references)
		//IL_069b: Unknown result type (might be due to invalid IL or missing references)
		//IL_06a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_06c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_06c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_06e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_06ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_070e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0713: Unknown result type (might be due to invalid IL or missing references)
		//IL_0735: Unknown result type (might be due to invalid IL or missing references)
		//IL_073a: Unknown result type (might be due to invalid IL or missing references)
		//IL_075c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0761: Unknown result type (might be due to invalid IL or missing references)
		//IL_0783: Unknown result type (might be due to invalid IL or missing references)
		//IL_0788: Unknown result type (might be due to invalid IL or missing references)
		//IL_0843: Unknown result type (might be due to invalid IL or missing references)
		//IL_08f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_093e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0989: Unknown result type (might be due to invalid IL or missing references)
		//IL_09d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a1f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a6a: Unknown result type (might be due to invalid IL or missing references)
		_cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "Enabled", true, Ui("Living Map on or off. Off: the vanilla map, as if the mod were not there.", "1. Map layers", "Living Map on", 100, advanced: false));
		_cfgPersist = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "SaveOverlay", true, Ui("Remember what was collected between sessions, in a file per world. Matters on a dedicated server, where you only get what the server has sent you; the host collects everything again within seconds anyway.", "5. Advanced", "Remember between sessions", 100, advanced: true));
		_cfgShowBuildings = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "ShowBuildings", true, Ui("Show buildings on the map.", "1. Map layers", "Buildings", 90, advanced: false));
		_cfgShowPaths = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "ShowPaths", true, Ui("Show dirt paths, fields and paving made with the hoe and the cultivator.", "1. Map layers", "Paths and fields", 88, advanced: false));
		_cfgShowForest = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "ShowClearedForest", true, Ui("Remove the forest from the map where you have cut it down (the vanilla map always shows the forest the world started with).", "1. Map layers", "Cleared forest", 80, advanced: false));
		_cfgShowPlanted = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "ShowPlantedForest", true, Ui("Draw forest where you have planted a grove.", "1. Map layers", "Planted forest", 75, advanced: false));
		_cfgOnlyPlayerBuilt = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "OnlyPlayerBuilt", false, Ui("Only buildings placed by players: ruins, villages and other buildings that come with the world are left out.", "1. Map layers", "Only player buildings", 70, advanced: false));
		_cfgRespectFog = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "RespectFog", true, Ui("Only draw on ground you have explored. Off shows everything the mod knows about - on the host, the whole world.", "1. Map layers", "Only explored ground", 65, advanced: false));
		_cfgPathWidth = ((BaseUnityPlugin)this).Config.Bind<float>("02 Layers", "PathWidthMeters", 0f, Ui("How wide a road is drawn, in metres. 0 = its real width. A road is often one cell across, which on the zoomed-out map and the minimap is a single pixel and easy to lose against a dark biome; 4 to 6 makes it a line you can follow. It only widens what is drawn, not what is remembered.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 12f), "1. Map layers", "Road width (m)", 87, advanced: false));
		_cfgPathsClearForest = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "PathsClearForest", true, Ui("Where a road runs through forest, stop drawing the forest pattern over it. The map draws its trees on top of everything, so a road under them is hidden whatever colour it is - and where a road was laid the trees are gone anyway.", "1. Map layers", "Roads show through the forest", 86, advanced: false));
		_cfgShowCleared = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "ShowClearedGround", true, Ui("Also mark ground that was only levelled or raised, never painted with the hoe: a terrace, a filled hollow, a causeway. It covers everything you have ever terraformed, so around a big base it paints wide areas - turn it off for a map that shows only what you painted and built.", "1. Map layers", "Levelled ground", 60, advanced: false));
		_cfgForestRadius = ((BaseUnityPlugin)this).Config.Bind<float>("02 Layers", "ClearedForestRadius", 12f, Ui("Metres around a map pixel checked for trees when deciding whether it is cleared forest. Larger ignores natural gaps between trees, smaller follows the edge of a clearing more closely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(6f, 96f), "5. Advanced", "Cleared forest: search radius (m)", 60, advanced: true));
		_cfgForestMaxTrees = ((BaseUnityPlugin)this).Config.Bind<int>("02 Layers", "ClearedForestMaxTrees", 2, Ui("Trees that may still stand in that area for it to count as cleared. A natural wood has 6 to 40 there.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), "5. Advanced", "Cleared forest: trees left", 59, advanced: true));
		_cfgPlantedMinTrees = ((BaseUnityPlugin)this).Config.Bind<int>("02 Layers", "PlantedForestMinTrees", 6, Ui("Trees a map pixel (12 x 12 m) must hold to be drawn as planted forest. A planted grove has 15 or more.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), "5. Advanced", "Planted forest: trees needed", 58, advanced: true));
		_cfgPlantedAnyBiome = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Layers", "PlantedForestAnyBiome", false, Ui("Also draw planted forest where the vanilla map never draws forest (swamps, mountains, Mistlands, Ashlands). Their natural woods then get the forest pattern too.", "5. Advanced", "Planted forest in every biome", 57, advanced: true));
		_cfgScanSource = ((BaseUnityPlugin)this).Config.Bind<string>("03 Scanning", "BuildingScanSource", "ZDO", Ui("Where buildings are read from. ZDO: the game's object database - the whole world on the host, what the server has sent on a dedicated server. Physics: only what is loaded around you (the old way). ZDO falls back to Physics by itself if needed.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "ZDO", "Physics" }), "5. Advanced", "Read buildings from", 50, advanced: true));
		_cfgPathSource = ((BaseUnityPlugin)this).Config.Bind<string>("03 Scanning", "PathScanSource", "ZDO", Ui("Where painted ground is read from. ZDO: the terrain records in the object database. Heightmap: the loaded terrain around you (the old way). ZDO needs buildings read from ZDO too.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "ZDO", "Heightmap" }), "5. Advanced", "Read paths from", 49, advanced: true));
		_cfgZdoInterval = ((BaseUnityPlugin)this).Config.Bind<float>("03 Scanning", "ZdoScanInterval", 5f, Ui("Seconds between passes over the object database: how long a new or removed building takes to reach the map.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), "5. Advanced", "Update every (s)", 48, advanced: true));
		_cfgMapScale = ((BaseUnityPlugin)this).Config.Bind<int>("04 Rendering", "MapTextureScale", 0, Ui("How sharp the map and the minimap are. 0 = chosen for your graphics card: 1 under 3 GB, 2 under 7 GB, 4 above. Or set it: 1 = 12 m a pixel (16 MB of video memory), 2 = 6 m (64 MB), 4 = 3 m (256 MB), 8 = 1.5 m (1 GB). This is what to raise if buildings look smeared, and the only setting that needs the world loaded again. Close up, the detailed map draws its own picture and is not affected. livingmap status says what is in use.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[5] { 0, 1, 2, 4, 8 }), "1. Map layers", "Map sharpness", 95, advanced: false));
		_cfgPieceSize = ((BaseUnityPlugin)this).Config.Bind<float>("04 Rendering", "MinPieceSizeMeters", 2f, Ui("Smallest size a building piece is drawn at on the zoomed-out map, so thin walls stay visible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 8f), "5. Advanced", "Smallest piece (m)", 44, advanced: true));
		_cfgOutline = ((BaseUnityPlugin)this).Config.Bind<bool>("04 Rendering", "BuildingOutline", true, Ui("A dark edge around buildings on the map and the minimap, so a wooden house does not blend into the dirt it stands on. Its colour is in the colours below.", "1. Map layers", "Building outline", 58, advanced: false));
		_cfgOutlineWidth = ((BaseUnityPlugin)this).Config.Bind<float>("04 Rendering", "OutlineWidthMeters", 1f, Ui("How wide that edge is.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 4f), "5. Advanced", "Outline width (m)", 42, advanced: true));
		_cfgTerrainGrid = ((BaseUnityPlugin)this).Config.Bind<float>("04 Rendering", "TerrainGridSize", 2f, Ui("Metres per cell of the paths layer. Smaller is finer but takes four times the memory for half the size. Changing it collects the paths again.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), "5. Advanced", "Paths cell size (m)", 41, advanced: true));
		_cfgMatColor = new ConfigEntry<Color>[MatNames.Length];
		Color[] array = (Color[])(object)new Color[13]
		{
			new Color(0.72f, 0.42f, 0.14f, 1f),
			new Color(0.72f, 0.74f, 0.8f, 1f),
			new Color(0.4f, 0.44f, 0.52f, 1f),
			new Color(0.55f, 0.34f, 0.16f, 1f),
			new Color(0.9f, 0.87f, 0.8f, 1f),
			new Color(0.45f, 0.2f, 0.2f, 1f),
			new Color(0.36f, 0.52f, 0.36f, 1f),
			new Color(0.65f, 0.85f, 0.95f, 1f),
			new Color(0.7f, 0.45f, 0.3f, 1f),
			new Color(0.8f, 0.66f, 0.34f, 1f),
			new Color(0.33f, 0.27f, 0.24f, 1f),
			new Color(0.42f, 0.55f, 0.26f, 1f),
			new Color(0.47f, 0.48f, 0.52f, 1f)
		};
		string[] array2 = new string[13]
		{
			"Wood", "Stone", "Iron", "Core wood", "Marble", "Ashstone", "Ancient", "Ice", "Timber", "Roof: thatch",
			"Roof: darkwood", "Roof: turf", "Roof: slate"
		};
		for (int i = 0; i < MatNames.Length; i++)
		{
			_cfgMatColor[i] = ((BaseUnityPlugin)this).Config.Bind<Color>("05 Colors", "Material_" + MatNames[i], array[i], Ui("Colour of " + array2[i].ToLowerInvariant() + " on the map.", "3. Colours", array2[i], 100 - i, advanced: false));
		}
		_cfgPaletteStrength = ((BaseUnityPlugin)this).Config.Bind<float>("05 Colors", "PaletteStrength", 0.85f, Ui("How bright the building colours are, all of them at once. The map's shader brightens what it is given by about half again, which on snow is fine and over a swamp or the Mistlands is a white spot. Lower this rather than editing every colour below. 1 = the colours exactly as they are below; 0.85 by default, which is about where a building stops being the loudest thing on the map.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), "3. Colours", "Building colours: brightness", 200, advanced: false));
		_cfgMatUnknownColor = ((BaseUnityPlugin)this).Config.Bind<Color>("05 Colors", "Material_Unknown", new Color(0.85f, 0.3f, 0.75f, 1f), Ui("Colour of pieces whose material is not known.", "3. Colours", "Other pieces", 80, advanced: false));
		_cfgCultivatedColor = ((BaseUnityPlugin)this).Config.Bind<Color>("05 Colors", "Terrain_Cultivated", new Color(0.36f, 0.26f, 0.12f, 0.85f), Ui("Colour of cultivated soil.", "3. Colours", "Fields", 79, advanced: false));
		_cfgPavedColor = ((BaseUnityPlugin)this).Config.Bind<Color>("05 Colors", "Terrain_Paved", new Color(0.62f, 0.62f, 0.66f, 0.9f), Ui("Colour of paving.", "3. Colours", "Paving", 78, advanced: false));
		_cfgDirtColor = ((BaseUnityPlugin)this).Config.Bind<Color>("05 Colors", "Terrain_DirtPath", new Color(0.42f, 0.36f, 0.3f, 0.8f), Ui("Colour of dirt paths.", "3. Colours", "Dirt paths", 77, advanced: false));
		_cfgClearedColor = ((BaseUnityPlugin)this).Config.Bind<Color>("05 Colors", "Terrain_Cleared", new Color(0.5f, 0.47f, 0.36f, 0.5f), Ui("Colour of levelled ground (when it is shown).", "3. Colours", "Levelled ground", 76, advanced: false));
		_cfgOutlineColor = ((BaseUnityPlugin)this).Config.Bind<Color>("05 Colors", "Building_Outline", new Color(0.1f, 0.07f, 0.04f, 0.85f), Ui("Colour of the building outline on the zoomed-out map.", "3. Colours", "Building outline", 75, advanced: false));
		_cfgZdoPerFrame = ((BaseUnityPlugin)this).Config.Bind<int>("06 Advanced", "ZdoObjectsPerFrame", 4000, Ui("Objects looked at per frame while reading the object database. Higher updates the map sooner, lower is smoother.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(500, 50000), "5. Advanced", "Objects per frame", 30, advanced: true));
		_cfgScanRadius = ((BaseUnityPlugin)this).Config.Bind<float>("06 Advanced", "ScanRadius", 64f, Ui("Radius scanned around you by the old Physics / Heightmap readers. Unused with the default ZDO.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(16f, 256f), "5. Advanced", "Old scan radius (m)", 29, advanced: true));
		_cfgMaxPieces = ((BaseUnityPlugin)this).Config.Bind<int>("06 Advanced", "MaxPieces", 300000, Ui("Most building pieces remembered (300 000 take about 7 MB).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1000, 3000000), "5. Advanced", "Max pieces", 28, advanced: true));
		_cfgMaxTerrain = ((BaseUnityPlugin)this).Config.Bind<int>("06 Advanced", "MaxTerrainCells", 500000, Ui("Most path cells remembered.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1000, 5000000), "5. Advanced", "Max path cells", 27, advanced: true));
		_cfgDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("07 Debug", "Debug", false, Ui("Verbose log with timings, for bug reports.", "6. Debug", "Debug log", 20, advanced: true));
		_cfgDebugMarker = ((BaseUnityPlugin)this).Config.Bind<bool>("07 Debug", "DebugMarker", false, Ui("A magenta cross at your position, to check the layer lines up with the map.", "6. Debug", "Position marker", 19, advanced: true));
		_cfgIncremental = ((BaseUnityPlugin)this).Config.Bind<bool>("07 Debug", "IncrementalRedraw", true, Ui("Draw only what changed. Off redraws everything on each change - only for tracking down a drawing glitch.", "6. Debug", "Draw changes only", 18, advanced: true));
		_cfgLinearFix = ((BaseUnityPlugin)this).Config.Bind<bool>("07 Debug", "LinearColorFix", true, Ui("Colour conversion for the game's linear colour space. Off only to check whether colours are the problem.", "6. Debug", "Colour conversion", 17, advanced: true));
		_cfgMapFlipY = ((BaseUnityPlugin)this).Config.Bind<bool>("07 Debug", "MapLayerFlipY", false, Ui("Flip the layer vertically, for a graphics API that draws it upside down.", "6. Debug", "Flip layer", 16, advanced: true));
		BindHighGroundConfig();
		BindHeightConfig();
		BindDetailConfig();
		MigrateDefaults();
		BindNoticeConfig();
		BindShareConfig();
	}

	private void MigrateDefaults()
	{
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		ConfigEntry<int> val = ((BaseUnityPlugin)this).Config.Bind<int>("01 General", "ConfigVersion", 1, Hidden("Internal: which defaults these sections have been updated to. Do not edit."));
		if (val.Value < 2 && _cfgMapScale.Value == 2)
		{
			_cfgMapScale.Value = 0;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Map sharpness now follows the graphics card (MapTextureScale = 0). Set it to 2 to keep what it was.");
		}
		if (val.Value < 3 && !_cfgShowCleared.Value)
		{
			_cfgShowCleared.Value = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Levelled ground is now drawn by default (ShowClearedGround = true). Set it back to false for a map of only what you painted and built.");
		}
		if (val.Value < 4)
		{
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0.95f, 0.55f, 0.15f, 1f);
			if (_cfgMatColor.Length > 0 && _cfgMatColor[0].Value == val2)
			{
				_cfgMatColor[0].Value = new Color(0.72f, 0.42f, 0.14f, 1f);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Wooden buildings are drawn a darker wood colour now (Material_Wood). Set it back to F28C26FF for what it was.");
			}
		}
		val.Value = 4;
	}

	private void MigrateConfigLayout(HashSet<ConfigDefinition> inFile)
	{
		Dictionary<ConfigDefinition, string> dictionary = OrphanedConfigEntries();
		if (dictionary == null || dictionary.Count == 0)
		{
			return;
		}
		Dictionary<string, ConfigEntryBase> dictionary2 = new Dictionary<string, ConfigEntryBase>();
		foreach (ConfigDefinition key in ((BaseUnityPlugin)this).Config.Keys)
		{
			dictionary2[key.Key] = ((BaseUnityPlugin)this).Config[key];
		}
		List<ConfigDefinition> list = new List<ConfigDefinition>();
		int num = 0;
		bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
		((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
		try
		{
			foreach (KeyValuePair<ConfigDefinition, string> item in dictionary)
			{
				string text = item.Key.Key;
				if (item.Key.Section == "06 Colors" && text == "BuildingOutline")
				{
					text = "Building_Outline";
				}
				if (!dictionary2.TryGetValue(text, out var value))
				{
					if (Array.IndexOf(DroppedSettings, text) >= 0)
					{
						list.Add(item.Key);
					}
					continue;
				}
				list.Add(item.Key);
				if (inFile == null || !inFile.Contains(value.Definition))
				{
					try
					{
						value.SetSerializedValue(item.Value);
						num++;
					}
					catch (Exception ex)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not carry the setting " + item.Key.Section + "/" + item.Key.Key + " over: " + ex.Message));
					}
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				dictionary.Remove(list[i]);
			}
		}
		finally
		{
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
		}
		if (list.Count != 0)
		{
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Settings file updated to the 0.15.0 layout: " + num + " values moved to their new sections, " + (list.Count - num) + " obsolete lines removed."));
		}
	}

	private HashSet<ConfigDefinition> SettingsInFile()
	{
		Dictionary<ConfigDefinition, string> dictionary = OrphanedConfigEntries();
		if (dictionary == null)
		{
			return null;
		}
		return new HashSet<ConfigDefinition>(dictionary.Keys);
	}

	private Dictionary<ConfigDefinition, string> OrphanedConfigEntries()
	{
		try
		{
			PropertyInfo property = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				return property.GetValue(((BaseUnityPlugin)this).Config, null) as Dictionary<ConfigDefinition, string>;
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not read the unbound settings: " + ex.Message));
		}
		return null;
	}

	private static ConfigDescription Ui(string description, string category, string name, int order, bool advanced)
	{
		return Ui(description, null, category, name, order, advanced);
	}

	private static ConfigDescription Ui(string description, AcceptableValueBase values, string category, string name, int order, bool advanced)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes();
		configurationManagerAttributes.Category = category;
		configurationManagerAttributes.DispName = name;
		configurationManagerAttributes.Order = order;
		configurationManagerAttributes.IsAdvanced = advanced;
		return new ConfigDescription(description, values, new object[1] { configurationManagerAttributes });
	}

	private static ConfigDescription Hidden(string description)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes();
		configurationManagerAttributes.Browsable = false;
		return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { configurationManagerAttributes });
	}

	private void Awake()
	{
		try
		{
			MigrateFromMapOverlay();
			HashSet<ConfigDefinition> inFile = SettingsInFile();
			BindConfig();
			MigrateConfigLayout(inFile);
			RegisterCommands();
			_pieceLayerMask = LayerMask.GetMask(new string[1] { "piece" });
			_zdoPieceLayer = LayerMask.NameToLayer("piece");
			FieldInfo field = typeof(ZDOMan).GetField("m_objectsBySector", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null && typeof(List<ZDO>[]).IsAssignableFrom(field.FieldType))
			{
				_fiObjectsBySector = field;
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"ZDOMan.m_objectsBySector was not found or has an unexpected type; build pieces will be scanned through physics instead.");
			}
			_fiPortalObjects = typeof(ZDOMan).GetField("m_portalObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (_fiPortalObjects != null && !typeof(IDictionary).IsAssignableFrom(_fiPortalObjects.FieldType))
			{
				_fiPortalObjects = null;
			}
			if (_fiPortalObjects == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"ZDOMan.m_portalObjects was not found; portals may be missing from the map.");
			}
			FieldInfo field2 = typeof(ZoneSystem).GetField("m_generatedZones", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field2 != null && typeof(HashSet<Vector2s>).IsAssignableFrom(field2.FieldType))
			{
				_fiGeneratedZones = field2;
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"ZoneSystem.m_generatedZones was not found; the cleared-forest layer will only cover zones you have visited this session, even on the host.");
			}
			_fiExplored = typeof(Minimap).GetField("m_explored", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_fiExploredOthers = typeof(Minimap).GetField("m_exploredOthers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Living Map 0.19.2 loaded. No Harmony patches are applied.");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Awake failed, mod is inert: " + ex));
			_disabledByErrors = true;
		}
	}

	private void OnDestroy()
	{
		try
		{
			SaveStore();
			Teardown(restoreVanillaTexture: true);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("OnDestroy: " + ex.Message));
		}
	}

	private void Update()
	{
		if (_disabledByErrors)
		{
			return;
		}
		try
		{
			Tick();
		}
		catch (Exception e)
		{
			HandleError("Tick", e);
		}
	}

	private void Tick()
	{
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_027f: Unknown result type (might be due to invalid IL or missing references)
		if (!_oldPluginChecked && OldPluginLoaded())
		{
			return;
		}
		Minimap instance = Minimap.instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null)
		{
			if ((Object)(object)_mm != (Object)null)
			{
				SaveStore();
				Teardown(restoreVanillaTexture: false);
			}
		}
		else if (!_cfgEnabled.Value)
		{
			if ((Object)(object)_mm != (Object)null)
			{
				SaveStore();
				Teardown(restoreVanillaTexture: true);
			}
		}
		else
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			bool flag = (Object)(object)_rt == (Object)null;
			bool flag2 = (Object)(object)_rt != (Object)null && ForestWanted != ((Object)(object)_rtMask != (Object)null);
			if ((!_ready || (Object)(object)_mm != (Object)(object)instance || flag || flag2 || (Object)(object)instance.m_mapTexture == (Object)null || _texSize != instance.m_textureSize || (Object)(object)_vanillaTex != (Object)(object)instance.m_mapTexture) && !Setup(instance))
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			if (realtimeSinceStartup >= _nextScanTime)
			{
				Vector3 val = position - _lastScanPos;
				bool flag3 = ((Vector3)(ref val)).sqrMagnitude >= 64f;
				bool flag4 = realtimeSinceStartup >= _lastScanTime + 10f;
				if (flag3 || flag4)
				{
					_nextScanTime = realtimeSinceStartup + 1f;
					_lastScanTime = realtimeSinceStartup;
					_lastScanPos = position;
					Scan(position);
				}
				else
				{
					_nextScanTime = realtimeSinceStartup + 0.25f;
				}
			}
			if (_cfgShowCleared.Value != _collectedCleared)
			{
				_collectedCleared = _cfgShowCleared.Value;
				_highWaterOn = HighWaterWanted;
				_heightOn = HeightsWanted;
				_heightDirty = true;
				_tcRevision.Clear();
				_nextZdoPass = 0f;
				_lastScanPos = new Vector3(float.MinValue, 0f, float.MinValue);
				_nextScanTime = 0f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Levelled ground turned " + (_collectedCleared ? "on" : "off") + ": the terrain records are read again."));
			}
			if (UseZdoScan && (_cfgShowBuildings.Value || _treeCount != null || (UseZdoPaths && _cfgShowPaths.Value)))
			{
				StepZdoScan(realtimeSinceStartup, position);
			}
			if (realtimeSinceStartup >= _nextRefogTime && (_fogPieces.Count > 0 || _fogTerrain.Count > 0 || _fogForest.Count > 0))
			{
				_nextRefogTime = realtimeSinceStartup + 15f;
				ReleaseFromFog();
				_highDirty = true;
			}
			if ((Object)(object)_rt != (Object)null)
			{
				if (!_rt.IsCreated())
				{
					_rtDirty = true;
				}
				if ((Object)(object)_rtMask != (Object)null && !_rtMask.IsCreated())
				{
					_maskDirty = true;
				}
				WatchDrawSettings(realtimeSinceStartup);
				if (!Mathf.Approximately(_paletteApplied, PaletteStrength))
				{
					_paletteApplied = PaletteStrength;
					_rtDirty = true;
					_highDirty = true;
					_highSig = -1L;
				}
				if (_addTerrain.Count > 0)
				{
					_maskDirty = true;
				}
				bool flag5 = _addPieces.Count > 0 || _addTerrain.Count > 0;
				if ((_rtDirty || flag5 || _maskDirty) && realtimeSinceStartup >= _nextRtRebuild)
				{
					_nextRtRebuild = realtimeSinceStartup + 0.5f;
					if (_rtDirty || (flag5 && (!_cfgIncremental.Value || _cfgDebugMarker.Value || _addPieces.Count > 1500 || _addTerrain.Count > 1500)))
					{
						_rtDirty = false;
						RebuildGpuLayer();
					}
					else if (flag5)
					{
						DrawAdditions();
					}
					if (_maskDirty && (Object)(object)_rt != (Object)null)
					{
						_maskDirty = false;
						RebuildMask();
					}
					_highDirty = true;
					if (_heightDirty)
					{
						RebuildHeightLayer();
					}
				}
			}
			try
			{
				ShareTick(realtimeSinceStartup);
			}
			catch (Exception e)
			{
				HandleError("share", e);
			}
			if (_heightDirty && realtimeSinceStartup >= _nextRtRebuild)
			{
				RebuildHeightLayer();
			}
			if (_highDirty && HighWanted)
			{
				RebuildHighLayer();
			}
			if (_cfgPersist.Value && _storeChanged && realtimeSinceStartup >= _nextSaveTime)
			{
				_nextSaveTime = realtimeSinceStartup + 60f;
				SaveStore();
			}
		}
	}

	private bool Setup(Minimap mm)
	{
		//IL_0230: Unknown result type (might be due to invalid IL or missing references)
		//IL_0235: Unknown result type (might be due to invalid IL or missing references)
		if (_ready)
		{
			SaveStore();
		}
		Teardown(restoreVanillaTexture: true);
		Texture2D mapTexture = mm.m_mapTexture;
		if ((Object)(object)mapTexture == (Object)null || mm.m_textureSize <= 0 || mm.m_pixelSize <= 0f)
		{
			return false;
		}
		if (((Texture)mapTexture).width != mm.m_textureSize || ((Texture)mapTexture).height != mm.m_textureSize)
		{
			return false;
		}
		_mm = mm;
		_vanillaTex = mapTexture;
		_texSize = mm.m_textureSize;
		_pixelSize = mm.m_pixelSize;
		MapPixelSize = _pixelSize;
		_colliderBuf = (Collider[])(object)new Collider[8192];
		_worldUid = 0L;
		try
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				_worldUid = ZNet.instance.GetWorldUID();
			}
		}
		catch (Exception e)
		{
			HandleError("Setup/GetWorldUID", e);
		}
		_pieces.Clear();
		_terrain.Clear();
		_pieceCount = 0;
		_storeChanged = false;
		_piecesOnlyPlayerBuilt = _cfgOnlyPlayerBuilt.Value;
		if (_cfgPersist.Value)
		{
			LoadStore();
		}
		ResetZdoPass();
		_zdoHavePrevOrigin = false;
		_nextZdoPass = 0f;
		InitForestLayer();
		if (PortWanted)
		{
			try
			{
				EnsurePort();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Port assets: " + ex.Message));
			}
		}
		bool flag = false;
		if (!_gpuUnavailable)
		{
			try
			{
				flag = InitGpuLayer();
			}
			catch (Exception e2)
			{
				_gpuUnavailable = true;
				ReleaseGpuLayer();
				HandleError("GPU map layer", e2);
			}
		}
		if (!flag)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"The map layer could not be created on the graphics card; Living Map is off for this session and the vanilla map is untouched.");
			Teardown(restoreVanillaTexture: true);
			_disabledByErrors = true;
			return false;
		}
		try
		{
			DropHeightTexture();
		}
		catch
		{
		}
		ApplyTextureToMap((Texture)(object)_rt, rememberOriginals: true);
		if ((Object)(object)_rtMask != (Object)null)
		{
			ApplyMaskToMap((Texture)(object)_rtMask, rememberOriginals: true);
		}
		else
		{
			DropForestData();
		}
		PokeMinimap();
		_rtDirty = true;
		_maskDirty = (Object)(object)_rtMask != (Object)null;
		_nextRtRebuild = 0f;
		_lastScanPos = new Vector3(float.MinValue, 0f, float.MinValue);
		_nextScanTime = 0f;
		_lastScanTime = 0f;
		_nextSaveTime = Time.realtimeSinceStartup + 60f;
		_collectedCleared = _cfgShowCleared.Value;
		_ready = true;
		((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Attached to map: vanilla {0}px at {1} m/px; map layer {2}{10}; buildings via {3}; forest layers {4}; world={5}, restored pieces={6}, terrain cells={7}, forest pixels cleared={8} planted={9}", _texSize, _pixelSize.ToString("0.##"), ((Texture)_rt).width + "px on the GPU = " + (_pixelSize / (float)_rtScale).ToString("0.##") + " m/px", UseZdoScan ? "the object database (ZDO)" : "physics", ((_treeCount != null) ? "on" : "off") + "; paths via " + (UseZdoPaths ? "terrain records (ZDO)" : "loaded heightmaps"), _worldUid, _pieceCount, _terrain.Count, _clearedForest.Count, _plantedForest.Count, (_cfgMapScale.Value > 0) ? "" : (", chosen for a card reporting " + SystemInfo.graphicsMemorySize + " MB")));
		return true;
	}

	private void Teardown(bool restoreVanillaTexture)
	{
		if (restoreVanillaTexture && (Object)(object)_mm != (Object)null)
		{
			try
			{
				RestoreOriginalTextures();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not restore the vanilla map texture: " + ex.Message));
			}
		}
		try
		{
			DropHeightTexture();
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Map heights teardown: " + ex2.Message));
		}
		ReleaseGpuLayer();
		try
		{
			HighTeardown();
		}
		catch (Exception ex3)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("High-ground layer teardown: " + ex3.Message));
		}
		try
		{
			DetailTeardown();
		}
		catch (Exception ex4)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Detail layer teardown: " + ex4.Message));
		}
		_mm = null;
		_vanillaTex = null;
		_origSmallTex = null;
		_origLargeTex = null;
		_vanillaMask = null;
		_origSmallMask = null;
		_origLargeMask = null;
		DropForestData();
		_clearedForest.Clear();
		_plantedForest.Clear();
		_trustedZones.Clear();
		_forestBiome.Clear();
		_colliderBuf = null;
		_texSize = 0;
		_pixelSize = 0f;
		_ready = false;
		_pieces.Clear();
		_terrain.Clear();
		_pieceCount = 0;
		_circleScratch.Clear();
		_scanBuckets.Clear();
		_terrainScratch.Clear();
		ResetZdoPass();
		_prefabCache.Clear();
		_dKind.Clear();
	}

	private void ApplyTextureToMap(Texture tex, bool rememberOriginals)
	{
		if ((Object)(object)_mm == (Object)null)
		{
			return;
		}
		if ((Object)(object)_mm.m_mapImageSmall != (Object)null && (Object)(object)((Graphic)_mm.m_mapImageSmall).material != (Object)null)
		{
			if (rememberOriginals)
			{
				_origSmallTex = ((Graphic)_mm.m_mapImageSmall).material.GetTexture("_MainTex");
			}
			((Graphic)_mm.m_mapImageSmall).material.SetTexture("_MainTex", tex);
		}
		if ((Object)(object)_mm.m_mapImageLarge != (Object)null && (Object)(object)((Graphic)_mm.m_mapImageLarge).material != (Object)null)
		{
			if (rememberOriginals)
			{
				_origLargeTex = ((Graphic)_mm.m_mapImageLarge).material.GetTexture("_MainTex");
			}
			((Graphic)_mm.m_mapImageLarge).material.SetTexture("_MainTex", tex);
		}
	}

	private void PokeMinimap()
	{
		if ((Object)(object)_mm != (Object)null && (Object)(object)_mm.m_smallRoot != (Object)null && _mm.m_smallRoot.activeSelf)
		{
			_mm.m_smallRoot.SetActive(false);
			_mm.m_smallRoot.SetActive(true);
		}
	}

	private void ApplyMaskToMap(Texture tex, bool rememberOriginals)
	{
		if ((Object)(object)_mm == (Object)null)
		{
			return;
		}
		if ((Object)(object)_mm.m_mapImageSmall != (Object)null && (Object)(object)((Graphic)_mm.m_mapImageSmall).material != (Object)null)
		{
			if (rememberOriginals)
			{
				_origSmallMask = ((Graphic)_mm.m_mapImageSmall).material.GetTexture("_MaskTex");
			}
			((Graphic)_mm.m_mapImageSmall).material.SetTexture("_MaskTex", tex);
		}
		if ((Object)(object)_mm.m_mapImageLarge != (Object)null && (Object)(object)((Graphic)_mm.m_mapImageLarge).material != (Object)null)
		{
			if (rememberOriginals)
			{
				_origLargeMask = ((Graphic)_mm.m_mapImageLarge).material.GetTexture("_MaskTex");
			}
			((Graphic)_mm.m_mapImageLarge).material.SetTexture("_MaskTex", tex);
		}
	}

	private void RestoreOriginalTextures()
	{
		if ((Object)(object)_mm == (Object)null)
		{
			return;
		}
		if ((Object)(object)_mm.m_mapImageSmall != (Object)null && (Object)(object)((Graphic)_mm.m_mapImageSmall).material != (Object)null)
		{
			((Graphic)_mm.m_mapImageSmall).material.SetTexture("_MainTex", (Texture)(((Object)(object)_origSmallTex != (Object)null) ? ((object)_origSmallTex) : ((object)_vanillaTex)));
			if ((Object)(object)_origSmallMask != (Object)null)
			{
				((Graphic)_mm.m_mapImageSmall).material.SetTexture("_MaskTex", _origSmallMask);
			}
		}
		if ((Object)(object)_mm.m_mapImageLarge != (Object)null && (Object)(object)((Graphic)_mm.m_mapImageLarge).material != (Object)null)
		{
			((Graphic)_mm.m_mapImageLarge).material.SetTexture("_MainTex", (Texture)(((Object)(object)_origLargeTex != (Object)null) ? ((object)_origLargeTex) : ((object)_vanillaTex)));
			if ((Object)(object)_origLargeMask != (Object)null)
			{
				((Graphic)_mm.m_mapImageLarge).material.SetTexture("_MaskTex", _origLargeMask);
			}
		}
		PokeMinimap();
	}

	private float PathDrawWidth(float grid)
	{
		float num = ((_cfgPathWidth != null) ? _cfgPathWidth.Value : 0f);
		if (!(num > grid))
		{
			return grid;
		}
		return num;
	}

	private void WatchDrawSettings(float now)
	{
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		if (now < _nextSigCheck)
		{
			return;
		}
		_nextSigCheck = now + 0.25f;
		long num = 17L;
		num = num * 31 + (_cfgShowBuildings.Value ? 1 : 0);
		num = num * 31 + (_cfgShowPaths.Value ? 1 : 0);
		num = num * 31 + (_cfgShowCleared.Value ? 1 : 0);
		num = num * 31 + (_cfgOutline.Value ? 1 : 0);
		num = num * 31 + (_cfgRespectFog.Value ? 1 : 0);
		num = num * 31 + _cfgOutlineWidth.Value.GetHashCode();
		num = num * 31 + _cfgPieceSize.Value.GetHashCode();
		num = num * 31 + _cfgPathWidth.Value.GetHashCode();
		num = num * 31 + _cfgHighStrength.Value.GetHashCode();
		num = num * 31 + ((object)_cfgDirtColor.Value/*cast due to .constrained prefix*/).GetHashCode();
		num = num * 31 + ((object)_cfgPavedColor.Value/*cast due to .constrained prefix*/).GetHashCode();
		num = num * 31 + ((object)_cfgCultivatedColor.Value/*cast due to .constrained prefix*/).GetHashCode();
		num = num * 31 + ((object)_cfgClearedColor.Value/*cast due to .constrained prefix*/).GetHashCode();
		num = num * 31 + ((object)_cfgOutlineColor.Value/*cast due to .constrained prefix*/).GetHashCode();
		for (int i = 0; i < _cfgMatColor.Length; i++)
		{
			num = num * 31 + ((object)_cfgMatColor[i].Value/*cast due to .constrained prefix*/).GetHashCode();
		}
		if (num != _drawSig)
		{
			if (_drawSig != -1)
			{
				_rtDirty = true;
				_highDirty = true;
				_highSig = -1L;
			}
			_drawSig = num;
		}
		if (HeightsWanted != _heightOn)
		{
			_heightOn = HeightsWanted;
			if (_heightOn)
			{
				_heightDirty = true;
			}
			else
			{
				DropHeightTexture();
			}
		}
		if (HighWaterWanted != _highWaterOn)
		{
			_highWaterOn = HighWaterWanted;
			_highPixels = null;
			_highDirty = true;
			_highSig = -1L;
		}
		long num2 = 17L;
		num2 = num2 * 31 + (_cfgPathsClearForest.Value ? 1 : 0);
		num2 = num2 * 31 + (_cfgShowForest.Value ? 1 : 0);
		num2 = num2 * 31 + (_cfgShowPlanted.Value ? 1 : 0);
		num2 = num2 * 31 + (_cfgShowPaths.Value ? 1 : 0);
		num2 = num2 * 31 + _cfgTerrainGrid.Value.GetHashCode();
		if (num2 != _maskCfgSig)
		{
			if (_maskCfgSig != -1)
			{
				_maskDirty = true;
			}
			_maskCfgSig = num2;
		}
	}

	private void RebuildPathForest()
	{
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		_pathForest.Clear();
		if (_cfgPathsClearForest == null || !_cfgPathsClearForest.Value || !_cfgShowPaths.Value)
		{
			return;
		}
		float num = Mathf.Max(0.5f, _cfgTerrainGrid.Value);
		int num2 = Mathf.Max(1, Mathf.CeilToInt(16f / (num * num)));
		Dictionary<int, int> dictionary = new Dictionary<int, int>();
		foreach (KeyValuePair<long, byte> item in _terrain)
		{
			if (item.Value == 4)
			{
				continue;
			}
			float num3 = ((float)(int)(item.Key >> 32) + 0.5f) * num;
			float num4 = ((float)(int)(item.Key & 0xFFFFFFFFu) + 0.5f) * num;
			if (WorldToIndex(new Vector3(num3, 0f, num4), out var idx))
			{
				dictionary.TryGetValue(idx, out var value);
				dictionary[idx] = value + 1;
				if (value + 1 == num2)
				{
					_pathForest.Add(idx);
				}
			}
		}
	}

	private Color TerrainColor(byte kind)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		return (Color)(kind switch
		{
			2 => _cfgPavedColor.Value, 
			3 => _cfgDirtColor.Value, 
			1 => _cfgCultivatedColor.Value, 
			_ => _cfgClearedColor.Value, 
		});
	}

	private Color Palette(Color c)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		float paletteStrength = PaletteStrength;
		if (!(paletteStrength >= 0.999f))
		{
			return new Color(c.r * paletteStrength, c.g * paletteStrength, c.b * paletteStrength, c.a);
		}
		return c;
	}

	private Color FlatMatColor(byte mat)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		Color val = MatColor(mat);
		return new Color(val.r * 0.85f, val.g * 0.85f, val.b * 0.85f, val.a);
	}

	private Color MatColor(byte mat)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		if (mat != byte.MaxValue && mat < _cfgMatColor.Length)
		{
			return Palette(_cfgMatColor[mat].Value);
		}
		return Palette(_cfgMatUnknownColor.Value);
	}

	private static bool IsExplored(BitArray explored, BitArray exploredOthers, int idx)
	{
		if (explored == null && exploredOthers == null)
		{
			return true;
		}
		if (explored != null && idx < explored.Length && explored[idx])
		{
			return true;
		}
		if (exploredOthers != null && idx < exploredOthers.Length && exploredOthers[idx])
		{
			return true;
		}
		return false;
	}

	private bool IsWorldExplored(BitArray explored, BitArray exploredOthers, float wx, float wz)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		if (!_cfgRespectFog.Value)
		{
			return true;
		}
		if (!WorldToIndex(new Vector3(wx, 0f, wz), out var idx))
		{
			return false;
		}
		return IsExplored(explored, exploredOthers, idx);
	}

	private bool WorldToIndex(Vector3 world, out int idx)
	{
		idx = -1;
		if (_texSize <= 0 || _pixelSize <= 0f)
		{
			return false;
		}
		float num = (float)_texSize * 0.5f;
		int num2 = Mathf.FloorToInt(world.x / _pixelSize + num);
		int num3 = Mathf.FloorToInt(world.z / _pixelSize + num);
		if (num2 < 0 || num3 < 0 || num2 >= _texSize || num3 >= _texSize)
		{
			return false;
		}
		idx = num3 * _texSize + num2;
		return true;
	}

	private void CollectPixelsInCircle(Vector3 center, float radius, List<int> result)
	{
		result.Clear();
		float num = (float)_texSize * 0.5f;
		int num2 = Mathf.RoundToInt(center.x / _pixelSize + num);
		int num3 = Mathf.RoundToInt(center.z / _pixelSize + num);
		int num4 = Mathf.CeilToInt(radius / _pixelSize) + 1;
		float num5 = radius * radius;
		for (int i = num3 - num4; i <= num3 + num4; i++)
		{
			if (i < 0 || i >= _texSize)
			{
				continue;
			}
			for (int j = num2 - num4; j <= num2 + num4; j++)
			{
				if (j >= 0 && j < _texSize)
				{
					float num6 = ((float)j + 0.5f - num) * _pixelSize;
					float num7 = ((float)i + 0.5f - num) * _pixelSize;
					float num8 = num6 - center.x;
					float num9 = num7 - center.z;
					if (!(num8 * num8 + num9 * num9 > num5))
					{
						result.Add(i * _texSize + j);
					}
				}
			}
		}
	}

	private void MigrateFromMapOverlay()
	{
		try
		{
			string text = Path.Combine(Paths.ConfigPath, "j1ga.mapoverlay.cfg");
			if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath) && File.Exists(text))
			{
				File.Copy(text, ((BaseUnityPlugin)this).Config.ConfigFilePath);
				((BaseUnityPlugin)this).Config.Reload();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Copied the MapOverlay settings to " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + "."));
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not carry the MapOverlay settings over: " + ex.Message));
		}
	}

	private void MigrateStoreFromMapOverlay(string path)
	{
		try
		{
			string text = Path.Combine(Path.Combine(Paths.ConfigPath, "MapOverlay"), _worldUid + ".bin");
			if (!File.Exists(path) && File.Exists(text))
			{
				string directoryName = Path.GetDirectoryName(path);
				if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				File.Copy(text, path);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Copied the MapOverlay data for this world to " + path + "."));
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not carry the MapOverlay data over: " + ex.Message));
		}
	}

	private bool OldPluginLoaded()
	{
		_oldPluginChecked = true;
		bool flag = false;
		try
		{
			flag = Chainloader.PluginInfos.ContainsKey("j1ga.mapoverlay");
		}
		catch
		{
		}
		if (!flag)
		{
			return false;
		}
		_disabledByErrors = true;
		((BaseUnityPlugin)this).Logger.LogError((object)"The old MapOverlay plugin is loaded as well. Living Map is the same mod renamed; remove BepInEx\\plugins\\MapOverlay\\MapOverlay.dll. Living Map stays inert until then.");
		return true;
	}

	private void LogOnce(string key, string message)
	{
		if (_loggedErrors.Add("once:" + key))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}
	}

	private void HandleError(string where, Exception e)
	{
		_errorCount++;
		string item = where + "|" + ((e != null) ? (e.GetType().Name + "|" + e.Message) : "null");
		if (_loggedErrors.Add(item))
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[" + where + "] " + e));
		}
		if (_errorCount >= 25 && !_disabledByErrors)
		{
			_disabledByErrors = true;
			((BaseUnityPlugin)this).Logger.LogError((object)"Too many errors, Living Map is switching itself off for this session. The game is unaffected.");
			try
			{
				Teardown(restoreVanillaTexture: true);
			}
			catch
			{
			}
		}
	}

	private void BindDetailConfig()
	{
		_cfgDetail = ((BaseUnityPlugin)this).Config.Bind<bool>("08 Detail", "DetailEnabled", true, Ui("When you zoom the big map in, draw it in detail: buildings with their roofs, paths, fields and the forest as they are, in the vanilla map's style. The minimap is not changed.", "2. Close-up detail", "Close-up detail", 100, advanced: false));
		_cfgDetailStartSpan = ((BaseUnityPlugin)this).Config.Bind<float>("08 Detail", "DetailStartSpanMeters", WeakCpu ? 3000f : 6000f, Ui("How far out the detail begins, as the width of the map on screen in metres. Larger: the detail starts earlier and grows more gradually as you zoom in; smaller: it starts later, closer in, and costs less. Default 6000 (3000 on a processor with 4 threads or fewer). Much above the default the view wants more tiles than the cache holds and the picture stops filling in, so there is little to gain.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1000f, 12000f), "2. Close-up detail", "Detail starts at map width (m)", 95, advanced: false));
		_cfgDetailFinest = ((BaseUnityPlugin)this).Config.Bind<float>("08 Detail", "DetailFinestMetersPerPixel", 1f, Ui("The finest detail level allowed, in metres per pixel. Coarser means fewer tiles to draw.", (AcceptableValueBase)(object)new AcceptableValueList<float>(new float[3] { 1f, 2f, 4f }), "5. Advanced", "Detail: finest level (m)", 20, advanced: true));
		_cfgDetailObjects = ((BaseUnityPlugin)this).Config.Bind<bool>("08 Detail", "DetailObjects", true, Ui("Close up, read buildings (turned, with roofs and heights) straight from the objects near you. Off: the stored outlines only.", "5. Advanced", "Detail: read objects", 19, advanced: true));
		_cfgDetailPrefetch = ((BaseUnityPlugin)this).Config.Bind<bool>("08 Detail", "DetailPrefetch", false, Ui("Draw a little more than is on screen: a ring of tiles around the view, so panning does not start from nothing at the edge, and the next closer level, so a step of the zoom has something to show at once. Off by default: it fills the cache with tiles that are not on screen, and unless DetailTileCache is raised well past its default that costs more in tiles drawn again than it saves.", "2. Close-up detail", "Draw ahead", 80, advanced: false));
		_cfgDetailCache = ((BaseUnityPlugin)this).Config.Bind<int>("08 Detail", "DetailTileCache", 96, Ui("How many drawn tiles are kept. Panning the map throws the oldest away, and coming back draws them again from nothing - a bigger cache keeps them instead. Each tile is about half a megabyte of memory, and as much again on the card.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 512), "5. Advanced", "Detail: tiles kept", 14, advanced: true));
		_cfgDetailWorkers = ((BaseUnityPlugin)this).Config.Bind<int>("08 Detail", "DetailWorkerThreads", 0, Ui("Background threads that draw the detail. 0 = automatic: the processor's threads less two, at most four. They run only while the big map is open, below the game's own priority, and whatever is left to draw is dropped when the map closes. Takes effect on the next world load.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 8), "5. Advanced", "Detail: threads", 15, advanced: true));
		BindPortConfig();
		MigrateDetailConfig();
	}

	private void MigrateDetailConfig()
	{
		ConfigEntry<int> val = ((BaseUnityPlugin)this).Config.Bind<int>("08 Detail", "DetailConfigVersion", 1, Hidden("Internal: which defaults this section has been updated to. Do not edit."));
		if (val.Value < 4)
		{
			if (val.Value < 3 && !WeakCpu && Mathf.Approximately(_cfgDetailStartSpan.Value, 3000f))
			{
				_cfgDetailStartSpan.Value = 6000f;
			}
			if (_cfgDetailWorkers.Value == 2)
			{
				_cfgDetailWorkers.Value = 0;
			}
			if (WeakCpu && Mathf.Approximately(_cfgDetailStartSpan.Value, 6000f))
			{
				_cfgDetailStartSpan.Value = 3000f;
			}
			val.Value = 4;
		}
	}

	private Color32 Tone(Color c)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		float num = 0.5f;
		Color legacyToneTint = LegacyToneTint;
		float num2 = 0.299f * c.r + 0.587f * c.g + 0.114f * c.b;
		return Color32.op_Implicit(new Color((c.r + (num2 - c.r) * num) * legacyToneTint.r, (c.g + (num2 - c.g) * num) * legacyToneTint.g, (c.b + (num2 - c.b) * num) * legacyToneTint.b, c.a));
	}

	private static long DZoneKey(int zx, int zz)
	{
		return ((long)zx << 32) | (uint)zz;
	}

	private void LateUpdate()
	{
		if (_disabledByErrors)
		{
			return;
		}
		try
		{
			if (_ready)
			{
				UpdateVanillaClouds();
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[clouds] " + ex));
		}
		try
		{
			NoticeTick();
		}
		catch (Exception ex2)
		{
			_noticeDone = true;
			((BaseUnityPlugin)this).Logger.LogError((object)("[notice] " + ex2));
		}
		if ((Object)(object)_probeImg != (Object)null)
		{
			try
			{
				ProbeTick();
				return;
			}
			catch (Exception ex3)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("[probe] " + ex3));
				ProbeHide();
				return;
			}
		}
		try
		{
			if (_ready)
			{
				HighTick();
			}
		}
		catch (Exception ex4)
		{
			_highUnavailable = true;
			((BaseUnityPlugin)this).Logger.LogError((object)("[high ground] " + ex4));
		}
		if (_dBroken)
		{
			return;
		}
		try
		{
			DetailTick();
		}
		catch (Exception e)
		{
			DetailFail("tick", e);
		}
	}

	private void DetailFail(string where, Exception e)
	{
		_dBroken = true;
		((BaseUnityPlugin)this).Logger.LogError((object)string.Concat("[detail ", where, "] ", e, "\nThe detailed map layer is off for this session; the rest of Living Map keeps working."));
		try
		{
			DetailTeardown();
		}
		catch
		{
		}
	}

	private void DetailWorldCheck()
	{
		if (_dHeightsWorld != _worldUid)
		{
			_dHeightsWorld = _worldUid;
			_dHeights.Clear();
			_dZoneRev.Clear();
		}
	}

	private void StoreHeightZone(Vector3 zonePos, int pitch, float scale, float[] delta)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		DetailWorldCheck();
		Vector2s zone = ZoneSystem.GetZone(zonePos);
		long key = DZoneKey(zone.x, zone.y);
		if (delta == null)
		{
			_dHeights.Remove(key);
		}
		else
		{
			HeightZone heightZone = new HeightZone();
			heightZone.Cx = zonePos.x;
			heightZone.Cz = zonePos.z;
			heightZone.Scale = scale;
			heightZone.Pitch = pitch;
			heightZone.Half = pitch / 2;
			heightZone.D = delta;
			_dHeights[key] = heightZone;
		}
		_heightDirty = true;
		_dZoneRev.TryGetValue(key, out var value);
		_dZoneRev[key] = value + 1;
	}

	private void DetailTick()
	{
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Invalid comparison between Unknown and I4
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_022c: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_050b: Unknown result type (might be due to invalid IL or missing references)
		Minimap mm = _mm;
		if (!_ready || !((Object)(object)mm != (Object)null) || !_cfgDetail.Value || (int)mm.m_mode != 2 || !((Object)(object)mm.m_mapImageLarge != (Object)null) || !((Object)(object)mm.m_largeRoot != (Object)null) || !mm.m_largeRoot.activeInHierarchy)
		{
			if ((Object)(object)_dRoot != (Object)null && ((Component)_dRoot).gameObject.activeSelf)
			{
				((Component)_dRoot).gameObject.SetActive(false);
			}
			_dViewCovered = false;
			DropPendingTiles();
			if (!_ready && (_dTiles.Count > 0 || _dWorkers != null))
			{
				DetailTeardown();
			}
			return;
		}
		DetailWorldCheck();
		RawImage mapImageLarge = mm.m_mapImageLarge;
		Rect uvRect = mapImageLarge.uvRect;
		float num = (float)_texSize * _pixelSize;
		UpdateLevels(num);
		float num2 = ((Rect)(ref uvRect)).width * num;
		float value = _cfgDetailStartSpan.Value;
		float num3 = Mathf.Clamp01((value - num2) / (value * 0.2f));
		if (num3 <= 0f)
		{
			if ((Object)(object)_dRoot != (Object)null && ((Component)_dRoot).gameObject.activeSelf)
			{
				((Component)_dRoot).gameObject.SetActive(false);
			}
			_dViewCovered = false;
			DropPendingTiles();
			return;
		}
		if (PortWanted)
		{
			num3 = 1f;
		}
		EnsureDetailLayer(mapImageLarge);
		if (!((Component)_dRoot).gameObject.activeSelf)
		{
			((Component)_dRoot).gameObject.SetActive(true);
		}
		_dGroup.alpha = num3;
		EnsureWorkers();
		_dEnqueuedThisFrame = 0;
		Canvas canvas = ((Graphic)mapImageLarge).canvas;
		Rect rect = ((Graphic)mapImageLarge).rectTransform.rect;
		float num4 = ((Rect)(ref rect)).width * (((Object)(object)canvas != (Object)null) ? canvas.scaleFactor : 1f);
		float num5 = num2 / Mathf.Max(1f, num4);
		float num6 = Mathf.Max(1f, _cfgDetailFinest.Value);
		int num7 = -1;
		if (PortWanted)
		{
			Rect rect2 = ((Graphic)mapImageLarge).rectTransform.rect;
			float num8 = ((((Rect)(ref rect2)).height > 0f) ? (((Rect)(ref rect2)).width / ((Rect)(ref rect2)).height) : 1f);
			float num9 = Mathf.Max(1f, mm.m_minZoom * num8 * num);
			float num10 = ((value > num9) ? Mathf.Clamp01(Mathf.Log(value / Mathf.Max(num2, num9)) / Mathf.Log(value / num9)) : 1f);
			num7 = Mathf.Min(DLevels.Length - 1, Mathf.FloorToInt(num10 * (float)DLevels.Length));
			while (num7 > 0 && DLevels[num7] < num6 * 0.85f)
			{
				num7--;
			}
		}
		else
		{
			for (int i = 0; i < DLevels.Length && !(DLevels[i] < num6 - 0.01f); i++)
			{
				num7 = i;
				if (DLevels[i] <= num5)
				{
					break;
				}
			}
		}
		if (num7 < 0)
		{
			num7 = 0;
		}
		float num11 = (((Rect)(ref uvRect)).xMin - 0.5f) * num;
		float num12 = (((Rect)(ref uvRect)).xMax - 0.5f) * num;
		float num13 = (((Rect)(ref uvRect)).yMin - 0.5f) * num;
		float num14 = (((Rect)(ref uvRect)).yMax - 0.5f) * num;
		while (num7 > 0 && TileCount(num7, num11, num12, num13, num14) > 64)
		{
			num7--;
		}
		_dShownLevel = num7;
		_dShownNeed = num5;
		_dShownAt = Time.realtimeSinceStartup;
		float cx = (num11 + num12) * 0.5f;
		float cz = (num13 + num14) * 0.5f;
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		foreach (DTile value2 in _dTiles.Values)
		{
			if ((Object)(object)value2.Img != (Object)null && ((Behaviour)value2.Img).enabled)
			{
				((Behaviour)value2.Img).enabled = false;
			}
			if ((Object)(object)value2.Img2 != (Object)null && ((Behaviour)value2.Img2).enabled)
			{
				((Behaviour)value2.Img2).enabled = false;
			}
		}
		int num15 = -1;
		float num16 = 0f;
		for (int num17 = num7 - 1; num17 >= 0; num17--)
		{
			ReadyTiles(num17, num11, num12, num13, num14, out var need, out var have);
			if (need > 0 && have > 0)
			{
				float num18 = (float)have / (float)need;
				if (num18 > num16 + 0.001f)
				{
					num16 = num18;
					num15 = num17;
				}
				if (num16 >= 0.999f)
				{
					break;
				}
			}
		}
		if (num15 >= 0)
		{
			ShowLevel(num15, num11, num12, num13, num14, uvRect, num, realtimeSinceStartup, request: false, cx, cz);
		}
		ShowLevel(num7, num11, num12, num13, num14, uvRect, num, realtimeSinceStartup, request: true, cx, cz);
		Prefetch(num7, num11, num12, num13, num14, realtimeSinceStartup, cx, cz);
		if (num15 >= 0)
		{
			HideCoveredFog(num7, num15);
		}
		UpdateClouds(uvRect);
		UploadDone();
		RecheckTiles(realtimeSinceStartup);
		RelightTiles(realtimeSinceStartup);
		Evict(realtimeSinceStartup);
	}

	private void ReadyTiles(int level, float vx0, float vx1, float vz0, float vz1, out int need, out int have)
	{
		need = 0;
		have = 0;
		float num = 256f * DLevels[level];
		int num2 = Mathf.FloorToInt(vx0 / num);
		int num3 = Mathf.FloorToInt(vx1 / num);
		int num4 = Mathf.FloorToInt(vz0 / num);
		int num5 = Mathf.FloorToInt(vz1 / num);
		for (int i = num4; i <= num5; i++)
		{
			for (int j = num2; j <= num3; j++)
			{
				need++;
				if (_dTiles.TryGetValue(TileKey(level, j, i), out var value) && value.Ready && (Object)(object)value.Img != (Object)null)
				{
					have++;
				}
			}
		}
	}

	private static int TileCount(int level, float x0, float x1, float z0, float z1)
	{
		float num = 256f * DLevels[level];
		int num2 = Mathf.FloorToInt(x1 / num) - Mathf.FloorToInt(x0 / num) + 1;
		int num3 = Mathf.FloorToInt(z1 / num) - Mathf.FloorToInt(z0 / num) + 1;
		return num2 * num3;
	}

	private static long TileKey(int level, int tx, int tz)
	{
		return ((long)level << 58) ^ ((long)(tx & 0x1FFFFFFF) << 29) ^ (tz & 0x1FFFFFFF);
	}

	private void ShowLevel(int level, float vx0, float vx1, float vz0, float vz1, Rect uv, float world, float now, bool request, float cx, float cz)
	{
		//IL_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		bool dViewCovered = true;
		float num = DLevels[level];
		float num2 = 256f * num;
		int num3 = Mathf.FloorToInt(vx0 / num2);
		int num4 = Mathf.FloorToInt(vx1 / num2);
		int num5 = Mathf.FloorToInt(vz0 / num2);
		int num6 = Mathf.FloorToInt(vz1 / num2);
		Vector2 a = default(Vector2);
		Vector2 a2 = default(Vector2);
		for (int i = num5; i <= num6; i++)
		{
			for (int j = num3; j <= num4; j++)
			{
				long key = TileKey(level, j, i);
				if (!_dTiles.TryGetValue(key, out var value))
				{
					if (!request)
					{
						continue;
					}
					DTile dTile = new DTile();
					dTile.Level = level;
					dTile.TX = j;
					dTile.TZ = i;
					dTile.Key = key;
					value = dTile;
					_dTiles[key] = value;
				}
				value.LastUsed = now;
				float num7 = (float)j * num2;
				float num8 = (float)i * num2;
				if (request && !value.Ready && !value.Queued && !value.Failed && CanEnqueue())
				{
					float num9 = num7 + num2 * 0.5f - cx;
					float num10 = num8 + num2 * 0.5f - cz;
					Enqueue(value, num9 * num9 + num10 * num10);
				}
				if (!value.Ready || (Object)(object)value.Img == (Object)null)
				{
					dViewCovered = false;
					continue;
				}
				float num11 = num7;
				float num12 = num8;
				if ((Object)(object)value.Img2 != (Object)null)
				{
					num11 -= num * 0.5f;
					num12 -= num * 0.5f;
				}
				((Vector2)(ref a))..ctor((num11 / world + 0.5f - ((Rect)(ref uv)).xMin) / ((Rect)(ref uv)).width, (num12 / world + 0.5f - ((Rect)(ref uv)).yMin) / ((Rect)(ref uv)).height);
				((Vector2)(ref a2))..ctor(((num11 + num2) / world + 0.5f - ((Rect)(ref uv)).xMin) / ((Rect)(ref uv)).width, ((num12 + num2) / world + 0.5f - ((Rect)(ref uv)).yMin) / ((Rect)(ref uv)).height);
				Place(value.Img, request ? _dFront : _dBack, a, a2);
				if ((Object)(object)value.Img2 != (Object)null)
				{
					Place(value.Img2, request ? _dFogFront : _dFogBack, a, a2);
				}
			}
		}
		if (request)
		{
			_dViewCovered = dViewCovered;
		}
	}

	private void Prefetch(int level, float vx0, float vx1, float vz0, float vz1, float now, float cx, float cz)
	{
		if (_cfgDetailPrefetch == null || !_cfgDetailPrefetch.Value || _dEnqueuedThisFrame > 0 || !CanEnqueue() || level == 0)
		{
			return;
		}
		float num = 256f * DLevels[level];
		int num2 = Mathf.FloorToInt(vx0 / num);
		int num3 = Mathf.FloorToInt(vx1 / num);
		int num4 = Mathf.FloorToInt(vz0 / num);
		int num5 = Mathf.FloorToInt(vz1 / num);
		for (int i = num4 - 1; i <= num5 + 1; i++)
		{
			for (int j = num2 - 1; j <= num3 + 1; j++)
			{
				if ((j < num2 || j > num3 || i < num4 || i > num5) && !Want(level, j, i, now, cx, cz, num, 1E+09f))
				{
					return;
				}
			}
		}
		if (level + 1 >= DLevels.Length)
		{
			return;
		}
		float num6 = 256f * DLevels[level + 1];
		int num7 = Mathf.FloorToInt(vx0 / num6);
		int num8 = Mathf.FloorToInt(vx1 / num6);
		int num9 = Mathf.FloorToInt(vz0 / num6);
		int num10 = Mathf.FloorToInt(vz1 / num6);
		for (int k = num9; k <= num10; k++)
		{
			for (int l = num7; l <= num8; l++)
			{
				if (!Want(level + 1, l, k, now, cx, cz,