Automap (client) [VS plugin mod]
Revision | 3a26629e3ee0961a34ddcac63e636581a371a7b1 (tree) |
---|---|
Zeit | 2020-02-25 09:55:26 |
Autor | The Grand Dog <alex.h@me.c...> |
Commiter | The Grand Dog |
Merge branch 'Split_renderers' into vgd
@@ -69,6 +69,7 @@ | ||
69 | 69 | <Reference Include="Pngcs"> |
70 | 70 | <HintPath>VS_libs\Pngcs.dll</HintPath> |
71 | 71 | </Reference> |
72 | + <Reference Include="System.Xml" /> | |
72 | 73 | </ItemGroup> |
73 | 74 | <ItemGroup> |
74 | 75 | <Compile Include="AutomapMod.cs" /> |
@@ -86,6 +87,9 @@ | ||
86 | 87 | <Compile Include="Renderers\AlternateRenderer.cs" /> |
87 | 88 | <Compile Include="Data\EntitiesOfInterest.cs" /> |
88 | 89 | <Compile Include="Data\EntityDesignator.cs" /> |
90 | + <Compile Include="Data\StatusData.cs" /> | |
91 | + <Compile Include="Data\CommandData.cs" /> | |
92 | + <Compile Include="Data\RunState.cs" /> | |
89 | 93 | </ItemGroup> |
90 | 94 | <ItemGroup> |
91 | 95 | <Folder Include="VS_libs\" /> |
@@ -19,41 +19,39 @@ namespace Automap | ||
19 | 19 | return forSide.IsClient(); |
20 | 20 | } |
21 | 21 | |
22 | - public override void StartClientSide(ICoreClientAPI api) | |
23 | - { | |
24 | - this.API = api; | |
22 | + public override void StartClientSide(ICoreClientAPI api) | |
23 | + { | |
24 | + this.API = api; | |
25 | 25 | |
26 | - if (api.Side == EnumAppSide.Client) | |
27 | - { | |
28 | - this.ClientAPI = api as ICoreClientAPI; | |
29 | - this.Logger = Mod.Logger; | |
26 | + if (api.Side == EnumAppSide.Client) | |
27 | + { | |
28 | + this.ClientAPI = api as ICoreClientAPI; | |
29 | + this.Logger = Mod.Logger; | |
30 | 30 | |
31 | 31 | |
32 | - ClientAPI.Logger.VerboseDebug("Automap Present!"); | |
33 | - _localAutomap = new AutomapSystem(this.ClientAPI, this.Logger); | |
34 | - _automapDialog = new AutomapGUIDialog(ClientAPI, _localAutomap); | |
32 | + ClientAPI.Logger.VerboseDebug("Automap Present!"); | |
33 | + _localAutomap = new AutomapSystem(this.ClientAPI, this.Logger); | |
34 | + _automapDialog = new AutomapGUIDialog(ClientAPI, _localAutomap); | |
35 | 35 | |
36 | - ClientAPI.Input.RegisterHotKey(AutomapGUIDialog._automapControlPanelKey, "Automap control panel", GlKeys.A, HotkeyType.GUIOrOtherControls); | |
37 | - ClientAPI.Input.SetHotKeyHandler(AutomapGUIDialog._automapControlPanelKey, ToggleAM_Dialog); | |
38 | - } | |
36 | + ClientAPI.Input.RegisterHotKey(AutomapGUIDialog._automapControlPanelKey, "Automap control panel", GlKeys.M, HotkeyType.GUIOrOtherControls, shiftPressed: true); | |
37 | + ClientAPI.Input.SetHotKeyHandler(AutomapGUIDialog._automapControlPanelKey, ToggleAM_Dialog); | |
38 | + } | |
39 | 39 | |
40 | 40 | base.StartClientSide(api); |
41 | 41 | } |
42 | 42 | |
43 | - public override double ExecuteOrder() | |
44 | - { | |
45 | - return 0.2; | |
46 | - } | |
47 | - | |
48 | - private bool ToggleAM_Dialog(KeyCombination comb) | |
49 | - { | |
50 | - if (_automapDialog.IsOpened()) _automapDialog.TryClose(); | |
51 | - else _automapDialog.TryOpen(); | |
52 | - | |
53 | - return true; | |
54 | - } | |
43 | + public override double ExecuteOrder() | |
44 | + { | |
45 | + return 0.2; | |
46 | + } | |
55 | 47 | |
48 | + private bool ToggleAM_Dialog(KeyCombination comb) | |
49 | + { | |
50 | + if (_automapDialog.IsOpened()) _automapDialog.TryClose(); | |
51 | + else _automapDialog.TryOpen(); | |
56 | 52 | |
57 | - } | |
53 | + return true; | |
54 | + } | |
55 | + } | |
58 | 56 | } |
59 | 57 |
@@ -9,45 +9,45 @@ using Vintagestory.API.MathTools; | ||
9 | 9 | |
10 | 10 | namespace Automap |
11 | 11 | { |
12 | - public delegate void BlockDesignatonAction(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block); | |
13 | - | |
14 | - /// <summary> | |
15 | - /// Point of Interest Rule Designator | |
16 | - /// </summary> | |
17 | - public class BlockDesignator | |
18 | - { | |
19 | - public Color OverwriteColor; | |
20 | - public BlockDesignatonAction SpecialAction; | |
21 | - public AssetLocation Pattern; | |
22 | - public EnumBlockMaterial? Material; | |
23 | - public bool Enabled { get; set; } | |
24 | - | |
25 | - private BlockDesignator() | |
26 | - { | |
27 | - throw new NotSupportedException(); | |
28 | - } | |
29 | - | |
30 | - public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material) | |
31 | - { | |
32 | - this.Pattern = pattern; | |
33 | - this.OverwriteColor = overwriteColor; | |
34 | - this.Material = material; | |
35 | - this.Enabled = true; | |
36 | - } | |
37 | - | |
38 | - public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material, BlockDesignatonAction specialAct) | |
39 | - { | |
40 | - this.Pattern = pattern; | |
41 | - this.OverwriteColor = overwriteColor; | |
42 | - this.Material = material; | |
43 | - this.SpecialAction = specialAct; | |
44 | - this.Enabled = true; | |
45 | - } | |
46 | - | |
47 | - public override string ToString() | |
48 | - { | |
49 | - return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + Material ?? ""; | |
50 | - } | |
51 | - } | |
12 | + public delegate void BlockDesignatonAction(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block); | |
13 | + | |
14 | + /// <summary> | |
15 | + /// Point of Interest Rule Designator | |
16 | + /// </summary> | |
17 | + public class BlockDesignator | |
18 | + { | |
19 | + public Color OverwriteColor; | |
20 | + public BlockDesignatonAction SpecialAction; | |
21 | + public AssetLocation Pattern; | |
22 | + public EnumBlockMaterial? Material; | |
23 | + public bool Enabled { get; set; } | |
24 | + | |
25 | + private BlockDesignator() | |
26 | + { | |
27 | + throw new NotSupportedException(); | |
28 | + } | |
29 | + | |
30 | + public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material) | |
31 | + { | |
32 | + this.Pattern = pattern; | |
33 | + this.OverwriteColor = overwriteColor; | |
34 | + this.Material = material; | |
35 | + this.Enabled = true; | |
36 | + } | |
37 | + | |
38 | + public BlockDesignator(AssetLocation pattern, Color overwriteColor, EnumBlockMaterial? material, BlockDesignatonAction specialAct) | |
39 | + { | |
40 | + this.Pattern = pattern; | |
41 | + this.OverwriteColor = overwriteColor; | |
42 | + this.Material = material; | |
43 | + this.SpecialAction = specialAct; | |
44 | + this.Enabled = true; | |
45 | + } | |
46 | + | |
47 | + public override string ToString() | |
48 | + { | |
49 | + return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + Material ?? ""; | |
50 | + } | |
51 | + } | |
52 | 52 | } |
53 | 53 |
@@ -11,155 +11,155 @@ using ProtoBuf; | ||
11 | 11 | |
12 | 12 | namespace Automap |
13 | 13 | { |
14 | - [ProtoContract] | |
15 | - public struct ColumnMeta | |
16 | - { | |
17 | - [ProtoMember(1)] | |
18 | - public Vec2i Location; | |
19 | - | |
20 | - [ProtoMember(2)] | |
21 | - public TimeSpan ChunkAge;//In game, calendar? | |
22 | - | |
23 | - [ProtoMember(3)] | |
24 | - public float Temperature;// Temperature | |
25 | - | |
26 | - [ProtoMember(4)] | |
27 | - public ushort YMax;// Y feature height | |
28 | - | |
29 | - [ProtoMember(5)] | |
30 | - public Dictionary<int, uint> RockRatio;//(surface) Geographic region (rock) Ratio. [BlockID * count] | |
31 | - | |
32 | - [ProtoMember(6)] | |
33 | - public float Fertility; | |
34 | - | |
35 | - [ProtoMember(7)] | |
36 | - public float ForestDensity; | |
37 | - | |
38 | - [ProtoMember(8)] | |
39 | - public float Rainfall; | |
40 | - | |
41 | - [ProtoMember(9)] | |
42 | - public float ShrubDensity; | |
43 | - | |
44 | - [ProtoMember(10)] | |
45 | - public ushort AirBlocks; | |
46 | - | |
47 | - [ProtoMember(11)] | |
48 | - public ushort NonAirBlocks; | |
49 | - | |
50 | - //[ProtoMember(12,OverwriteList = true)] | |
51 | - [ProtoIgnore] | |
52 | - public ushort[,] HeightMap; | |
53 | - | |
54 | - public ColumnMeta(Vec2i loc, int chunkSize = 32) | |
55 | - { | |
56 | - Location = loc; | |
57 | - ChunkAge = TimeSpan.Zero; | |
58 | - Temperature = 0f; | |
59 | - YMax = 0; | |
60 | - RockRatio = new Dictionary<int, uint>(10); | |
61 | - Fertility = 0f; | |
62 | - ForestDensity = 0f; | |
63 | - Rainfall = 0f; | |
64 | - ShrubDensity = 0f; | |
65 | - AirBlocks = 0; | |
66 | - NonAirBlocks = 0; | |
67 | - HeightMap = new ushort[chunkSize, chunkSize]; | |
68 | - } | |
69 | - | |
70 | - internal void UpdateFieldsFrom(ClimateCondition climate, IMapChunk mapChunk, TimeSpan chunkAge) | |
71 | - { | |
72 | - this.ChunkAge = chunkAge; | |
73 | - this.Temperature = climate.Temperature; | |
74 | - this.Fertility = climate.Fertility; | |
75 | - this.ForestDensity = climate.ForestDensity; | |
76 | - this.Rainfall = climate.Rainfall; | |
77 | - this.ShrubDensity = climate.ShrubDensity; | |
78 | - | |
79 | - this.YMax = mapChunk.YMax; | |
80 | - | |
81 | - } | |
82 | - } | |
83 | - | |
84 | - public class ColumnsMetadata : KeyedCollection<Vec2i, ColumnMeta> | |
85 | - { | |
86 | - private ColumnsMetadata() | |
87 | - { | |
88 | - throw new NotSupportedException(); | |
89 | - } | |
90 | - | |
91 | - public ColumnsMetadata(Vec2i startChunkColumn) | |
92 | - { | |
93 | - North_mostChunk = startChunkColumn.Y; | |
94 | - South_mostChunk = startChunkColumn.Y; | |
95 | - East_mostChunk = startChunkColumn.X; | |
96 | - West_mostChunk = startChunkColumn.X; | |
97 | - } | |
98 | - | |
99 | - public int North_mostChunk | |
100 | - { | |
101 | - get; private set; | |
102 | - } | |
103 | - | |
104 | - public int South_mostChunk | |
105 | - { | |
106 | - get; private set; | |
107 | - } | |
108 | - | |
109 | - public int East_mostChunk | |
110 | - { | |
111 | - get; private set; | |
112 | - } | |
113 | - | |
114 | - public int West_mostChunk | |
115 | - { | |
116 | - get; private set; | |
117 | - } | |
118 | - | |
119 | - protected override Vec2i GetKeyForItem(ColumnMeta item) | |
120 | - { | |
121 | - return item.Location; | |
122 | - } | |
123 | - | |
124 | - internal void Update(ColumnMeta metaData) | |
125 | - { | |
126 | - if (this.Contains(metaData.Location)) | |
127 | - { | |
128 | - this.Remove(metaData.Location); | |
129 | - this.Add(metaData); | |
130 | - } | |
131 | - else | |
132 | - { | |
133 | - this.Add(metaData); | |
134 | - } | |
135 | - | |
136 | - } | |
137 | - | |
138 | - public new void Add(ColumnMeta newItem) | |
139 | - { | |
140 | - if (North_mostChunk > newItem.Location.Y) | |
141 | - { | |
142 | - North_mostChunk = newItem.Location.Y; | |
143 | - } | |
144 | - | |
145 | - if (South_mostChunk < newItem.Location.Y) | |
146 | - { | |
147 | - South_mostChunk = newItem.Location.Y; | |
148 | - } | |
149 | - | |
150 | - if (East_mostChunk < newItem.Location.X) | |
151 | - { | |
152 | - East_mostChunk = newItem.Location.X; | |
153 | - } | |
154 | - | |
155 | - if (West_mostChunk > newItem.Location.X) | |
156 | - { | |
157 | - West_mostChunk = newItem.Location.X; | |
158 | - } | |
159 | - | |
160 | - base.Add(newItem); | |
161 | - } | |
162 | - | |
163 | - } | |
14 | + [ProtoContract] | |
15 | + public struct ColumnMeta | |
16 | + { | |
17 | + [ProtoMember(1)] | |
18 | + public Vec2i Location; | |
19 | + | |
20 | + [ProtoMember(2)] | |
21 | + public TimeSpan ChunkAge;//OLDEST CHUNK. from chunk last edit | |
22 | + | |
23 | + [ProtoMember(3)] | |
24 | + public float Temperature;// Temperature - surface | |
25 | + | |
26 | + [ProtoMember(4)] | |
27 | + public ushort YMax;// Y feature height | |
28 | + | |
29 | + [ProtoMember(5)] | |
30 | + public Dictionary<int, uint> RockRatio;//[Column] Geographic region (rock) Ratio. [BlockID * count] | |
31 | + | |
32 | + [ProtoMember(6)] | |
33 | + public float Fertility; | |
34 | + | |
35 | + [ProtoMember(7)] | |
36 | + public float ForestDensity; | |
37 | + | |
38 | + [ProtoMember(8)] | |
39 | + public float Rainfall; | |
40 | + | |
41 | + [ProtoMember(9)] | |
42 | + public float ShrubDensity; | |
43 | + | |
44 | + [ProtoMember(10)] | |
45 | + public ushort AirBlocks; | |
46 | + | |
47 | + [ProtoMember(11)] | |
48 | + public ushort NonAirBlocks; | |
49 | + | |
50 | + //[ProtoMember(12,OverwriteList = true)] | |
51 | + [ProtoIgnore] | |
52 | + public ushort[,] HeightMap; | |
53 | + | |
54 | + public ColumnMeta(Vec2i loc, int chunkSize = 32) | |
55 | + { | |
56 | + Location = loc; | |
57 | + ChunkAge = TimeSpan.Zero; | |
58 | + Temperature = 0f; | |
59 | + YMax = 0; | |
60 | + RockRatio = new Dictionary<int, uint>(10); | |
61 | + Fertility = 0f; | |
62 | + ForestDensity = 0f; | |
63 | + Rainfall = 0f; | |
64 | + ShrubDensity = 0f; | |
65 | + AirBlocks = 0; | |
66 | + NonAirBlocks = 0; | |
67 | + HeightMap = new ushort[chunkSize, chunkSize]; | |
68 | + } | |
69 | + | |
70 | + internal void UpdateFieldsFrom(ClimateCondition climate, IMapChunk mapChunk, TimeSpan chunkAge) | |
71 | + { | |
72 | + this.ChunkAge = chunkAge; | |
73 | + this.Temperature = climate.Temperature; | |
74 | + this.Fertility = climate.Fertility; | |
75 | + this.ForestDensity = climate.ForestDensity; | |
76 | + this.Rainfall = climate.Rainfall; | |
77 | + this.ShrubDensity = climate.ShrubDensity; | |
78 | + | |
79 | + this.YMax = mapChunk.YMax; | |
80 | + | |
81 | + } | |
82 | + } | |
83 | + | |
84 | + public class ColumnsMetadata : KeyedCollection<Vec2i, ColumnMeta> | |
85 | + { | |
86 | + private ColumnsMetadata() | |
87 | + { | |
88 | + throw new NotSupportedException(); | |
89 | + } | |
90 | + | |
91 | + public ColumnsMetadata(Vec2i startChunkColumn) | |
92 | + { | |
93 | + North_mostChunk = startChunkColumn.Y; | |
94 | + South_mostChunk = startChunkColumn.Y; | |
95 | + East_mostChunk = startChunkColumn.X; | |
96 | + West_mostChunk = startChunkColumn.X; | |
97 | + } | |
98 | + | |
99 | + public int North_mostChunk | |
100 | + { | |
101 | + get; private set; | |
102 | + } | |
103 | + | |
104 | + public int South_mostChunk | |
105 | + { | |
106 | + get; private set; | |
107 | + } | |
108 | + | |
109 | + public int East_mostChunk | |
110 | + { | |
111 | + get; private set; | |
112 | + } | |
113 | + | |
114 | + public int West_mostChunk | |
115 | + { | |
116 | + get; private set; | |
117 | + } | |
118 | + | |
119 | + protected override Vec2i GetKeyForItem(ColumnMeta item) | |
120 | + { | |
121 | + return item.Location; | |
122 | + } | |
123 | + | |
124 | + internal void Update(ColumnMeta metaData) | |
125 | + { | |
126 | + if (this.Contains(metaData.Location)) | |
127 | + { | |
128 | + this.Remove(metaData.Location); | |
129 | + this.Add(metaData); | |
130 | + } | |
131 | + else | |
132 | + { | |
133 | + this.Add(metaData); | |
134 | + } | |
135 | + | |
136 | + } | |
137 | + | |
138 | + public new void Add(ColumnMeta newItem) | |
139 | + { | |
140 | + if (North_mostChunk > newItem.Location.Y) | |
141 | + { | |
142 | + North_mostChunk = newItem.Location.Y; | |
143 | + } | |
144 | + | |
145 | + if (South_mostChunk < newItem.Location.Y) | |
146 | + { | |
147 | + South_mostChunk = newItem.Location.Y; | |
148 | + } | |
149 | + | |
150 | + if (East_mostChunk < newItem.Location.X) | |
151 | + { | |
152 | + East_mostChunk = newItem.Location.X; | |
153 | + } | |
154 | + | |
155 | + if (West_mostChunk > newItem.Location.X) | |
156 | + { | |
157 | + West_mostChunk = newItem.Location.X; | |
158 | + } | |
159 | + | |
160 | + base.Add(newItem); | |
161 | + } | |
162 | + | |
163 | + } | |
164 | 164 | } |
165 | 165 |
@@ -0,0 +1,116 @@ | ||
1 | +using System; | |
2 | +using System.Collections; | |
3 | +using System.Collections.Generic; | |
4 | +using System.Drawing; | |
5 | +using System.IO; | |
6 | + | |
7 | +using ProtoBuf; | |
8 | + | |
9 | +using Vintagestory.API.Common; | |
10 | +using Vintagestory.API.Datastructures; | |
11 | + | |
12 | +namespace Automap | |
13 | +{ | |
14 | + [ProtoContract(SkipConstructor = true)] | |
15 | + //[ProtoInclude(5,typeof(DelegateState))] | |
16 | + public class CommandData : IAttribute | |
17 | + { | |
18 | + [ProtoMember(1)] | |
19 | + public RunState State { get; set; }//Run , Stop, SingleSnapshot -> Stop | |
20 | + | |
21 | + [ProtoMember(2)] | |
22 | + private List<DelegateState> DelegatesFlags; | |
23 | + | |
24 | + [ProtoMember(3)] | |
25 | + public string Notation { get; set; } | |
26 | + | |
27 | + //POI Delegate list {enabled/Disable}, color? | |
28 | + //Other params...? Tick rate? | |
29 | + //Choose : Renderer(s) | |
30 | + | |
31 | + | |
32 | + public CommandData(RunState assumeState, bool[] theseDelegates) | |
33 | + { | |
34 | + State = assumeState; | |
35 | + | |
36 | + DelegatesFlags = new List<DelegateState>(theseDelegates.Length); | |
37 | + foreach (var df in theseDelegates) | |
38 | + { | |
39 | + DelegatesFlags.Add(new DelegateState() | |
40 | + { | |
41 | + Enabled = df, | |
42 | + AlternateColor = null, | |
43 | + }); | |
44 | + | |
45 | + } | |
46 | + | |
47 | + } | |
48 | + | |
49 | + public CommandData(RunState assumeState) | |
50 | + { | |
51 | + State = assumeState;//Never RUN. | |
52 | + | |
53 | + DelegatesFlags = new List<DelegateState>(); | |
54 | + } | |
55 | + | |
56 | + | |
57 | + public void FromBytes(BinaryReader stream) | |
58 | + { | |
59 | + var temp = ProtoBuf.Serializer.Deserialize<CommandData>(stream.BaseStream); | |
60 | + this.State = temp.State; | |
61 | + this.DelegatesFlags = temp.DelegatesFlags; | |
62 | + | |
63 | + } | |
64 | + | |
65 | + public void ToBytes(BinaryWriter stream) | |
66 | + { | |
67 | + ProtoBuf.Serializer.Serialize<CommandData>(stream.BaseStream, this); | |
68 | + | |
69 | + | |
70 | + } | |
71 | + | |
72 | + | |
73 | + | |
74 | + public int GetAttributeId() | |
75 | + { | |
76 | + return 12346; | |
77 | + } | |
78 | + | |
79 | + public object GetValue() | |
80 | + { | |
81 | + return this; | |
82 | + } | |
83 | + | |
84 | + | |
85 | + public string ToJsonToken() | |
86 | + { | |
87 | + return $"New-State:{State}, Delegates# {DelegatesFlags.Count} "; | |
88 | + } | |
89 | + | |
90 | + public bool Equals(IWorldAccessor worldForResolve, IAttribute attr) | |
91 | + { | |
92 | + var other = attr.GetValue() as CommandData; | |
93 | + | |
94 | + if (this.State == other.State) | |
95 | + { | |
96 | + return true; | |
97 | + } | |
98 | + | |
99 | + return false; | |
100 | + } | |
101 | + } | |
102 | + | |
103 | + [ProtoContract] | |
104 | + internal struct DelegateState | |
105 | + { | |
106 | + [ProtoMember(1)] | |
107 | + public bool Enabled; | |
108 | + | |
109 | + [ProtoMember(2)] | |
110 | + public Color? AlternateColor; | |
111 | + | |
112 | + | |
113 | + | |
114 | + } | |
115 | +} | |
116 | + |
@@ -8,44 +8,43 @@ using Vintagestory.API.Common.Entities; | ||
8 | 8 | |
9 | 9 | namespace Automap |
10 | 10 | { |
11 | - /// <summary> | |
12 | - /// Entities of interest. | |
13 | - /// </summary> | |
14 | - /// <remarks>Tracked by ID - these never leave.</remarks> | |
15 | - public class EntitiesOfInterest | |
16 | - { | |
17 | - private Dictionary<long, PointOfInterest> entitySet = new Dictionary<long, PointOfInterest>(50); | |
18 | - | |
19 | - | |
20 | - internal void Upsert(Entity something, string message = @"") | |
21 | - { | |
22 | - if (entitySet.ContainsKey(something.EntityId)) | |
23 | - { | |
24 | - var movingPOI = entitySet[something.EntityId]; | |
25 | - movingPOI.Location = something.Pos.AsBlockPos.Copy(); | |
26 | - movingPOI.Timestamp = DateTimeOffset.UtcNow; | |
27 | - } | |
28 | - else | |
29 | - { | |
30 | - PointOfInterest newPOI = new PointOfInterest(); | |
31 | - newPOI.EntityId = something.EntityId; | |
32 | - newPOI.Location = something.Pos.AsBlockPos.Copy(); | |
33 | - newPOI.Timestamp = DateTimeOffset.UtcNow; | |
34 | - newPOI.Notes = message; | |
35 | - entitySet.Add(something.EntityId, newPOI); | |
36 | - } | |
37 | - | |
38 | - } | |
39 | - | |
40 | - | |
41 | - public List<PointOfInterest> PointsList | |
42 | - { | |
43 | - get | |
44 | - { | |
45 | - return entitySet.Values.ToList(); | |
46 | - } | |
47 | - } | |
48 | - | |
49 | - } | |
11 | + /// <summary> | |
12 | + /// Entities of interest. | |
13 | + /// </summary> | |
14 | + /// <remarks>Tracked by ID - these never leave.</remarks> | |
15 | + public class EntitiesOfInterest | |
16 | + { | |
17 | + private Dictionary<long, PointOfInterest> entitySet = new Dictionary<long, PointOfInterest>(50); | |
18 | + | |
19 | + | |
20 | + internal void Upsert(Entity something, string message = @"") | |
21 | + { | |
22 | + if (entitySet.ContainsKey(something.EntityId)) | |
23 | + { | |
24 | + var movingPOI = entitySet[something.EntityId]; | |
25 | + movingPOI.Location = something.Pos.AsBlockPos.Copy(); | |
26 | + movingPOI.Timestamp = DateTimeOffset.UtcNow; | |
27 | + } | |
28 | + else | |
29 | + { | |
30 | + PointOfInterest newPOI = new PointOfInterest(); | |
31 | + newPOI.EntityId = something.EntityId; | |
32 | + newPOI.Location = something.Pos.AsBlockPos.Copy(); | |
33 | + newPOI.Timestamp = DateTimeOffset.UtcNow; | |
34 | + newPOI.Notes = message; | |
35 | + entitySet.Add(something.EntityId, newPOI); | |
36 | + } | |
37 | + | |
38 | + } | |
39 | + | |
40 | + | |
41 | + public List<PointOfInterest> PointsList | |
42 | + { | |
43 | + get { | |
44 | + return entitySet.Values.ToList(); | |
45 | + } | |
46 | + } | |
47 | + | |
48 | + } | |
50 | 49 | } |
51 | 50 |
@@ -10,45 +10,45 @@ using Vintagestory.API.MathTools; | ||
10 | 10 | |
11 | 11 | namespace Automap |
12 | 12 | { |
13 | - public delegate void EntityDesignatonAction(ICoreClientAPI clientAPI, EntitiesOfInterest poi, BlockPos posn, Entity entity); | |
14 | - | |
15 | - /// <summary> | |
16 | - /// Point of Interest Rule Designator | |
17 | - /// </summary> | |
18 | - public class EntityDesignator | |
19 | - { | |
20 | - public Color OverwriteColor; | |
21 | - public EntityDesignatonAction SpecialAction; | |
22 | - public AssetLocation Pattern; | |
23 | - public EnumEntityState? StateCheck;//Needed? | |
24 | - public bool Enabled { get; set; } | |
25 | - | |
26 | - private EntityDesignator() | |
27 | - { | |
28 | - throw new NotSupportedException(); | |
29 | - } | |
30 | - | |
31 | - public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state) | |
32 | - { | |
33 | - this.Pattern = pattern; | |
34 | - this.OverwriteColor = overwriteColor; | |
35 | - this.StateCheck = state; | |
36 | - this.Enabled = true; | |
37 | - } | |
38 | - | |
39 | - public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state, EntityDesignatonAction specialAct) | |
40 | - { | |
41 | - this.Pattern = pattern; | |
42 | - this.OverwriteColor = overwriteColor; | |
43 | - this.StateCheck = state; | |
44 | - this.SpecialAction = specialAct; | |
45 | - this.Enabled = true; | |
46 | - } | |
47 | - | |
48 | - public override string ToString() | |
49 | - { | |
50 | - return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + StateCheck ?? ""; | |
51 | - } | |
52 | - } | |
13 | + public delegate void EntityDesignatonAction(ICoreClientAPI clientAPI, EntitiesOfInterest poi, BlockPos posn, Entity entity); | |
14 | + | |
15 | + /// <summary> | |
16 | + /// Point of Interest Rule Designator | |
17 | + /// </summary> | |
18 | + public class EntityDesignator | |
19 | + { | |
20 | + public Color OverwriteColor; | |
21 | + public EntityDesignatonAction SpecialAction; | |
22 | + public AssetLocation Pattern; | |
23 | + public EnumEntityState? StateCheck;//Needed? | |
24 | + public bool Enabled { get; set; } | |
25 | + | |
26 | + private EntityDesignator() | |
27 | + { | |
28 | + throw new NotSupportedException(); | |
29 | + } | |
30 | + | |
31 | + public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state) | |
32 | + { | |
33 | + this.Pattern = pattern; | |
34 | + this.OverwriteColor = overwriteColor; | |
35 | + this.StateCheck = state; | |
36 | + this.Enabled = true; | |
37 | + } | |
38 | + | |
39 | + public EntityDesignator(AssetLocation pattern, Color overwriteColor, EnumEntityState? state, EntityDesignatonAction specialAct) | |
40 | + { | |
41 | + this.Pattern = pattern; | |
42 | + this.OverwriteColor = overwriteColor; | |
43 | + this.StateCheck = state; | |
44 | + this.SpecialAction = specialAct; | |
45 | + this.Enabled = true; | |
46 | + } | |
47 | + | |
48 | + public override string ToString() | |
49 | + { | |
50 | + return Pattern.ToShortString() + "|" + OverwriteColor.Name + "|" + StateCheck ?? ""; | |
51 | + } | |
52 | + } | |
53 | 53 | } |
54 | 54 |
@@ -7,49 +7,49 @@ using Hjg.Pngcs.Chunks; | ||
7 | 7 | |
8 | 8 | namespace Automap |
9 | 9 | { |
10 | - /// <summary> | |
11 | - /// Png metadata chunk. | |
12 | - /// </summary> | |
13 | - /// <remarks>There can be only one. (per PNG file)</remarks> | |
14 | - public class PngMetadataChunk : PngChunkSingle | |
15 | - { | |
16 | - // ID must follow the PNG conventions: four ascii letters, | |
17 | - public readonly static string ID = "cHUK"; | |
10 | + /// <summary> | |
11 | + /// Png metadata chunk. | |
12 | + /// </summary> | |
13 | + /// <remarks>There can be only one. (per PNG file)</remarks> | |
14 | + public class PngMetadataChunk : PngChunkSingle | |
15 | + { | |
16 | + // ID must follow the PNG conventions: four ascii letters, | |
17 | + public readonly static string ID = "cHUK"; | |
18 | 18 | |
19 | - public ColumnMeta ChunkMetadata { get; set; } | |
19 | + public ColumnMeta ChunkMetadata { get; set; } | |
20 | 20 | |
21 | 21 | |
22 | - public PngMetadataChunk(ImageInfo info) : base(ID, info) | |
23 | - { | |
22 | + public PngMetadataChunk(ImageInfo info) : base(ID, info) | |
23 | + { | |
24 | 24 | |
25 | - } | |
25 | + } | |
26 | 26 | |
27 | - public override ChunkOrderingConstraint GetOrderingConstraint() | |
28 | - { | |
29 | - return ChunkOrderingConstraint.NONE; | |
30 | - } | |
27 | + public override ChunkOrderingConstraint GetOrderingConstraint() | |
28 | + { | |
29 | + return ChunkOrderingConstraint.NONE; | |
30 | + } | |
31 | 31 | |
32 | - public override ChunkRaw CreateRawChunk() | |
33 | - { | |
34 | - var datas = SerializerUtil.Serialize<ColumnMeta>(ChunkMetadata); | |
32 | + public override ChunkRaw CreateRawChunk() | |
33 | + { | |
34 | + var datas = SerializerUtil.Serialize<ColumnMeta>(ChunkMetadata); | |
35 | 35 | |
36 | - ChunkRaw rawChunk = createEmptyChunk(datas.Length, true); | |
37 | - rawChunk.Data = datas; | |
36 | + ChunkRaw rawChunk = createEmptyChunk(datas.Length, true); | |
37 | + rawChunk.Data = datas; | |
38 | 38 | |
39 | - return rawChunk; | |
40 | - } | |
39 | + return rawChunk; | |
40 | + } | |
41 | 41 | |
42 | - public override void ParseFromRaw(ChunkRaw rawChunk) | |
43 | - { | |
44 | - this.ChunkMetadata = SerializerUtil.Deserialize<ColumnMeta>(rawChunk.Data); | |
45 | - } | |
42 | + public override void ParseFromRaw(ChunkRaw rawChunk) | |
43 | + { | |
44 | + this.ChunkMetadata = SerializerUtil.Deserialize<ColumnMeta>(rawChunk.Data); | |
45 | + } | |
46 | 46 | |
47 | - public override void CloneDataFromRead(PngChunk other) | |
48 | - { | |
49 | - PngMetadataChunk clone = (PngMetadataChunk)other; | |
50 | - this.ChunkMetadata = clone.ChunkMetadata; | |
51 | - } | |
47 | + public override void CloneDataFromRead(PngChunk other) | |
48 | + { | |
49 | + PngMetadataChunk clone = (PngMetadataChunk) other; | |
50 | + this.ChunkMetadata = clone.ChunkMetadata; | |
51 | + } | |
52 | 52 | |
53 | - } | |
53 | + } | |
54 | 54 | } |
55 | 55 |
@@ -6,35 +6,35 @@ using Vintagestory.API.MathTools; | ||
6 | 6 | |
7 | 7 | namespace Automap |
8 | 8 | { |
9 | - /// <summary> | |
10 | - /// Actual Physical Point in space - that is interesting. | |
11 | - /// </summary> | |
12 | - public struct PointOfInterest | |
13 | - { | |
14 | - public string Notes; | |
15 | - public BlockPos Location; | |
16 | - public DateTimeOffset Timestamp; | |
17 | - public long? EntityId; | |
18 | - } | |
9 | + /// <summary> | |
10 | + /// Actual Physical Point in space - that is interesting. | |
11 | + /// </summary> | |
12 | + public struct PointOfInterest | |
13 | + { | |
14 | + public string Notes; | |
15 | + public BlockPos Location; | |
16 | + public DateTimeOffset Timestamp; | |
17 | + public long? EntityId; | |
18 | + } | |
19 | 19 | |
20 | - public class PointsOfInterest : KeyedCollection<BlockPos, PointOfInterest> | |
21 | - { | |
22 | - protected override BlockPos GetKeyForItem(PointOfInterest item) | |
23 | - { | |
24 | - return item.Location; | |
25 | - } | |
20 | + public class PointsOfInterest : KeyedCollection<BlockPos, PointOfInterest> | |
21 | + { | |
22 | + protected override BlockPos GetKeyForItem(PointOfInterest item) | |
23 | + { | |
24 | + return item.Location; | |
25 | + } | |
26 | 26 | |
27 | - internal void AddReplace(PointOfInterest poi) | |
28 | - { | |
29 | - if (this.Contains(poi.Location)) | |
30 | - { | |
31 | - this.Remove(poi.Location); | |
32 | - this.Add(poi); | |
33 | - } | |
34 | - else | |
35 | - { | |
36 | - this.Add(poi); | |
37 | - } | |
27 | + internal void AddReplace(PointOfInterest poi) | |
28 | + { | |
29 | + if (this.Contains(poi.Location)) | |
30 | + { | |
31 | + this.Remove(poi.Location); | |
32 | + this.Add(poi); | |
33 | + } | |
34 | + else | |
35 | + { | |
36 | + this.Add(poi); | |
37 | + } | |
38 | 38 | |
39 | 39 | } |
40 | 40 | } |
@@ -0,0 +1,15 @@ | ||
1 | +using System; | |
2 | +namespace Automap | |
3 | +{ | |
4 | + /// <summary> | |
5 | + /// Command Type | |
6 | + /// </summary> | |
7 | + public enum RunState : byte | |
8 | + { | |
9 | + Stop = 0x00, | |
10 | + Run = 0x01, | |
11 | + Snapshot = 0x02, | |
12 | + Notation = 0x03, | |
13 | + } | |
14 | +} | |
15 | + |
@@ -0,0 +1,60 @@ | ||
1 | +using System; | |
2 | +using System.IO; | |
3 | +using Vintagestory.API.Common; | |
4 | +using Vintagestory.API.Datastructures; | |
5 | + | |
6 | +namespace Automap { | |
7 | + public class StatusData : IAttribute { | |
8 | + public uint TotalUpdates { get; set; } | |
9 | + public uint VoidChunks { get; set; } | |
10 | + public uint Delta { get; set; } | |
11 | + public uint Max_N, Max_E, Max_S, Max_W; | |
12 | + public RunState CurrentState { get; set; } | |
13 | + | |
14 | + public StatusData(uint totalUpdates, uint voidChunks, uint delta, RunState currently) { | |
15 | + TotalUpdates = totalUpdates; | |
16 | + VoidChunks = voidChunks; | |
17 | + Delta = delta; | |
18 | + CurrentState = currently; | |
19 | + } | |
20 | + | |
21 | + public void FromBytes(BinaryReader stream) { | |
22 | + TotalUpdates = stream.ReadUInt32(); | |
23 | + VoidChunks = stream.ReadUInt32(); | |
24 | + Delta = stream.ReadUInt32(); | |
25 | + CurrentState = (RunState) stream.ReadByte(); | |
26 | + } | |
27 | + | |
28 | + public int GetAttributeId() { | |
29 | + return 12345; | |
30 | + } | |
31 | + | |
32 | + public object GetValue() { | |
33 | + return this; | |
34 | + } | |
35 | + | |
36 | + public void ToBytes(BinaryWriter stream) { | |
37 | + stream.Write(TotalUpdates); | |
38 | + stream.Write(VoidChunks); | |
39 | + stream.Write(Delta); | |
40 | + stream.Write((byte) CurrentState); | |
41 | + } | |
42 | + | |
43 | + public string ToJsonToken() { | |
44 | + return $"TotalUpdate:{TotalUpdates}, VoidChunks:{VoidChunks}, Delta:{Delta}"; | |
45 | + } | |
46 | + | |
47 | + public bool Equals(IWorldAccessor worldForResolve, IAttribute attr) { | |
48 | + StatusData other = attr.GetValue() as StatusData; | |
49 | + | |
50 | + if (this.TotalUpdates == other.TotalUpdates && | |
51 | + this.VoidChunks == other.VoidChunks && | |
52 | + this.Delta == other.Delta && | |
53 | + this.CurrentState == other.CurrentState) { | |
54 | + return true; | |
55 | + } | |
56 | + return false; | |
57 | + } | |
58 | + } | |
59 | +} | |
60 | + |
@@ -55,7 +55,7 @@ namespace Automap | ||
55 | 55 | new EntityDesignator( |
56 | 56 | new AssetLocation("game", "humanoid-trader"), |
57 | 57 | Color.LightGoldenrodYellow, |
58 | - EnumEntityState.Active, | |
58 | + EnumEntityState.Active, | |
59 | 59 | KeepTrackOfMerchant |
60 | 60 | ); |
61 | 61 |
@@ -63,9 +63,9 @@ namespace Automap | ||
63 | 63 | /// Not just blocks, but block-entities as well! |
64 | 64 | /// </summary> |
65 | 65 | /// <returns>The block designators.</returns> |
66 | - public static List<BlockDesignator> DefaultBlockDesignators( ) | |
66 | + public static List<BlockDesignator> DefaultBlockDesignators() | |
67 | 67 | { |
68 | - return new List<BlockDesignator>{ | |
68 | + return new List<BlockDesignator>{ | |
69 | 69 | DefaultDesignators.Roads, |
70 | 70 | DefaultDesignators.GroundSigns, |
71 | 71 | DefaultDesignators.WallSigns, |
@@ -74,7 +74,7 @@ namespace Automap | ||
74 | 74 | }; |
75 | 75 | } |
76 | 76 | |
77 | - public static List<EntityDesignator> DefaultEntityDesignators( ) | |
77 | + public static List<EntityDesignator> DefaultEntityDesignators() | |
78 | 78 | { |
79 | 79 | return new List<EntityDesignator>{ |
80 | 80 | DefaultDesignators.Traders, |
@@ -83,82 +83,92 @@ namespace Automap | ||
83 | 83 | |
84 | 84 | internal static void DecodeSign(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block) |
85 | 85 | { |
86 | - clientAPI.Logger.VerboseDebug("Sign Designator Invoked!"); | |
87 | - //sign Text into a POI field... | |
88 | - BlockEntitySign signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySign; | |
89 | - | |
90 | - if (signEntity != null && !String.IsNullOrEmpty(signEntity.text)) | |
91 | - { | |
92 | - | |
93 | - poi.AddReplace( | |
94 | - new PointOfInterest { | |
95 | - Location = posn.Copy( ), | |
96 | - Notes = signEntity.text, | |
97 | - Timestamp = DateTimeOffset.UtcNow, | |
98 | - } | |
99 | - ); | |
100 | - | |
101 | - } | |
86 | +#if DEBUG | |
87 | + clientAPI.Logger.VerboseDebug("Sign Designator Invoked!"); | |
88 | +#endif | |
89 | + //sign Text into a POI field... | |
90 | + BlockEntitySign signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySign; | |
91 | + | |
92 | + if (signEntity != null && !String.IsNullOrEmpty(signEntity.text)) | |
93 | + { | |
94 | + | |
95 | + poi.AddReplace( | |
96 | + new PointOfInterest | |
97 | + { | |
98 | + Location = posn.Copy(), | |
99 | + Notes = signEntity.text, | |
100 | + Timestamp = DateTimeOffset.UtcNow, | |
101 | + } | |
102 | + ); | |
103 | + | |
104 | + } | |
102 | 105 | |
103 | 106 | } |
104 | 107 | |
105 | 108 | |
106 | 109 | internal static void DecodePostSign(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block) |
107 | 110 | { |
108 | - clientAPI.Logger.VerboseDebug("Post-sign Designator Invoked!"); | |
109 | - //sign post Text into a POI field... | |
110 | - BlockEntitySignPost signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySignPost; | |
111 | - | |
112 | - if (signEntity != null && signEntity.textByCardinalDirection?.Length > 0 ) { | |
113 | - | |
114 | - poi.AddReplace( | |
115 | - new PointOfInterest { | |
116 | - Location = posn.Copy( ), | |
117 | - Notes = string.Join(",", signEntity.textByCardinalDirection), | |
118 | - Timestamp = DateTimeOffset.UtcNow, | |
119 | - } | |
120 | - ); | |
121 | - | |
122 | - } | |
111 | +#if DEBUG | |
112 | + clientAPI.Logger.VerboseDebug("Post-sign Designator Invoked!"); | |
113 | +#endif | |
114 | + //sign post Text into a POI field... | |
115 | + BlockEntitySignPost signEntity = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntitySignPost; | |
116 | + | |
117 | + if (signEntity != null && signEntity.textByCardinalDirection?.Length > 0) | |
118 | + { | |
119 | + | |
120 | + poi.AddReplace( | |
121 | + new PointOfInterest | |
122 | + { | |
123 | + Location = posn.Copy(), | |
124 | + Notes = string.Join(",", signEntity.textByCardinalDirection), | |
125 | + Timestamp = DateTimeOffset.UtcNow, | |
126 | + } | |
127 | + ); | |
128 | + | |
129 | + } | |
123 | 130 | } |
124 | 131 | |
125 | 132 | internal static void KeepTrackOfMerchant(ICoreClientAPI clientAPI, EntitiesOfInterest poi, BlockPos posn, Entity entity) |
126 | 133 | { |
127 | - clientAPI.Logger.VerboseDebug("Trader: {0} @ {1}", entity.GetName( ), posn); | |
128 | - | |
129 | - var message = $"{entity.GetName( )}"; | |
130 | - var traderJoe = entity as EntityTrader; | |
131 | - if (traderJoe.TradeProps != null) { | |
132 | - message = $"{traderJoe.GetName( )} Alive:{traderJoe.Alive} - Gears: {traderJoe.TradeProps.Money}, "; | |
133 | - } | |
134 | - poi.Upsert(entity, message); | |
134 | + clientAPI.Logger.VerboseDebug("Trader: {0} @ {1}", entity.GetName(), posn); | |
135 | + | |
136 | + var message = $"{entity.GetName()}"; | |
137 | + var traderJoe = entity as EntityTrader; | |
138 | + if (traderJoe.TradeProps != null) | |
139 | + { | |
140 | + message = $"{traderJoe.GetName()} Alive:{traderJoe.Alive} - Gears: {traderJoe.TradeProps.Money}, "; | |
141 | + } | |
142 | + poi.Upsert(entity, message); | |
135 | 143 | } |
136 | 144 | |
137 | 145 | internal static void DecodeTranslocator(ICoreClientAPI clientAPI, PointsOfInterest poi, BlockPos posn, Block block) |
138 | 146 | { |
139 | - clientAPI.Logger.VerboseDebug("TRANSLOCATOR Designator Invoked!"); | |
140 | - //Where to? and from! | |
141 | - | |
142 | - BlockEntityStaticTranslocator te = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntityStaticTranslocator; | |
147 | + clientAPI.Logger.VerboseDebug("TRANSLOCATOR Designator Invoked!"); | |
148 | + //Where to? and from! | |
143 | 149 | |
144 | - if (te != null ) { | |
150 | + BlockEntityStaticTranslocator te = clientAPI.World.BlockAccessor.GetBlockEntity(posn) as BlockEntityStaticTranslocator; | |
145 | 151 | |
146 | - StringBuilder textTarget = new StringBuilder( ); | |
147 | - //translocatorEntity.GetBlockInfo(clientAPI.World.Player, textTarget); | |
152 | + if (te != null) | |
153 | + { | |
148 | 154 | |
149 | - textTarget.Append(te.Activated ? "Online " : "offline "); | |
150 | - textTarget.Append(" Dest.: "); | |
151 | - textTarget.Append(te.TargetLocation != null ? te.TargetLocation.PrettyCoords(clientAPI) : "???");//Or ABS coords? | |
155 | + StringBuilder textTarget = new StringBuilder(); | |
156 | + //translocatorEntity.GetBlockInfo(clientAPI.World.Player, textTarget); | |
152 | 157 | |
153 | - poi.AddReplace( | |
154 | - new PointOfInterest { | |
155 | - Location = posn.Copy( ), | |
156 | - Notes = textTarget.ToString(), | |
157 | - Timestamp = DateTimeOffset.UtcNow, | |
158 | - } | |
159 | - ); | |
158 | + textTarget.Append(te.Activated ? "Online " : "offline "); | |
159 | + textTarget.Append(" Dest.: "); | |
160 | + textTarget.Append(te.TargetLocation != null ? te.TargetLocation.PrettyCoords(clientAPI) : "???");//Or ABS coords? | |
160 | 161 | |
161 | - } | |
162 | + poi.AddReplace( | |
163 | + new PointOfInterest | |
164 | + { | |
165 | + Location = posn.Copy(), | |
166 | + Notes = textTarget.ToString(), | |
167 | + Timestamp = DateTimeOffset.UtcNow, | |
168 | + } | |
169 | + ); | |
170 | + | |
171 | + } | |
162 | 172 | } |
163 | 173 | } |
164 | 174 | } |
@@ -9,127 +9,127 @@ using Vintagestory.API.MathTools; | ||
9 | 9 | |
10 | 10 | namespace Automap |
11 | 11 | { |
12 | - public class AlternateRenderer : IChunkRenderer | |
13 | - { | |
14 | - private readonly int chunkSize; | |
15 | - | |
16 | - | |
17 | - /// <summary> | |
18 | - /// V.G.D:'s Alternative renderer | |
19 | - /// </summary> | |
20 | - /// <param name="clientAPI">Client API.</param> | |
21 | - /// <param name="logger">Logger.</param> | |
22 | - public AlternateRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger) | |
23 | - { | |
24 | - chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; | |
25 | - } | |
26 | - | |
27 | - public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount) | |
28 | - { | |
29 | - pixelCount = 0; | |
30 | - BlockPos tmpPos = new BlockPos(); | |
31 | - Vec2i localpos = new Vec2i(); | |
32 | - var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize]; | |
33 | - | |
34 | - int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate! | |
35 | - //Metadata of DateTime chunk was edited, chunk coords.,world-seed? Y-Max feature height | |
36 | - //Grab a chunk COLUMN... Topmost Y down... | |
37 | - for (int chunkY = 0; chunkY <= topChunkY; chunkY++) | |
38 | - { | |
39 | - chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y); | |
40 | - //What to do if chunk is a void? invalid? | |
41 | - } | |
42 | - | |
43 | - //pre-create PNG line slices... | |
44 | - ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray(); | |
45 | - ushort[] allMapYs = mc.RainHeightMap; | |
46 | - for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++) | |
47 | - { | |
48 | - int currY = allMapYs[posIndex]; | |
49 | - int localChunkY = currY / chunkSize; | |
50 | - if (localChunkY >= (chunksColumn.Length)) continue; //Out of range! | |
51 | - if (chunksColumn[localChunkY] == null) continue; | |
52 | - | |
53 | - MapUtil.PosInt2d(posIndex, chunkSize, localpos); | |
54 | - int localX = localpos.X; | |
55 | - int localZ = localpos.Y; | |
56 | - | |
57 | - chunksColumn[localChunkY].Unpack(); | |
58 | - int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localX, currY % chunkSize, localZ, chunkSize, chunkSize)]; | |
59 | - | |
60 | - Block block = ClientAPI.World.Blocks[blockId]; | |
61 | - | |
62 | - tmpPos.Set(chunkSize * chunkPos.X + localX, currY, chunkSize * chunkPos.Y + localZ); | |
63 | - | |
64 | - int red; | |
65 | - int green; | |
66 | - int blue; | |
67 | - | |
68 | - //============ POI Population ================= | |
69 | - if (BlockID_Designators.ContainsKey(blockId)) | |
70 | - { | |
71 | - var desig = BlockID_Designators[blockId]; | |
72 | - red = desig.OverwriteColor.R; | |
73 | - green = desig.OverwriteColor.G; | |
74 | - blue = desig.OverwriteColor.B; | |
75 | - | |
76 | - | |
77 | - ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue); | |
78 | - continue; | |
79 | - } | |
80 | - | |
81 | - float b = GetSlope(localX, localZ, allMapYs); | |
82 | - | |
83 | - int col = block.GetColor(ClientAPI, tmpPos); | |
84 | - int packedFormat = ColorUtil.ColorMultiply3Clamped(col, b); | |
85 | - | |
86 | - red = ColorUtil.ColorB(packedFormat); | |
87 | - green = ColorUtil.ColorG(packedFormat); | |
88 | - blue = ColorUtil.ColorR(packedFormat); | |
89 | - ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue); | |
90 | - | |
91 | - //chunkImage.SetPixel(localX, localZ, pixelColor); | |
92 | - pixelCount++; | |
93 | - } | |
94 | - | |
95 | - for (int row = 0; row < pngWriter.ImgInfo.Rows; row++) | |
96 | - { | |
97 | - pngWriter.WriteRow(lines[row], row); | |
98 | - } | |
99 | - | |
100 | - pngWriter.End(); | |
101 | - } | |
102 | - | |
103 | - private float GetSlope(int x, int y, ushort[] heightMap) | |
104 | - { | |
105 | - int baseY = heightMap[MapUtil.Index2d(x, y, chunkSize)]; | |
106 | - float runningY = 0; | |
107 | - // check bounds. i hate this. | |
108 | - int locIndex; | |
109 | - if (x > 0) | |
110 | - { | |
111 | - locIndex = MapUtil.Index2d(x - 1, y, chunkSize); | |
112 | - runningY += (baseY - heightMap[locIndex]); | |
113 | - } | |
114 | - if (x < chunkSize - 1) | |
115 | - { | |
116 | - locIndex = MapUtil.Index2d(x + 1, y, chunkSize); | |
117 | - runningY += (baseY - heightMap[locIndex]); | |
118 | - } | |
119 | - if (y > 0) | |
120 | - { | |
121 | - locIndex = MapUtil.Index2d(x, y - 1, chunkSize); | |
122 | - runningY += (baseY - heightMap[locIndex]); | |
123 | - } | |
124 | - if (y < chunkSize - 1) | |
125 | - { | |
126 | - locIndex = MapUtil.Index2d(x, y + 1, chunkSize); | |
127 | - runningY += (baseY - heightMap[locIndex]); | |
128 | - } | |
129 | - runningY /= 4; // average now | |
130 | - runningY /= 5; // idk | |
131 | - return 1 + runningY; | |
132 | - } | |
133 | - } | |
12 | + public class AlternateRenderer : IChunkRenderer | |
13 | + { | |
14 | + private readonly int chunkSize; | |
15 | + | |
16 | + | |
17 | + /// <summary> | |
18 | + /// V.G.D:'s Alternative renderer | |
19 | + /// </summary> | |
20 | + /// <param name="clientAPI">Client API.</param> | |
21 | + /// <param name="logger">Logger.</param> | |
22 | + public AlternateRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger) | |
23 | + { | |
24 | + chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; | |
25 | + } | |
26 | + | |
27 | + public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount) | |
28 | + { | |
29 | + pixelCount = 0; | |
30 | + BlockPos tmpPos = new BlockPos(); | |
31 | + Vec2i localpos = new Vec2i(); | |
32 | + var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize]; | |
33 | + | |
34 | + int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate! | |
35 | + //Metadata of DateTime chunk was edited, chunk coords.,world-seed? Y-Max feature height | |
36 | + //Grab a chunk COLUMN... Topmost Y down... | |
37 | + for (int chunkY = 0; chunkY <= topChunkY; chunkY++) | |
38 | + { | |
39 | + chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y); | |
40 | + //What to do if chunk is a void? invalid? | |
41 | + } | |
42 | + | |
43 | + //pre-create PNG line slices... | |
44 | + ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray(); | |
45 | + ushort[] allMapYs = mc.RainHeightMap; | |
46 | + for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++) | |
47 | + { | |
48 | + int currY = allMapYs[posIndex]; | |
49 | + int localChunkY = currY / chunkSize; | |
50 | + if (localChunkY >= (chunksColumn.Length)) continue; //Out of range! | |
51 | + if (chunksColumn[localChunkY] == null) continue; | |
52 | + | |
53 | + MapUtil.PosInt2d(posIndex, chunkSize, localpos); | |
54 | + int localX = localpos.X; | |
55 | + int localZ = localpos.Y; | |
56 | + | |
57 | + chunksColumn[localChunkY].Unpack(); | |
58 | + int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localX, currY % chunkSize, localZ, chunkSize, chunkSize)]; | |
59 | + | |
60 | + Block block = ClientAPI.World.Blocks[blockId]; | |
61 | + | |
62 | + tmpPos.Set(chunkSize * chunkPos.X + localX, currY, chunkSize * chunkPos.Y + localZ); | |
63 | + | |
64 | + int red; | |
65 | + int green; | |
66 | + int blue; | |
67 | + | |
68 | + //============ POI Population ================= | |
69 | + if (BlockID_Designators.ContainsKey(blockId)) | |
70 | + { | |
71 | + var desig = BlockID_Designators[blockId]; | |
72 | + red = desig.OverwriteColor.R; | |
73 | + green = desig.OverwriteColor.G; | |
74 | + blue = desig.OverwriteColor.B; | |
75 | + | |
76 | + | |
77 | + ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue); | |
78 | + continue; | |
79 | + } | |
80 | + | |
81 | + float b = GetSlope(localX, localZ, allMapYs); | |
82 | + | |
83 | + int col = block.GetColor(ClientAPI, tmpPos); | |
84 | + int packedFormat = ColorUtil.ColorMultiply3Clamped(col, b); | |
85 | + | |
86 | + red = ColorUtil.ColorB(packedFormat); | |
87 | + green = ColorUtil.ColorG(packedFormat); | |
88 | + blue = ColorUtil.ColorR(packedFormat); | |
89 | + ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue); | |
90 | + | |
91 | + //chunkImage.SetPixel(localX, localZ, pixelColor); | |
92 | + pixelCount++; | |
93 | + } | |
94 | + | |
95 | + for (int row = 0; row < pngWriter.ImgInfo.Rows; row++) | |
96 | + { | |
97 | + pngWriter.WriteRow(lines[row], row); | |
98 | + } | |
99 | + | |
100 | + pngWriter.End(); | |
101 | + } | |
102 | + | |
103 | + private float GetSlope(int x, int y, ushort[] heightMap) | |
104 | + { | |
105 | + int baseY = heightMap[MapUtil.Index2d(x, y, chunkSize)]; | |
106 | + float runningY = 0; | |
107 | + // check bounds. i hate this. | |
108 | + int locIndex; | |
109 | + if (x > 0) | |
110 | + { | |
111 | + locIndex = MapUtil.Index2d(x - 1, y, chunkSize); | |
112 | + runningY += (baseY - heightMap[locIndex]); | |
113 | + } | |
114 | + if (x < chunkSize - 1) | |
115 | + { | |
116 | + locIndex = MapUtil.Index2d(x + 1, y, chunkSize); | |
117 | + runningY += (baseY - heightMap[locIndex]); | |
118 | + } | |
119 | + if (y > 0) | |
120 | + { | |
121 | + locIndex = MapUtil.Index2d(x, y - 1, chunkSize); | |
122 | + runningY += (baseY - heightMap[locIndex]); | |
123 | + } | |
124 | + if (y < chunkSize - 1) | |
125 | + { | |
126 | + locIndex = MapUtil.Index2d(x, y + 1, chunkSize); | |
127 | + runningY += (baseY - heightMap[locIndex]); | |
128 | + } | |
129 | + runningY /= 4; // average now | |
130 | + runningY /= 5; // idk | |
131 | + return 1 + runningY; | |
132 | + } | |
133 | + } | |
134 | 134 | } |
135 | 135 |
@@ -15,11 +15,11 @@ namespace Automap | ||
15 | 15 | public virtual ILogger Logger { get; protected set; } |
16 | 16 | public virtual Dictionary<int, BlockDesignator> BlockID_Designators { get; set; } |
17 | 17 | |
18 | - protected IChunkRenderer(ICoreClientAPI clientAPI, ILogger logger) | |
19 | - { | |
20 | - this.ClientAPI = clientAPI; | |
21 | - this.Logger = logger; | |
22 | - } | |
18 | + protected IChunkRenderer(ICoreClientAPI clientAPI, ILogger logger) | |
19 | + { | |
20 | + this.ClientAPI = clientAPI; | |
21 | + this.Logger = logger; | |
22 | + } | |
23 | 23 | |
24 | 24 | public abstract void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mapChunk, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount); |
25 | 25 | } |
@@ -9,153 +9,153 @@ using Vintagestory.API.MathTools; | ||
9 | 9 | |
10 | 10 | namespace Automap |
11 | 11 | { |
12 | - public class StandardRenderer : IChunkRenderer | |
13 | - { | |
14 | - private readonly int chunkSize; | |
15 | - | |
16 | - | |
17 | - /// <summary> | |
18 | - /// Renders shards similar to the Default VS version, plus P.O.I. markings. | |
19 | - /// </summary> | |
20 | - /// <param name="clientAPI">Client API.</param> | |
21 | - /// <param name="logger">Logger.</param> | |
22 | - public StandardRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger) | |
23 | - { | |
24 | - chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; | |
25 | - } | |
26 | - | |
27 | - public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount) | |
28 | - { | |
29 | - pixelCount = 0; | |
30 | - BlockPos tmpPos = new BlockPos(); | |
31 | - Vec2i localpos = new Vec2i(); | |
32 | - | |
33 | - var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize]; | |
34 | - | |
35 | - int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate! | |
36 | - | |
37 | - for (int chunkY = 0; chunkY <= topChunkY; chunkY++) | |
38 | - { | |
39 | - chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y); | |
40 | - //What to do if chunk is a void? invalid? | |
41 | - } | |
42 | - | |
43 | - // Prefetch map chunks, in pattern | |
44 | - IMapChunk[] mapChunks = new IMapChunk[] | |
45 | - { | |
46 | - ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y - 1), | |
47 | - ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y), | |
48 | - ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X, chunkPos.Y - 1) | |
49 | - }; | |
50 | - | |
51 | - //pre-create PNG line slices... | |
52 | - ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray(); | |
53 | - | |
54 | - for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++) | |
55 | - { | |
56 | - int mapY = mc.RainHeightMap[posIndex]; | |
57 | - int localChunkY = mapY / chunkSize; | |
58 | - if (localChunkY >= (chunksColumn.Length)) continue;//Out of range! | |
59 | - | |
60 | - MapUtil.PosInt2d(posIndex, chunkSize, localpos); | |
61 | - int localX = localpos.X; | |
62 | - int localZ = localpos.Y; | |
63 | - | |
64 | - float b = 1; | |
65 | - int leftTop, rightTop, leftBot; | |
66 | - | |
67 | - IMapChunk leftTopMapChunk = mc; | |
68 | - IMapChunk rightTopMapChunk = mc; | |
69 | - IMapChunk leftBotMapChunk = mc; | |
70 | - | |
71 | - int topX = localX - 1; | |
72 | - int botX = localX; | |
73 | - int leftZ = localZ - 1; | |
74 | - int rightZ = localZ; | |
75 | - | |
76 | - if (topX < 0 && leftZ < 0) | |
77 | - { | |
78 | - leftTopMapChunk = mapChunks[0]; | |
79 | - rightTopMapChunk = mapChunks[1]; | |
80 | - leftBotMapChunk = mapChunks[2]; | |
81 | - } | |
82 | - else | |
83 | - { | |
84 | - if (topX < 0) | |
85 | - { | |
86 | - leftTopMapChunk = mapChunks[1]; | |
87 | - rightTopMapChunk = mapChunks[1]; | |
88 | - } | |
89 | - if (leftZ < 0) | |
90 | - { | |
91 | - leftTopMapChunk = mapChunks[2]; | |
92 | - leftBotMapChunk = mapChunks[2]; | |
93 | - } | |
94 | - } | |
95 | - | |
96 | - topX = GameMath.Mod(topX, chunkSize); | |
97 | - leftZ = GameMath.Mod(leftZ, chunkSize); | |
98 | - | |
99 | - leftTop = leftTopMapChunk == null ? 0 : Math.Sign(mapY - leftTopMapChunk.RainHeightMap[leftZ * chunkSize + topX]); | |
100 | - rightTop = rightTopMapChunk == null ? 0 : Math.Sign(mapY - rightTopMapChunk.RainHeightMap[rightZ * chunkSize + topX]); | |
101 | - leftBot = leftBotMapChunk == null ? 0 : Math.Sign(mapY - leftBotMapChunk.RainHeightMap[leftZ * chunkSize + botX]); | |
102 | - | |
103 | - float slopeness = (leftTop + rightTop + leftBot); | |
104 | - | |
105 | - if (slopeness > 0) b = 1.2f; | |
106 | - if (slopeness < 0) b = 0.8f; | |
107 | - | |
108 | - b -= 0.15f; //Slope boost value | |
109 | - | |
110 | - if (chunksColumn[localChunkY] == null) | |
111 | - { | |
112 | - | |
113 | - continue; | |
114 | - } | |
115 | - | |
116 | - chunksColumn[localChunkY].Unpack(); | |
117 | - int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localpos.X, mapY % chunkSize, localpos.Y, chunkSize, chunkSize)]; | |
118 | - | |
119 | - Block block = ClientAPI.World.Blocks[blockId]; | |
120 | - | |
121 | - tmpPos.Set(chunkSize * chunkPos.X + localpos.X, mapY, chunkSize * chunkPos.Y + localpos.Y); | |
122 | - | |
123 | - int avgCol = block.GetColor(ClientAPI, tmpPos); | |
124 | - int rndCol = block.GetRandomColor(ClientAPI, tmpPos, BlockFacing.UP); | |
125 | - int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.125f); | |
126 | - var packedFormat = ColorUtil.ColorMultiply3Clamped(col, b); | |
127 | - | |
128 | - int red = ColorUtil.ColorB(packedFormat); | |
129 | - int green = ColorUtil.ColorG(packedFormat); | |
130 | - int blue = ColorUtil.ColorR(packedFormat); | |
131 | - | |
132 | - | |
133 | - //============ POI Population ================= | |
134 | - if (BlockID_Designators.ContainsKey(blockId)) | |
135 | - { | |
136 | - var desig = BlockID_Designators[blockId]; | |
137 | - | |
138 | - if (desig.Enabled) | |
139 | - { | |
140 | - red = desig.OverwriteColor.R; | |
141 | - green = desig.OverwriteColor.G; | |
142 | - blue = desig.OverwriteColor.B; | |
143 | - } | |
144 | - } | |
145 | - | |
146 | - ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue); | |
147 | - | |
148 | - //chunkImage.SetPixel(localX, localZ, pixelColor); | |
149 | - pixelCount++; | |
150 | - } | |
151 | - | |
152 | - for (int row = 0; row < pngWriter.ImgInfo.Rows; row++) | |
153 | - { | |
154 | - pngWriter.WriteRow(lines[row], row); | |
155 | - } | |
156 | - | |
157 | - pngWriter.End(); | |
158 | - } | |
159 | - } | |
12 | + public class StandardRenderer : IChunkRenderer | |
13 | + { | |
14 | + private readonly int chunkSize; | |
15 | + | |
16 | + | |
17 | + /// <summary> | |
18 | + /// Renders shards similar to the Default VS version, plus P.O.I. markings. | |
19 | + /// </summary> | |
20 | + /// <param name="clientAPI">Client API.</param> | |
21 | + /// <param name="logger">Logger.</param> | |
22 | + public StandardRenderer(ICoreClientAPI clientAPI, ILogger logger) : base(clientAPI, logger) | |
23 | + { | |
24 | + chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; | |
25 | + } | |
26 | + | |
27 | + public override void GenerateChunkPngShard(Vec2i chunkPos, IMapChunk mc, ColumnMeta metaData, PngWriter pngWriter, out uint pixelCount) | |
28 | + { | |
29 | + pixelCount = 0; | |
30 | + BlockPos tmpPos = new BlockPos(); | |
31 | + Vec2i localpos = new Vec2i(); | |
32 | + | |
33 | + var chunksColumn = new IWorldChunk[ClientAPI.World.BlockAccessor.MapSizeY / chunkSize]; | |
34 | + | |
35 | + int topChunkY = mc.YMax / chunkSize;//Heywaitaminute -- this isn't a highest FEATURE, if Rainmap isn't accurate! | |
36 | + | |
37 | + for (int chunkY = 0; chunkY <= topChunkY; chunkY++) | |
38 | + { | |
39 | + chunksColumn[chunkY] = ClientAPI.World.BlockAccessor.GetChunk(chunkPos.X, chunkY, chunkPos.Y); | |
40 | + //What to do if chunk is a void? invalid? | |
41 | + } | |
42 | + | |
43 | + // Prefetch map chunks, in pattern | |
44 | + IMapChunk[] mapChunks = new IMapChunk[] | |
45 | + { | |
46 | + ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y - 1), | |
47 | + ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X - 1, chunkPos.Y), | |
48 | + ClientAPI.World.BlockAccessor.GetMapChunk(chunkPos.X, chunkPos.Y - 1) | |
49 | + }; | |
50 | + | |
51 | + //pre-create PNG line slices... | |
52 | + ImageLine[] lines = Enumerable.Repeat(new object(), chunkSize).Select(l => new ImageLine(pngWriter.ImgInfo)).ToArray(); | |
53 | + | |
54 | + for (int posIndex = 0; posIndex < (chunkSize * chunkSize); posIndex++) | |
55 | + { | |
56 | + int mapY = mc.RainHeightMap[posIndex]; | |
57 | + int localChunkY = mapY / chunkSize; | |
58 | + if (localChunkY >= (chunksColumn.Length)) continue;//Out of range! | |
59 | + | |
60 | + MapUtil.PosInt2d(posIndex, chunkSize, localpos); | |
61 | + int localX = localpos.X; | |
62 | + int localZ = localpos.Y; | |
63 | + | |
64 | + float b = 1; | |
65 | + int leftTop, rightTop, leftBot; | |
66 | + | |
67 | + IMapChunk leftTopMapChunk = mc; | |
68 | + IMapChunk rightTopMapChunk = mc; | |
69 | + IMapChunk leftBotMapChunk = mc; | |
70 | + | |
71 | + int topX = localX - 1; | |
72 | + int botX = localX; | |
73 | + int leftZ = localZ - 1; | |
74 | + int rightZ = localZ; | |
75 | + | |
76 | + if (topX < 0 && leftZ < 0) | |
77 | + { | |
78 | + leftTopMapChunk = mapChunks[0]; | |
79 | + rightTopMapChunk = mapChunks[1]; | |
80 | + leftBotMapChunk = mapChunks[2]; | |
81 | + } | |
82 | + else | |
83 | + { | |
84 | + if (topX < 0) | |
85 | + { | |
86 | + leftTopMapChunk = mapChunks[1]; | |
87 | + rightTopMapChunk = mapChunks[1]; | |
88 | + } | |
89 | + if (leftZ < 0) | |
90 | + { | |
91 | + leftTopMapChunk = mapChunks[2]; | |
92 | + leftBotMapChunk = mapChunks[2]; | |
93 | + } | |
94 | + } | |
95 | + | |
96 | + topX = GameMath.Mod(topX, chunkSize); | |
97 | + leftZ = GameMath.Mod(leftZ, chunkSize); | |
98 | + | |
99 | + leftTop = leftTopMapChunk == null ? 0 : Math.Sign(mapY - leftTopMapChunk.RainHeightMap[leftZ * chunkSize + topX]); | |
100 | + rightTop = rightTopMapChunk == null ? 0 : Math.Sign(mapY - rightTopMapChunk.RainHeightMap[rightZ * chunkSize + topX]); | |
101 | + leftBot = leftBotMapChunk == null ? 0 : Math.Sign(mapY - leftBotMapChunk.RainHeightMap[leftZ * chunkSize + botX]); | |
102 | + | |
103 | + float slopeness = (leftTop + rightTop + leftBot); | |
104 | + | |
105 | + if (slopeness > 0) b = 1.2f; | |
106 | + if (slopeness < 0) b = 0.8f; | |
107 | + | |
108 | + b -= 0.15f; //Slope boost value | |
109 | + | |
110 | + if (chunksColumn[localChunkY] == null) | |
111 | + { | |
112 | + | |
113 | + continue; | |
114 | + } | |
115 | + | |
116 | + chunksColumn[localChunkY].Unpack(); | |
117 | + int blockId = chunksColumn[localChunkY].Blocks[MapUtil.Index3d(localpos.X, mapY % chunkSize, localpos.Y, chunkSize, chunkSize)]; | |
118 | + | |
119 | + Block block = ClientAPI.World.Blocks[blockId]; | |
120 | + | |
121 | + tmpPos.Set(chunkSize * chunkPos.X + localpos.X, mapY, chunkSize * chunkPos.Y + localpos.Y); | |
122 | + | |
123 | + int avgCol = block.GetColor(ClientAPI, tmpPos); | |
124 | + int rndCol = block.GetRandomColor(ClientAPI, tmpPos, BlockFacing.UP); | |
125 | + int col = ColorUtil.ColorOverlay(avgCol, rndCol, 0.125f); | |
126 | + var packedFormat = ColorUtil.ColorMultiply3Clamped(col, b); | |
127 | + | |
128 | + int red = ColorUtil.ColorB(packedFormat); | |
129 | + int green = ColorUtil.ColorG(packedFormat); | |
130 | + int blue = ColorUtil.ColorR(packedFormat); | |
131 | + | |
132 | + | |
133 | + //============ POI Population ================= | |
134 | + if (BlockID_Designators.ContainsKey(blockId)) | |
135 | + { | |
136 | + var desig = BlockID_Designators[blockId]; | |
137 | + | |
138 | + if (desig.Enabled) | |
139 | + { | |
140 | + red = desig.OverwriteColor.R; | |
141 | + green = desig.OverwriteColor.G; | |
142 | + blue = desig.OverwriteColor.B; | |
143 | + } | |
144 | + } | |
145 | + | |
146 | + ImageLineHelper.SetPixel(lines[localZ], localX, red, green, blue); | |
147 | + | |
148 | + //chunkImage.SetPixel(localX, localZ, pixelColor); | |
149 | + pixelCount++; | |
150 | + } | |
151 | + | |
152 | + for (int row = 0; row < pngWriter.ImgInfo.Rows; row++) | |
153 | + { | |
154 | + pngWriter.WriteRow(lines[row], row); | |
155 | + } | |
156 | + | |
157 | + pngWriter.End(); | |
158 | + } | |
159 | + } | |
160 | 160 | } |
161 | 161 |
@@ -3,77 +3,154 @@ | ||
3 | 3 | |
4 | 4 | using Vintagestory.API.Client; |
5 | 5 | using Vintagestory.API.Common; |
6 | +using Vintagestory.API.Datastructures; | |
6 | 7 | |
7 | 8 | namespace Automap |
8 | 9 | { |
9 | - public class AutomapGUIDialog : GuiDialog | |
10 | - { | |
11 | - public const string _automapControlPanelKey = "automapControlPanelKey"; | |
12 | - | |
13 | - private ILogger Logger; | |
14 | - private AutomapSystem _automapSystem; | |
15 | - | |
16 | - public override string ToggleKeyCombinationCode | |
17 | - { | |
18 | - get | |
19 | - { | |
20 | - return _automapControlPanelKey; | |
21 | - } | |
22 | - } | |
23 | - | |
24 | - public AutomapGUIDialog(ICoreClientAPI capi, AutomapSystem ams) : base(capi) | |
25 | - { | |
26 | - _automapSystem = ams; | |
27 | - Logger = capi.Logger; | |
28 | - SetupDialog(); | |
29 | - } | |
30 | - | |
31 | - private void SetupDialog() | |
32 | - { | |
33 | - ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle); | |
34 | - | |
35 | - ElementBounds textBounds = ElementBounds.Fixed(0, 40, 500, 300); | |
36 | - | |
37 | - ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding); | |
38 | - bgBounds.BothSizing = ElementSizing.FitToChildren; | |
39 | - bgBounds.WithChildren(textBounds); | |
40 | - | |
41 | - ElementBounds toggleBounds = textBounds.CopyOffsetedSibling(3, 5, 5, 2); | |
42 | - toggleBounds.fixedHeight = 18; | |
43 | - toggleBounds.fixedWidth = 50; | |
44 | - | |
45 | - ElementBounds txtStatusBounds = textBounds.CopyOffsetedSibling(0, 20, 2, 4); | |
46 | - txtStatusBounds.fixedHeight = 16; | |
47 | - txtStatusBounds.percentWidth = 1; | |
48 | - | |
49 | - | |
50 | - this.SingleComposer = capi.Gui.CreateCompo("automapControlPanel", dialogBounds) | |
51 | - .AddShadedDialogBG(bgBounds) | |
52 | - .AddDialogTitleBar("Automap Controls", OnTitleBarCloseClicked) | |
53 | - .AddStaticText("Configure Automap settings:", CairoFont.WhiteDetailText(), textBounds) | |
54 | - .AddToggleButton("Run", CairoFont.ButtonText(), RunToggle, toggleBounds, "btnRun") | |
55 | - .AddStaticText("Idle.", CairoFont.WhiteSmallText().WithFontSize(12), txtStatusBounds, "txtStatus") | |
56 | - .Compose(); | |
57 | - } | |
58 | - | |
59 | - private void OnTitleBarCloseClicked() | |
60 | - { | |
61 | - TryClose(); | |
62 | - } | |
63 | - | |
64 | - /// <summary> | |
65 | - /// Toggle Automap from/to RUN state | |
66 | - /// </summary> | |
67 | - /// <returns>The toggle.</returns> | |
68 | - /// <param name="t1">T1.</param> | |
69 | - internal void RunToggle(bool toggle) | |
70 | - { | |
71 | - _automapSystem.Enabled = toggle; | |
72 | - Logger.VerboseDebug("Dialog Changed; [ Automap Enabled: {0} ]", toggle); | |
73 | - var statusText = this.SingleComposer.GetStaticText("txtStatus"); | |
74 | - statusText.SetValue($"Running: {this._automapSystem.Enabled}, Total: {this._automapSystem.updatedChunksTotal}, Nulls: {this._automapSystem.nullChunkCount} "); | |
75 | - | |
76 | - } | |
77 | - } | |
10 | + public class AutomapGUIDialog : GuiDialog | |
11 | + { | |
12 | + public const string _automapControlPanelKey = "automapControlPanelKey"; | |
13 | + private const string _statusTextKey = @"txtStatus"; | |
14 | + private const string _noteTextKey = @"edtNote"; | |
15 | + | |
16 | + private ILogger Logger; | |
17 | + | |
18 | + private long dashTickHandle; | |
19 | + | |
20 | + public override string ToggleKeyCombinationCode | |
21 | + { | |
22 | + get { | |
23 | + return _automapControlPanelKey; | |
24 | + } | |
25 | + } | |
26 | + | |
27 | + private uint totalShards, voidShards, changesThisTick; | |
28 | + private RunState lastState; | |
29 | + | |
30 | + | |
31 | + public AutomapGUIDialog(ICoreClientAPI capi, AutomapSystem ams) : base(capi) | |
32 | + { | |
33 | + | |
34 | + Logger = capi.Logger; | |
35 | + SetupDialog(); | |
36 | + capi.Event.RegisterEventBusListener(AutomapStatusMsg, 1.0D, AutomapSystem.AutomapStatusEventKey); | |
37 | + | |
38 | + } | |
39 | + | |
40 | + //Event for GUI status display | |
41 | + | |
42 | + private void SetupDialog() | |
43 | + { | |
44 | + ElementBounds dialogBounds = ElementStdBounds.AutosizedMainDialog.WithAlignment(EnumDialogArea.CenterMiddle); | |
45 | + | |
46 | + ElementBounds textBounds = ElementBounds.Fixed(0, 40, 500, 300); | |
47 | + | |
48 | + ElementBounds bgBounds = ElementBounds.Fill.WithFixedPadding(GuiStyle.ElementToDialogPadding); | |
49 | + bgBounds.BothSizing = ElementSizing.FitToChildren; | |
50 | + bgBounds.WithChildren(textBounds); | |
51 | + | |
52 | + ElementBounds toggleBounds = textBounds.CopyOffsetedSibling(0, 72, 5, 2); | |
53 | + toggleBounds.fixedHeight = 24; | |
54 | + toggleBounds.fixedWidth = 64; | |
55 | + | |
56 | + ElementBounds txtStatusBounds = textBounds.CopyOffsetedSibling(0, 26, 2, 4); | |
57 | + txtStatusBounds.fixedHeight = 16; | |
58 | + txtStatusBounds.percentWidth = 1; | |
59 | + | |
60 | + ElementBounds btnNoteArea = textBounds.CopyOffsetedSibling(0, 42, 2, 5); | |
61 | + btnNoteArea.fixedHeight = 24; | |
62 | + btnNoteArea.fixedWidth = 20; | |
63 | + | |
64 | + ElementBounds txtNoteArea = btnNoteArea.CopyOffsetedSibling(64, 0, 1, 6); | |
65 | + txtNoteArea.fixedHeight = 24; | |
66 | + txtNoteArea.fixedWidth = 256; | |
67 | + | |
68 | + | |
69 | + this.SingleComposer = capi.Gui.CreateCompo("automapControlPanel", dialogBounds) | |
70 | + .AddShadedDialogBG(bgBounds) | |
71 | + .AddDialogTitleBar("Automap Controls", OnTitleBarCloseClicked) | |
72 | + .AddStaticText("Configure Automap settings:", CairoFont.WhiteDetailText(), textBounds) | |
73 | + .AddToggleButton("Run", CairoFont.ButtonText(), RunToggle, toggleBounds, "btnRun") | |
74 | + .AddDynamicText("Idle.", CairoFont.WhiteSmallText().WithFontSize(12), EnumTextOrientation.Left, txtStatusBounds, _statusTextKey) | |
75 | + .AddTextInput(txtNoteArea, null, CairoFont.WhiteMediumText().WithFontSize(16), _noteTextKey) | |
76 | + .AddButton("Note:", CreateNote, btnNoteArea, CairoFont.ButtonText()) | |
77 | + .Compose(); | |
78 | + | |
79 | + //Controls for ALL Block & Entity Designators (Enable/Disable) | |
80 | + //_automapSystem.BlockID_Designators | |
81 | + //_automapSystem.Entity_Designators | |
82 | + //Renderer selection | |
83 | + //Message verbosity? Speed? | |
84 | + | |
85 | + //A Button to add POI - notes manually (when AM running) | |
86 | + | |
87 | + } | |
88 | + | |
89 | + private void OnTitleBarCloseClicked() | |
90 | + { | |
91 | + TryClose(); | |
92 | + } | |
93 | + | |
94 | + /// <summary> | |
95 | + /// Toggle Automap from/to RUN state | |
96 | + /// </summary> | |
97 | + /// <returns>The toggle.</returns> | |
98 | + /// <param name="toggle">Run.</param> | |
99 | + internal void RunToggle(bool toggle) | |
100 | + { | |
101 | + Logger.VerboseDebug("Dialog Changed; [ Automap Enabled: {0} ]", toggle); | |
102 | + var statusText = this.SingleComposer.GetDynamicText(_statusTextKey); | |
103 | + statusText.SetNewText($"State: {(toggle ? "Run" : "Halt")}, Total: {totalShards}, Nulls: {voidShards} "); | |
104 | + | |
105 | + CommandData cmd; | |
106 | + | |
107 | + if (toggle) | |
108 | + { | |
109 | + dashTickHandle = capi.Event.RegisterGameTickListener(UpdateDashDisplay, 6001); | |
110 | + cmd = new CommandData(toggle ? RunState.Run : RunState.Stop, new bool[] { true, true, true, true, true }); | |
111 | + } | |
112 | + else | |
113 | + { | |
114 | + capi.Event.UnregisterGameTickListener(dashTickHandle); | |
115 | + cmd = new CommandData(toggle ? RunState.Run : RunState.Stop); | |
116 | + } | |
117 | + | |
118 | + capi.Event.PushEvent(AutomapSystem.AutomapCommandEventKey, cmd); | |
119 | + } | |
120 | + | |
121 | + private bool CreateNote() | |
122 | + { | |
123 | + var noteCmd = new CommandData(RunState.Notation); | |
124 | + var txtNote = this.SingleComposer.GetTextInput(_noteTextKey); | |
125 | + if (!String.IsNullOrWhiteSpace(txtNote.GetText())) | |
126 | + { | |
127 | + noteCmd.Notation = txtNote.GetText(); | |
128 | + txtNote.SetValue(string.Empty); | |
129 | + | |
130 | + capi.Event.PushEvent(AutomapSystem.AutomapCommandEventKey, noteCmd); | |
131 | + } | |
132 | + return false;//??? | |
133 | + } | |
134 | + | |
135 | + private void AutomapStatusMsg(string eventName, ref EnumHandling handling, IAttribute data) | |
136 | + { | |
137 | + Logger.VerboseDebug("MsgBus RX: AutomapStatusMsg"); | |
138 | + StatusData realData = data as StatusData; | |
139 | + totalShards = realData.TotalUpdates; | |
140 | + voidShards = realData.VoidChunks; | |
141 | + changesThisTick = realData.Delta; | |
142 | + lastState = realData.CurrentState; | |
143 | + } | |
144 | + | |
145 | + private void UpdateDashDisplay(float delay) | |
146 | + { | |
147 | + var statusText = this.SingleComposer.GetDynamicText(_statusTextKey); | |
148 | + statusText.SetNewText($"State: {lastState}, Total: {totalShards}, Delta: {changesThisTick} Nulls: {voidShards} "); | |
149 | + | |
150 | + | |
151 | + } | |
152 | + | |
153 | + | |
154 | + } | |
78 | 155 | } |
79 | 156 |
@@ -16,617 +16,812 @@ using Hjg.Pngcs.Chunks; | ||
16 | 16 | using Vintagestory.API.Client; |
17 | 17 | using Vintagestory.API.Common; |
18 | 18 | using Vintagestory.API.Common.Entities; |
19 | +using Vintagestory.API.Config; | |
20 | +using Vintagestory.API.Datastructures; | |
19 | 21 | using Vintagestory.API.MathTools; |
20 | 22 | using Vintagestory.Common; |
21 | 23 | |
22 | 24 | namespace Automap |
23 | 25 | { |
24 | - public class AutomapSystem | |
25 | - { | |
26 | - private Thread cartographer_thread; | |
27 | - private ICoreClientAPI ClientAPI { get; set; } | |
28 | - private ILogger Logger { get; set; } | |
29 | - private IChunkRenderer ChunkRenderer { get; set; } | |
26 | + public class AutomapSystem | |
27 | + { | |
28 | + private Thread cartographer_thread; | |
29 | + private ICoreClientAPI ClientAPI { get; set; } | |
30 | + private ILogger Logger { get; set; } | |
31 | + private IChunkRenderer ChunkRenderer { get; set; } | |
30 | 32 | |
31 | - private const string _mapPath = @"Maps"; | |
32 | - private const string _chunkPath = @"Chunks"; | |
33 | - private const string _domain = @"automap"; | |
34 | - private const string chunkFile_filter = @"*_*.png"; | |
35 | - private static Regex chunkShardRegex = new Regex(@"(?<X>[\d]+)_(?<Z>[\d]+).png", RegexOptions.Singleline); | |
33 | + private const string _mapPath = @"Maps"; | |
34 | + private const string _chunkPath = @"Chunks"; | |
35 | + private const string _domain = @"automap"; | |
36 | + private const string chunkFile_filter = @"*_*.png"; | |
37 | + private static Regex chunkShardRegex = new Regex(@"(?<X>[\d]+)_(?<Z>[\d]+).png", RegexOptions.Singleline); | |
36 | 38 | |
37 | - private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>(3, 150); | |
38 | - private ColumnsMetadata chunkTopMetadata; | |
39 | - private PointsOfInterest POIs; | |
40 | - private EntitiesOfInterest EOIs; | |
39 | + private ConcurrentDictionary<Vec2i, uint> columnCounter = new ConcurrentDictionary<Vec2i, uint>(3, 150); | |
40 | + private ColumnsMetadata chunkTopMetadata; | |
41 | + private PointsOfInterest POIs = new PointsOfInterest(); | |
42 | + private EntitiesOfInterest EOIs = new EntitiesOfInterest(); | |
41 | 43 | |
42 | - internal Dictionary<int, BlockDesignator> BlockID_Designators { get; private set; } | |
43 | - internal Dictionary<AssetLocation, EntityDesignator> Entity_Designators { get; private set; } | |
44 | + internal Dictionary<int, BlockDesignator> BlockID_Designators { get; private set; } | |
45 | + internal Dictionary<AssetLocation, EntityDesignator> Entity_Designators { get; private set; } | |
46 | + internal Dictionary<int, string> RockIdCodes { get; private set; } | |
44 | 47 | |
45 | - internal bool Enabled { get; set; } | |
46 | - //Run status, Chunks processed, stats, center of map.... | |
47 | - internal uint nullChunkCount, updatedChunksTotal; | |
48 | - internal Vec2i startChunkColumn; | |
48 | + internal RunState CurrentState { get; set; } | |
49 | + //Run status, Chunks processed, stats, center of map.... | |
50 | + private uint nullChunkCount, updatedChunksTotal; | |
51 | + private Vec2i startChunkColumn; | |
49 | 52 | |
50 | - private readonly int chunkSize; | |
51 | - private string path; | |
52 | - private IAsset stylesFile; | |
53 | + private readonly int chunkSize; | |
54 | + private string path; | |
55 | + private IAsset stylesFile; | |
53 | 56 | |
57 | + public static string AutomapStatusEventKey = @"AutomapStatus"; | |
58 | + public static string AutomapCommandEventKey = @"AutomapCommand"; | |
54 | 59 | |
55 | - public AutomapSystem(ICoreClientAPI clientAPI, ILogger logger) | |
56 | - { | |
57 | - this.ClientAPI = clientAPI; | |
58 | - this.Logger = logger; | |
59 | - chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; | |
60 | - ClientAPI.Event.LevelFinalize += EngageAutomap; | |
61 | 60 | |
62 | - //TODO:Choose which one from GUI | |
63 | - this.ChunkRenderer = new StandardRenderer(clientAPI, logger); | |
64 | - } | |
61 | + public AutomapSystem(ICoreClientAPI clientAPI, ILogger logger) | |
62 | + { | |
63 | + this.ClientAPI = clientAPI; | |
64 | + this.Logger = logger; | |
65 | + chunkSize = ClientAPI.World.BlockAccessor.ChunkSize; | |
66 | + ClientAPI.Event.LevelFinalize += EngageAutomap; | |
65 | 67 | |
68 | + //TODO:Choose which one from GUI | |
69 | + this.ChunkRenderer = new StandardRenderer(clientAPI, logger); | |
66 | 70 | |
67 | - #region Internals | |
68 | - private void EngageAutomap() | |
69 | - { | |
70 | - path = ClientAPI.GetOrCreateDataPath(_mapPath); | |
71 | - path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));//Add name of World too...'ServerApi.WorldManager.CurrentWorldName' | |
71 | + //Listen on bus for commands | |
72 | + ClientAPI.Event.RegisterEventBusListener(CommandListener, 1.0, AutomapSystem.AutomapCommandEventKey); | |
72 | 73 | |
73 | - stylesFile = ClientAPI.World.AssetManager.Get(new AssetLocation(_domain, "config/automap_format.css")); | |
74 | - Logger.VerboseDebug("CSS loaded: {0} size: {1}", stylesFile.IsLoaded(), stylesFile.ToText().Length); | |
74 | + } | |
75 | 75 | |
76 | - Prefill_POI_Designators(); | |
77 | - startChunkColumn = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / chunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z / chunkSize)); | |
78 | - chunkTopMetadata = new ColumnsMetadata(startChunkColumn); | |
79 | 76 | |
80 | - Logger.Notification("AUTOMAP Start {0}", startChunkColumn); | |
81 | - Reload_Metadata(); | |
77 | + #region Internals | |
78 | + private void EngageAutomap() | |
79 | + { | |
80 | + path = ClientAPI.GetOrCreateDataPath(_mapPath); | |
81 | + path = ClientAPI.GetOrCreateDataPath(Path.Combine(path, "World_" + ClientAPI.World.Seed));//Add name of World too...'ServerApi.WorldManager.CurrentWorldName' | |
82 | 82 | |
83 | - ClientAPI.Event.ChunkDirty += ChunkAChanging; | |
83 | + stylesFile = ClientAPI.World.AssetManager.Get(new AssetLocation(_domain, "config/automap_format.css")); | |
84 | + Logger.VerboseDebug("CSS loaded: {0} size: {1}", stylesFile.IsLoaded(), stylesFile.ToText().Length); | |
84 | 85 | |
85 | - cartographer_thread = new Thread(Cartographer) | |
86 | - { | |
87 | - Name = "Cartographer", | |
88 | - Priority = ThreadPriority.Lowest, | |
89 | - IsBackground = true | |
90 | - }; | |
86 | + Prefill_POI_Designators(); | |
87 | + startChunkColumn = new Vec2i((ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.X / chunkSize), (ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Z / chunkSize)); | |
88 | + chunkTopMetadata = new ColumnsMetadata(startChunkColumn); | |
91 | 89 | |
92 | - ClientAPI.Event.RegisterGameTickListener(AwakenCartographer, 6000); | |
93 | - } | |
90 | + Logger.Notification("AUTOMAP Start {0}", startChunkColumn); | |
91 | + Reload_Metadata(); | |
94 | 92 | |
95 | - private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) | |
96 | - { | |
97 | - Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z); | |
93 | + ClientAPI.Event.ChunkDirty += ChunkAChanging; | |
98 | 94 | |
99 | - columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1); | |
100 | - } | |
95 | + cartographer_thread = new Thread(Cartographer) | |
96 | + { | |
97 | + Name = "Cartographer", | |
98 | + Priority = ThreadPriority.Lowest, | |
99 | + IsBackground = true | |
100 | + }; | |
101 | 101 | |
102 | - private void AwakenCartographer(float delayed) | |
103 | - { | |
102 | + ClientAPI.Event.RegisterGameTickListener(AwakenCartographer, 6000); | |
103 | + } | |
104 | 104 | |
105 | - if (Enabled && (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true)) | |
106 | - { | |
105 | + private void ChunkAChanging(Vec3i chunkCoord, IWorldChunk chunk, EnumChunkDirtyReason reason) | |
106 | + { | |
107 | + Vec2i topPosition = new Vec2i(chunkCoord.X, chunkCoord.Z); | |
108 | + | |
109 | + columnCounter.AddOrUpdate(topPosition, 1, (key, colAct) => colAct + 1); | |
110 | + } | |
111 | + | |
112 | + private void AwakenCartographer(float delayed) | |
113 | + { | |
114 | + | |
115 | + if (CurrentState == RunState.Run && (ClientAPI.IsGamePaused != false || ClientAPI.IsShuttingDown != true)) | |
116 | + { | |
107 | 117 | #if DEBUG |
108 | - Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState); | |
118 | + Logger.VerboseDebug("Cartographer re-trigger from [{0}]", cartographer_thread.ThreadState); | |
109 | 119 | #endif |
110 | 120 | |
111 | - if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted)) | |
112 | - { | |
113 | - cartographer_thread.Start(); | |
114 | - } | |
115 | - else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin)) | |
116 | - { | |
117 | - //Time to (re)write chunk shards | |
118 | - cartographer_thread.Interrupt(); | |
119 | - } | |
121 | + if (cartographer_thread.ThreadState.HasFlag(ThreadState.Unstarted)) | |
122 | + { | |
123 | + cartographer_thread.Start(); | |
124 | + } | |
125 | + else if (cartographer_thread.ThreadState.HasFlag(ThreadState.WaitSleepJoin)) | |
126 | + { | |
127 | + //Time to (re)write chunk shards | |
128 | + cartographer_thread.Interrupt(); | |
129 | + } | |
130 | + //#if DEBUG | |
131 | + //ClientAPI.TriggerChatMessage($"Automap {updatedChunksTotal} Updates - MAX (N:{chunkTopMetadata.North_mostChunk},S:{chunkTopMetadata.South_mostChunk},E:{chunkTopMetadata.East_mostChunk}, W:{chunkTopMetadata.West_mostChunk} - TOTAL: {chunkTopMetadata.Count})"); | |
132 | + //#endif | |
133 | + } | |
134 | + else if (CurrentState == RunState.Snapshot) | |
135 | + { | |
136 | + //TODO: Snapshot generator second thread... | |
137 | + } | |
138 | + | |
139 | + } | |
140 | + | |
141 | + | |
142 | + private void Cartographer() | |
143 | + { | |
144 | + wake: | |
145 | + Logger.VerboseDebug("Cartographer thread awoken"); | |
146 | + | |
147 | + try | |
148 | + { | |
149 | + uint ejectedItem = 0; | |
150 | + uint updatedChunks = 0; | |
151 | + | |
152 | + //-- Should dodge enumerator changing underfoot....at a cost. | |
153 | + if (!columnCounter.IsEmpty) | |
154 | + { | |
155 | + var tempSet = columnCounter.ToArray().OrderByDescending(kvp => kvp.Value); | |
156 | + foreach (var mostActiveCol in tempSet) | |
157 | + { | |
158 | + | |
159 | + var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key); | |
160 | + | |
161 | + if (mapChunk == null) | |
162 | + { | |
163 | + Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key); | |
164 | + nullChunkCount++; | |
165 | + columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); | |
166 | + continue; | |
167 | + } | |
168 | + | |
169 | + ColumnMeta chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk); | |
170 | + PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, chunkMeta); | |
171 | + UpdateEntityMetadata(); | |
172 | + ProcessChunkBlocks(mostActiveCol.Key, mapChunk, chunkMeta); | |
173 | + | |
174 | + uint updatedPixels = 0; | |
175 | + | |
176 | + ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, pngWriter, out updatedPixels); | |
177 | + | |
178 | + if (updatedPixels > 0) | |
179 | + { | |
180 | + | |
120 | 181 | #if DEBUG |
121 | - ClientAPI.TriggerChatMessage($"Automap {updatedChunksTotal} Updates - MAX (N:{chunkTopMetadata.North_mostChunk},S:{chunkTopMetadata.South_mostChunk},E:{chunkTopMetadata.East_mostChunk}, W:{chunkTopMetadata.West_mostChunk} - TOTAL: {chunkTopMetadata.Count})"); | |
182 | + Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels); | |
122 | 183 | #endif |
123 | - } | |
184 | + updatedChunks++; | |
185 | + chunkTopMetadata.Update(chunkMeta); | |
186 | + columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); | |
187 | + } | |
188 | + else | |
189 | + { | |
190 | + columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); | |
191 | + Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key); | |
192 | + } | |
124 | 193 | |
125 | - } | |
194 | + } | |
195 | + } | |
126 | 196 | |
197 | + UpdateStatus(this.updatedChunksTotal, this.nullChunkCount, updatedChunks); | |
127 | 198 | |
128 | - private void Cartographer() | |
129 | - { | |
130 | - wake: | |
131 | - Logger.VerboseDebug("Cartographer thread awoken"); | |
199 | + if (updatedChunks > 0) | |
200 | + { | |
201 | + //What about chunk updates themselves; a update bitmap isn't kept... | |
202 | + updatedChunksTotal += updatedChunks; | |
203 | + GenerateMapHTML(); | |
204 | + GenerateJSONMetadata(); | |
205 | + updatedChunks = 0; | |
206 | + } | |
207 | + | |
208 | + //Then sleep until interupted again, and repeat | |
209 | + | |
210 | + Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name); | |
211 | + | |
212 | + Thread.Sleep(Timeout.Infinite); | |
213 | + | |
214 | + } | |
215 | + catch (ThreadInterruptedException) | |
216 | + { | |
217 | + | |
218 | + Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name); | |
219 | + goto wake; | |
220 | + | |
221 | + } | |
222 | + catch (ThreadAbortException) | |
223 | + { | |
224 | + Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name); | |
225 | + | |
226 | + } | |
227 | + finally | |
228 | + { | |
229 | + Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name); | |
230 | + } | |
231 | + } | |
232 | + | |
233 | + private void UpdateStatus(uint totalUpdates, uint voidChunks, uint delta) | |
234 | + { | |
235 | + StatusData updateData = new StatusData(totalUpdates, voidChunks, delta, RunState.Run); | |
236 | + | |
237 | + this.ClientAPI.Event.PushEvent(AutomapStatusEventKey, updateData); | |
238 | + } | |
239 | + | |
240 | + private void Prefill_POI_Designators() | |
241 | + { | |
242 | + | |
243 | + this.BlockID_Designators = new Dictionary<int, BlockDesignator>(); | |
244 | + this.Entity_Designators = new Dictionary<AssetLocation, EntityDesignator>(); | |
245 | + this.RockIdCodes = Helpers.ArbitrarytBlockIdHunter(ClientAPI, new AssetLocation(GlobalConstants.DefaultDomain, "rock"), EnumBlockMaterial.Stone); | |
246 | + | |
247 | + //Add special marker types for BlockID's of "Interest", overwrite colour, and method | |
248 | + | |
249 | + Install_POI_Designators(DefaultDesignators.DefaultBlockDesignators(), DefaultDesignators.DefaultEntityDesignators()); | |
250 | + } | |
251 | + | |
252 | + private void Install_POI_Designators(ICollection<BlockDesignator> blockDesig, List<EntityDesignator> entDesig) | |
253 | + { | |
254 | + Logger.VerboseDebug("Connecting {0} standard Block-Designators", blockDesig.Count); | |
255 | + foreach (var designator in blockDesig) | |
256 | + { | |
257 | + var blockIDs = Helpers.ArbitrarytBlockIdHunter(ClientAPI, designator.Pattern, designator.Material); | |
258 | + if (blockIDs.Count > 0) { Logger.VerboseDebug("Designator {0} has {1} associated blockIDs", designator.ToString(), blockIDs.Count); } | |
259 | + foreach (var entry in blockIDs) | |
260 | + { | |
261 | + BlockID_Designators.Add(entry.Key, designator); | |
262 | + } | |
263 | + } | |
264 | + this.ChunkRenderer.BlockID_Designators = BlockID_Designators; | |
265 | + | |
266 | + | |
267 | + Logger.VerboseDebug("Connecting {0} standard Entity-Designators", entDesig.Count); | |
268 | + foreach (var designator in entDesig) | |
269 | + { | |
270 | + //Get Variants first, from EntityTypes...better be populated! | |
271 | + var matched = ClientAPI.World.EntityTypes.FindAll(entp => entp.Code.BeginsWith(designator.Pattern.Domain, designator.Pattern.Path)); | |
272 | + | |
273 | + foreach (var match in matched) | |
274 | + { | |
275 | + Logger.VerboseDebug("Linked Entity: {0} Designator: {1}", match.Code, designator); | |
276 | + this.Entity_Designators.Add(match.Code, designator); | |
277 | + } | |
278 | + | |
279 | + | |
280 | + | |
281 | + //EntityProperties props = ClientAPI.World.GetEntityType(designator.Pattern); | |
282 | + } | |
283 | + | |
284 | + | |
285 | + } | |
286 | + | |
287 | + | |
288 | + private void GenerateMapHTML() | |
289 | + { | |
290 | + string mapFilename = Path.Combine(path, "Automap.html"); | |
291 | + | |
292 | + int TopNorth = chunkTopMetadata.North_mostChunk; | |
293 | + int TopSouth = chunkTopMetadata.South_mostChunk; | |
294 | + int TopEast = chunkTopMetadata.East_mostChunk; | |
295 | + int TopWest = chunkTopMetadata.West_mostChunk; | |
296 | + | |
297 | + using (StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))) | |
298 | + { | |
299 | + using (HtmlTextWriter tableWriter = new HtmlTextWriter(outputText)) | |
300 | + { | |
301 | + tableWriter.BeginRender(); | |
302 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Html); | |
303 | + | |
304 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Head); | |
305 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Title); | |
306 | + tableWriter.WriteEncodedText("Generated Automap"); | |
307 | + tableWriter.RenderEndTag(); | |
308 | + //CSS style here | |
309 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Style); | |
310 | + tableWriter.Write(stylesFile.ToText()); | |
311 | + tableWriter.RenderEndTag();//</style> | |
312 | + | |
313 | + tableWriter.RenderEndTag(); | |
314 | + | |
315 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Body); | |
316 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.P); | |
317 | + tableWriter.WriteEncodedText($"Created {DateTimeOffset.UtcNow.ToString("u")}"); | |
318 | + tableWriter.RenderEndTag(); | |
319 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.P); | |
320 | + tableWriter.WriteEncodedText($"W:{TopWest}, E: {TopEast}, N:{TopNorth}, S:{TopSouth} "); | |
321 | + tableWriter.RenderEndTag(); | |
322 | + tableWriter.WriteLine(); | |
323 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Table); | |
324 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Caption); | |
325 | + tableWriter.WriteEncodedText($"Start: {startChunkColumn}, Seed: {ClientAPI.World.Seed}\n"); | |
326 | + tableWriter.RenderEndTag(); | |
327 | + | |
328 | + //################ X-Axis <thead> ####################### | |
329 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Thead); | |
330 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr); | |
331 | + | |
332 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Th); | |
333 | + tableWriter.Write("N, W"); | |
334 | + tableWriter.RenderEndTag(); | |
335 | + | |
336 | + for (int xAxisT = TopWest; xAxisT <= TopEast; xAxisT++) | |
337 | + { | |
338 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Th); | |
339 | + tableWriter.Write(xAxisT); | |
340 | + tableWriter.RenderEndTag(); | |
341 | + } | |
342 | + | |
343 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Th); | |
344 | + tableWriter.Write("N, E"); | |
345 | + tableWriter.RenderEndTag(); | |
346 | + | |
347 | + tableWriter.RenderEndTag(); | |
348 | + tableWriter.RenderEndTag(); | |
349 | + //###### </thead> ################################ | |
350 | + | |
351 | + //###### <tbody> - Chunk rows & Y-axis cols | |
352 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Tbody); | |
353 | + | |
354 | + //######## <tr> for every vertical row | |
355 | + for (int yAxis = TopNorth; yAxis <= TopSouth; yAxis++) | |
356 | + { | |
357 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr); | |
358 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
359 | + tableWriter.Write(yAxis);//legend: Y-axis | |
360 | + tableWriter.RenderEndTag(); | |
361 | + | |
362 | + for (int xAxis = TopWest; xAxis <= TopEast; xAxis++) | |
363 | + { | |
364 | + //###### <td> #### for chunk shard | |
365 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
366 | + var colLoc = new Vec2i(xAxis, yAxis); | |
367 | + if (chunkTopMetadata.Contains(colLoc)) | |
368 | + { | |
369 | + ColumnMeta meta = chunkTopMetadata[colLoc]; | |
370 | + //Tooltip first | |
371 | + tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltip"); | |
372 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Div); | |
373 | + | |
374 | + tableWriter.AddAttribute(HtmlTextWriterAttribute.Src, $"{xAxis}_{yAxis}.png"); | |
375 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Img); | |
376 | + tableWriter.RenderEndTag(); | |
377 | + // <span class="tooltiptext">Tooltip text | |
378 | + tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltiptext"); | |
379 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Span); | |
380 | + | |
381 | + StringBuilder tooltipText = new StringBuilder(); | |
382 | + tooltipText.Append($"{meta.Location.PrettyCoords(ClientAPI)} "); | |
383 | + tooltipText.Append($" Max-Height: {meta.YMax}, Temp: {meta.Temperature.ToString("F1")} "); | |
384 | + tooltipText.Append($" Rainfall: {meta.Rainfall.ToString("F1")}, "); | |
385 | + tooltipText.Append($" Shrubs: {meta.ShrubDensity.ToString("F1")}, "); | |
386 | + tooltipText.Append($" Forest: {meta.ForestDensity.ToString("F1")}, "); | |
387 | + tooltipText.Append($" Fertility: {meta.Fertility.ToString("F1")}, "); | |
388 | + | |
389 | + if (meta.RockRatio != null) | |
390 | + { | |
391 | + foreach (KeyValuePair<int, uint> blockID in meta.RockRatio) | |
392 | + { | |
393 | + var block = ClientAPI.World.GetBlock(blockID.Key); | |
394 | + tooltipText.AppendFormat(" {0} × {1},\t", block.Code.GetName(), meta.RockRatio[blockID.Key]); | |
395 | + } | |
396 | + } | |
397 | + | |
398 | + tableWriter.WriteEncodedText(tooltipText.ToString()); | |
399 | + | |
400 | + tableWriter.RenderEndTag();//</span> | |
401 | + | |
402 | + | |
403 | + tableWriter.RenderEndTag();//</div> --tooltip enclosure | |
404 | + } | |
405 | + else | |
406 | + { | |
407 | + tableWriter.Write("?"); | |
408 | + } | |
409 | + | |
410 | + tableWriter.RenderEndTag(); | |
411 | + }//############ </td> ########### | |
412 | + | |
413 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
414 | + tableWriter.Write(yAxis);//legend: Y-axis | |
415 | + tableWriter.RenderEndTag(); | |
416 | + | |
417 | + tableWriter.RenderEndTag(); | |
418 | + | |
419 | + } | |
420 | + tableWriter.RenderEndTag(); | |
421 | + | |
422 | + //################ X-Axis <tfoot> ####################### | |
423 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Tfoot); | |
424 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr); | |
425 | + | |
426 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
427 | + tableWriter.Write("S, W"); | |
428 | + tableWriter.RenderEndTag(); | |
429 | + | |
430 | + for (int xAxisB = TopWest; xAxisB <= TopEast; xAxisB++) | |
431 | + { | |
432 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
433 | + tableWriter.Write(xAxisB); | |
434 | + tableWriter.RenderEndTag(); | |
435 | + } | |
436 | + | |
437 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
438 | + tableWriter.Write("S, E"); | |
439 | + tableWriter.RenderEndTag(); | |
440 | + | |
441 | + tableWriter.RenderEndTag(); | |
442 | + tableWriter.RenderEndTag(); | |
443 | + //###### </tfoot> ################################ | |
444 | + | |
445 | + | |
446 | + tableWriter.RenderEndTag();//</table> | |
447 | + | |
448 | + //############## POI list ##################### | |
449 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.P); | |
450 | + tableWriter.WriteLine("Points of Interest"); | |
451 | + tableWriter.RenderEndTag(); | |
452 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Ul); | |
453 | + foreach (var poi in this.POIs) | |
454 | + { | |
455 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Li); | |
456 | + tableWriter.WriteEncodedText(poi.Location.PrettyCoords(this.ClientAPI) + "\t"); | |
457 | + tableWriter.WriteEncodedText(poi.Notes + "\t"); | |
458 | + tableWriter.WriteEncodedText(poi.Timestamp.ToString("u")); | |
459 | + tableWriter.RenderEndTag(); | |
460 | + } | |
461 | + | |
462 | + foreach (var eoi in this.EOIs.PointsList) | |
463 | + { | |
464 | + tableWriter.RenderBeginTag(HtmlTextWriterTag.Li); | |
465 | + tableWriter.WriteEncodedText(eoi.Location.PrettyCoords(this.ClientAPI) + "\t"); | |
466 | + tableWriter.WriteEncodedText(eoi.Notes + "\t"); | |
467 | + tableWriter.WriteEncodedText(eoi.Timestamp.ToString("u")); | |
468 | + tableWriter.RenderEndTag(); | |
469 | + } | |
470 | + | |
471 | + tableWriter.RenderEndTag(); | |
472 | + | |
473 | + | |
474 | + | |
475 | + | |
476 | + tableWriter.RenderEndTag();//### </BODY> ### | |
477 | + | |
478 | + tableWriter.EndRender(); | |
479 | + tableWriter.Flush(); | |
480 | + } | |
481 | + outputText.Flush(); | |
482 | + } | |
483 | + | |
484 | + Logger.VerboseDebug("Generated HTML map"); | |
485 | + } | |
486 | + | |
487 | + /// <summary> | |
488 | + /// Generates the JSON Metadata. (in MAP object format ) | |
489 | + /// </summary> | |
490 | + private void GenerateJSONMetadata() | |
491 | + { | |
492 | + string jsonFilename = Path.Combine(path, "Metadata.js"); | |
493 | + | |
494 | + StreamWriter jsonWriter = new StreamWriter(jsonFilename, false, Encoding.UTF8); | |
495 | + using (jsonWriter) | |
496 | + { | |
497 | + jsonWriter.Write("ViewFrame.chunks={};"); | |
498 | + jsonWriter.Write("ViewFrame.chunks.worldSeedNum={0};", ClientAPI.World.Seed); | |
499 | + jsonWriter.Write("ViewFrame.chunks.genTime=new Date('{0}');", DateTimeOffset.UtcNow.ToString("O")); | |
500 | + jsonWriter.Write("ViewFrame.chunks.startCoords=[{0},{1}];", startChunkColumn.X, startChunkColumn.Y); | |
501 | + jsonWriter.Write("ViewFrame.chunks.chunkSize={0};", chunkSize); | |
502 | + jsonWriter.Write("ViewFrame.chunks.northMostChunk={0};", chunkTopMetadata.North_mostChunk); | |
503 | + jsonWriter.Write("ViewFrame.chunks.southMostChunk={0};", chunkTopMetadata.South_mostChunk); | |
504 | + jsonWriter.Write("ViewFrame.chunks.eastMostChunk={0};", chunkTopMetadata.East_mostChunk); | |
505 | + jsonWriter.Write("ViewFrame.chunks.westMostChunk={0};", chunkTopMetadata.West_mostChunk); | |
506 | + //MAP object format - [key, value]: key is "x_y" | |
507 | + jsonWriter.Write("ViewFrame.chunks.chunkMetadata=new Map(["); | |
508 | + foreach (var shard in chunkTopMetadata) | |
509 | + { | |
510 | + jsonWriter.Write("['{0}_{1}',", shard.Location.X, shard.Location.Y); | |
511 | + jsonWriter.Write("{"); | |
512 | + jsonWriter.Write("prettyCoord:'{0}',", shard.Location.PrettyCoords(ClientAPI)); | |
513 | + jsonWriter.Write("chunkAge:'{0}',", shard.ChunkAge.ToString("g"));//World age - relative? or last edit ?? | |
514 | + jsonWriter.Write("temp:{0},", shard.Temperature.ToString("F1")); | |
515 | + jsonWriter.Write("YMax:{0},", shard.YMax); | |
516 | + jsonWriter.Write("fert:{0},", shard.Fertility.ToString("F1")); | |
517 | + jsonWriter.Write("forestDens:{0},", shard.ForestDensity.ToString("F1")); | |
518 | + jsonWriter.Write("rain:{0},", shard.Rainfall.ToString("F1")); | |
519 | + jsonWriter.Write("shrubDens:{0},", shard.ShrubDensity.ToString("F1")); | |
520 | + jsonWriter.Write("airBlocks:{0},", shard.AirBlocks); | |
521 | + jsonWriter.Write("nonAirBlocks:{0},", shard.NonAirBlocks); | |
522 | + //TODO: Heightmap | |
523 | + //TODO: Rock-ratio | |
524 | + jsonWriter.Write("}],"); | |
525 | + } | |
526 | + jsonWriter.Write("]);"); | |
527 | + | |
528 | + | |
529 | + jsonWriter.Write("ViewFrame.chunks.pointsOfInterest = new Map(["); | |
530 | + foreach (var poi in POIs) | |
531 | + { | |
532 | + jsonWriter.Write("['{0}_{1}',", poi.Location.X, poi.Location.Z); | |
533 | + jsonWriter.Write("{"); | |
534 | + jsonWriter.Write("prettyCoord:'{0}',", poi.Location.PrettyCoords(ClientAPI)); | |
535 | + jsonWriter.Write("notes:'{0}',", poi.Notes.Replace("'", "\'").Replace("\n","\\n")); | |
536 | + jsonWriter.Write("time:new Date('{0}'),", poi.Timestamp.ToString("O")); | |
537 | + jsonWriter.Write("chunkPos:'{0}_{1}',", (poi.Location.X / chunkSize), (poi.Location.Z / chunkSize)); | |
538 | + jsonWriter.Write("}],"); | |
539 | + } | |
540 | + | |
541 | + foreach (var poi in EOIs.PointsList) | |
542 | + { | |
543 | + jsonWriter.Write("['{0}_{1}',", poi.Location.X, poi.Location.Z); | |
544 | + jsonWriter.Write("{"); | |
545 | + jsonWriter.Write("prettyCoord:'{0}',", poi.Location.PrettyCoords(ClientAPI)); | |
546 | + jsonWriter.Write("notes:'{0}',", poi.Notes.Replace("'", "\'")); | |
547 | + jsonWriter.Write("time:new Date('{0}'),", poi.Timestamp.ToString("O")); | |
548 | + jsonWriter.Write("chunkPos:'{0}_{1}',", (poi.Location.X / chunkSize), (poi.Location.Z / chunkSize)); | |
549 | + jsonWriter.Write("}],"); | |
550 | + } | |
551 | + jsonWriter.Write("]);"); | |
552 | + | |
553 | + jsonWriter.Flush(); | |
554 | + } | |
555 | + | |
556 | + } | |
557 | + | |
558 | + | |
559 | + private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, uint> mostActiveCol, IMapChunk mapChunk) | |
560 | + { | |
561 | + ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), chunkSize); | |
562 | + BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize, | |
563 | + mapChunk.YMax, | |
564 | + mostActiveCol.Key.Y * chunkSize); | |
565 | + | |
566 | + var climate = ClientAPI.World.BlockAccessor.GetClimateAt(equivBP); | |
567 | + data.UpdateFieldsFrom(climate, mapChunk, TimeSpan.FromHours(ClientAPI.World.Calendar.TotalHours)); | |
568 | + | |
569 | + return data; | |
570 | + } | |
571 | + | |
572 | + /// <summary> | |
573 | + /// Reload chunk bounds from chunk shards | |
574 | + /// </summary> | |
575 | + /// <returns>The metadata.</returns> | |
576 | + private void Reload_Metadata() | |
577 | + { | |
578 | + var worldmapDir = new DirectoryInfo(path); | |
579 | + | |
580 | + if (worldmapDir.Exists) | |
581 | + { | |
582 | + | |
583 | + var files = worldmapDir.GetFiles(chunkFile_filter); | |
584 | + | |
585 | + if (files.Length > 0) | |
586 | + { | |
587 | +#if DEBUG | |
588 | + Logger.VerboseDebug("{0} Existing world chunk shards", files.Length); | |
589 | +#endif | |
132 | 590 | |
133 | - try | |
134 | - { | |
135 | - uint ejectedItem = 0; | |
136 | - uint updatedChunks = 0; | |
137 | 591 | |
138 | - //-- Should dodge enumerator changing underfoot....at a cost. | |
139 | - if (!columnCounter.IsEmpty) | |
140 | - { | |
141 | - var tempSet = columnCounter.ToArray().OrderByDescending(kvp => kvp.Value); | |
142 | - foreach (var mostActiveCol in tempSet) | |
143 | - { | |
144 | 592 | |
145 | - var mapChunk = ClientAPI.World.BlockAccessor.GetMapChunk(mostActiveCol.Key); | |
593 | + foreach (var shardFile in files) | |
594 | + { | |
146 | 595 | |
147 | - if (mapChunk == null) | |
148 | - { | |
149 | - Logger.Warning("SKIP CHUNK: ({0}) - Map Chunk NULL!", mostActiveCol.Key); | |
150 | - nullChunkCount++; | |
151 | - columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); | |
152 | - continue; | |
153 | - } | |
596 | + if (shardFile.Length < 512) continue; | |
597 | + var result = chunkShardRegex.Match(shardFile.Name); | |
598 | + if (result.Success) | |
599 | + { | |
600 | + int X_chunk_pos = int.Parse(result.Groups["X"].Value); | |
601 | + int Z_chunk_pos = int.Parse(result.Groups["Z"].Value); | |
154 | 602 | |
155 | - ColumnMeta chunkMeta = CreateColumnMetadata(mostActiveCol, mapChunk); | |
156 | - PngWriter pngWriter = SetupPngImage(mostActiveCol.Key, chunkMeta); | |
157 | - UpdateEntityMetadata(); | |
158 | - ProcessChunkBlocks(mostActiveCol.Key, mapChunk, chunkMeta); | |
603 | + //Parse PNG chunks for METADATA in shard | |
604 | + using (var fileStream = shardFile.OpenRead()) | |
605 | + { | |
606 | + //TODO: Add corrupted PNG Exception handing HERE ! | |
607 | + PngReader pngRead = new PngReader(fileStream); | |
608 | + pngRead.ReadSkippingAllRows(); | |
609 | + pngRead.End(); | |
159 | 610 | |
611 | + PngMetadataChunk metadataFromPng = pngRead.GetChunksList().GetById1(PngMetadataChunk.ID) as PngMetadataChunk; | |
160 | 612 | |
161 | - ChunkRenderer.GenerateChunkPngShard(mostActiveCol.Key, mapChunk, chunkMeta, pngWriter, out uint updatedPixels); | |
613 | + chunkTopMetadata.Add(metadataFromPng.ChunkMetadata); | |
614 | + } | |
162 | 615 | |
163 | - if (updatedPixels > 0) | |
164 | - { | |
616 | + } | |
617 | + } | |
165 | 618 | |
619 | + } | |
620 | + } | |
621 | + else | |
622 | + { | |
166 | 623 | #if DEBUG |
167 | - Logger.VerboseDebug("Wrote chunk shard: ({0}) - Edits#:{1}, Pixels#:{2}", mostActiveCol.Key, mostActiveCol.Value, updatedPixels); | |
624 | + Logger.VerboseDebug("Could not open world map directory"); | |
168 | 625 | #endif |
169 | - updatedChunks++; | |
170 | - chunkTopMetadata.Update(chunkMeta); | |
171 | - columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); | |
172 | - } | |
173 | - else | |
174 | - { | |
175 | - columnCounter.TryRemove(mostActiveCol.Key, out ejectedItem); | |
176 | - Logger.VerboseDebug("Un-painted chunk: ({0}) ", mostActiveCol.Key); | |
177 | - } | |
178 | - | |
179 | - } | |
180 | - } | |
181 | - | |
182 | - if (updatedChunks > 0) | |
183 | - { | |
184 | - //What about chunk updates themselves; a update bitmap isn't kept... | |
185 | - updatedChunksTotal += updatedChunks; | |
186 | - GenerateMapHTML(); | |
187 | - updatedChunks = 0; | |
188 | - } | |
189 | - | |
190 | - //Then sleep until interupted again, and repeat | |
191 | - | |
192 | - Logger.VerboseDebug("Thread '{0}' about to sleep indefinitely.", Thread.CurrentThread.Name); | |
193 | - | |
194 | - Thread.Sleep(Timeout.Infinite); | |
195 | - | |
196 | - } | |
197 | - catch (ThreadInterruptedException) | |
198 | - { | |
199 | - | |
200 | - Logger.VerboseDebug("Thread '{0}' interupted [awoken]", Thread.CurrentThread.Name); | |
201 | - goto wake; | |
202 | - | |
203 | - } | |
204 | - catch (ThreadAbortException) | |
205 | - { | |
206 | - Logger.VerboseDebug("Thread '{0}' aborted.", Thread.CurrentThread.Name); | |
207 | - | |
208 | - } | |
209 | - finally | |
210 | - { | |
211 | - Logger.VerboseDebug("Thread '{0}' executing finally block.", Thread.CurrentThread.Name); | |
212 | - } | |
213 | - } | |
214 | - | |
215 | - private void Prefill_POI_Designators() | |
216 | - { | |
217 | - this.POIs = new PointsOfInterest(); | |
218 | - this.EOIs = new EntitiesOfInterest(); | |
219 | - this.BlockID_Designators = new Dictionary<int, BlockDesignator>(); | |
220 | - this.Entity_Designators = new Dictionary<AssetLocation, EntityDesignator>(); | |
221 | - | |
222 | - //Add special marker types for BlockID's of "Interest", overwrite colour, and method | |
223 | - | |
224 | - Install_POI_Designators(DefaultDesignators.DefaultBlockDesignators(), DefaultDesignators.DefaultEntityDesignators()); | |
225 | - } | |
226 | - | |
227 | - private void Install_POI_Designators(ICollection<BlockDesignator> blockDesig, List<EntityDesignator> entDesig) | |
228 | - { | |
229 | - Logger.VerboseDebug("Connecting {0} standard Block-Designators", blockDesig.Count); | |
230 | - foreach (var designator in blockDesig) | |
231 | - { | |
232 | - var blockIDs = Helpers.ArbitrarytBlockIdHunter(ClientAPI, designator.Pattern, designator.Material); | |
233 | - if (blockIDs.Count > 0) { Logger.VerboseDebug("Designator {0} has {1} associated blockIDs", designator.ToString(), blockIDs.Count); } | |
234 | - foreach (var entry in blockIDs) | |
235 | - { | |
236 | - BlockID_Designators.Add(entry.Key, designator); | |
237 | - } | |
238 | - } | |
239 | - this.ChunkRenderer.BlockID_Designators = BlockID_Designators; | |
240 | - | |
241 | - | |
242 | - Logger.VerboseDebug("Connecting {0} standard Entity-Designators", entDesig.Count); | |
243 | - foreach (var designator in entDesig) | |
244 | - { | |
245 | - //Get Variants first, from EntityTypes...better be populated! | |
246 | - var matched = ClientAPI.World.EntityTypes.FindAll(entp => entp.Code.BeginsWith(designator.Pattern.Domain, designator.Pattern.Path)); | |
247 | - | |
248 | - foreach (var match in matched) | |
249 | - { | |
250 | - Logger.VerboseDebug("Linked Entity: {0} Designator: {1}", match.Code, designator); | |
251 | - this.Entity_Designators.Add(match.Code, designator); | |
252 | - } | |
253 | - | |
254 | - | |
255 | - | |
256 | - //EntityProperties props = ClientAPI.World.GetEntityType(designator.Pattern); | |
257 | - } | |
258 | - | |
259 | - | |
260 | - } | |
261 | - | |
262 | - //TODO: Convert to RAZOR model | |
263 | - private void GenerateMapHTML() | |
264 | - { | |
265 | - string mapFilename = Path.Combine(path, "Automap.html"); | |
266 | - | |
267 | - int TopNorth = chunkTopMetadata.North_mostChunk; | |
268 | - int TopSouth = chunkTopMetadata.South_mostChunk; | |
269 | - int TopEast = chunkTopMetadata.East_mostChunk; | |
270 | - int TopWest = chunkTopMetadata.West_mostChunk; | |
271 | - | |
272 | - using (StreamWriter outputText = new StreamWriter(File.Open(mapFilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))) | |
273 | - { | |
274 | - using (HtmlTextWriter tableWriter = new HtmlTextWriter(outputText)) | |
275 | - { | |
276 | - tableWriter.BeginRender(); | |
277 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Html); | |
278 | - | |
279 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Head); | |
280 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Title); | |
281 | - tableWriter.WriteEncodedText("Generated Automap"); | |
282 | - tableWriter.RenderEndTag(); | |
283 | - //CSS style here | |
284 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Style); | |
285 | - tableWriter.Write(stylesFile.ToText()); | |
286 | - tableWriter.RenderEndTag();//</style> | |
287 | - | |
288 | - //## JSON map-state data ###################### | |
289 | - tableWriter.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript"); | |
290 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Script); | |
291 | - | |
292 | - tableWriter.Write("var available_images = ["); | |
293 | - | |
294 | - foreach (var shard in this.chunkTopMetadata) | |
295 | - { | |
296 | - tableWriter.Write("{{X:{0},Y:{1} }}, ", shard.Location.X, shard.Location.Y); | |
297 | - } | |
298 | - | |
299 | - tableWriter.Write(" ];\n"); | |
300 | - | |
301 | - tableWriter.RenderEndTag(); | |
302 | - | |
303 | - tableWriter.RenderEndTag(); | |
304 | - | |
305 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Body); | |
306 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.P); | |
307 | - tableWriter.WriteEncodedText($"Created {DateTimeOffset.UtcNow.ToString("u")}"); | |
308 | - tableWriter.RenderEndTag(); | |
309 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.P); | |
310 | - tableWriter.WriteEncodedText($"W:{TopWest}, E: {TopEast}, N:{TopNorth}, S:{TopSouth} "); | |
311 | - tableWriter.RenderEndTag(); | |
312 | - tableWriter.WriteLine(); | |
313 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Table); | |
314 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Caption); | |
315 | - tableWriter.WriteEncodedText($"Start: {startChunkColumn}, Seed: {ClientAPI.World.Seed}\n"); | |
316 | - tableWriter.RenderEndTag(); | |
317 | - | |
318 | - //################ X-Axis <thead> ####################### | |
319 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Thead); | |
320 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr); | |
321 | - | |
322 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Th); | |
323 | - tableWriter.Write("N, W"); | |
324 | - tableWriter.RenderEndTag(); | |
325 | - | |
326 | - for (int xAxisT = TopWest; xAxisT <= TopEast; xAxisT++) | |
327 | - { | |
328 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Th); | |
329 | - tableWriter.Write(xAxisT); | |
330 | - tableWriter.RenderEndTag(); | |
331 | - } | |
332 | - | |
333 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Th); | |
334 | - tableWriter.Write("N, E"); | |
335 | - tableWriter.RenderEndTag(); | |
336 | - | |
337 | - tableWriter.RenderEndTag(); | |
338 | - tableWriter.RenderEndTag(); | |
339 | - //###### </thead> ################################ | |
340 | - | |
341 | - //###### <tbody> - Chunk rows & Y-axis cols | |
342 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Tbody); | |
343 | - | |
344 | - //######## <tr> for every vertical row | |
345 | - for (int yAxis = TopNorth; yAxis <= TopSouth; yAxis++) | |
346 | - { | |
347 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr); | |
348 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
349 | - tableWriter.Write(yAxis);//legend: Y-axis | |
350 | - tableWriter.RenderEndTag(); | |
351 | - | |
352 | - for (int xAxis = TopWest; xAxis <= TopEast; xAxis++) | |
353 | - { | |
354 | - //###### <td> #### for chunk shard | |
355 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
356 | - var colLoc = new Vec2i(xAxis, yAxis); | |
357 | - if (chunkTopMetadata.Contains(colLoc)) | |
358 | - { | |
359 | - ColumnMeta meta = chunkTopMetadata[colLoc]; | |
360 | - //Tooltip first | |
361 | - tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltip"); | |
362 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Div); | |
363 | - | |
364 | - tableWriter.AddAttribute(HtmlTextWriterAttribute.Src, $"{xAxis}_{yAxis}.png"); | |
365 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Img); | |
366 | - tableWriter.RenderEndTag(); | |
367 | - // <span class="tooltiptext">Tooltip text | |
368 | - tableWriter.AddAttribute(HtmlTextWriterAttribute.Class, "tooltiptext"); | |
369 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Span); | |
370 | - | |
371 | - StringBuilder tooltipText = new StringBuilder(); | |
372 | - tooltipText.Append($"{meta.Location.PrettyCoords(ClientAPI)} "); | |
373 | - tooltipText.Append($" Max-Height: {meta.YMax}, Temp: {meta.Temperature.ToString("F1")} "); | |
374 | - tooltipText.Append($" Rainfall: {meta.Rainfall.ToString("F1")}, "); | |
375 | - tooltipText.Append($" Shrubs: {meta.ShrubDensity.ToString("F1")}, "); | |
376 | - tooltipText.Append($" Forest: {meta.ForestDensity.ToString("F1")}, "); | |
377 | - tooltipText.Append($" Fertility: {meta.Fertility.ToString("F1")}, "); | |
378 | - | |
379 | - if (meta.RockRatio != null) | |
380 | - { | |
381 | - foreach (KeyValuePair<int, uint> blockID in meta.RockRatio) | |
382 | - { | |
383 | - var block = ClientAPI.World.GetBlock(blockID.Key); | |
384 | - tooltipText.AppendFormat(" {0} × {1},\t", block.Code.GetName(), meta.RockRatio[blockID.Key]); | |
385 | - } | |
386 | - } | |
387 | - | |
388 | - tableWriter.WriteEncodedText(tooltipText.ToString()); | |
389 | - | |
390 | - tableWriter.RenderEndTag();//</span> | |
391 | - | |
392 | - | |
393 | - tableWriter.RenderEndTag();//</div> --tooltip enclosure | |
394 | - } | |
395 | - else | |
396 | - { | |
397 | - tableWriter.Write("?"); | |
398 | - } | |
399 | - | |
400 | - tableWriter.RenderEndTag(); | |
401 | - }//############ </td> ########### | |
402 | - | |
403 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
404 | - tableWriter.Write(yAxis);//legend: Y-axis | |
405 | - tableWriter.RenderEndTag(); | |
406 | - | |
407 | - tableWriter.RenderEndTag(); | |
408 | - | |
409 | - } | |
410 | - tableWriter.RenderEndTag(); | |
411 | - | |
412 | - //################ X-Axis <tfoot> ####################### | |
413 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Tfoot); | |
414 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Tr); | |
415 | - | |
416 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
417 | - tableWriter.Write("S, W"); | |
418 | - tableWriter.RenderEndTag(); | |
419 | - | |
420 | - for (int xAxisB = TopWest; xAxisB <= TopEast; xAxisB++) | |
421 | - { | |
422 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
423 | - tableWriter.Write(xAxisB); | |
424 | - tableWriter.RenderEndTag(); | |
425 | - } | |
426 | - | |
427 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Td); | |
428 | - tableWriter.Write("S, E"); | |
429 | - tableWriter.RenderEndTag(); | |
430 | - | |
431 | - tableWriter.RenderEndTag(); | |
432 | - tableWriter.RenderEndTag(); | |
433 | - //###### </tfoot> ################################ | |
434 | - | |
435 | - | |
436 | - tableWriter.RenderEndTag();//</table> | |
437 | - | |
438 | - //############## POI list ##################### | |
439 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.P); | |
440 | - tableWriter.WriteLine("Points of Interest"); | |
441 | - tableWriter.RenderEndTag(); | |
442 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Ul); | |
443 | - foreach (var poi in this.POIs) | |
444 | - { | |
445 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Li); | |
446 | - tableWriter.WriteEncodedText(poi.Location.PrettyCoords(this.ClientAPI) + "\t"); | |
447 | - tableWriter.WriteEncodedText(poi.Notes + "\t"); | |
448 | - tableWriter.WriteEncodedText(poi.Timestamp.ToString("u")); | |
449 | - tableWriter.RenderEndTag(); | |
450 | - } | |
451 | - | |
452 | - foreach (var eoi in this.EOIs.PointsList) | |
453 | - { | |
454 | - tableWriter.RenderBeginTag(HtmlTextWriterTag.Li); | |
455 | - tableWriter.WriteEncodedText(eoi.Location.PrettyCoords(this.ClientAPI) + "\t"); | |
456 | - tableWriter.WriteEncodedText(eoi.Notes + "\t"); | |
457 | - tableWriter.WriteEncodedText(eoi.Timestamp.ToString("u")); | |
458 | - tableWriter.RenderEndTag(); | |
459 | - } | |
626 | + } | |
460 | 627 | |
461 | - tableWriter.RenderEndTag(); | |
462 | 628 | |
463 | 629 | |
630 | + } | |
464 | 631 | |
632 | + private PngWriter SetupPngImage(Vec2i coord, ColumnMeta metadata) | |
633 | + { | |
634 | + ImageInfo imageInf = new ImageInfo(chunkSize, chunkSize, 8, false); | |
465 | 635 | |
466 | - tableWriter.RenderEndTag();//### </BODY> ### | |
636 | + string filename = $"{coord.X}_{coord.Y}.png"; | |
637 | + filename = Path.Combine(path, filename); | |
467 | 638 | |
468 | - tableWriter.EndRender(); | |
469 | - tableWriter.Flush(); | |
470 | - } | |
471 | - outputText.Flush(); | |
472 | - } | |
639 | + PngWriter pngWriter = FileHelper.CreatePngWriter(filename, imageInf, true); | |
640 | + PngMetadata meta = pngWriter.GetMetadata(); | |
641 | + meta.SetTimeNow(); | |
642 | + meta.SetText("Chunk_X", coord.X.ToString("D")); | |
643 | + meta.SetText("Chunk_Y", coord.Y.ToString("D")); | |
644 | + //Setup specialized meta-data PNG chunks here... | |
645 | + PngMetadataChunk pngChunkMeta = new PngMetadataChunk(pngWriter.ImgInfo); | |
646 | + pngChunkMeta.ChunkMetadata = metadata; | |
647 | + pngWriter.GetChunksList().Queue(pngChunkMeta); | |
473 | 648 | |
474 | - Logger.VerboseDebug("Generated HTML map"); | |
475 | - } | |
649 | + return pngWriter; | |
650 | + } | |
476 | 651 | |
652 | + /// <summary> | |
653 | + /// Does the heavy lifting of Scanning columns of chunks - creates Heightmap and Processes POIs, Entities, and stats... | |
654 | + /// </summary> | |
655 | + /// <param name="key">Chunk Coordinate</param> | |
656 | + /// <param name="mapChunk">Map chunk.</param> | |
657 | + /// <param name="chunkMeta">Chunk metadata</param> | |
658 | + private void ProcessChunkBlocks(Vec2i key, IMapChunk mapChunk, ColumnMeta chunkMeta) | |
659 | + { | |
477 | 660 | |
661 | + int targetChunkY = mapChunk.YMax / chunkSize;//Surface ... | |
662 | + for (; targetChunkY > 0; targetChunkY--) | |
663 | + { | |
664 | + WorldChunk chunkData = ClientAPI.World.BlockAccessor.GetChunk(key.X, targetChunkY, key.Y) as WorldChunk; | |
478 | 665 | |
479 | - private ColumnMeta CreateColumnMetadata(KeyValuePair<Vec2i, uint> mostActiveCol, IMapChunk mapChunk) | |
480 | - { | |
481 | - ColumnMeta data = new ColumnMeta(mostActiveCol.Key.Copy(), chunkSize); | |
482 | - BlockPos equivBP = new BlockPos(mostActiveCol.Key.X * chunkSize, | |
483 | - mapChunk.YMax, | |
484 | - mostActiveCol.Key.Y * chunkSize); | |
666 | + if (chunkData == null || chunkData.BlockEntities == null) | |
667 | + { | |
668 | +#if DEBUG | |
669 | + Logger.VerboseDebug("Chunk null or empty X{0} Y{1} Z{2}", key.X, targetChunkY, key.Y); | |
670 | +#endif | |
671 | + continue; | |
672 | + } | |
485 | 673 | |
486 | - var climate = ClientAPI.World.BlockAccessor.GetClimateAt(equivBP); | |
487 | - data.UpdateFieldsFrom(climate, mapChunk, TimeSpan.FromHours(ClientAPI.World.Calendar.TotalHours)); | |
674 | + /*************** Chunk Entities Scanning *********************/ | |
675 | + if (chunkData.BlockEntities != null && chunkData.BlockEntities.Length > 0) | |
676 | + { | |
677 | +#if DEBUG | |
678 | + Logger.VerboseDebug("Surface@ {0} = BlockEntities: {1}", key, chunkData.BlockEntities.Length); | |
679 | +#endif | |
488 | 680 | |
489 | - return data; | |
490 | - } | |
681 | + foreach (var blockEnt in chunkData.BlockEntities) | |
682 | + { | |
683 | + | |
684 | + if (blockEnt != null && blockEnt.Block != null && BlockID_Designators.ContainsKey(blockEnt.Block.BlockId)) | |
685 | + { | |
686 | + var designator = BlockID_Designators[blockEnt.Block.BlockId]; | |
687 | + designator.SpecialAction(ClientAPI, POIs, blockEnt.Pos.Copy(), blockEnt.Block); | |
688 | + } | |
689 | + } | |
690 | + | |
691 | + } | |
692 | + /********************* Chunk/Column BLOCKs scanning ****************/ | |
693 | + //Heightmap, Stats, block tally | |
694 | + chunkData.Unpack(); | |
695 | + | |
696 | + int X_index, Y_index, Z_index; | |
697 | + X_index = Y_index = Z_index = 0; | |
698 | + | |
699 | + do | |
700 | + { | |
701 | + do | |
702 | + { | |
703 | + do | |
704 | + { | |
705 | + /* Encode packed indicie | |
706 | + (y * chunksize + z) * chunksize + x | |
707 | + */ | |
708 | + var indicie = Helpers.ChunkBlockIndicie16(X_index, Y_index, Z_index); | |
709 | + int aBlockId = chunkData.Blocks[indicie]; | |
710 | + | |
711 | + if (aBlockId == 0) | |
712 | + {//Air | |
713 | + chunkMeta.AirBlocks++; | |
714 | + continue; | |
715 | + } | |
716 | + | |
717 | + if (RockIdCodes.ContainsKey(aBlockId)) | |
718 | + { | |
719 | + if (chunkMeta.RockRatio.ContainsKey(aBlockId)) { chunkMeta.RockRatio[aBlockId]++; } else { chunkMeta.RockRatio.Add(aBlockId, 1); } | |
720 | + } | |
721 | + | |
722 | + chunkMeta.NonAirBlocks++; | |
723 | + | |
724 | + //Heightmap | |
725 | + if (chunkMeta.HeightMap[X_index, Z_index] == 0) | |
726 | + { chunkMeta.HeightMap[X_index, Z_index] = (ushort) (Y_index + (targetChunkY * chunkSize)); } | |
727 | + | |
728 | + } | |
729 | + while (X_index++ < (chunkSize - 1)); | |
730 | + X_index = 0; | |
731 | + } | |
732 | + while (Z_index++ < (chunkSize - 1)); | |
733 | + Z_index = 0; | |
734 | + } | |
735 | + while (Y_index++ < (chunkSize - 1)); | |
736 | + | |
737 | + } | |
738 | + } | |
739 | + | |
740 | + private void UpdateEntityMetadata() | |
741 | + { | |
742 | + Logger.Debug("Presently {0} Entities", ClientAPI.World.LoadedEntities.Count); | |
743 | + //Mabey scan only for 'new' entities by tracking ID in set? | |
744 | + foreach (var loadedEntity in ClientAPI.World.LoadedEntities.ToList()) | |
745 | + { | |
491 | 746 | |
492 | - /// <summary> | |
493 | - /// Reload chunk bounds from chunk shards | |
494 | - /// </summary> | |
495 | - /// <returns>The metadata.</returns> | |
496 | - private void Reload_Metadata() | |
497 | - { | |
498 | - var worldmapDir = new DirectoryInfo(path); | |
747 | +#if DEBUG | |
748 | + //Logger.VerboseDebug($"ENTITY: ({loadedEntity.Value.Code}) = #{loadedEntity.Value.EntityId} {loadedEntity.Value.State} {loadedEntity.Value.LocalPos} <<<<<<<<<<<<"); | |
749 | +#endif | |
499 | 750 | |
500 | - if (worldmapDir.Exists) | |
501 | - { | |
751 | + var dMatch = Entity_Designators.SingleOrDefault(se => se.Key.Equals(loadedEntity.Value.Code)); | |
752 | + if (dMatch.Value != null) | |
753 | + { | |
754 | + dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Value.LocalPos.AsBlockPos.Copy(), loadedEntity.Value); | |
755 | + } | |
502 | 756 | |
503 | - var files = worldmapDir.GetFiles(chunkFile_filter); | |
757 | + } | |
504 | 758 | |
505 | - if (files.Length > 0) | |
506 | - { | |
507 | -#if DEBUG | |
508 | - Logger.VerboseDebug("{0} Existing world chunk shards", files.Length); | |
509 | -#endif | |
510 | - foreach (var shardFile in files) | |
511 | - { | |
512 | 759 | |
513 | - if (shardFile.Length < 1000) continue; | |
514 | - var result = chunkShardRegex.Match(shardFile.Name); | |
515 | - if (result.Success) | |
516 | - { | |
517 | - int X_chunk_pos = int.Parse(result.Groups["X"].Value); | |
518 | - int Z_chunk_pos = int.Parse(result.Groups["Z"].Value); | |
760 | + } | |
519 | 761 | |
520 | - //Parse PNG chunks for METADATA in shard | |
521 | - using var fileStream = shardFile.OpenRead(); | |
522 | - PngReader pngRead = new PngReader(fileStream); | |
523 | - pngRead.ReadSkippingAllRows(); | |
524 | - pngRead.End(); | |
762 | + private void AddNote(string notation) | |
763 | + { | |
764 | + var playerNodePoi = new PointOfInterest() | |
765 | + { | |
766 | + Location = ClientAPI.World.Player.Entity.LocalPos.AsBlockPos.Copy(), | |
767 | + Notes = notation, | |
768 | + Timestamp = DateTimeOffset.UtcNow, | |
769 | + }; | |
525 | 770 | |
526 | - PngMetadataChunk metadataFromPng = pngRead.GetChunksList().GetById1(PngMetadataChunk.ID) as PngMetadataChunk; | |
771 | + this.POIs.AddReplace(playerNodePoi); | |
772 | + } | |
527 | 773 | |
528 | - chunkTopMetadata.Add(metadataFromPng.ChunkMetadata); | |
529 | 774 | |
530 | - } | |
531 | - } | |
532 | 775 | |
533 | - } | |
534 | - } | |
535 | - else | |
536 | - { | |
537 | -#if DEBUG | |
538 | - Logger.VerboseDebug("Could not open world map directory"); | |
539 | -#endif | |
540 | - } | |
541 | - | |
542 | - | |
543 | - | |
544 | - } | |
545 | - | |
546 | - private PngWriter SetupPngImage(Vec2i coord, ColumnMeta metadata) | |
547 | - { | |
548 | - ImageInfo imageInf = new ImageInfo(chunkSize, chunkSize, 8, false); | |
549 | - | |
550 | - string filename = $"{coord.X}_{coord.Y}.png"; | |
551 | - filename = Path.Combine(path, filename); | |
552 | - | |
553 | - PngWriter pngWriter = FileHelper.CreatePngWriter(filename, imageInf, true); | |
554 | - PngMetadata meta = pngWriter.GetMetadata(); | |
555 | - meta.SetTimeNow(); | |
556 | - meta.SetText("Chunk_X", coord.X.ToString("D")); | |
557 | - meta.SetText("Chunk_Y", coord.Y.ToString("D")); | |
558 | - //Setup specialized meta-data PNG chunks here... | |
559 | - PngMetadataChunk pngChunkMeta = new PngMetadataChunk(pngWriter.ImgInfo) | |
560 | - { | |
561 | - ChunkMetadata = metadata | |
562 | - }; | |
563 | - pngWriter.GetChunksList().Queue(pngChunkMeta); | |
564 | - | |
565 | - return pngWriter; | |
566 | - } | |
567 | - | |
568 | - /// <summary> | |
569 | - /// Does the heavy lifting of Scanning columns of chunks - creates Heightmap and Processes POIs, Entities, and stats... | |
570 | - /// </summary> | |
571 | - /// <param name="key">Chunk Coordinate</param> | |
572 | - /// <param name="mapChunk">Map chunk.</param> | |
573 | - /// <param name="chunkMeta">Chunk metadata</param> | |
574 | - private void ProcessChunkBlocks(Vec2i key, IMapChunk mapChunk, ColumnMeta chunkMeta) | |
575 | - { | |
576 | - //TODO: build stack of chunk(s) - surface down to bedrock | |
577 | - int topChunkY = mapChunk.YMax / chunkSize; | |
578 | - WorldChunk chunkData = (Vintagestory.Common.WorldChunk)ClientAPI.World.BlockAccessor.GetChunk(key.X, topChunkY, key.Y); | |
579 | - | |
580 | - if (chunkData.BlockEntities != null && chunkData.BlockEntities.Length > 0) | |
581 | - { | |
582 | -#if DEBUG | |
583 | - Logger.VerboseDebug("Surface@ {0} = BlockEntities: {1}", key, chunkData.BlockEntities.Length); | |
584 | - | |
585 | - foreach (var blockEnt in chunkData.BlockEntities) | |
586 | - { | |
587 | - if (BlockID_Designators.ContainsKey(blockEnt.Block.BlockId)) | |
588 | - { | |
589 | - var designator = BlockID_Designators[blockEnt.Block.BlockId]; | |
590 | - designator.SpecialAction(ClientAPI, POIs, blockEnt.Pos.Copy(), blockEnt.Block); | |
591 | - } | |
592 | - } | |
593 | -#endif | |
594 | - } | |
776 | + private void CommandListener(string eventName, ref EnumHandling handling, IAttribute data) | |
777 | + { | |
778 | + Logger.VerboseDebug("MsgBus RX: AutomapCommandMsg: {0}", data.ToJsonToken()); | |
595 | 779 | |
596 | - } | |
780 | + CommandData cmdData = data as CommandData; | |
597 | 781 | |
598 | - private void UpdateEntityMetadata() | |
599 | - { | |
600 | - Logger.Debug("Presently {0} Entities", ClientAPI.World.LoadedEntities.Count); | |
601 | - //Mabey scan only for 'new' entities by tracking ID in set? | |
602 | - foreach (var loadedEntity in ClientAPI.World.LoadedEntities.ToList()) | |
603 | - { | |
604 | 782 | |
605 | -#if DEBUG | |
606 | - //Logger.VerboseDebug($"ENTITY: ({loadedEntity.Value.Code}) = #{loadedEntity.Value.EntityId} {loadedEntity.Value.State} {loadedEntity.Value.LocalPos} <<<<<<<<<<<<"); | |
607 | -#endif | |
783 | + if (CurrentState != RunState.Snapshot) | |
784 | + { | |
785 | + switch (cmdData.State) | |
786 | + { | |
787 | + case RunState.Run: | |
788 | + CurrentState = cmdData.State; | |
789 | + AwakenCartographer(0.0f); | |
790 | + break; | |
608 | 791 | |
609 | - var dMatch = Entity_Designators.SingleOrDefault(se => se.Key.Equals(loadedEntity.Value.Code)); | |
610 | - if (dMatch.Value != null) | |
611 | - { | |
612 | - if (dMatch.Value.Enabled) | |
613 | - { | |
614 | - dMatch.Value.SpecialAction(ClientAPI, this.EOIs, loadedEntity.Value.LocalPos.AsBlockPos.Copy(), loadedEntity.Value); | |
615 | - } | |
616 | - } | |
792 | + case RunState.Stop: | |
793 | + CurrentState = cmdData.State; | |
794 | + break; | |
617 | 795 | |
618 | - } | |
796 | + case RunState.Snapshot: | |
797 | + CurrentState = RunState.Stop; | |
798 | + //Snapshot starts a second thread/process... | |
619 | 799 | |
800 | + break; | |
620 | 801 | |
621 | - } | |
802 | + case RunState.Notation: | |
803 | + //Add to POI list where player location | |
804 | + AddNote(cmdData.Notation); | |
805 | + break; | |
806 | + } | |
622 | 807 | |
808 | + } | |
623 | 809 | |
810 | + if (CurrentState != cmdData.State) | |
811 | + { | |
812 | + CurrentState = cmdData.State; | |
813 | + AwakenCartographer(0.0f); | |
814 | + } | |
624 | 815 | |
816 | +#if DEBUG | |
817 | + ClientAPI.TriggerChatMessage($"Automap commanded to: {cmdData.State} "); | |
818 | +#endif | |
625 | 819 | |
820 | + } | |
626 | 821 | |
627 | - #endregion | |
628 | 822 | |
823 | + #endregion | |
629 | 824 | |
630 | - } | |
825 | + } | |
631 | 826 | |
632 | 827 | } |
\ No newline at end of file |
@@ -7,7 +7,7 @@ | ||
7 | 7 | "version": "0.1.2", |
8 | 8 | "side":"Client", |
9 | 9 | "dependencies": { |
10 | - "game": "1.11.0" | |
10 | + "game": "1.12.0" | |
11 | 11 | }, |
12 | 12 | "website": "http://nowebsite.nope" |
13 | 13 | } |
\ No newline at end of file |