-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathUIPanelUnitsControl.cs
613 lines (518 loc) · 21.7 KB
/
UIPanelUnitsControl.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
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Linq;
/// <summary>
/// Panel displayed when 1+ units are selected.
/// Contains info regarding selected units and handles action buttons.
/// </summary>
public class UIPanelUnitsControl : UIPanel
{
/// <summary>
/// Event raised when the 'REMOVE' units button is pressed.
/// </summary>
public static event Action<ICollection<Unit>> OnUnitsRemoved;
/// <summary>
/// Event raised when the 'MOVE' units button is toggled.
/// True = Selecting a move destination.
/// </summary>
public static event Action<bool> OnMoveSelect;
/// <summary>
/// Event raised when the selected units start/stop moving.
/// True = Moving.
/// </summary>
public static event Action<bool> OnMoving;
/// <summary>
/// Event raised after the selected units try to harvest their tiles.
/// </summary>
public static event Action OnHarvest;
/// <summary>
/// Event raised when a turn ends.
/// </summary>
public static event Action OnNewTurn;
// Serialized variables.
[Header("ANIMATOR")]
[Tooltip("Animator component of info sub-panel.")]
[SerializeField] private Animator _subPanelAnim;
[Header("UNIT'S TYPE/COUNT")]
[Tooltip("Text component where the type of the single selected unit or " +
"number of overall selected units is displayed.")]
[SerializeField] private TMP_Text _unitTypeOrCountTxt;
[Tooltip("Text component that follows up the one before. Displays " +
"\"UNIT(S) SELECTED\".")]
[SerializeField] private TMP_Text _unitOrUnitsSelectedTxt;
[Header("UNIT ICONS")]
[Tooltip("Parent game object where the unit icons are stored.")]
[SerializeField] private Transform _unitIconsFolder;
[Tooltip("Prefab of unit icon.")]
[SerializeField] private GameObject _unitIcon;
[Tooltip("Limit of displayable icons.")]
[SerializeField] private int _iconsDisplayedLimit;
[Tooltip("Prefab of unit overflow count.")]
[SerializeField] private GameObject _unitOverflowCount;
[Header("UNIT RESOURCES")]
[Tooltip("Parent game object where the resource counters are stored.")]
[SerializeField] private Transform _resourceCountFolder;
[Tooltip("Prefab of resource counter.")]
[SerializeField] private GameObject _resourceCount;
[Header("BUTTONS")]
[Tooltip("Move button.")]
[SerializeField] private Button _moveButton;
[Tooltip("Harvest button.")]
[SerializeField] private Button _harvestButton;
[Tooltip("Color for 'MOVE' button to displayed when it toggled OFF.")]
[SerializeField] private Color _normalColor;
[Tooltip("Color for 'MOVE' button to displayed when it toggled ON.")]
[SerializeField] private Color _selectedColor;
[Tooltip("Buttons to disable when units are moving.")]
[SerializeField] private Button[] _toggleButtons;
[Header("MOVEMENT TARGET")]
[Tooltip("Parent game object where enemies are instantiated.")]
[SerializeField] private Transform _enemiesFolder;
[Tooltip("Prefab of units target destination.")]
[SerializeField] private GameObject _targetDestinationPrefab;
[Header("GAME DATA")]
[Tooltip("Scriptable Object with Ongoing Game Data.")]
[SerializeField] private OngoingGameDataSO _ongoingData;
[Tooltip("Scriptable Object with Preset Game Data.")]
[SerializeField] private PresetGameDataSO _gameData;
// Private list containing displayed selected units.
private List<Unit> _selectedUnits;
// Private reference to hold block of button colors.
private ColorBlock _colorBlock;
// Control variables for moving units.
private bool _isSelectingMove, _isMoving;
/// <summary>
/// Unity method, on enable, subscribes to events.
/// </summary>
private void OnEnable()
{
UnitSelection.OnUnitsSelected += DisplayUnitsData;
MapCell.OnTargeted += MoveTo;
}
/// <summary>
/// Unity method, on disable, unsubscribes to events.
/// </summary>
private void OnDisable()
{
UnitSelection.OnUnitsSelected -= DisplayUnitsData;
MapCell.OnTargeted -= MoveTo;
}
/// <summary>
/// Sets up panel.
/// </summary>
public void SetupPanel()
{
// Sets the color of the move buttons.
_colorBlock = _moveButton.colors;
// Closes the panel.
ClosePanel();
}
/// <summary>
/// Reveals panel.
/// </summary>
/// <param name="p_transitionTime">Reveal time (s).</param>
public void OpenPanel(float p_transitionTime = 0)
{
// Reveals the panel.
base.Open(p_transitionTime);
// Activates opening trigger of sub-panel animator.
_subPanelAnim.SetBool("visible", true);
}
/// <summary>
/// Hides panel and destroys all the visual unit info.
/// </summary>
/// <param name="p_transitionTime">Hiding time (s).</param>
public void ClosePanel(float p_transitionTime = 0)
{
// Resets movement control variables.
_isSelectingMove = false;
_isMoving = false;
// Updates buttons.
UpdateButtons();
// Activate closing trigger of sub-panel animator.
_subPanelAnim.SetBool("visible", false);
// Hides the panel.
base.Close(p_transitionTime);
// Destroys instantiated objects.
DestroyPrefabs();
}
/// <summary>
/// Destroys instantiated unit icons + resource count prefabs.
/// </summary>
private void DestroyPrefabs()
{
// Destroys all unit icons that might be instantiated.
foreach (Transform unitIcon in _unitIconsFolder)
Destroy(unitIcon.gameObject);
// Destroys all resource counts that might be instantiated.
foreach (Transform resourceCount in _resourceCountFolder)
Destroy(resourceCount.gameObject);
}
/// <summary>
/// Displays data based on the units selected.
/// Reveals & Hides panel if no units are selected.
/// </summary>
/// <param name="p_selectedUnits">Units selected by the user.</param>
private void DisplayUnitsData(ICollection<Unit> p_selectedUnits)
{
// Stores the selected units in a list.
_selectedUnits = new List<Unit>(p_selectedUnits);
// Instantiates a hash set that stores the names of the possible resources.
HashSet<string> m_possibleResources = new HashSet<string>();
// Destroys left-over instantiated prefabs.
DestroyPrefabs();
// Toggles buttons and resets display of move button.
UpdateButtons();
// If there's only one unit selected.
if (p_selectedUnits.Count == 1)
{
// Changes the panels text to display a singular unit sample text,
//plus its name.
_unitTypeOrCountTxt.text = p_selectedUnits.First().Name.ToUpper();
_unitOrUnitsSelectedTxt.text = "UNIT SELECTED";
}
// If there's more than one unit selected.
else
{
// Changes the panels text to display multiple units sample text,
// plus their count.
_unitTypeOrCountTxt.text = p_selectedUnits.Count.ToString();
_unitOrUnitsSelectedTxt.text = "UNITS SELECTED";
}
// Index control variable.
int m_iconsIndex = 0;
// If there are more icons to display than there is space.
if (p_selectedUnits.Count > _iconsDisplayedLimit)
{
// Updates index to not display excess icons.
m_iconsIndex = p_selectedUnits.Count - _iconsDisplayedLimit + 1;
// Instantiates a counter, representing how many icons are hidden.
Instantiate(_unitOverflowCount, _unitIconsFolder)
.GetComponent<TMP_Text>().text = $"+{m_iconsIndex}";
}
// Displays most recent unit icons.
for (int i = m_iconsIndex; i < p_selectedUnits.Count; i++)
{
// Instantiates unit icons.
Instantiate(_unitIcon, _unitIconsFolder)
.GetComponent<Image>().sprite = _selectedUnits[i].Icon;
}
// Clears possible resources hash set.
m_possibleResources.Clear();
// Goes through selected units.
foreach (Unit _currentUnit in p_selectedUnits)
{
// Goes through each resource name that the current unit can collect.
foreach (string f_resourceName in _currentUnit.ResourceNamesToCollect)
{
// Adds resource name to previous hash set.
m_possibleResources.Add(f_resourceName);
}
}
// Stores current resource counter.
GameObject m_rCounter;
// Goes through each possible resource name.
foreach (string f_resourceName in m_possibleResources)
{
// Instantiates a resource counter.
m_rCounter = Instantiate(_resourceCount, _resourceCountFolder);
// Updates the image of the resource counter to match the resource
// type.
m_rCounter.GetComponentInChildren<Image>().sprite = _gameData.ResourceDefaultSprites[f_resourceName];
// Updates the counter value, with the number of this resource
// across all selected units.
m_rCounter.GetComponentInChildren<TMP_Text>().text = p_selectedUnits
.SelectMany(u => u.Resources)
.Count(u => u.Name == f_resourceName)
.ToString("00");
}
}
/// <summary>
/// Toggles selected buttons, depending if units are moving.
/// Toggles 'HARVEST' button, depending if there are resources to harvest.
/// Changes color of 'MOVE' button to manually display if it's selected.
/// </summary>
private void UpdateButtons()
{
// Buttons are toggled off if selecting units' move destination or moving.
foreach(Button f_btn in _toggleButtons)
f_btn.interactable = !(_isSelectingMove || _isMoving);
// Disables move button while units are moving.
_moveButton.interactable = !_isMoving;
// Enables harvest button if not moving and there are resources to harvest.
_harvestButton.interactable = !(_isSelectingMove || _isMoving)
&& SelectedUnitsCanHarvest();
// Updates move button colors.
if (_isSelectingMove)
{
_colorBlock.normalColor = _selectedColor;
_colorBlock.pressedColor = _selectedColor;
_colorBlock.selectedColor = _selectedColor;
}
else
{
_colorBlock.normalColor = _normalColor;
_colorBlock.pressedColor = _normalColor;
_colorBlock.selectedColor = _normalColor;
}
_moveButton.colors = _colorBlock;
}
/// <summary>
/// Checks if there are harvestable resources where the selected units are.
/// </summary>
/// <returns>True if a unit is on a tile with resources it can collect.</returns>
private bool SelectedUnitsCanHarvest()
{
// Ignores method if there aren't any selected units.
if (_selectedUnits == null) return true;
GameTile m_targetTile;
// Iterates every selected unit.
foreach (Unit f_unit in _selectedUnits)
{
// Gets the unit's tile.
m_targetTile = _ongoingData.MapCells[f_unit.MapPosition].Tile;
// Iterates resource names this unit can collect.
for (int i = 0; i < f_unit.ResourceNamesToCollect.Count; i++)
{
// Iterates resources present in this game tile.
foreach (Resource f_resource in m_targetTile.Resources)
{
// If the resource's name the unit collects matches this one.
if (f_resource.Name == f_unit.ResourceNamesToCollect[i])
return true;
}
}
}
return false;
}
/// <summary>
/// Toggles units movement mode.
/// </summary>
/// <remarks>
/// Called by the 'MOVE' Unity button, in this panel.
/// </remarks>
public void OnMoveButton()
{
// Toggles selecting destination control variable and raises event.
_isSelectingMove = !_isSelectingMove;
OnMoveSelect?.Invoke(_isSelectingMove);
// Toggles affected buttons and update move button display.
UpdateButtons();
// Toggles target selection.
if (_isSelectingMove) StartCoroutine(SelectingMoveTarget());
else StopCoroutine(SelectingMoveTarget());
}
/// <summary>
/// Handles units movement target selection mode.
/// </summary>
/// <returns>Null.</returns>
private IEnumerator SelectingMoveTarget()
{
// While right click isn't pressed.
while (!Input.GetMouseButtonDown(1))
yield return null;
// Calls move button again, to negate variables.
OnMoveButton();
}
/// <summary>
/// Stops selection mode coroutine and starts moving units coroutine.
/// </summary>
/// <param name="p_targetCell">Target cell to move to.</param>
private void MoveTo(MapCell p_targetCell)
{
StopAllCoroutines();
StartCoroutine(MovingUnitsTo(p_targetCell));
}
/// <summary>
/// Handles selected units' movement, advancing a turn each time a set of units move.
/// Units stop moving when blocked or when they reach the destination.
/// </summary>
/// <param name="p_targetCell">Target map cell to move to.</param>
/// <returns>Time in seconds a unit takes to move.</returns>
private IEnumerator MovingUnitsTo(MapCell p_targetCell)
{
// Stores moving and blocked units in hash sets.
ISet<Unit> m_movingUnits = new HashSet<Unit>(_selectedUnits);
ISet<Unit> m_blockedUnits = new HashSet<Unit>();
// Waits for some seconds.
YieldInstruction m_waitForUnitsToMove =
new WaitForSeconds(_gameData.UnitMoveTime * 1.25f);
// Toggles move button.
OnMoveButton();
// Sets is moving variable to true and raises event.
_isMoving = true;
OnMoving?.Invoke(_isMoving);
// Updates buttons with moving info.
UpdateButtons();
// Calculates destination target prompt spawn position.
Vector3 m_spawnPos = p_targetCell.transform.position;
m_spawnPos.y += (_ongoingData.MapCellSize * _gameData.UnitDisplayOffset);
// Instantiates destination target prompt on top of target cell.
UnitTarget m_target = Instantiate(_targetDestinationPrefab, m_spawnPos,
Quaternion.identity, _enemiesFolder).GetComponent<UnitTarget>();
// Initializes target.
m_target.Initialize(_gameData.UnitDisplaySize);
// Holds coordinates for the next move.
Vector2 m_nextMove;
// Holds the world space for the next move.
Vector3 m_worldPosMove;
// While there are moving units.
while (m_movingUnits.Count > 0)
{
// Iterates every moving unit.
foreach (Unit f_unit in m_movingUnits)
{
// Saves unit's next move towards destination.
m_nextMove = f_unit.GetNextMoveTowards(p_targetCell.MapPosition);
// If next move isn't out of the map's bounds.
if (_ongoingData.MapCells.ContainsKey(m_nextMove))
{
// If the cell the unit is moving to isn't already occupied.
if (_ongoingData.MapUnits[m_nextMove] == null)
{
// Calculates world position for this unit to move to.
m_worldPosMove =
_ongoingData.MapCells[m_nextMove].transform.position;
m_worldPosMove.y +=
(_ongoingData.MapCellSize * _gameData.UnitDisplayOffset);
// Moves unit.
_ongoingData.MoveUnitTo(f_unit, m_nextMove);
f_unit.MoveTo(m_nextMove, m_worldPosMove);
// Iterates next unit.
continue;
}
}
// If the unit didn't move, add it to blocked units collection.
m_blockedUnits.Add(f_unit);
}
// Removes blocked units from moving units collection.
m_movingUnits.ExceptWith(m_blockedUnits);
// Clears blocked units.
m_blockedUnits.Clear();
// Waits for units to move and ends turn.
if (m_movingUnits.Count > 0)
{
yield return m_waitForUnitsToMove;
OnNewTurn?.Invoke();
}
}
// Sets is moving variable to false and raises event.
_isMoving = false;
OnMoving?.Invoke(_isMoving);
// Removes the outline of the map cell and updates the buttons.
p_targetCell.OnPointerExit(null);
UpdateButtons();
}
/// <summary>
/// Selected units try to harvest resources from the tile they are on.
/// Depending on the unit, may also add a resource to the game tile.
/// </summary>
/// <remarks>
/// Called by the 'HARVEST' Unity button, in this panel.
/// </remarks>
public void OnHarvestButton()
{
// Stores tile that unit is standing on.
GameTile m_targetTile;
// Controls if a resource has been collected.
bool m_resourceCollected = false;
// Controls duplicate resources when trying to add to the tile.
bool m_dupResource = false;
// For each selected unit.
foreach (Unit f_unit in _selectedUnits)
{
// Sets resource collected control to false;
m_resourceCollected = false;
// Gets the unit's tile.
m_targetTile = _ongoingData.MapCells[f_unit.MapPosition].Tile;
// Iterates resource names this unit can collect.
for (int i = 0; i < f_unit.ResourceNamesToCollect.Count; i++)
{
// Iterates resources present in this game tile.
foreach (Resource f_resource in m_targetTile.Resources)
{
// If the resource's name the unit collects matches this one.
if (f_resource.Name == f_unit.ResourceNamesToCollect[i])
{
// Adds resource to unit.
f_unit.HarvestResource(f_resource);
// Removes resource from tile.
m_targetTile.RemoveResource(f_resource);
// Sets resource collected control to true.
m_resourceCollected = true;
break;
}
}
}
// If the unit managed to collect any resource.
if (m_resourceCollected)
{
// Iterates resource names this unit can generate.
for (int i = 0; i < f_unit.ResourceNamesToGenerate.Count; i++)
{
// Sets duplicate resource control to false.
m_dupResource = false;
// Iterates resources present in this game tile.
foreach (Resource f_resource in m_targetTile.Resources)
{
// If the resource's name the unit generates matches this one.
if (f_resource.Name == f_unit.ResourceNamesToGenerate[i])
{
// It already exists in the tile.
// Sets duplicate resource control to false.
m_dupResource = true;
break;
}
}
// If the tile doesn't have the resource the unit is generating.
if (!m_dupResource)
{
// Iterates data of every possible game resource.
foreach (PresetResourcesData f_resourceData in _gameData.Resources)
{
// If the resource the units is generating is found.
if (f_resourceData.Name == f_unit.ResourceNamesToGenerate[i])
{
// Adds the resource to the tile.
m_targetTile.AddResource(new Resource(
f_resourceData.Name,
f_resourceData.Coin,
f_resourceData.Food,
_gameData.GetSpriteDictOf(f_resourceData.Name),
f_resourceData.DefaultResourceSprite));
}
}
}
}
}
// Updates map cell's sprites.
_ongoingData.MapCells[f_unit.MapPosition].UpdateResourceSprites();
}
// Raises event that harvest action has been completed.
if (m_resourceCollected) OnHarvest?.Invoke();
// Raises event that turn has ended.
OnNewTurn?.Invoke();
// Updates data in this panel.
DisplayUnitsData(_selectedUnits);
}
/// <summary>
/// Removes every selected unit from the ongoing data collection, destroys
/// their game objects and raises event containing the units removed.
/// </summary>
/// <remarks>
/// Called by the 'REMOVE' Unity button, in this panel.
/// </remarks>
public void OnRemoveButton()
{
foreach (Unit f_unit in _selectedUnits)
{
_ongoingData.RemoveUnit(f_unit);
Destroy(f_unit.gameObject);
}
OnUnitsRemoved?.Invoke(_selectedUnits);
}
}