using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using Hjg.Pngcs.Chunks; using Vintagestory.API.Client; using Vintagestory.API.Common; using Vintagestory.API.MathTools; namespace Automap { public static class Helpers { static Helpers() { //Called once - thus it can only be in a static constructor. PngChunk.FactoryRegister(PngMetadataChunk.ID, typeof(PngMetadataChunk)); } /// /// Hue, Saturation Value colorspace /// /// The color equiv. /// 0 - 360 for hue. /// 0 - 1 for saturation or value.. /// 0 - 1 for saturation or value.. public static Color FromHSV(double hue, double saturation, double value) { int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6; double f = hue / 60 - Math.Floor(hue / 60); value *= 255; int v = Convert.ToInt32(value); int p = Convert.ToInt32(value * (1 - saturation)); int q = Convert.ToInt32(value * (1 - f * saturation)); int t = Convert.ToInt32(value * (1 - (1 - f) * saturation)); switch (hi) { case 0: return Color.FromArgb(255, v, t, p); case 1: return Color.FromArgb(255, q, v, p); case 2: return Color.FromArgb(255, p, v, t); case 3: return Color.FromArgb(255, p, q, v); case 4: return Color.FromArgb(255, t, p, v); default: return Color.FromArgb(255, v, p, q); }; } public static string PrettyCoords(this BlockPos location, ICoreClientAPI ClientApi) { var start = ClientApi.World.DefaultSpawnPosition.AsBlockPos; return string.Format("X{0}, Y{1}, Z{2}", location.X - start.X, location.Y, location.Z - start.Z); } /// /// Chunk location to User display coordinate system /// /// Friendly string /// Chunk Coords. public static string PrettyCoords(this Vec2i location, ICoreClientAPI ClientApi) { var start = ClientApi.World.DefaultSpawnPosition.AsBlockPos; var chunkSize = ClientApi.World.BlockAccessor.ChunkSize; return string.Format("X{0}, Z{1}", (location.X * chunkSize) - start.X, (location.Y * chunkSize) - start.Z); } public static BlockPos AverageHighestPos(List positions) { int x = 0, y = 0, z = 0, length = positions.Count; foreach (BlockPos pos in positions) { x += pos.X; y = Math.Max(y, pos.Y);//Mutant Y-axis, take "HIGHEST" z += pos.Z; } return new BlockPos(x / length, y, z / length); } public static BlockPos PickRepresentativePosition(List positions) { var averagePos = AverageHighestPos(positions); if (positions.Any(pos => pos.X == averagePos.X && pos.Y == averagePos.Y && pos.Z == averagePos.Z)) { return averagePos;//lucky ~ center was it! } //Otherwise...pick one var whichever = positions.Last(poz => poz.Y == averagePos.Y); return whichever; } /// /// Find a BLOCK partial path match: BlockID /// /// Matching finds /// Asset name. public static Dictionary ArbitrarytBlockIdHunter(this ICoreAPI CoreApi, AssetLocation assetName, EnumBlockMaterial? material = null) { Dictionary arbBlockIDTable = new Dictionary(); uint emptyCount = 0; if (CoreApi.World.Blocks != null) { #if DEBUG CoreApi.World.Logger.VerboseDebug(" World Blocks [Count: {0}]", CoreApi.World.Blocks.Count); #endif //If Brute force won't work; use GROOT FORCE! //var theBlock = ClientApi.World.BlockAccessor.GetBlock(0); if (!material.HasValue) { foreach (Block blk in CoreApi.World.Blocks) { if (blk.IsMissing || blk.Id == 0 || blk.BlockId == 0) { emptyCount++; } else if (blk.Code != null && blk.Code.BeginsWith(assetName.Domain, assetName.Path)) { #if DEBUG //CoreApi.World.Logger.VerboseDebug("Block: [{0} ({1})] = #{2}", blk.Code.Path, blk.BlockMaterial, blk.BlockId); #endif arbBlockIDTable.Add(blk.BlockId, blk.Code.Path); } } } else { foreach (Block blk in CoreApi.World.Blocks) { if (blk.IsMissing || blk.Id == 0 || blk.BlockId == 0) { emptyCount++; } else if (blk.Code != null && material.Value == blk.BlockMaterial && blk.Code.BeginsWith(assetName.Domain, assetName.Path)) { #if DEBUG //CoreApi.World.Logger.VerboseDebug("Block: [{0} ({1})] = #{2}", blk.Code.Path, blk.BlockMaterial, blk.BlockId); #endif arbBlockIDTable.Add(blk.BlockId, blk.Code.Path); } } } #if DEBUG CoreApi.World.Logger.VerboseDebug("Block gaps: {0}", emptyCount); #endif } return arbBlockIDTable; } /// /// Chunk local index. Not block position! /// /// Clamps to 5 bit ranges automagically public static int ChunkBlockIndicie16(int X_index, int Y_index, int Z_index) { return ((Y_index & 31) * 32 + (Z_index & 31)) * 32 + (X_index & 31); } /// /// Chunk index converted from block position (in world) /// /// The block indicie. /// Block position. /// Clamps to 5 bit ranges automagically public static int ChunkBlockIndicie16(BlockPos blockPos) { //Chunk masked return ((blockPos.Y & 31) * 32 + (blockPos.Z & 31)) * 32 + (blockPos.X & 31); } } }