-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGameDayTimerPanel.cs
415 lines (365 loc) · 17.4 KB
/
GameDayTimerPanel.cs
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
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
using ColossalFramework.UI;
using UnityEngine;
using System;
using ColossalFramework;
using ColossalFramework.Plugins;
using static ColossalFramework.Plugins.PluginManager;
namespace GameDayTimer
{
/// <summary>
/// panel to display timings
/// </summary>
public class GameDayTimerPanel : UIPanel
{
// default panel position
public const float DefaultPanelPositionX = 50f;
public const float DefaultPanelPositionY = 50f;
// UI components on the panel
private UILabel CurrentDayLabel;
private UILabel PreviousDayLabel;
private UILabel FramesPerSecLabel;
private UILabel CurrentDayValue;
private UILabel PreviousDayValue;
private UILabel FramesPerSecValue;
// flags for dragging the panel
private bool MouseDown;
private bool Dragging;
// variables for doing game day timing (GDT)
private bool GDTFirstDayChanged; // whether or not the first day of timing changed, used prevent timing a partial first day
private int GDTPreviousDay; // previous game day, used to detect game day change
private double GDTTotalTimeInDay; // accumulcated total real time spent in a game day
private string GDTPreviousTotalTimeInDayText; // the previous text value of TotalTimeInDay, used to detect change in time to be displayed
private int GDTPreviousSimSpeed; // previous simulation speed, used to detect change in simulation speed
private double GDTPreviousRealTime; // previous real time, used to compute time delta from previous frame
// variables for doing frames per second (FPS) timing
private double FPSPreviousRealTime; // previous real time, used to compute when one second has elapsed
private int FPSFrameCount; // count of frames
// define text colors
Color32 ColorRed = new Color32(255, 0, 0, 255);
Color32 ColorOrange = new Color32(255, 136, 0, 255);
Color32 ColorYellow = new Color32(255, 255, 0, 255);
Color32 ColorWhite = new Color32(255, 255, 255, 255);
// whether or not the Real Time mod is enabled
private bool RealTimeModEnabled = false;
/// <summary>
/// Start is called after the panel is created in Loading
/// set up and populate the panel
/// </summary>
public override void Start()
{
// do base processing
base.Start();
try
{
// set panel properties
name = "GameDayTimerPanel";
width = 150f;
height = 68f;
backgroundSprite = "MenuPanel2";
canFocus = true;
opacity = 0.75f;
// move panel to initial position according to the config
GameDayTimerConfiguration config = Configuration<GameDayTimerConfiguration>.Load();
MovePanelToPosition(config.PanelPositionX, config.PanelPositionY);
// set up mouse event handlers for dragging the panel
eventMouseDown += GameDayTimerPanel_eventMouseDown;
eventMouseMove += GameDayTimerPanel_eventMouseMove;
eventMouseUp += GameDayTimerPanel_eventMouseUp;
// set position and size for labels
const float LabelLeft = 8f; // all labels have same left position, but value labels are right justified
const float LabelTop = 4f; // top of topmost labels
float LabelWidth = width - 2 * LabelLeft;
float LabelHeight = (height - 2 * LabelTop) / 3f;
// create label components to show text
CurrentDayLabel = AddUIComponent<UILabel>();
CurrentDayLabel.name = "GameDayTimerCurrentDayLabel";
CurrentDayLabel.text = "Curr Day";
CurrentDayLabel.relativePosition = new Vector3(LabelLeft, LabelTop);
CurrentDayLabel.textAlignment = UIHorizontalAlignment.Left;
CurrentDayLabel.autoSize = false;
CurrentDayLabel.width = LabelWidth;
CurrentDayLabel.height = LabelHeight;
PreviousDayLabel = AddUIComponent<UILabel>();
PreviousDayLabel.name = "GameDayTimerPreviousDayLabel";
PreviousDayLabel.text = "Prev Day";
PreviousDayLabel.relativePosition = new Vector3(LabelLeft, LabelTop + LabelHeight);
PreviousDayLabel.textAlignment = UIHorizontalAlignment.Left;
PreviousDayLabel.autoSize = false;
PreviousDayLabel.width = LabelWidth;
PreviousDayLabel.height = LabelHeight;
FramesPerSecLabel = AddUIComponent<UILabel>();
FramesPerSecLabel.name = "GameDayTimerFramesPerSecLabel";
FramesPerSecLabel.text = "FPS";
FramesPerSecLabel.relativePosition = new Vector3(LabelLeft, LabelTop + 2 * LabelHeight);
FramesPerSecLabel.textAlignment = UIHorizontalAlignment.Left;
FramesPerSecLabel.autoSize = false;
FramesPerSecLabel.width = LabelWidth;
FramesPerSecLabel.height = LabelHeight;
// create label components to show timing values
CurrentDayValue = AddUIComponent<UILabel>();
CurrentDayValue.name = "GameDayTimerCurrentDayValue";
CurrentDayValue.text = "";
CurrentDayValue.relativePosition = CurrentDayLabel.relativePosition;
CurrentDayValue.textAlignment = UIHorizontalAlignment.Right;
CurrentDayValue.autoSize = false;
CurrentDayValue.width = LabelWidth;
CurrentDayValue.height = LabelHeight;
PreviousDayValue = AddUIComponent<UILabel>();
PreviousDayValue.name = "GameDayTimerPreviousDayValue";
PreviousDayValue.text = "";
PreviousDayValue.relativePosition = PreviousDayLabel.relativePosition;
PreviousDayValue.textAlignment = UIHorizontalAlignment.Right;
PreviousDayValue.autoSize = false;
PreviousDayValue.width = LabelWidth;
PreviousDayValue.height = LabelHeight;
FramesPerSecValue = AddUIComponent<UILabel>();
FramesPerSecValue.name = "GameDayTimerFramesPerSecValue";
FramesPerSecValue.text = "";
FramesPerSecValue.relativePosition = FramesPerSecLabel.relativePosition;
FramesPerSecValue.textAlignment = UIHorizontalAlignment.Right;
FramesPerSecValue.autoSize = false;
FramesPerSecValue.width = LabelWidth;
FramesPerSecValue.height = LabelHeight;
// initialize timing things
GDTTotalTimeInDay = 0;
GDTPreviousTotalTimeInDayText = "xx";
GDTPreviousDay = SimulationManager.instance.m_currentGameTime.Day;
GDTPreviousSimSpeed = SimulationManager.instance.SelectedSimulationSpeed;
GDTPreviousRealTime = GetCurrentRealTime();
FPSPreviousRealTime = GetCurrentRealTime();
// determine if Real Time mod is enabled
foreach (PluginInfo mod in Singleton<PluginManager>.instance.GetPluginsInfo())
{
// ignore builtin mods and camera script
if (!mod.isBuiltin && !mod.isCameraScript)
{
// check against the Real Time workshop ID
if (mod.publishedFileID.AsUInt64 == 1420955187)
{
RealTimeModEnabled = mod.isEnabled;
break;
}
}
}
if (RealTimeModEnabled)
{
// don't wait for a day change
GDTFirstDayChanged = true;
}
else
{
// wait for a day change
WaitForDayChange();
}
// show or hide the panel according to the config
isVisible = config.PanelIsVisible;
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
/// <summary>
/// Update is called every frame
/// all the timing calcs are performed here
/// </summary>
public override void Update()
{
// do base processing
base.Update();
try
{
// get current real time (used often)
double CurrentRealTime = GetCurrentRealTime();
// do game day timing only if not paused
if (!SimulationManager.instance.SimulationPaused && !SimulationManager.instance.ForcedSimulationPaused)
{
// check for simulation speed change
int SimSpeed = SimulationManager.instance.SelectedSimulationSpeed;
if (SimSpeed != GDTPreviousSimSpeed)
{
// sim speed changed, save new sim speed
GDTPreviousSimSpeed = SimSpeed;
// if Real time mod is not enabled, wait for day to change before starting game day timings again
if (!RealTimeModEnabled)
{
WaitForDayChange();
}
}
else
{
// accumulate time delta
GDTTotalTimeInDay += CurrentRealTime - GDTPreviousRealTime;
// if first day has changed, then show current day running total
if (GDTFirstDayChanged)
{
// show current day total only when it changes
string GDTTotalTimeInDayText = FormatGameDayTime(GDTTotalTimeInDay);
if (GDTTotalTimeInDayText != GDTPreviousTotalTimeInDayText)
{
CurrentDayValue.text = GDTTotalTimeInDayText;
GDTPreviousTotalTimeInDayText = GDTTotalTimeInDayText;
// set color based on sim speed and elapsed time
Color32 color = GetGameDayTimeColor(SimSpeed, GDTTotalTimeInDay);
CurrentDayLabel.textColor = color;
CurrentDayValue.textColor = color;
}
}
// check for day change
int CurrentGameDay = SimulationManager.instance.m_currentGameTime.Day;
if (CurrentGameDay != GDTPreviousDay)
{
// day has changed, display current running total as previous day value
// display only on second and subsequent day changes
if (GDTFirstDayChanged)
{
PreviousDayValue.text = FormatGameDayTime(GDTTotalTimeInDay);
// set color based on sim speed and elapsed time
Color32 color = GetGameDayTimeColor(SimSpeed, GDTTotalTimeInDay);
PreviousDayLabel.textColor = color;
PreviousDayValue.textColor = color;
}
// reset for next day
GDTFirstDayChanged = true;
GDTPreviousDay = CurrentGameDay;
GDTTotalTimeInDay = 0;
GDTPreviousTotalTimeInDayText = "xx";
}
}
}
// always save current real time, even if simulation is paused,
// so that next time delta is computed correctly when simulation resumes
GDTPreviousRealTime = CurrentRealTime;
// count frames
FPSFrameCount++;
// check if one second has elapsed
double ElapsedTime = CurrentRealTime - FPSPreviousRealTime;
if (ElapsedTime >= 1d)
{
// display FPS
double FramesPerSecond = FPSFrameCount / ElapsedTime;
FramesPerSecValue.text = FramesPerSecond.ToString("0");
// change color of FPS display
Color32 color;
if (FramesPerSecond <= 10) color = ColorRed;
else if (FramesPerSecond <= 20) color = ColorOrange;
else if (FramesPerSecond <= 30) color = ColorYellow;
else color = ColorWhite;
FramesPerSecLabel.textColor = color;
FramesPerSecValue.textColor = color;
// reset for next second
FPSPreviousRealTime = CurrentRealTime;
FPSFrameCount = 0;
}
}
catch (Exception ex)
{
Debug.LogException(ex);
WaitForDayChange();
}
}
/// <summary>
/// format the game time
/// </summary>
private string FormatGameDayTime(double gameDayTime)
{
if (RealTimeModEnabled)
{
// format as MM:SS
int minutes = (int)(gameDayTime / 60);
int seconds = (int)gameDayTime - minutes * 60;
return minutes.ToString("00") + ":" + seconds.ToString("00");
}
else
{
// format as 0.0
return gameDayTime.ToString("0.0");
}
}
/// <summary>
/// Get the color associated with the sim speed and elapsed time
/// </summary>
private Color32 GetGameDayTimeColor(int SimSpeed, double ElapsedTime)
{
Color32 color;
if (RealTimeModEnabled)
{
// don't do color coding when Real Time mod is enabled
color = ColorWhite;
}
else
{
// define standard elapsed time based on the speed
double StandardElapsedTime;
if (SimSpeed == 1) StandardElapsedTime = 10.0;
else if (SimSpeed == 2) StandardElapsedTime = 5.0;
else StandardElapsedTime = 2.5;
// compute the multiple of elapsed time to standard time
double TimeMultiple = ElapsedTime / StandardElapsedTime;
// set color based on the multiple
if (TimeMultiple >= 4) color = ColorRed;
else if (TimeMultiple >= 3) color = ColorOrange;
else if (TimeMultiple >= 2) color = ColorYellow;
else color = ColorWhite;
}
return color;
}
private void GameDayTimerPanel_eventMouseDown(UIComponent component, UIMouseEventParameter eventParam)
{
// remember that user did mouse down
MouseDown = true;
}
private void GameDayTimerPanel_eventMouseMove(UIComponent component, UIMouseEventParameter eventParam)
{
// if moving while the mouse button is down, then start dragging and make panel follow mouse
if (MouseDown)
{
Dragging = true;
MovePanelToPosition(relativePosition.x + eventParam.moveDelta.x, relativePosition.y - eventParam.moveDelta.y);
// don't save position to config file while dragging
}
}
private void GameDayTimerPanel_eventMouseUp(UIComponent component, UIMouseEventParameter eventParam)
{
// if dragging, then drag is done, save final panel position
if (Dragging)
{
GameDayTimerConfiguration.SavePanelPosition(relativePosition.x, relativePosition.y);
}
// reset dragging flags
MouseDown = false;
Dragging = false;
}
/// <summary>
/// Move panel to specified position
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public void MovePanelToPosition(float x, float y)
{
relativePosition = new Vector3(x,y);
}
/// <summary>
/// Wait for a day to change
/// </summary>
private void WaitForDayChange()
{
// first game day has not changed
GDTFirstDayChanged = false;
// clear some values
CurrentDayLabel.textColor = ColorWhite;
CurrentDayValue.text = "";
PreviousDayLabel.textColor = ColorWhite;
PreviousDayValue.text = "";
}
/// <summary>
/// get the current real time in seconds and fractions of a second
/// </summary>
/// <returns></returns>
private double GetCurrentRealTime()
{
// cannot use the simulation real timer because it counts up to 60 and then resets to 0
return DateTime.Now.Ticks / (double)TimeSpan.TicksPerSecond;
}
}
}