using System.Linq;
using Vintagestory.API.Common.Entities;
+using Vintagestory.API.MathTools;
namespace Automap
{
/// <summary>
+ /// Actual Physical Point in space - that is interesting.
+ /// </summary>
+ public struct EntityOfInterest
+ {
+ public string Notes;
+ public BlockPos Location;
+ public DateTimeOffset Timestamp;
+ public long EntityId;
+ }
+
+ /// <summary>
/// Entities of interest.
/// </summary>
/// <remarks>Tracked by ID - these never leave.</remarks>
- public class EntitiesOfInterest
+ public class EntitiesOfInterest : KeyedCollection<long, EntityOfInterest>
{
- private Dictionary<long, PointOfInterest> entitySet = new Dictionary<long, PointOfInterest>(50);
-
- internal void Upsert(Entity something, string message = @"")
+ internal void AddReplace(EntityOfInterest entity)
{
- if (entitySet.ContainsKey(something.EntityId))
- {
- var movingPOI = entitySet[something.EntityId];
- movingPOI.Location = something.Pos.AsBlockPos.Copy();
- movingPOI.Timestamp = DateTimeOffset.UtcNow;
- }
- else
- {
- PointOfInterest newPOI = new PointOfInterest();
- newPOI.EntityId = something.EntityId;
- newPOI.Location = something.Pos.AsBlockPos.Copy();
- newPOI.Timestamp = DateTimeOffset.UtcNow;
- newPOI.Notes = message;
- entitySet.Add(something.EntityId, newPOI);
- }
-
- }
+ if (Contains(entity.EntityId))
+ Remove(entity.EntityId);
-
- public List<PointOfInterest> PointsList
- {
- get {
- return entitySet.Values.ToList();
- }
+ Add(entity);
}
+ protected override long GetKeyForItem(EntityOfInterest item)
+ => item.EntityId;
}
}
/// </summary>
public class EntityDesignator
{
- public Color OverwriteColor;
+ public Color Color;
public EntityDesignatonAction SpecialAction;
public AssetLocation Pattern;
public EnumEntityState? StateCheck;//Needed?
throw new NotSupportedException();
}
- public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state)
+ public EntityDesignator(AssetLocation pattern, Color color, EnumEntityState? state)
{
- this.Pattern = pattern;
- this.OverwriteColor = overwriteColor;
- this.StateCheck = state;
- this.Enabled = true;
+ Pattern = pattern;
+ Color = color;
+ StateCheck = state;
+ Enabled = true;
}
- public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state, EntityDesignatonAction specialAct)
+ public EntityDesignator(AssetLocation pattern, Color color, EnumEntityState? state, EntityDesignatonAction specialAct)
{
- this.Pattern = pattern;
- this.OverwriteColor = overwriteColor;
- this.StateCheck = state;
- this.SpecialAction = specialAct;
- this.Enabled = true;
+ Pattern = pattern;
+ Color = color;
+ StateCheck = state;
+ SpecialAction = specialAct;
+ Enabled = true;
}
public override string ToString()
- {
- return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + StateCheck ?? "";
- }
+ => Pattern.ToShortString() + "|" + Color.Name + "|" + StateCheck ?? "";
}
}
public string Notes;
public BlockPos Location;
public DateTimeOffset Timestamp;
- public long? EntityId;
}
public class PointsOfInterest : KeyedCollection<BlockPos, PointOfInterest>
{
protected override BlockPos GetKeyForItem(PointOfInterest item)
- {
- return item.Location;
- }
+ => item.Location;
internal void AddReplace(PointOfInterest poi)
{
- if (this.Contains(poi.Location))
- {
- this.Remove(poi.Location);
- this.Add(poi);
- }
- else
- {
- this.Add(poi);
- }
+ if (Contains(poi.Location))
+ Remove(poi.Location);
- }
- }
+ Add(poi);
+ }
+ }
}
{
clientAPI.Logger.VerboseDebug("Trader: {0} @ {1}", entity.GetName(), posn);
- var message = $"{entity.GetName()}";
var traderJoe = entity as EntityTrader;
+ var message = $"{entity.GetName()} Alive: {traderJoe.Alive}";
if (traderJoe.TradeProps != null)
{
- message = $"{traderJoe.GetName()} Alive:{traderJoe.Alive} - Gears: {traderJoe.TradeProps.Money}, ";
+ message += $" - Gears: {traderJoe.TradeProps.Money}, ";
}
- poi.Upsert(entity, message);
+ poi.AddReplace(new EntityOfInterest
+ {
+ Location = posn.Copy(),
+ Notes = message,
+ Timestamp = DateTimeOffset.UtcNow,
+ EntityId = entity.EntityId
+ });
}
internal static void DecodeTranslocator(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block)
private readonly int chunkSize;
private string path;
- private IAsset stylesFile;
+ private IAsset staticMap;
public static string AutomapStatusEventKey = @"AutomapStatus";
public static string AutomapCommandEventKey = @"AutomapCommand";
path = ClientAPI.GetOrCreateDataPath(_mapPath);
path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));//Add name of World too...'ServerApi.WorldManager.CurrentWorldName'
- stylesFile = ClientAPI.World.AssetManager.Get(new AssetLocation(_domain, "config/automap_format.css"));
- Logger.VerboseDebug("CSS loaded: {0} size: {1}", stylesFile.IsLoaded(), stylesFile.ToText().Length);
+
+ string mapFilename = Path.Combine(path, "automap.html");
+ StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite));
+
+ staticMap = ClientAPI.World.AssetManager.Get(new AssetLocation(_domain, "config/automap.html"));
+ outputText.Write(staticMap.ToText());
+ outputText.Flush();
Prefill_POI_Designators();
startChunkColumn = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / chunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z / chunkSize));
{
//What about chunk updates themselves; a update bitmap isn't kept...
updatedChunksTotal += updatedChunks;
- GenerateMapHTML();
GenerateJSONMetadata();
updatedChunks = 0;
}
}
-
- private void GenerateMapHTML()
- {
- string mapFilename = Path.Combine(path, "Automap.html");
-
- int TopNorth = chunkTopMetadata.North_mostChunk;
- int TopSouth = chunkTopMetadata.South_mostChunk;
- int TopEast = chunkTopMetadata.East_mostChunk;
- int TopWest = chunkTopMetadata.West_mostChunk;
-
- using (StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)))
- {
- using (HtmlTextWriter tableWriter = new HtmlTextWriter(outputText))
- {
- tableWriter.BeginRender();
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Html);
-
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Head);
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Title);
- tableWriter.WriteEncodedText("Generated Automap");
- tableWriter.RenderEndTag();
- //CSS style here
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Style);
- tableWriter.Write(stylesFile.ToText());
- tableWriter.RenderEndTag();//</style>
-
- tableWriter.RenderEndTag();
-
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Body);
- tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
- tableWriter.WriteEncodedText($"Created {DateTimeOffset.UtcNow.ToString("u")}");
- tableWriter.RenderEndTag();
- tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
- tableWriter.WriteEncodedText($"W:{TopWest}, E: {TopEast}, N:{TopNorth}, S:{TopSouth} ");
- tableWriter.RenderEndTag();
- tableWriter.WriteLine();
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Table);
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Caption);
- tableWriter.WriteEncodedText($"Start: {startChunkColumn}, Seed: {ClientAPI.World.Seed}\n");
- tableWriter.RenderEndTag();
-
- //################ X-Axis <thead> #######################
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Thead);
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
-
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
- tableWriter.Write("N, W");
- tableWriter.RenderEndTag();
-
- for (int xAxisT = TopWest; xAxisT <= TopEast; xAxisT++)
- {
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
- tableWriter.Write(xAxisT);
- tableWriter.RenderEndTag();
- }
-
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Th);
- tableWriter.Write("N, E");
- tableWriter.RenderEndTag();
-
- tableWriter.RenderEndTag();
- tableWriter.RenderEndTag();
- //###### </thead> ################################
-
- //###### <tbody> - Chunk rows & Y-axis cols
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tbody);
-
- //######## <tr> for every vertical row
- for (int yAxis = TopNorth; yAxis <= TopSouth; yAxis++)
- {
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
- tableWriter.Write(yAxis);//legend: Y-axis
- tableWriter.RenderEndTag();
-
- for (int xAxis = TopWest; xAxis <= TopEast; xAxis++)
- {
- //###### <td> #### for chunk shard
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
- var colLoc = new Vec2i(xAxis, yAxis);
- if (chunkTopMetadata.Contains(colLoc))
- {
- ColumnMeta meta = chunkTopMetadata[colLoc];
- //Tooltip first
- tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltip");
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Div);
-
- tableWriter.AddAttribute(HtmlTextWriterAttribute.Src, $"{xAxis}_{yAxis}.png");
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Img);
- tableWriter.RenderEndTag();
- // <span class="tooltiptext">Tooltip text
- tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltiptext");
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Span);
-
- StringBuilder tooltipText = new StringBuilder();
- tooltipText.Append($"{meta.Location.PrettyCoords(ClientAPI)} ");
- tooltipText.Append($" Max-Height: {meta.YMax}, Temp: {meta.Temperature.ToString("F1")} ");
- tooltipText.Append($" Rainfall: {meta.Rainfall.ToString("F1")}, ");
- tooltipText.Append($" Shrubs: {meta.ShrubDensity.ToString("F1")}, ");
- tooltipText.Append($" Forest: {meta.ForestDensity.ToString("F1")}, ");
- tooltipText.Append($" Fertility: {meta.Fertility.ToString("F1")}, ");
-
- if (meta.RockRatio != null)
- {
- foreach (KeyValuePair<int, uint> blockID in meta.RockRatio)
- {
- var block = ClientAPI.World.GetBlock(blockID.Key);
- tooltipText.AppendFormat(" {0} × {1},\t", block.Code.GetName(), meta.RockRatio[blockID.Key]);
- }
- }
-
- tableWriter.WriteEncodedText(tooltipText.ToString());
-
- tableWriter.RenderEndTag();//</span>
-
-
- tableWriter.RenderEndTag();//</div> --tooltip enclosure
- }
- else
- {
- tableWriter.Write("?");
- }
-
- tableWriter.RenderEndTag();
- }//############ </td> ###########
-
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
- tableWriter.Write(yAxis);//legend: Y-axis
- tableWriter.RenderEndTag();
-
- tableWriter.RenderEndTag();
-
- }
- tableWriter.RenderEndTag();
-
- //################ X-Axis <tfoot> #######################
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tfoot);
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr);
-
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
- tableWriter.Write("S, W");
- tableWriter.RenderEndTag();
-
- for (int xAxisB = TopWest; xAxisB <= TopEast; xAxisB++)
- {
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
- tableWriter.Write(xAxisB);
- tableWriter.RenderEndTag();
- }
-
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Td);
- tableWriter.Write("S, E");
- tableWriter.RenderEndTag();
-
- tableWriter.RenderEndTag();
- tableWriter.RenderEndTag();
- //###### </tfoot> ################################
-
-
- tableWriter.RenderEndTag();//</table>
-
- //############## POI list #####################
- tableWriter.RenderBeginTag(HtmlTextWriterTag.P);
- tableWriter.WriteLine("Points of Interest");
- tableWriter.RenderEndTag();
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Ul);
- foreach (var poi in this.POIs)
- {
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Li);
- tableWriter.WriteEncodedText(poi.Location.PrettyCoords(this.ClientAPI) + "\t");
- tableWriter.WriteEncodedText(poi.Notes + "\t");
- tableWriter.WriteEncodedText(poi.Timestamp.ToString("u"));
- tableWriter.RenderEndTag();
- }
-
- foreach (var eoi in this.EOIs.PointsList)
- {
- tableWriter.RenderBeginTag(HtmlTextWriterTag.Li);
- tableWriter.WriteEncodedText(eoi.Location.PrettyCoords(this.ClientAPI) + "\t");
- tableWriter.WriteEncodedText(eoi.Notes + "\t");
- tableWriter.WriteEncodedText(eoi.Timestamp.ToString("u"));
- tableWriter.RenderEndTag();
- }
-
- tableWriter.RenderEndTag();
-
-
-
-
- tableWriter.RenderEndTag();//### </BODY> ###
-
- tableWriter.EndRender();
- tableWriter.Flush();
- }
- outputText.Flush();
- }
-
- Logger.VerboseDebug("Generated HTML map");
- }
-
/// <summary>
- /// Generates the JSON Metadata. (in MAP object format )
+ /// Generates the JSON Metadata. (in Map object format )
/// </summary>
private void GenerateJSONMetadata()
{
jsonWriter.Write("}],");
}
- foreach (var poi in EOIs.PointsList)
+ foreach (var poi in EOIs)
{
jsonWriter.Write("['{0}_{1}',", poi.Location.X, poi.Location.Z);
jsonWriter.Write("{");
--- /dev/null
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+
+<head>
+ <meta charset="utf-8">
+ <title>Automap</title>
+ <style media="screen">.m,body,html{width:100%;height:100%;margin:0;outline:1px dotted #000}.m img{position:absolute;image-rendering:pixelated}.i{width:15em;background-color:rgba(200,200,200,.5);left:0;top:0;font-family:sans-serif;position:absolute;z-index:1}h1{font-size:22px;margin:.6em 1em;text-align:center}.t{margin:.3em auto;width:90%;outline:1px solid #333}.t th{width:30%}.t td{text-align:right}</style>
+</head>
+
+<body>
+ <div class="i">
+ <h1>Chunk Info</h1>
+ <table class="t"></table>
+ </div>
+ <script type="text/javascript">
+ function ViewFrame() {
+ this.map = document.createElement("div"), this.map.className = "m", this.infobox = document.getElementsByClassName("t")[0], this.infoboxSlots = new Map, ViewFrame.initInfobox(this.infobox, this.infoboxSlots), document.getElementsByTagName("body")[
+ 0].append(this.map), this.loadedChunksByName = new Map, this.loadedChunksByCoords = new Map, this.availableChunks = null, this.chunkScript = null, this.renderOnReload = !0, this.dirty = !1, this.rendering = !1, this.x = null, this.y = null,
+ this._zoom = 32, this.updateEdges()
+ }
+
+ function decode(t, e) {
+ t.decode().then(() => {
+ e(t)
+ }).catch(() => {})
+ }
+ ViewFrame.prototype.reloadChunkList = function() {
+ console.log("Reloading chunks!"), this.chunkScript && (this.chunkScript.remove(), delete this.chunkScript), this.chunkScript = document.createElement("script"), this.chunkScript.type = "text/javascript", this.chunkScript.src = "Metadata.js",
+ document.getElementsByTagName("body")[0].append(this.chunkScript), this.chunkScript.onload = (() => {
+ this.availableChunks = ViewFrame.chunks.chunkMetadata, null !== this.x && null !== this.y || (this.x = ViewFrame.chunks.startCoords[0], this.y = ViewFrame.chunks.startCoords[1]), this.renderOnReload && this.render()
+ }), this.dirty = !0
+ }, ViewFrame.prototype.render = function() {
+ if (!this.availableChunks) return;
+ if (this.rendering) return;
+ this.rendering = !0, this.dirty && this.updateEdges(), this.loadedChunksByCoords.forEach((t, e) => {
+ if (e[0] < this.eastChunk && e[0] >= this.westChunk && e[1] <= this.northChunk && e[1] >= this.southChunk) {
+ let [s, i] = this.chunkToScreen(e[0], e[1]);
+ t.style.left = s + "px", t.style.top = i + "px";
+ const n = this.zoom / 32;
+ 1 != n && (t.style.transform = `scale(${n})`)
+ } else this.loadedChunksByCoords.delete(e), this.loadedChunksByName.delete(e.join("_")), t.remove()
+ });
+ const t = new Set;
+ for (var e = this.westChunk; e < this.eastChunk; e++)
+ for (var s = this.southChunk; s < this.northChunk; s++) {
+ const i = [e, s],
+ n = i.join("_");
+ this.availableChunks.has(n) && !this.loadedChunksByName.has(n) && t.add(i)
+ }
+ const i = t.values();
+ let n = setInterval(() => {
+ let t = i.next();
+ if (t.done) clearInterval(n), this.rendering = !1;
+ else {
+ const e = new Image(32, 32),
+ s = t.value.join("_");
+ e.src = s + ".png", e.alt = s, decode(e, e => {
+ let [s, i] = this.chunkToScreen(t.value[0], t.value[1]);
+ e.style.left = s + "px", e.style.top = i + "px";
+ const n = this.zoom / 32;
+ 1 != n && (chunk.style.transform = `scale(${n})`), this.map.append(e)
+ }), this.loadedChunksByName.set(s, e), this.loadedChunksByCoords.set(t.value, e)
+ }
+ }, 4)
+ }, ViewFrame.prototype.updateInfobox = function(t) {
+ const e = this.availableChunks.get(t);
+ this.infoboxSlots.forEach((t, s) => {
+ t.innerText = e[s]
+ })
+ }, ViewFrame.initInfobox = function(t, e) {
+ ["prettyCoord:Loc.", "chunkAge:Age", "temp:Temp.", "YMax:Y Max", "fert:Fert.", "forestDens:Forest", "rain:Rain", "shrubDens:Shrub", "airBlocks:Air", "nonAirBlocks:Non-Air"].map(t => t.split(":")).forEach(s => {
+ const i = s[0],
+ n = s[1],
+ h = document.createElement("tr"),
+ o = document.createElement("th");
+ o.innerText = n;
+ const r = document.createElement("td");
+ r.innerText = "0", e.set(i, r), h.append(o, r), t.append(h)
+ })
+ }, ViewFrame.prototype.screenToChunk = function(t, e) {
+ return [(t - this.width / 2) / this.zoom, (e - this.height / 2) / this.zoom]
+ }, ViewFrame.prototype.chunkToScreen = function(t, e) {
+ return [(t - this.west) * this.zoom, (e - this.south) * this.zoom]
+ }, ViewFrame.prototype.updateEdges = function() {
+ if (!this.dirty) return;
+ const t = Math.ceil(this.width / this.zoom),
+ e = Math.ceil(this.height / this.zoom);
+ this.east = this.x + t / 2, this.eastChunk = Math.ceil(this.east), this.west = this.x - t / 2, this.westChunk = Math.floor(this.west), this.north = this.y + e / 2, this.northChunk = Math.ceil(this.north), this.south = this.y - e / 2, this.southChunk =
+ Math.floor(this.south), this.dirty = !1
+ }, ViewFrame.prototype.moveCenter = function(t, e) {
+ let [s, i] = this.screenToChunk(t, e);
+ this.x += s, this.y += i, this.dirty = !0
+ }, ViewFrame.prototype.clear = function() {
+ this.loadedChunksByName.clear(), this.loadedChunksByCoords.clear(), this.chunkScript && this.chunkScript.remove(), delete this.chunkScript, delete ViewFrame.chunks, this.map.innerHTML = ""
+ }, Object.defineProperties(ViewFrame.prototype, {
+ width: {
+ get() {
+ return this.map.clientWidth
+ }
+ },
+ height: {
+ get() {
+ return this.map.clientHeight
+ }
+ },
+ zoom: {
+ get() {
+ return this._zoom
+ },
+ set(t) {
+ this._zoom = t, this.dirty = !0
+ }
+ }
+ });
+ const vf = new ViewFrame;
+ vf.reloadChunkList(),
+ function() {
+ var t;
+ window.addEventListener("resize", () => {
+ clearTimeout(t), vf.clear(), t = setTimeout(() => {
+ vf.updateEdges(), vf.render()
+ }, 500)
+ })
+ }(), vf.map.addEventListener("mousedown", t => {
+ vf.moveCenter(t.x, t.y), setTimeout(() => {
+ vf.render()
+ }, 250)
+ }),
+ function() {
+ var t;
+ vf.map.addEventListener("mousemove", e => {
+ e.target instanceof HTMLImageElement && t !== e.target && (t = e.target, vf.updateInfobox(e.target.alt))
+ })
+ }();
+ (function() {
+ setInterval(() => {
+ vf.reloadChunkList();
+ }, 6000);
+ }());
+ </script>
+</body>
+
+</html>
\ No newline at end of file
+++ /dev/null
-table {
-border: 1px solid black;
-border-collapse: collapse;
-}
-
-thead tr th {
-max-width:32px;
-margin: 0px;
-padding: 0px;
-overflow:hidden;
-border-right: 1px solid black;
-border-bottom: 1px solid black;
-font-size:8pt;
-font-weight:bold;
-font-family: Monospace;
-}
-
-tfoot tr td {
-max-width: 32px;
-padding: 0px;
-overflow:hidden;
-border-right: 1px solid black;
-border-top: 1px solid black;
-font-size:8pt;
-font-weight:bold;
-font-family: Monospace;
-}
-
-tbody tr {
-max-height:32px;
-padding: 0px;
-overflow:hidden;
-}
-
-tbody td {
-padding: 0px;
-border: 0px none black;
-max-width: 32px;
-background-color: white;
-width:32px;
-font-size: 6pt;
-}
-
-/* Tooltip container */
-.tooltip {
-position: relative;
-display: inline-block;
-}
-
-/* Tooltip text */
-.tooltip .tooltiptext {
-visibility: hidden;
-background-color: black;
-color: #fff;
-text-align: center;
-padding: 5px 0;
-position: absolute;
-z-index: 1;
-}
-
-/* Show the tooltip text when you mouse over the tooltip container */
-.tooltip:hover .tooltiptext {
-left: 33px;
-top: 33px;
-opacity: 0.75;
-position: absolute;
-visibility: visible;
-min-width: 120px;
-box-shadow: 5px 5px 8px 12px black;
-}
-
-.tooltip img:hover {
-outline: 1px dashed orange;
-opacity: 0.9;
-}