-
Notifications
You must be signed in to change notification settings - Fork 48
/
BaseConflict.EntityComponents.Client.pas
4800 lines (4320 loc) · 156 KB
/
BaseConflict.EntityComponents.Client.pas
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
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit BaseConflict.EntityComponents.Client;
interface
uses
Generics.Collections,
SysUtils,
Classes,
Math,
Vcl.Forms,
RegularExpressions,
// ------- ThirdParty -----------
FMOD.Common,
FMOD.Studio.Common,
FMOD.Studio.Classes,
// ------- Engine ---------
Engine.Mesh,
Engine.ParticleEffects,
Engine.Helferlein,
Engine.Helferlein.Threads,
Engine.Helferlein.Windows,
Engine.Core,
Engine.Core.Camera,
Engine.Core.Types,
Engine.Math,
Engine.Animation,
Engine.GUI,
Engine.GUI.Editor,
Engine.Vertex,
Engine.Math.Collision2D,
Engine.Math.Collision3D,
Engine.GFXApi,
Engine.GFXApi.Types,
Engine.Log,
Engine.Network,
Engine.Network.RPC,
Engine.Script,
Engine.Input,
// -------- Game ----------
BaseConflict.Api,
BaseConflict.Constants,
BaseConflict.Constants.Cards,
BaseConflict.Constants.Client,
BaseConflict.Constants.Scenario,
BaseConflict.Globals,
BaseConflict.Entity,
BaseConflict.EntityComponents.Shared,
BaseConflict.Game,
BaseConflict.Map,
BaseConflict.Classes.MiniMap,
BaseConflict.Types.Shared,
BaseConflict.Types.Target,
BaseConflict.Types.Client,
BaseConflict.Settings.Client,
BaseConflict.Classes.Client;
type
{$RTTI INHERIT}
/// <summary> Each units gets this component at the client for determining the unit clicked onto. </summary>
TClickCollisionComponent = class(TEntityComponent)
public
class var CLICK_BIAS : single;
protected
FLowPriority : boolean;
FCapsule : RCapsule;
{$IFDEF DEBUG}
FFrame : integer;
{$ENDIF}
function CurrentCapsule : RCapsule;
published
[XEvent(eiGetUnitsAtCursor, epMiddle, etRead, esGlobal)]
/// <summary> Returns this if hit by ray and more near than previous. </summary>
function OnGetUnitsAtCursor(ClickRay : RParam; Previous : RParam) : RParam;
public
function SetCapsule(Origin, Endpoint : RVector3; Radius : single) : TClickCollisionComponent;
function LowPriority : TClickCollisionComponent;
end;
{$RTTI INHERIT}
/// <summary> General Component that handle a changed option. Should be used for any option that not direct connect to any
/// subsystem like GUI or Sound and so has no own ManagerComponent.</summary>
TClientSettingsComponent = class(TEntityComponent)
protected
procedure HandleOption(Option : EnumClientOption);
procedure Init;
published
[XEvent(eiClientOption, epLast, etTrigger, esGlobal)]
/// <summary> Handle a option.</summary>
function OnClientOption(Option : RParam) : boolean;
public
constructor Create(Entity : TEntity); override;
end;
{$RTTI INHERIT}
ProcTokenCallBack = procedure(TokenMapping : TList<integer>) of object;
RPingLogData = record
Timestamp : int64;
Ping : integer;
end;
/// <summary> Handles network for client. </summary>
TClientNetworkComponent = class(TNetworkComponent)
private const
PING_LOG_INTERVAL = 2000;
protected type
TSocketPromise = class(TPromise<TTCPClientSocketDeluxe>);
TReconnectThread = class(TThread)
private const
MAXTIME_FOR_TRYING = 20000;
TIME_BETWEEN_ATTEMPTS = 1000;
private
FReconnectTimer : TTimer;
FAttemptTime : TTimer;
FNewSocket : TSocketPromise;
FServerAddress : RInetAddress;
FLastReceivedIndex : integer;
FToken : string;
protected
procedure Execute; override;
public
property NewSocket : TSocketPromise read FNewSocket;
constructor Create(const ServerAddress : RInetAddress; LastReceivedIndex : integer; const Token : string);
destructor Destroy; override;
end;
protected
FTokenCallBack : ProcTokenCallBack;
FTCPClientSocket : TTCPClientSocketDeluxe;
FServerAddress : RInetAddress;
FServerCrashedTimer : TTimer;
FServerCrashedTimerStarted : boolean;
FAuthentificationToken : string;
FLastReceivedIndex : integer;
FStopReconnect : boolean;
FReconnectPromise : TSocketPromise;
FFinishedReceivedGameData : ProcCallback;
FPingLogTimer : TTimer;
FPingLog : TList<RPingLogData>;
procedure NewData(Data : TDatapacket); override;
procedure DeserializeEntity(Stream : TStream);
procedure Reconnect;
procedure LogPing;
procedure SendPingLog;
procedure SendRageQuit;
procedure BeforeComponentFree; override;
published
[XEvent(eiIdle, epFirst, etTrigger, esGlobal)]
/// <summary> Handles networkdata. </summary>
function OnIdle : boolean;
[XEvent(eiClientReady, epLast, etTrigger, esGlobal)]
function OnClientReady : boolean;
public
constructor Create(Owner : TEntity; Socket : TTCPClientSocketDeluxe; const AuthentificationToken : string; FinishedReceivedGameData : ProcCallback); reintroduce;
function Ping : integer;
procedure Send(Data : TCommandSequence); override;
destructor Destroy; override;
end;
{$RTTI INHERIT}
/// <summary> Manages multicommander players. </summary>
TCommanderManagerComponent = class(TEntityComponent)
protected
FCommander : TList<TEntity>;
FActiveCommander : TEntity;
FActiveIndex : integer;
published
[XEvent(eiChangeCommander, epMiddle, etTrigger, esGlobal)]
/// <summary> Try to change the active commander. </summary>
function OnChangeCommander(Index : RParam) : boolean;
[XEvent(eiNewCommander, epLast, etTrigger, esGlobal)]
/// <summary> Adds a new commander to the list. </summary>
function OnNewCommander(Commander : RParam) : boolean;
public
function Count : integer;
function HasActiveCommander : boolean;
property ActiveCommander : TEntity read FActiveCommander;
property ActiveCommanderIndex : integer read FActiveIndex;
function ActiveCommanderTeamID : integer;
procedure ChangeCommander(Index : integer);
function GetCommanderByIndex(Index : integer) : TEntity;
function TryGetCommanderByIndex(Index : integer; out Commander : TEntity) : boolean;
procedure RegisterCommander(Commander : TEntity);
procedure ClearCommanders;
function EnumerateCommanders : TList<TEntity>;
constructor Create(Owner : TEntity); override;
destructor Destroy; override;
end;
{$RTTI INHERIT}
/// <summary> Handles some commanderfunctionality like enumeration. </summary>
TCommanderComponent = class(TEntityComponent)
published
[XEvent(eiEnumerateCommanders, epFirst, etRead, esGlobal)]
/// <summary> Add this commander to the enumeration. </summary>
function OnEnumerateCommanders(PrevValue : RParam) : RParam;
end;
{$RTTI INHERIT}
/// <summary> Stifles eiFire events if the owner not belongs to your team. </summary>
TClientFireTeamFilterComponent = class(TEntityComponent)
published
[XEvent(eiFire, epFirst, etTrigger)]
function OnFire(Targets : RParam) : boolean;
end;
{$RTTI INHERIT}
/// <summary> Handles the camera. </summary>
TClientCameraComponent = class(TEntityComponent)
public const
// top, right, bottom, left
SCROLLBORDERWIDTH : RIntVector4 = (X : 10; Y : 10; Z : 2; W : 10);
private
procedure setFree(const Value : boolean);
procedure setLocked(const Value : boolean);
protected
const
CAMERA_ROTATION_SPEED = 0.008;
var
FRotation : single;
FZoom : single;
FCamPos : RVector2;
FMoving, FHasMoved, FFree, FWasFree, FFreeLocked : boolean;
FMousePos : RIntVector2;
FMovedLength : single;
FLeft, FUp, FRight, FBottom, FLaneLeft, FLaneRight : boolean;
FDragPosition : RVector2;
FTransitionTarget, FTransitionStart : RVector2;
FTransitionDuration : TTimer;
FTransition : boolean;
FFreeCamera : RRay;
procedure Scroll(Amount : single; Left, Up, Right, Bottom, LaneLeft, LaneRight : boolean);
function MinZoom : single;
function MaxZoom : single;
procedure ResetZoom;
procedure SetCamera(Position : RVector2);
procedure MoveCamera(Translation : RVector2);
procedure ClampCamera;
procedure ApplyCamera;
procedure ApplyOption(ChangedOption : EnumClientOption);
function MouseWorldPosition : RVector2;
published
[XEvent(eiMouseMoveEvent, epHigh, etTrigger, esGlobal)]
/// <summary> Moves the camera related to mouseevents. </summary>
function OnMouseMoveEvent(Position, Difference : RParam) : boolean;
[XEvent(eiKeybindingEvent, epHigh, etTrigger, esGlobal)]
function OnKeybindingEvent() : boolean;
[XEvent(eiMouseWheelEvent, epLast, etTrigger, esGlobal)]
/// <summary> Zooms. </summary>
function OnMouseWheelEvent(dZ : RParam) : boolean;
[XEvent(eiIdle, epMiddle, etTrigger, esGlobal)]
/// <summary> Set the camera in GFXD. </summary>
function OnIdle : boolean;
[XEvent(eiClientInit, epMiddle, etTrigger, esGlobal)]
/// <summary> Initialized the camera at the nexus. </summary>
function OnClientInit : boolean;
[XEvent(eiCameraMoveTo, epLast, etTrigger, esGlobal)]
/// <summary> Moves the camera to pos over duration. Ignore user Input while moving. </summary>
function OnCameraMoveTo(Pos : RParam; TransitionDuration : RParam) : boolean;
[XEvent(eiCameraMove, epMiddle, etTrigger, esGlobal)]
/// <summary> Moves the camera to pos. Clips the position. </summary>
function OnCameraMove(var Pos : RParam) : boolean;
[XEvent(eiCameraPosition, epFirst, etRead, esGlobal)]
function OnCameraPosition() : RParam;
[XEvent(eiClientOption, epLast, etTrigger, esGlobal)]
/// <summary> React to option changes. </summary>
function OnClientOption(ChangedOption : RParam) : boolean;
[XEvent(eiMiniMapMoveToEvent, epLast, etTrigger, esGlobal)]
/// <summary> Move to minimap position. </summary>
function OnMiniMapMoveToEvent(WorldPosition : RParam) : boolean;
[XEvent(eiClientCommand, epMiddle, etTrigger, esGlobal)]
function OnClientCommand(ClientCommand, Param1 : RParam) : boolean;
public
EnableScrollingBorder : boolean;
Vertical, FreeSlow : boolean;
property FreeCamera : boolean read FFree write setFree;
property Locked : boolean read FFreeLocked write setLocked;
procedure ResetPosition;
constructor Create(Owner : TEntity); override;
destructor Destroy; override;
end;
{$RTTI INHERIT}
/// <summary> Describes a commander spell, which the user is settings targets for.
/// The abilitybuttons are owning each one of this as a pattern, and if the user activates a spell
/// the Inputhandler copies it and gather targets of the user input to finally execute the spell. </summary>
TCommanderSpellData = class
protected
type
ProcDoSomethingWithTarget = reference to procedure(Target : PCommanderAbilityTarget);
var
FGlobalEventbus : TEventbus;
FIsInMode : integer;
procedure ApplyToNextTarget(proc : ProcDoSomethingWithTarget);
public
EntityID : integer; // Commander
ComponentGroup : SetComponentGroup; // Spellgroup
Targets : ACommanderAbilityTarget; // Targets
Range : single; // max distance between targets if > 0
ClosesTooltip, IsSpawner, IsDrop, IsSpell, IsEpic : boolean;
MultiModes : TArray<byte>;
constructor Create(GlobalEventbus : TEventbus; EntityID : integer; ComponentGroup : SetComponentGroup; Targets : array of RCommanderAbilityTarget; Range : single);
function TryGetCommander(out Commander : TEntity) : boolean;
function NextTargetType : EnumCommanderAbilityTargetType;
function TargetCount : integer;
function AllTargetsSet : boolean;
function IsMultiMode : boolean;
function IsReady : boolean;
/// <summary> Returns true if any of the already set targets is invalid </summary>
function AnySetTargetIsInvalid : boolean;
procedure SetCoordinateTarget(Position : RVector2);
procedure SetEntityTarget(Entity : TEntity);
procedure SetBuildgridTarget(BuildZoneID : integer; Coordinate : RIntVector2);
function HasEntityTarget(Entity : TEntity) : boolean;
function WouldEntityBeValidTarget(Entity : TEntity) : boolean;
procedure Execute;
procedure Visualize;
procedure HideVisualization;
procedure Unset;
function Copy : TCommanderSpellData;
end;
/// <summary> Handles all input of the user. </summary>
TClientInputComponent = class(TEntityComponent)
private
FLastCursorPos : RIntVector2;
FWorldMousePosition : RVector2;
FClickVector : RRay;
FPreparedSpell : TCommanderSpellData;
FSpellPreparedOnMouseDown : boolean;
FDropZoneRenderer : TZoneRenderer;
procedure PrepareAction(CustomData : TCommanderSpellData);
function IsActionPrepared : boolean;
function ApplyGridOffset(Spell : TCommanderSpellData; BuildZone : TBuildZone; Pos : RVector2) : RIntVector2;
procedure UpdateTargetDisplay;
function WaitingForSpellTargets : boolean;
procedure SetNextTarget(Spell : TCommanderSpellData);
/// <summary> Returns the first matching unit for the spell. </summary>
function TryGetTargetableUnitAtCursor(Spell : TCommanderSpellData; out UnitAtCursor : TEntity) : boolean;
function GetUnitsAtCursor() : TList<RTuple<single, TEntity>>;
/// <summary> Returns the foremost unit at the cursor. </summary>
function TryGetUnitAtCursor(out UnitAtCursor : TEntity) : boolean;
procedure UpdateCursorPos(Position : RIntVector2);
procedure OnCommanderAbilityClick(CommanderSpellData : TCommanderSpellData; MouseEvent : EnumGUIEvent);
published
[XEvent(eiLose, epLast, etTrigger, esGlobal)]
/// <summary> Clear any prepared action. </summary>
function OnLose(TeamID : RParam) : boolean;
[XEvent(eiChangeCommander, epLast, etTrigger, esGlobal)]
/// <summary> Refreshes the commanderswitch. </summary>
function OnChangeCommander(Index : RParam) : boolean;
[XEvent(eiCameraMove, epLast, etTrigger, esGlobal)]
/// <summary> Update world-cursor. </summary>
function OnCameraMove(Pos : RParam) : boolean;
[XEvent(eiKeybindingEvent, epLast, etTrigger, esGlobal)]
/// <summary> Hotkeys for build/spellabilities. </summary>
function OnKeybindingEvent() : boolean;
[XEvent(eiMouseMoveEvent, epLast, etTrigger, esGlobal)]
/// <summary> Handles commanderability preview or the use of an ability. </summary>
function OnMouseMoveEvent(Position, Difference : RParam) : boolean;
[XEvent(eiIdle, epLow, etTrigger, esGlobal)]
/// <summary> Updates the abilitypreview. </summary>
function OnIdle : boolean;
public
constructor Create(Owner : TEntity); override;
procedure ClearAction();
destructor Destroy; override;
end;
{$RTTI INHERIT}
/// <summary> Handles the gui. </summary>
TClientGUIComponent = class(TEntityComponent)
protected
FHealthbars : TGUIComponent;
FFirstGameTick : boolean;
procedure UpdateDisplays;
published
[XEvent(eiGameCommencing, epLast, etTrigger, esGlobal)]
/// <summary> Update Hud. </summary>
function OnGameCommencing() : boolean;
[XEvent(eiGameEvent, epLast, etTrigger, esGlobal)]
function OnGameEvent(const EventUID : RParam) : boolean;
[XEvent(eiChangeCommander, epHigh, etTrigger, esGlobal)]
/// <summary> Refreshes the commanderswitch. </summary>
function OnChangeCommander(Index : RParam) : boolean;
[XEvent(eiNewCommander, epLast, etTrigger, esGlobal)]
/// <summary> Updates the commanderswitch. </summary>
function OnAddCommander(Entity : RParam) : boolean;
[XEvent(eiKeybindingEvent, epMiddle, etTrigger, esGlobal)]
/// <summary> React on some debugkeys. </summary>
function OnKeybindingEvent() : boolean;
[XEvent(eiIdle, epMiddle, etTrigger, esGlobal)]
/// <summary> Updates the resource displays. </summary>
function OnIdle : boolean;
[XEvent(eiClientInit, epHigh, etTrigger, esGlobal)]
/// <summary> Initializes the score board the active commander. </summary>
function OnClientInit : boolean;
[XEvent(eiGameTick, epLast, etTrigger, esGlobal)]
function OnGameTick() : boolean;
public
constructor Create(Owner : TEntity); override;
destructor Destroy; override;
end;
{$RTTI INHERIT}
TLogicToWorldComponent = class(TEntityComponent)
published
[XEvent(eiDisplayPosition, epFirst, etRead)]
/// <summary> Return saved logic position. </summary>
function OnDisplayPosition() : RParam;
[XEvent(eiDisplayFront, epFirst, etRead)]
/// <summary> Return saved logic front. </summary>
function OnDisplayFront() : RParam;
[XEvent(eiDisplayUp, epFirst, etRead)]
/// <summary> Return saved up. </summary>
function OnDisplayUp() : RParam;
[XEvent(eiAfterCreate, epMiddle, etTrigger)]
/// <summary> Updates the displayed properties of the entity. </summary>
function OnAfterCreate : boolean;
[XEvent(eiIdle, epHigh, etTrigger, esGlobal)]
/// <summary> Updates the displayed properties of the entity. </summary>
function OnIdle : boolean;
end;
{$RTTI INHERIT}
/// <summary> Clientspecific methods. </summary>
TClientEntityManagerComponent = class(TEntityManagerComponent)
protected
FFinishTimer, FEndScreenTimer : TTimer;
FVictory : boolean;
published
[XEvent(eiLose, epLast, etTrigger, esGlobal)]
/// <summary> Kill client if lose. </summary>
function OnLose(TeamID : RParam) : boolean;
[XEvent(eiGUIEvent, epLast, etTrigger, esGlobal)]
/// <summary> Handles the continue button. </summary>
function OnGUIEvent(Event : RParam) : boolean;
[XEvent(eiIdle, epMiddle, etTrigger, esGlobal)]
/// <summary> Handle termination of game. </summary>
function OnIdle() : boolean;
public
constructor Create(Owner : TEntity); override;
destructor Destroy; override;
end;
{$RTTI INHERIT}
/// <summary> Kills the entity on finishing the game. </summary>
TSuicideOnGameEndComponent = class(TEntityComponent)
published
[XEvent(eiLose, epLast, etTrigger, esGlobal)]
function OnLose(TeamID : RParam) : boolean;
end;
{$RTTI INHERIT}
TMiniMapComponent = class(TEntityComponent)
protected
FMiniMap : TMiniMap;
FMouseDown : boolean;
published
[XEvent(eiIdle, epMiddle, etTrigger, esGlobal)]
/// <summary> Updates and renders the positions of the markers. </summary>
function OnIdle() : boolean;
[XEvent(eiKeybindingEvent, epMiddle, etTrigger, esGlobal)]
/// <summary> Clicking on the minimap triggers a minimap event. </summary>
function OnKeybindingEvent() : boolean;
[XEvent(eiMouseMoveEvent, epHigh, etTrigger, esGlobal)]
/// <summary> Moves the camera related to mouseevents. </summary>
function OnMouseMoveEvent(Position, Difference : RParam) : boolean;
public
property MiniMap : TMiniMap read FMiniMap;
constructor Create(Owner : TEntity); override;
procedure AddToMinimap(Entity : TEntity; IconPath : string; IconSize : single);
procedure RemoveFromMinimap(Entity : TEntity);
destructor Destroy(); override;
end;
{$RTTI INHERIT}
TGameIntensityComponent = class(TEntityComponent)
published
[XEvent(eiShortestBattleFrontDistance, epFirst, etRead, esGlobal)]
/// <summary> Returns distance. </summary>
function OnShortestBattleFrontDistance(Previous : RParam) : RParam;
end;
{$RTTI INHERIT}
/// <summary> Renders the buildgrid. Displays the spawn mechanics. </summary>
TBuildGridManagerComponent = class(TEntityComponent)
protected
type
TTile = class
strict private
const
GLOW_TIME_IN = 500;
GLOW_TIME_OUT = 1000;
GLOW_INTENSITY = 0.032;
GLOW_COLOR_INTENSITY = 0.4;
strict private
FBuildZone : TBuildZone;
FCoordinate : RIntVector2;
FMesh : TMesh;
FIsActive : boolean;
FGlowTransition : TGUITransitionValueSingle;
procedure SetUpShader(CurrentShader : TShader; Stage : EnumRenderStage; PassIndex : integer);
public
property BuildZone : TBuildZone read FBuildZone;
property Coordinate : RIntVector2 read FCoordinate;
property Mesh : TMesh read FMesh;
property IsActive : boolean read FIsActive;
constructor Create(BuildZone : TBuildZone; const Coordinate : RIntVector2);
procedure Activate;
procedure Reset;
destructor Destroy; override;
end;
TBuildGridVisualizer = class
strict private
FActivateEffect : TParticleEffect;
FTiles : TObjectList<TTile>;
FCurrentRotationCount, FFieldCount : integer;
public
property CurrentRotationCount : integer read FCurrentRotationCount;
property FieldCount : integer read FFieldCount;
procedure Spawn(const Coordinate : RIntVector2);
procedure Reset;
/// <summary> Render occupied tiles red and others green. </summary>
procedure ShowOccupation(const ReferencePosition : RVector2; TeamID : integer);
/// <summary> All tiles are rendered red. </summary>
procedure ShowInvalid;
/// <summary> All tiles are reset to uncolored. </summary>
procedure ResetColors;
constructor Create(BuildZone : TBuildZone);
destructor Destroy; override;
end;
var
FBuildGridVisualizers : TObjectDictionary<integer, TBuildGridVisualizer>;
published
[XEvent(eiWaveSpawn, epLast, etTrigger, esGlobal)]
/// <summary> Updates tile states. </summary>
function OnWaveSpawn(GridID, Coordinate : RParam) : boolean;
public
constructor Create(Owner : TEntity); override;
/// <summary> Render occupied tiles red and others green. </summary>
function ShowOccupation(const ReferencePosition : RVector2; TeamID : integer) : TBuildGridManagerComponent;
/// <summary> All tiles are rendered red. </summary>
function ShowInvalid : TBuildGridManagerComponent;
/// <summary> All tiles are reset to uncolored. </summary>
function ResetColors : TBuildGridManagerComponent;
destructor Destroy; override;
end;
{$RTTI INHERIT}
TVertexTraceManagerComponent = class(TEntityComponent)
protected type
TVertexTraceHandle = class
Trace : TVertexTrace;
RollUpSpeed : single;
constructor Create(Trace : TVertexTrace; RollUpSpeed : single);
destructor Destroy; override;
end;
protected
FTraces : TObjectList<TVertexTraceHandle>;
published
[XEvent(eiIdle, epMiddle, etTrigger, esGlobal)]
/// <summary> Handle Traces. </summary>
function OnIdle() : boolean;
public
constructor Create(Owner : TEntity); reintroduce;
procedure AddTrace(Trace : TVertexTrace; RollUpSpeed : single);
destructor Destroy; override;
end;
{$RTTI INHERIT}
/// <summary> Handles the procedural death effect of units. </summary>
TUnitDecayManagerComponent = class(TEntityComponent)
protected type
TDecayingUnit = class
Mesh : TMesh;
DecayTimer : TTimer;
constructor Create;
destructor Destroy; override;
end;
protected
FDecayingUnits : TObjectList<TDecayingUnit>;
published
[XEvent(eiIdle, epMiddle, etTrigger, esGlobal)]
/// <summary> Free entities which are decayed finally. </summary>
function OnIdle() : boolean;
public
constructor Create(Owner : TEntity); reintroduce;
procedure AddMesh(Mesh : TMesh; ColorIdentity : EnumEntityColor);
destructor Destroy; override;
end;
{$RTTI INHERIT}
{$REGION 'TutorialDirectorActions'}
TTutorialDirectorComponent = class;
TTutorialDirectorAction = class abstract
protected
FOwner : TTutorialDirectorComponent;
public
procedure Execute; virtual; abstract;
end;
TTutorialDirectorActionSendGameevent = class(TTutorialDirectorAction)
strict private
FEventnames : TArray<string>;
public
function Eventname(const Eventname : string) : TTutorialDirectorActionSendGameevent;
procedure Execute; override;
end;
TTutorialDirectorActionWorld = class(TTutorialDirectorAction)
private
FPosition, FLeft, FUp : RVector3;
FSize : RVector2;
FColor : RColor;
public
constructor Create;
function Position(const X, Y, Z : single) : TTutorialDirectorActionWorld;
function Left(const X, Y, Z : single) : TTutorialDirectorActionWorld;
function Up(const X, Y, Z : single) : TTutorialDirectorActionWorld;
function Size(const Width, Height : single) : TTutorialDirectorActionWorld;
function Color(const Color : cardinal) : TTutorialDirectorActionWorld;
end;
TTutorialDirectorActionScript = class(TTutorialDirectorAction)
private
FScript, FEntityUID : string;
FEntityID : integer;
public
constructor Create;
function Scriptfile(const Filename : string) : TTutorialDirectorActionScript;
function Entity(const EntityID : integer) : TTutorialDirectorActionScript; overload;
function Entity(const EntityUID : string) : TTutorialDirectorActionScript; overload;
procedure Execute; override;
end;
TTutorialDirectorActionWorldText = class(TTutorialDirectorActionWorld)
strict private
FWorldToPixelFactor, FFontHeight : single;
FText : string;
public
constructor Create;
function Text(const Text : string) : TTutorialDirectorActionWorldText;
function WorldToPixelFactor(const Factor : single) : TTutorialDirectorActionWorldText;
function FontHeight(const FontHeight : single) : TTutorialDirectorActionWorldText;
procedure Execute; override;
end;
TTutorialDirectorActionTutorialHint = class(TTutorialDirectorAction)
strict private
FText, FButtonText : string;
FIsWindow, FClear : boolean;
FPosition : RIntVector2;
public
function Text(const Text : string) : TTutorialDirectorActionTutorialHint;
function Passive : TTutorialDirectorActionTutorialHint;
function ButtonText(const ButtonText : string) : TTutorialDirectorActionTutorialHint;
function Clear : TTutorialDirectorActionTutorialHint;
procedure Execute; override;
end;
TTutorialDirectorActionGUIElement = class(TTutorialDirectorAction)
strict private
FElements, FAddClass, FRemoveClass : TArray<string>;
FHide, FEnable, FClearLock, FClearVisibility, FDisable : boolean;
public
function Element(const ElementUID : string) : TTutorialDirectorActionGUIElement;
function LoadElementsFromGroup(const GroupName : string) : TTutorialDirectorActionGUIElement;
function Hide : TTutorialDirectorActionGUIElement;
function Lock : TTutorialDirectorActionGUIElement;
function Unlock : TTutorialDirectorActionGUIElement;
function Clear : TTutorialDirectorActionGUIElement;
function ClearLock : TTutorialDirectorActionGUIElement;
function ClearVisibility : TTutorialDirectorActionGUIElement;
function AddClass(const Classname : string) : TTutorialDirectorActionGUIElement;
function RemoveClass(const Classname : string) : TTutorialDirectorActionGUIElement;
procedure Execute; override;
destructor Destroy; override;
end;
TTutorialDirectorActionKeybinding = class(TTutorialDirectorAction)
strict private
FKeybindings : TArray<EnumKeybinding>;
FBlock : boolean;
public
function Keybinding(const Keybinding : EnumKeybinding) : TTutorialDirectorActionKeybinding;
function LoadElementsFromGroup(const GroupName : string) : TTutorialDirectorActionKeybinding;
function Block : TTutorialDirectorActionKeybinding;
function Unblock : TTutorialDirectorActionKeybinding;
procedure Execute; override;
destructor Destroy; override;
end;
TTutorialDirectorActionArrowHighlight = class(TTutorialDirectorAction)
strict private
FWorldPoint : RVector3;
FWorldRadius : single;
FText, FElement, FWindowButton : string;
FElementAnchor, FWindowAnchor : EnumComponentAnchor;
FLockGUI, FClear, FNoBackdrop, FNoArrow : boolean;
public
constructor Create;
function Text(const Text : string) : TTutorialDirectorActionArrowHighlight;
function WorldPoint(const PointX, PointY, PointZ : single) : TTutorialDirectorActionArrowHighlight;
function WorldRadius(const Radius : single) : TTutorialDirectorActionArrowHighlight;
function LoadElementFromGroup(const GroupName : string) : TTutorialDirectorActionArrowHighlight;
function Element(const ElementUID : string) : TTutorialDirectorActionArrowHighlight;
function ElementAnchor(Anchor : EnumComponentAnchor) : TTutorialDirectorActionArrowHighlight;
function WindowAnchor(Anchor : EnumComponentAnchor) : TTutorialDirectorActionArrowHighlight;
function WindowButton(const ButtonText : string) : TTutorialDirectorActionArrowHighlight;
function NoBackdrop : TTutorialDirectorActionArrowHighlight;
function NoArrow : TTutorialDirectorActionArrowHighlight;
function LockGUI : TTutorialDirectorActionArrowHighlight;
function Clear : TTutorialDirectorActionArrowHighlight;
procedure Execute; override;
end;
TTutorialDirectorActionWorldTexture = class(TTutorialDirectorActionWorld)
strict private
FTexturePath : string;
public
function TexturePath(const TexturePath : string) : TTutorialDirectorActionWorldTexture;
procedure Execute; override;
end;
TTutorialDirectorActionHUD = class(TTutorialDirectorAction)
strict private
FPreventConsecutiveCardPlay : EnumNullableBoolean;
public
function PreventMultiCardPlay() : TTutorialDirectorActionHUD;
function AllowMultiCardPlay() : TTutorialDirectorActionHUD;
procedure Execute; override;
end;
TTutorialDirectorActionClearWorldObjects = class(TTutorialDirectorAction)
public
procedure Execute; override;
end;
TTutorialDirectorActionCamera = class(TTutorialDirectorAction)
protected
FSpawnerJumpEnabled : boolean;
public
function SpawnerJumpAllowed : TTutorialDirectorActionCamera;
end;
TTutorialDirectorActionLockCamera = class(TTutorialDirectorActionCamera)
protected
FLimit : boolean;
FThreshold : single;
public
function LimitX(Threshold : single) : TTutorialDirectorActionLockCamera;
procedure Execute; override;
end;
TTutorialDirectorActionUnlockCamera = class(TTutorialDirectorActionCamera)
public
procedure Execute; override;
end;
TTutorialDirectorActionMoveCamera = class(TTutorialDirectorAction)
strict private
FTargetPos : RVector2;
FTime : integer;
FFollow : boolean;
public
function MoveTo(X, Y : single) : TTutorialDirectorActionMoveCamera;
function Time(Time : integer) : TTutorialDirectorActionMoveCamera;
function FollowLastSpawnedEntity : TTutorialDirectorActionMoveCamera;
procedure Execute; override;
end;
TTutorialDirectorActionGotoStep = class(TTutorialDirectorAction)
strict private
FStepUID : string;
public
function UID(const StepUID : string) : TTutorialDirectorActionGotoStep;
procedure Execute; override;
end;
{$ENDREGION}
EnumStepTrigger = (stNever, stEvent, stGameTick, stNewEntity, stClientInit, stTime, stLastUnitDies, stCamPos, stUnitsDead);
SetStepTrigger = set of EnumStepTrigger;
TTutorialDirectorStep = class
strict private
FOwner : TTutorialDirectorComponent;
public
Eventname, StepUID : string;
FUnitUIDs : TArray<string>;
FOptional : boolean;
GameTick, TriggerCount, TriggeredTimes : integer;
TriggerOn : EnumStepTrigger;
UnitPropertyMustHave : SetUnitProperty;
Actions : TObjectList<TTutorialDirectorAction>;
Timer : TTimer;
TargetCamPos : RCircle;
constructor Create(Owner : TTutorialDirectorComponent);
function IncrementAndCheckTriggerCount : boolean;
function CheckUIDDead : boolean;
function CheckTimeTrigger : boolean;
function CheckLastUnitDiesTrigger(UnitID : integer) : boolean;
function CheckGameEventTrigger(const GameEvent : string) : boolean;
function CheckNewEntityTrigger(Entity : TEntity) : boolean;
function CheckClientInitTrigger : boolean;
function CheckGameTickTrigger(CurrentGameTick : integer) : boolean;
function CheckCamPos : boolean;
destructor Destroy; override;
end;
/// <summary> Handles all actions in the tutorial. </summary>
TTutorialDirectorComponent = class(TEntityComponent)
strict private
FActionQueue : TObjectList<TTutorialDirectorStep>;
FFirstGameTickSend, FExecuting : boolean;
FCurrentGameTick : integer;
FCurrentStepIndex : integer;
FUnitFilterMustHave, FUnitFilterMustNotHave : SetUnitProperty;
/// <summary> Returns all coming steps from the current execution point to the next mandatory step. Nil if no more steps are ahead. </summary>
function CurrentSteps : TArray<TTutorialDirectorStep>;
/// <summary> Returns all coming step triggers from the current execution point to the next mandatory step. Empty if no more steps are ahead. </summary>
function CurrentStepsTriggers : SetStepTrigger;
/// <summary> Executes the specified step if not executing another step at the moment. </summary>
procedure ExecuteStep(Step : TTutorialDirectorStep);
/// <summary> Executes all actions of the specified step. </summary>
procedure ExecuteStepActions(Step : TTutorialDirectorStep);
/// <summary> Moves the execution point to the next step. Not executing anything. Should be called before
/// executing steps as they can manipulate the execution point. </summary>
procedure NextStep;
protected
FWorldObjects : TObjectList<TVertexObject>;
FLastUnit : integer;
procedure DoGotoStep(StepLabel : string);
protected
class var FGroups : TDictionary<string, TArray<string>>;
class var FKeybindingGroups : TDictionary<string, TArray<EnumKeybinding>>;
class constructor Create;
class destructor Destroy;
published
[XEvent(eiIdle, epLast, etTrigger, esGlobal)]
function OnIdle() : boolean;
[XEvent(eiGameEvent, epLast, etTrigger, esGlobal)]
function OnGameEvent(Eventname : RParam) : boolean;
[XEvent(eiClientInit, epLower, etTrigger, esGlobal)]
function OnClientInit : boolean;
[XEvent(eiGameTick, epLast, etTrigger, esGlobal)]
function OnGameTick() : boolean;
[XEvent(eiNewEntity, epLast, etTrigger, esGlobal)]
function OnNewEntity(NewEntity : RParam) : boolean;
public
constructor Create(Owner : TEntity); override;
function AddStepNever() : TTutorialDirectorComponent;
function AddStepClientInit() : TTutorialDirectorComponent;
function AddStepGameEvent(const GameEvent : string) : TTutorialDirectorComponent;
function AddStepUIDsDead(const UIDs : TArray<string>) : TTutorialDirectorComponent;
function AddStepNewEntity(MustHave : TArray<byte>) : TTutorialDirectorComponent;
function AddStepGameTick(const GameTick : integer) : TTutorialDirectorComponent;
function AddStepTimer(const Interval : integer) : TTutorialDirectorComponent;
function AddStepLastEntityDies() : TTutorialDirectorComponent;
function AddStepCamPos(X, Y, Range : single) : TTutorialDirectorComponent;
function StepTriggerCount(const StepTriggerCount : integer) : TTutorialDirectorComponent;
function StepLabel(const StepLabel : string) : TTutorialDirectorComponent;
function StepIsOptional : TTutorialDirectorComponent;
function IfGameEventGotoStep(const GameEvent : string; const TargetLabel : string) : TTutorialDirectorComponent;
function IfGameTickGotoStep(const GameTick : integer; const TargetLabel : string) : TTutorialDirectorComponent;
function IfTimerGotoStep(const Interval : integer; const TargetLabel : string) : TTutorialDirectorComponent;
function IfLastEntityDiesGotoStep(const TargetLabel : string) : TTutorialDirectorComponent;
function AddAction(const Action : TTutorialDirectorAction) : TTutorialDirectorComponent;
function AddActions(const A1 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4, A5 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4, A5, A6 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4, A5, A6, A7 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4, A5, A6, A7, A8 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4, A5, A6, A7, A8, A9 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4, A5, A6, A7, A8, A9, A10 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function AddActions(const A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11 : TTutorialDirectorAction) : TTutorialDirectorComponent; overload;
function BuildElementGroup(const GroupName : string; const Elements : TArray<string>) : TTutorialDirectorComponent;
function BuildKeybindingGroup(const GroupName : string; const Keybindings : TArray<byte>) : TTutorialDirectorComponent;
// shortcuts
function HideGroup(const GroupName : string) : TTutorialDirectorComponent;
function LockGroup(const GroupName : string) : TTutorialDirectorComponent;
function UnlockGroup(const GroupName : string) : TTutorialDirectorComponent;
function UnshadowGroup(const GroupName : string) : TTutorialDirectorComponent;
function ClearGroupUnshadow(const GroupName : string) : TTutorialDirectorComponent;
function ClearGroupLock(const GroupName : string) : TTutorialDirectorComponent;
function ClearGroupStates(const GroupName : string) : TTutorialDirectorComponent;
function SendGameevent(const Eventname : string) : TTutorialDirectorComponent;
function GotoStep(const StepLabel : string) : TTutorialDirectorComponent;
function LockCamera : TTutorialDirectorComponent;
function UnlockCamera : TTutorialDirectorComponent;
function MoveCamera(X, Y : single) : TTutorialDirectorComponent;
function MoveCameraOverTime(X, Y : single; Time : integer) : TTutorialDirectorComponent;
function ClearWorldObjects : TTutorialDirectorComponent;
function ClearTutorialHint : TTutorialDirectorComponent;
function GroundText(X : single; const Text : string) : TTutorialDirectorComponent;
function PassiveText(const Text, ButtonText : string) : TTutorialDirectorComponent;
function PreventMultiCardPlay : TTutorialDirectorComponent;
function AllowMultiCardPlay : TTutorialDirectorComponent;
destructor Destroy; override;
end;
implementation
uses
BaseConflict.Globals.Client,
BaseConflict.Map.Client,
BaseConflict.Classes.Gamestates.GUI,
BaseConflict.EntityComponents.Client.GUI;
{ TClientNetworkComponent }
constructor TClientNetworkComponent.Create(Owner : TEntity; Socket : TTCPClientSocketDeluxe; const AuthentificationToken : string; FinishedReceivedGameData : ProcCallback);
begin
inherited Create(Owner);
FPingLog := TList<RPingLogData>.Create;
FPingLogTimer := TTimer.CreateAndStart(PING_LOG_INTERVAL);
FServerAddress := Socket.RemoteInetAddress;
FTCPClientSocket := Socket;
FTCPClientSocket.SendCommand(NET_CLIENT_ENTER_CORE);
FFinishedReceivedGameData := FinishedReceivedGameData;
FAuthentificationToken := AuthentificationToken;
end;
procedure TClientNetworkComponent.DeserializeEntity(Stream : TStream);
var
Entity : TEntity;
EntityID : integer;
begin
EntityID := Stream.ReadInteger;
if not Game.EntityManager.HasEntityByID(EntityID) then
begin
Entity := TEntity.Deserialize(EntityID, Stream, GlobalEventbus, nil{ ENTITYCOMPONENTCLASSMAPPING });
TLogicToWorldComponent.Create(Entity);
Entity.Eventbus.Trigger(eiAfterCreate, []);
Entity.Deploy;
end;
end;
destructor TClientNetworkComponent.Destroy;
begin
SendPingLog;
FTCPClientSocket.Free;
FServerCrashedTimer.Free;
FPingLogTimer.Free;
FPingLog.Free;
inherited;
end;
procedure TClientNetworkComponent.LogPing;
var
NewLog : RPingLogData;
begin
if FPingLogTimer.Expired then
begin
NewLog.Timestamp := TimeManager.GetTimeStamp;
if assigned(FTCPClientSocket) then
NewLog.Ping := FTCPClientSocket.Ping
else
NewLog.Ping := -2;
FPingLog.Add(NewLog);
FPingLogTimer.Start;
end;
end;
procedure TClientNetworkComponent.NewData(Data : TDatapacket);
var
DataStream : TStream;
Count, i, ErrorCode : integer;
ErrorMessage : string;
begin
case Data.Command of
NET_SERVER_FINISHED_SEND_GAME_DATA :
begin