-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMagicDeepSeaPanel.cs
More file actions
395 lines (335 loc) · 11 KB
/
MagicDeepSeaPanel.cs
File metadata and controls
395 lines (335 loc) · 11 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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
using System;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using System.Collections.Generic;
using UnityEngine;
namespace Oxide.Plugins;
[Info("Magic Deep Sea Panel", "HunterZ", "1.0.0")]
[Description("Provides a Magic Panel that displays Deep Sea status")]
public class MagicDeepSeaPanel : RustPlugin
{
#region Class Fields
[PluginReference] Plugin MagicPanel;
private PluginConfig _pluginConfig;
private Timer _timer;
private enum DeepSeaState { Open, Rads, Closed, Busy }
private enum UpdateType
{
All = 1,
// Panel = 2,
Image = 3,
Text = 4
}
#endregion
#region Oxide API
private void Init()
{
// nothing for now
}
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 (Exception ex)
{
RaiseError($"Exception reading config file {path}: " + ex.Message);
LoadDefaultConfig();
}
configFile.WriteObject(_pluginConfig);
}
private void OnServerInitialized() => MagicPanelRegisterPanels();
private void Unload()
{
_pluginConfig = null;
DestroyTimer(ref _timer);
}
#endregion
#region MagicPanel API
private void MagicPanelRegisterPanels()
{
if (MagicPanel == null)
{
PrintError("Missing plugin dependency MagicPanel: https://umod.org/plugins/magic-panel");
ToggleTimer(false);
return;
}
if (!_pluginConfig.PanelLayout.Image.Enabled &&
!_pluginConfig.PanelLayout.Text.Enabled)
{
PrintWarning("Not registering panel because all items are disabled in config");
ToggleTimer(false);
return;
}
MagicPanel?.Call("RegisterGlobalPanel",
this, Name, JsonConvert.SerializeObject(_pluginConfig.PanelSettings),
nameof(GetPanel));
ToggleTimer(true);
}
/*
private void PrintHash(Hash<string, object> hash, int indent = 0)
{
var padding = string.Empty.PadLeft(indent);
foreach (var (key, value) in hash)
{
switch (value)
{
case int i: Puts($"{padding}[{key}]<int>={i}"); break;
case float f: Puts($"{padding}[{key}]<float>=float {f}"); break;
case string s: Puts($"{padding}[{key}]<string>={s}"); break;
case Hash<string, object> h:
{
Puts($"{padding}[{key}]<hash>=");
PrintHash(h, indent + 1);
break;
}
}
}
}
*/
private Hash<string, object> GetPanel() => _pluginConfig.PanelLayout.ToHash();
// {
// var hash = _pluginConfig.PanelLayout.ToHash();
// PrintHash(hash);
// return hash;
// }
#endregion
#region Helper Methods
private static void DestroyTimer(ref Timer t)
{
if (TimerValid(t)) t.Destroy();
t = null;
}
private static bool TimerValid(Timer t) => false == t?.Destroyed;
private void ToggleTimer(bool enable)
{
DestroyTimer(ref _timer);
if (enable)
{
// check at 1Hz in case admin/plugins manually change Deep Sea times
_timer = timer.Every(1.0f, CheckUpdate);
}
}
private string GetColor(DeepSeaState deepSeaState) => deepSeaState switch
{
DeepSeaState.Open => _pluginConfig.ActiveColor,
DeepSeaState.Rads => _pluginConfig.IrradiatedColor,
_ => _pluginConfig.InactiveColor
};
private string GetText(DeepSeaManager deepSeaManager)
{
var deepSeaState = GetDeepSeaState(deepSeaManager);
if (DeepSeaState.Busy == deepSeaState) return _pluginConfig.TextBusy;
var timeSec = GetDeepSeaTime(deepSeaManager, deepSeaState);
// largest relevant time denomination, and increments remaining within it
// var (timeInc, timeDenom) = timeSec switch
// {
// >= 3600 => (timeSec / 3600, 'h'), // at least 1 hour
// >= 60 => (timeSec / 60, 'm'), // at least 1 minute
// _ => (timeSec , 's') // less than 1 minute
// };
// return
// DeepSeaState.Busy == deepSeaState ? "..." : $"{timeInc}{timeDenom}";
var timeSpan = TimeSpan.FromSeconds(timeSec);
return string.Format(timeSec switch
{
>= 3600 => _pluginConfig.FormatHours,
>= 60 => _pluginConfig.FormatMinutes,
_ => _pluginConfig.FormatSeconds,
}, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
}
private static DeepSeaState GetDeepSeaState(DeepSeaManager deepSeaManager)
{
if (deepSeaManager.IsBusy()) return DeepSeaState.Busy;
if (!deepSeaManager.IsOpen()) return DeepSeaState.Closed;
return deepSeaManager.HasFlag(DeepSeaManager.Flag_AboutToClose) ?
DeepSeaState.Rads : DeepSeaState.Open;
}
private static int GetDeepSeaTime(
DeepSeaManager deepSeaManager, DeepSeaState deepSeaState) =>
(int)(DeepSeaState.Closed == deepSeaState ?
deepSeaManager.TimeToNextOpening : deepSeaManager.TimeToWipe);
private void CheckUpdate()
{
var deepSeaManager = DeepSeaManager.ServerInstance;
if (!deepSeaManager)
{
PrintError("GetPanel(): DeepSeaManager.ServerInstance is null");
return;
}
var deepSeaState = GetDeepSeaState(deepSeaManager);
var color = GetColor(deepSeaState);
// calculate new panel data
var imageChanged = false;
var imageConfig = _pluginConfig.PanelLayout.Image;
if (imageConfig.Enabled &&
_pluginConfig.ColoredImage && color != imageConfig.Color)
{
imageConfig.Color = color;
imageChanged = true;
}
var textChanged = false;
var textConfig = _pluginConfig.PanelLayout.Text;
if (textConfig.Enabled)
{
if (_pluginConfig.ColoredText && color != textConfig.Color)
{
textConfig.Color = color;
textChanged = true;
}
var text = GetText(deepSeaManager);
if (text != textConfig.Text)
{
textConfig.Text = text;
textChanged = true;
}
}
// avoid requesting a Magic Panel update if nothing changed
if (!imageChanged && !textChanged)
{
return;
}
var updateType = (imageChanged, textChanged) switch
{
(false, _ ) => UpdateType.Text,
(true, false) => UpdateType.Image,
(true, true ) => UpdateType.All
};
// Puts($"***** imageChanged={imageChanged}, imageColor={imageConfig.Color}, textChanged={textChanged}, textColor={textConfig.Color}, textText={textConfig.Text}, updateType={updateType}");
MagicPanel?.Call("UpdatePanel", Name, (int)updateType);
}
#endregion
#region Classes
private sealed class PluginConfig
{
[JsonProperty(PropertyName = "Active Color")]
public string ActiveColor { get; set; } = "#FFFFFFBF";
[JsonProperty(PropertyName = "Inactive Color")]
public string InactiveColor { get; set; } = "#BFBFBF7F";
[JsonProperty(PropertyName = "Irradiated Color")]
public string IrradiatedColor { get; set; } = "#FFFFFFBF";
[JsonProperty(PropertyName = "Apply Color To Image")]
public bool ColoredImage { get; set; } = true;
[JsonProperty(PropertyName = "Apply Color To Text")]
public bool ColoredText { get; set; } = true;
[JsonProperty(PropertyName = "Text Format When Hour(s) Remaining")]
public string FormatHours { get; set; } = "{0}h {1}m";
[JsonProperty(PropertyName = "Text Format When Minute(s) Remaining")]
public string FormatMinutes { get; set; } = "{1}m";
[JsonProperty(PropertyName = "Text Format When Second(s) Remaining")]
public string FormatSeconds { get; set; } = "{2}s";
[JsonProperty(PropertyName = "Text When Deep Sea Busy")]
public string TextBusy { get; set; } = "BUSY";
[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; } = "centerupper";
public float Width { get; set; } = 0.05f;
public int Order { get; set; } = 1;
public string BackgroundColor { get; set; } = "#FFF2DF08";
}
private sealed class PanelLayout
{
public PanelImage Image { get; set; } = new();
public PanelText Text { 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();
_panelHash[nameof(Text)] = Text.ToHash();
return _panelHash;
}
}
private abstract class PanelBase
{
public bool Enabled { get; set; } = true;
public int Order { get; set; } = 0;
public float Width { get; set; } = 0.5f;
public TypePadding Padding { get; set; } = new();
public string Color { get; set; } = "#FFFFFFFF";
[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/MZhXzvW2/anchor-512.png";
public override Hash<string, object> ToHash()
{
var hash = base.ToHash();
hash.TryAdd(nameof(Url), Url);
return hash;
}
}
private sealed class PanelText : PanelBase
{
// this is not exposed to the config file, because it gets overwritten by
// logic
[JsonIgnore]
public string Text { get; set; } = "";
public int FontSize { get; set; } = 14;
[JsonConverter(typeof(StringEnumConverter))]
public TextAnchor TextAnchor { get; set; } = TextAnchor.MiddleCenter;
public override Hash<string, object> ToHash()
{
var hash = base.ToHash();
// update this every time
hash[nameof(Text)] = Text;
// only add these if not already present, as they won't change
hash.TryAdd(nameof(FontSize), FontSize);
hash.TryAdd(nameof(TextAnchor), TextAnchor);
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
}