-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Threading;
-using System.Drawing;
-using System.Drawing.Imaging;
+using System.Collections.ObjectModel;
using System.Linq;
-
using Vintagestory.API.Client;
using Vintagestory.API.Common;
-using Vintagestory.API.Common.Entities;
-using Vintagestory.API.Config;
-using Vintagestory.API.MathTools;
-using Vintagestory.API.Server;
-using Vintagestory.GameContent;
-using Vintagestory.API.Datastructures;
namespace Automap
{
- public class AutomapMod : ModSystem
- {
- private ICoreAPI API { get; set; }
- private ICoreClientAPI ClientAPI { get; set; }
- private ILogger Logger { get; set; }
+ public class AutomapMod : ModSystem
+ {
+ public const string _configFilename = @"automap.json";
- private const string _mapPath = @"Maps";
- private const string _chunkPath = @"Chunks";
- private readonly object colLock = new object( );
- private Dictionary<Vec2i, uint> columnCounter = new Dictionary<Vec2i, uint>();
- private HashSet<Vec2i> knownChunkTops = new HashSet<Vec2i>();
- private Vec2i startPosition;
+ private ICoreAPI API { get; set; }
+ private ICoreClientAPI ClientAPI { get; set; }
+ private ILogger Logger { get; set; }
+ private AutomapSystem _localAutomap;
+ private AutomapGUIDialog _automapDialog;
- private Thread cartographer_thread;
- public override bool ShouldLoad(EnumAppSide forSide)
- {
- return forSide.IsClient();
- }
+ public override bool ShouldLoad(EnumAppSide forSide)
+ {
+ return forSide.IsClient();
+ }
public override void StartClientSide(ICoreClientAPI api)
- {
+ {
this.API = api;
- if (api.Side == EnumAppSide.Client) {
- this.ClientAPI = api as ICoreClientAPI;
- this.Logger = Mod.Logger;
-
- ClientAPI.Event.LevelFinalize += StartAutomap;
- }
+ if (api.Side == EnumAppSide.Client)
+ {
+ this.ClientAPI = api as ICoreClientAPI;
+ this.Logger = Mod.Logger;
- base.StartClientSide(api);
- }
+ ClientAPI.Logger.VerboseDebug("Automap Present!");
+ PrepareClientsideConfig( );
+ _localAutomap = new AutomapSystem(this.ClientAPI, this.Logger, this.CachedConfiguration);
+ _automapDialog = new AutomapGUIDialog(ClientAPI, _localAutomap, this.CachedConfiguration);
- public override double ExecuteOrder( )
- {
- return 0.2;
- }
+ ClientAPI.Input.RegisterHotKey(AutomapGUIDialog._automapControlPanelKey, "Automap control panel", GlKeys.M, HotkeyType.GUIOrOtherControls, shiftPressed: true);
+ ClientAPI.Input.SetHotKeyHandler(AutomapGUIDialog._automapControlPanelKey, ToggleAM_Dialog);
+ ClientAPI.Event.LeaveWorld += PersistParameterChange;
+ }
+ base.StartClientSide(api);
+ }
- #region Internals
- private void StartAutomap( )
+ public override double ExecuteOrder()
{
- Mod.Logger.Notification("AUTOMAP SETUP");
- startPosition = new Vec2i(ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X, ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z);
- ClientAPI.Event.ChunkDirty += ChunkAChanging;
-
- cartographer_thread = new Thread(Cartographer);
- cartographer_thread.Name = "Cartographer";
- cartographer_thread.Priority = ThreadPriority.Lowest;
- cartographer_thread.IsBackground = true;
-
- ClientAPI.Event.RegisterGameTickListener(AwakenCartographer, 6000);
+ return 1.2;
}
- private void AwakenCartographer(float delayed)
+ private bool ToggleAM_Dialog(KeyCombination comb)
{
+ if (_automapDialog.IsOpened()) _automapDialog.TryClose();
+ else _automapDialog.TryOpen();
- if (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true)
- {
- #if DEBUG
- Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState);
- #endif
-
- if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted))
- {
- cartographer_thread.Start( );
- }
- else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin)) {
- //Time to (re)write chunk shards
- cartographer_thread.Interrupt( );
- }
+ return true;
}
- }
-
-
- private void Cartographer( )
- {
- wake:
- Logger.VerboseDebug("Cartographer thread awoken");
-
- try {
-
- while (columnCounter.Count > 0)
+ internal PersistedConfiguration CachedConfiguration {
+ get
{
- var mostActiveCol = columnCounter.OrderByDescending(kvp => kvp.Value).First();
- var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key);
- Logger.VerboseDebug("Selected: ({0}) - Edits#:{1}", mostActiveCol.Key, mostActiveCol.Value);
-
- if (mapChunk == null) {
- Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!",mostActiveCol.Key);
- columnCounter.Remove(mostActiveCol.Key);
- continue;
+ return ( PersistedConfiguration )ClientAPI.ObjectCache[_configFilename];
}
-
- string filename = $"{mostActiveCol.Key.X}_{mostActiveCol.Key.Y}.png";
-
- filename = Path.Combine(ClientAPI.GetOrCreateDataPath(_mapPath), filename);
-
- var chkImg = GenerateChunkImage(mostActiveCol.Key, mapChunk);
-
- chkImg.Save(filename, ImageFormat.Png);
-
- knownChunkTops.Add(mostActiveCol.Key);
- columnCounter.Remove(mostActiveCol.Key);
- }
-
-
-
- //Then sleep until interupted again, and repeat
-
- Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name);
-
- Thread.Sleep(Timeout.Infinite);
-
- } catch (ThreadInterruptedException) {
-
- Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name);
- goto wake;
-
- } catch (ThreadAbortException) {
- Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name);
-
- } finally {
- Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name);
- }
- }
- #endregion
-
-
-
- private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason)
- {
- //Logger.VerboseDebug($"Change: @({chunkCoord}) R: {reason}");
- Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z);
-
- lock (colLock)
+ set
{
- if (columnCounter.ContainsKey(topPosition)) { columnCounter[topPosition]++; } else { columnCounter.Add(topPosition, 1); }
+ ClientAPI.ObjectCache.Add(_configFilename, value);
}
-
}
- private void print_stats(float interval)
+ private void PrepareClientsideConfig( )
{
- if (columnCounter != null && columnCounter.Count > 0) {
- foreach (var count in columnCounter) {
- Logger.VerboseDebug($"({count.Key}): {count.Value}");
- }
- }
+ PersistedConfiguration config = ClientAPI.LoadModConfig<PersistedConfiguration>(_configFilename);
+ if (config == null) {
+ //Regen default
+ Mod.Logger.Warning("Regenerating default config as it was missing / unparsable...");
+ ClientAPI.StoreModConfig<PersistedConfiguration>(new PersistedConfiguration(defaults: true ), _configFilename);
+ config = ClientAPI.LoadModConfig<PersistedConfiguration>(_configFilename);
}
-
- #region COPYPASTA
- public Bitmap GenerateChunkImage(Vec2i chunkPos, IMapChunk mc)
- {
- BlockPos tmpPos = new BlockPos( );
- Vec2i localpos = new Vec2i( );
- int chunkSize = ClientAPI.World.BlockAccessor.ChunkSize;
- var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize];
- Bitmap chunkImage = new Bitmap(chunkSize, chunkSize, PixelFormat.Format24bppRgb );
- int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate!
- //Metadata of DateTime chunk was edited, chunk coords.,world-seed? Y-Max feature height
- //Grab a chunk COLUMN... Topmost Y down...
- for (int chunkY = 0; chunkY < topChunkY; chunkY++) {
- chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y);
- //What to do if chunk is a void? invalid?
+ this.CachedConfiguration = config;
}
-
- // Prefetch map chunks, in pattern
- IMapChunk[ ] mapChunks = new IMapChunk[ ]
- {
- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y - 1),
- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y),
- ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X, chunkPos.Y - 1)
- };
-
-
- for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++) {
- int mapY = mc.RainHeightMap[posIndex];
- int localChunkY = mapY / chunkSize;
- if (localChunkY >= (chunksColumn.Length ) ) continue;//Out of range!
-
- MapUtil.PosInt2d(posIndex, chunkSize, localpos);
- int localX = localpos.X;
- int localZ = localpos.Y;
- float b = 1;
- int leftTop, rightTop, leftBot;
-
- IMapChunk leftTopMapChunk = mc;
- IMapChunk rightTopMapChunk = mc;
- IMapChunk leftBotMapChunk = mc;
-
- int topX = localX - 1;
- int botX = localX;
- int leftZ = localZ - 1;
- int rightZ = localZ;
+ internal void PersistParameterChange( )
+ {
+ //Store altered parameters
- if (topX < 0 && leftZ < 0) {
- leftTopMapChunk = mapChunks[0];
- rightTopMapChunk = mapChunks[1];
- leftBotMapChunk = mapChunks[2];
- }
- else {
- if (topX < 0) {
- leftTopMapChunk = mapChunks[1];
- rightTopMapChunk = mapChunks[1];
+ ClientAPI.StoreModConfig<PersistedConfiguration>(this.CachedConfiguration, _configFilename);
}
- if (leftZ < 0) {
- leftTopMapChunk = mapChunks[2];
- leftBotMapChunk = mapChunks[2];
- }
- }
-
- topX = GameMath.Mod(topX, chunkSize);
- leftZ = GameMath.Mod(leftZ, chunkSize);
-
- leftTop = leftTopMapChunk == null ? 0 : Math.Sign(mapY - leftTopMapChunk.RainHeightMap[leftZ * chunkSize + topX]);
- rightTop = rightTopMapChunk == null ? 0 : Math.Sign(mapY - rightTopMapChunk.RainHeightMap[rightZ * chunkSize + topX]);
- leftBot = leftBotMapChunk == null ? 0 : Math.Sign(mapY - leftBotMapChunk.RainHeightMap[leftZ * chunkSize + botX]);
-
- float slopeness = (leftTop + rightTop + leftBot);
- if (slopeness > 0) b = 1.2f;
- if (slopeness < 0) b = 0.8f;
+ #region External Interfaces
+ //Perhaps Other mods can make use of POI / EOI?
- b -= 0.15f; // Map seems overally a bit too bright
- //b = 1;
- if (chunksColumn[localChunkY] == null) {
-
- continue;
+ public ReadOnlyCollection<PointOfInterest> PointsOfInterest {
+ get
+ {
+ return _localAutomap.POIs.ToList().AsReadOnly();
+ }
}
- chunksColumn[localChunkY].Unpack( );
- int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localpos.X, mapY % chunkSize, localpos.Y, chunkSize, chunkSize)];
- Block block = ClientAPI.World.Blocks[blockId];
-
- tmpPos.Set(chunkSize * chunkPos.X + localpos.X, mapY, chunkSize * chunkPos.Y + localpos.Y);
-
- int avgCol = block.GetColor(ClientAPI, tmpPos);
- int rndCol = block.GetRandomColor(ClientAPI, tmpPos, BlockFacing.UP);
- //Merge color?
- int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.25f);
- var packedFormat = ColorUtil.ColorMultiply3Clamped(col, b) | 255 << 24;//Is the Struct, truly so undesirable?
-
- Color pixelColor = Color.FromArgb(ColorUtil.ColorR(packedFormat), ColorUtil.ColorG(packedFormat), ColorUtil.ColorB(packedFormat));
- chunkImage.SetPixel(localX,localZ, pixelColor);
+ public ReadOnlyCollection<EntityOfInterest> EntitiesOfInterest {
+ get
+ {
+ return _localAutomap.EOIs.ToList().AsReadOnly();
+ }
}
-
- return chunkImage;
+ public CommandType Status
+ {
+ get { return _localAutomap.CurrentState; }
}
+
#endregion
+
+
}
}