-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMagicTravelingVendorPanel.cs
More file actions
265 lines (217 loc) · 6.74 KB
/
MagicTravelingVendorPanel.cs
File metadata and controls
265 lines (217 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
using Newtonsoft.Json;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using System.Collections.Generic;
namespace Oxide.Plugins;
[Info("Magic Traveling Vendor Panel", "HunterZ", "1.0.0")]
[Description("Provides a Magic Panel that displays Traveling Vendor status")]
public class MagicTravelingVendorPanel : RustPlugin
{
#region Class Fields
[PluginReference] Plugin MagicPanel;
private PluginConfig _pluginConfig;
private readonly HashSet<TravellingVendor> _activeVendors = new();
private enum UpdateType
{
// All = 1,
// Panel = 2,
Image = 3
// Text = 4
}
#endregion
#region Oxide API
private void Init()
{
// unsubscribe hook until after loaded, as we will instead proactively scan
// in case of post-start plugin (re)load
ToggleHooks(false);
}
protected override void LoadDefaultConfig() =>
_pluginConfig = new PluginConfig();
protected override void LoadConfig()
{
var path = $"{Manager.ConfigPath}/MagicPanel/{Name}.json";
var configFile = new DynamicConfigFile(path);
if (!configFile.Exists())
{
Puts($"No config file {path}; creating a new one");
LoadDefaultConfig();
configFile.WriteObject(_pluginConfig);
return;
}
try
{
_pluginConfig = configFile.ReadObject<PluginConfig>();
}
catch (System.Exception ex)
{
RaiseError($"Exception reading config file {path}: " + ex.Message);
LoadDefaultConfig();
}
configFile.WriteObject(_pluginConfig);
}
private void OnServerInitialized()
{
foreach (var entity in BaseNetworkable.serverEntities)
{
if (entity is TravellingVendor travelingVendor && travelingVendor)
{
_activeVendors.Add(travelingVendor);
}
}
MagicPanelRegisterPanels();
}
public void Unload()
{
_pluginConfig = null;
_activeVendors.Clear();
}
private void OnEntityKill(TravellingVendor vendor)
{
var wasActive = _activeVendors.Count > 0;
if (vendor && _activeVendors.Remove(vendor)) CheckEvent(wasActive);
}
private void OnEntitySpawned(TravellingVendor vendor)
{
var wasActive = _activeVendors.Count > 0;
if (vendor && _activeVendors.Add(vendor)) CheckEvent(wasActive);
}
#endregion
#region MagicPanel API
private void MagicPanelRegisterPanels()
{
if (MagicPanel?.IsLoaded is not true)
{
PrintError("Missing plugin dependency MagicPanel: https://umod.org/plugins/magic-panel");
ToggleHooks(false);
return;
}
if (!_pluginConfig.PanelLayout.Image.Enabled)
{
PrintWarning("Not registering panel because all items are disabled in config");
ToggleHooks(false);
return;
}
// setup initial data, but don't request a callback from MagicPanel, because
// it will already do that on its own
CheckEvent();
MagicPanel?.Call("RegisterGlobalPanel",
this, Name, JsonConvert.SerializeObject(_pluginConfig.PanelSettings),
nameof(GetPanel));
ToggleHooks(true);
}
private Hash<string, object> GetPanel() => _pluginConfig.PanelLayout.ToHash();
#endregion
#region Helper Methods
private void CheckEvent(bool? wasActive = null)
{
// only off->1 and on->0 transitions affect icon state
var isActive = _activeVendors.Count > 0;
if (isActive == wasActive) return;
_pluginConfig.PanelLayout.Image.Color =
isActive ? _pluginConfig.ActiveColor : _pluginConfig.InactiveColor;
// special case: wasActive is null on initial check, and we don't want to
// request a callback for that, because MagicPanel will do that on its own
if (wasActive is null) return;
MagicPanel?.Call("UpdatePanel", Name, (int)UpdateType.Image);
}
private void ToggleHooks(bool enable)
{
if (enable)
{
Subscribe(nameof(OnEntityKill));
Subscribe(nameof(OnEntitySpawned));
}
else
{
Unsubscribe(nameof(OnEntityKill));
Unsubscribe(nameof(OnEntitySpawned));
}
}
#endregion
#region Classes
private sealed class PluginConfig
{
[JsonProperty(PropertyName = "Active Color")]
public string ActiveColor { get; set; } = "#FFFFFF7F";
[JsonProperty(PropertyName = "Inactive Color")]
public string InactiveColor { get; set; } = "#FFFFFF0F";
[JsonProperty(PropertyName = "Panel Settings")]
public PanelRegistration PanelSettings { get; set; } = new();
[JsonProperty(PropertyName = "Panel Layout")]
public PanelLayout PanelLayout { get; set; } = new();
}
private sealed class PanelRegistration
{
public string Dock { get; set; } = "center";
public float Width { get; set; } = 0.02f;
public int Order { get; set; } = 1;
public string BackgroundColor { get; set; } = "#FFFFFF08";
}
private sealed class PanelLayout
{
public PanelImage Image { get; set; } = new();
// cache hash instead of regenerating it on every call/change
[JsonIgnore]
private Hash<string, object> _panelHash;
public Hash<string, object> ToHash()
{
// only create new hash if none exists yet
_panelHash ??= new Hash<string, object>();
_panelHash[nameof(Image)] = Image.ToHash();
return _panelHash;
}
}
private abstract class PanelBase
{
public bool Enabled { get; set; } = true;
public int Order { get; set; } = 0;
public float Width { get; set; } = 1.0f;
public TypePadding Padding { get; set; } = new();
// this is not exposed to the config file, because it gets overwritten by
// the appropriate higher-level settings
[JsonIgnore] public string Color { get; set; } = "#00000000";
[JsonIgnore] private Hash<string, object> _hash;
public virtual Hash<string, object> ToHash()
{
// only create new hash if one doesn't yet exist
// populate it with non-changing data only on creation
_hash ??= new Hash<string, object>
{
[nameof(Enabled)] = Enabled,
[nameof(Order)] = Order,
[nameof(Width)] = Width,
[nameof(Padding)] = Padding.ToHash()
};
// update this every time
_hash[nameof(Color)] = Color;
return _hash;
}
}
private sealed class PanelImage : PanelBase
{
public string Url { get; set; } =
"https://i.postimg.cc/4NN4WVD7/vendor.png";
public override Hash<string, object> ToHash()
{
var hash = base.ToHash();
hash.TryAdd(nameof(Url), Url);
return hash;
}
}
private sealed class TypePadding
{
public float Left { get; set; } = 0.05f;
public float Right { get; set; } = 0.05f;
public float Top { get; set; } = 0.05f;
public float Bottom { get; set; } = 0.05f;
public Hash<string, object> ToHash() => new()
{
[nameof(Left)] = Left,
[nameof(Right)] = Right,
[nameof(Top)] = Top,
[nameof(Bottom)] = Bottom
};
}
#endregion
}