@bf6mods/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/cli/index.d.ts +1 -0
  2. package/dist/cli/index.js +196 -0
  3. package/dist/cli/index.js.map +1 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +1 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/resources/prepare/tsconfig.json +15 -0
  8. package/dist/resources/prepare/types/config.ts +10 -0
  9. package/dist/resources/templates/AcePursuit/src/config.json +1 -0
  10. package/dist/resources/templates/AcePursuit/src/index.ts +3421 -0
  11. package/dist/resources/templates/AcePursuit/src/levels.tscn +6922 -0
  12. package/dist/resources/templates/AcePursuit/src/strings.json +191 -0
  13. package/dist/resources/templates/All/bf6.config.ts +6 -0
  14. package/dist/resources/templates/All/package.json +13 -0
  15. package/dist/resources/templates/All/tsconfig.json +4 -0
  16. package/dist/resources/templates/Basic/bf6.config.ts +3 -0
  17. package/dist/resources/templates/Basic/src/index.ts +112 -0
  18. package/dist/resources/templates/BombSquad/src/config.json +1 -0
  19. package/dist/resources/templates/BombSquad/src/index.ts +3683 -0
  20. package/dist/resources/templates/BombSquad/src/levels.tscn +3212 -0
  21. package/dist/resources/templates/BombSquad/src/strings.json +123 -0
  22. package/dist/resources/templates/Exfil/src/config.json +1 -0
  23. package/dist/resources/templates/Exfil/src/index.ts +2393 -0
  24. package/dist/resources/templates/Exfil/src/levels.tscn +5600 -0
  25. package/dist/resources/templates/Exfil/src/strings.json +185 -0
  26. package/dist/resources/templates/Vertigo/src/config.json +1 -0
  27. package/dist/resources/templates/Vertigo/src/index.ts +1948 -0
  28. package/dist/resources/templates/Vertigo/src/levels.tscn +6387 -0
  29. package/dist/resources/templates/Vertigo/src/strings.json +308 -0
  30. package/package.json +41 -0
@@ -0,0 +1,3421 @@
1
+ // === acePursuit.ts ===
2
+ const VERSION = [1, 8, 239];
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+ // Version Format [ship, delivery, patch/compile]
14
+ const debugPlayer = false;
15
+
16
+ const catchupMechanicSprintDisable = true;
17
+
18
+ const MinimumPlayerToStart = 1;
19
+ const MapPlayers = 8;
20
+
21
+ const CIRCLE_MAX = 70;
22
+ const CIRCLE_MIN = 25;
23
+
24
+ enum GameType {
25
+ race = 0,
26
+ timeSurvival = 1,
27
+ }
28
+
29
+ type Checkpoint = {
30
+ id: number;
31
+ position: Vector3;
32
+ checkpointStart: Vector3;
33
+ checkpointEnd: Vector3;
34
+ flipdir?: boolean;
35
+ };
36
+
37
+ type RaceTrack = {
38
+ trackId: string;
39
+ name: string;
40
+ laps: number;
41
+ gametype: GameType;
42
+ availableVehicles: mod.VehicleList[]
43
+ checkPoints: Checkpoint[];
44
+ };
45
+
46
+ type Vector3 = { x: number; y: number, z: number };
47
+
48
+ function getTrackById(id: string): RaceTrack | undefined {
49
+ return tracks.find(track => track.trackId === id);
50
+ }
51
+
52
+ class TrackData {
53
+
54
+ trackId: string;
55
+ checkPoints: Checkpoint[];
56
+ laps: number;
57
+ playersInRace: PlayerProfile[] = [];
58
+ raceTime: number = 0;
59
+ winner: boolean = false;
60
+ #maxReadyupCountdown: number = 350;
61
+ readyupCountDown: number = 350;
62
+ countdownToStart: number = 3;
63
+ countdownToEnd: number = 45;
64
+ winnerPlayer: PlayerProfile | undefined;
65
+ availableVehicles: mod.VehicleList[] = [];
66
+ trackState: TrackState = TrackState.none
67
+ gametype: GameType = GameType.race;
68
+ firstPlayerHasJoined: boolean = false
69
+
70
+ constructor(track: RaceTrack) {
71
+
72
+ console.log("Prepere Track " + track.name)
73
+
74
+ this.checkPoints = track.checkPoints;
75
+ this.laps = track.laps;
76
+ this.availableVehicles = track.availableVehicles;
77
+ this.trackId = track.trackId;
78
+ this.trackState = TrackState.selected;
79
+ this.gametype = track.gametype;
80
+ }
81
+
82
+ PlayerCompletedTrack(playerProfile: PlayerProfile) {
83
+ playerProfile.playerRaceTime = mod.GetMatchTimeElapsed();
84
+ playerProfile.completedTrack = true;
85
+
86
+ this.playersInRace.forEach(playerProfile => {
87
+ playerProfile.ScoreboardUI?.update()
88
+ })
89
+
90
+ if (this.winnerPlayer == undefined) {
91
+ console.log("Winner Found")
92
+ this.Winner(playerProfile)
93
+
94
+ } else {
95
+ this.CompletedTrackShowPlacement(playerProfile)
96
+ }
97
+ }
98
+
99
+ CompletedTrackShowPlacement(playerProfile: PlayerProfile) {
100
+ playerProfile.PlacementUI?.Open(mod.stringkeys.header_placement, currentRace.playersInRace.findIndex(pp => pp.player == playerProfile.player) + 1, 45)
101
+ }
102
+
103
+ async Winner(playerProfile: PlayerProfile) {
104
+
105
+ this.winnerPlayer = playerProfile;
106
+ playerProfile.PlacementUI?.Open(mod.stringkeys.victory, 0, 85)
107
+ currentRace.trackState = TrackState.winnerFound;
108
+
109
+ await mod.Wait(2)
110
+
111
+ currentRace.playersInRace.forEach(playerProfile => {
112
+ playerProfile.EndingCountDownUI?.Open(this.countdownToEnd)
113
+ });
114
+
115
+ while (this.countdownToEnd > 0 && this.hasActiveRacers()) {
116
+ currentRace.playersInRace.forEach(playerProfile => {
117
+ playerProfile.EndingCountDownUI?.update(this.countdownToEnd)
118
+ });
119
+ await mod.Wait(1)
120
+ this.countdownToEnd--;
121
+ }
122
+
123
+ currentRace.playersInRace.forEach(playerProfile => {
124
+ playerProfile.EndingCountDownUI?.Close()
125
+ });
126
+
127
+ currentRace.trackState = TrackState.over;
128
+
129
+ console.log("Ending game")
130
+ mod.EndGameMode(playerProfile.player)
131
+ }
132
+
133
+
134
+ hasActiveRacers() {
135
+ return currentRace.playersInRace.some(player => player.completedTrack === false);
136
+ }
137
+
138
+ async AddPlayerToTrack(player: mod.Player) {
139
+ const playerP = PlayerProfile.get(player);
140
+
141
+ if (playerP) {
142
+
143
+ if (this.playersInRace.includes(playerP)) {
144
+ console.log("Player already in the race")
145
+ return
146
+ }
147
+
148
+ playerP.readyUp = false;
149
+ playerP.lap = 0;
150
+ playerP.checkpoint = 0;
151
+ playerP.nextCheckpoint = 0;
152
+ playerP.playerRaceTime = null;
153
+ playerP.completedTrack = false;
154
+ playerP.currentTrackID = currentRace.trackId;
155
+ playerP.checkPointPosition = ConverVector3ToModVector(currentRace.checkPoints[0].position)
156
+ this.playersInRace.push(playerP)
157
+ currentRace.AssignPlayerNumber(playerP)
158
+
159
+ if (currentRace.trackState == TrackState.selected) {
160
+ playerP.VehicleShopUI?.cameraTeleport()
161
+ playerP.OpenVehicleOptionsUI()
162
+ playerP.ScoreboardUI?.open()
163
+
164
+ currentRace.playersInRace.forEach(PlayerProfile => {
165
+ PlayerProfile.VehicleShopUI?.UIUpdatePlayersReady()
166
+ PlayerProfile.ScoreboardUI?.update()
167
+ });
168
+ }
169
+
170
+ console.log("player pushed to race array")
171
+ } else {
172
+ console.log("could not find player to put into race")
173
+ }
174
+
175
+ }
176
+
177
+ PlayerLeftGame() {
178
+ // Remove invalid players and clean them up
179
+ currentRace.playersInRace = currentRace.playersInRace.filter(playerProfile => {
180
+ if (!this.isValidPlayer(playerProfile.player)) {
181
+ playerProfile.CloseAllUI()
182
+ playerProfile.DeletePlayerWidgets()
183
+ playerProfile.DestroyVehicle();
184
+ playerProfile.RemoveWorldIcons();
185
+
186
+ return false; // remove from list
187
+ }
188
+ return true; // keep
189
+ });
190
+
191
+ // Update UI and possibly start countdown
192
+ if (currentRace.trackState === TrackState.selected) {
193
+ currentRace.playersInRace.forEach(player => {
194
+ player.VehicleShopUI?.UIUpdatePlayersReady();
195
+ player.ScoreboardUI?.update();
196
+ });
197
+ // Updated scoreboard
198
+ } else if (currentRace.trackState === TrackState.running || currentRace.trackState === TrackState.winnerFound) {
199
+ currentRace.playersInRace.forEach(player => {
200
+ player.ScoreboardUI?.update();
201
+ });
202
+ this.UpdateOrder()
203
+ }
204
+ }
205
+
206
+
207
+ isValidPlayer(player: mod.Player | null | undefined): boolean {
208
+ return player != null && mod.IsPlayerValid(player);
209
+ }
210
+
211
+ AssignPlayerNumber(playerProfile: PlayerProfile) {
212
+
213
+ const usedIds = currentRace.playersInRace.map(p => p.playerRacerNumber);
214
+
215
+ // Find the first free ID.
216
+ let freeId = -1;
217
+ for (let i = 0; i < MapPlayers; i++) {
218
+ if (!usedIds.includes(i)) {
219
+ freeId = i;
220
+ break;
221
+ }
222
+ }
223
+
224
+ if (freeId === -1) {
225
+ console.log("ERROR: No available slots for new players!");
226
+ }
227
+
228
+ // Assign the ID to the joining player
229
+ playerProfile.playerRacerNumber = freeId;
230
+ console.log("Player joined race, Assign race number: " + freeId)
231
+ }
232
+
233
+ async RaceStartCountdown() {
234
+ currentRace.playersInRace.forEach(playerProfile => {
235
+ playerProfile.StartCountDownUI?.Open(mod.stringkeys.scoreboard_1_name, this.countdownToStart)
236
+ });
237
+
238
+ await mod.Wait(1)
239
+ while (this.countdownToStart > 1) {
240
+ this.countdownToStart--;
241
+
242
+ currentRace.playersInRace.forEach(playerProfile => {
243
+ playerProfile.StartCountDownUI?.update(mod.stringkeys.scoreboard_1_name, this.countdownToStart)
244
+ });
245
+
246
+ await mod.Wait(1)
247
+ }
248
+
249
+ currentRace.playersInRace.forEach(playerProfile => {
250
+ playerProfile.StartCountDownUI?.update(mod.stringkeys.gamestart, this.countdownToStart)
251
+ });
252
+
253
+ currentRace.playersInRace.forEach(playerProfile => {
254
+ playerProfile.StartCountDownUI?.Close(3)
255
+ });
256
+ }
257
+
258
+ countdownInProgress: boolean = false;
259
+
260
+ async StartCountdown() {
261
+
262
+ if (this.countdownInProgress === true) {
263
+ console.log("Countdown already running");
264
+ return;
265
+ }
266
+
267
+ if (!this.HasMinimumPlayers()) {
268
+ console.log("Countdown cancelled: not enough players in game");
269
+ return
270
+ }
271
+
272
+ this.countdownInProgress = true;
273
+ this.readyupCountDown = this.#maxReadyupCountdown;
274
+
275
+ while (this.readyupCountDown > 0) {
276
+ this.readyupCountDown--;
277
+
278
+ this.playersInRace.forEach(player => {
279
+ player.VehicleShopUI?.UIUpdatePlayersReady()
280
+ });
281
+
282
+ await mod.Wait(1)
283
+
284
+ const playerReady = this.GetPlayersReady();
285
+
286
+ if (playerReady.total == 0) {
287
+ console.log("Countdown cancelled: No players in lobby");
288
+ this.countdownInProgress = false;
289
+ return
290
+ } else if (playerReady.ready == playerReady.total) {
291
+ this.readyupCountDown = 0;
292
+ }
293
+ }
294
+
295
+ console.log("Starting game...");
296
+
297
+ await this.StartGame()
298
+ this.countdownInProgress = false;
299
+ }
300
+ HasMinimumPlayers() {
301
+ return this.playersInRace.length >= MinimumPlayerToStart
302
+ }
303
+
304
+ IsPlayersReady() {
305
+ return this.playersInRace.every(player => player.readyUp === true);
306
+ }
307
+
308
+ IndexExists<T>(array: T[], index: number): boolean {
309
+ return index >= 0 && index < array.length;
310
+ }
311
+
312
+ GetPlayersReady() {
313
+ const readyCount = this.playersInRace.filter(p => p.readyUp).length;
314
+ return { ready: readyCount, total: this.playersInRace.length };
315
+ }
316
+
317
+ IsPlayerInRace(player: mod.Player) {
318
+
319
+ const playerPro = PlayerProfile.get(player)
320
+
321
+ if (!playerPro) {
322
+ return false;
323
+ }
324
+
325
+ return currentRace?.playersInRace?.some(
326
+ item => mod.GetObjId(item.player) == mod.GetObjId(playerPro.player)
327
+ ) ?? false;
328
+ }
329
+
330
+ async StartGame() {
331
+ mod.DisablePlayerJoin()
332
+
333
+ currentRace.trackState = TrackState.starting
334
+
335
+ this.playersInRace.forEach(playerProfile => {
336
+ playerProfile.VehicleShopUI?.close();
337
+ if (mod.IsPlayerValid(playerProfile.player) && mod.GetSoldierState(playerProfile.player, mod.SoldierStateBool.IsAlive)) {
338
+ mod.EnableAllInputRestrictions(playerProfile.player, true)
339
+ }
340
+ playerProfile.FadeInScreenUI?.FadeIn()
341
+ });
342
+
343
+ await mod.Wait(1)
344
+
345
+ this.playersInRace.forEach(playerProfile => {
346
+ playerProfile.InitRacer();
347
+ });
348
+
349
+ await mod.Wait(5)
350
+
351
+
352
+ this.playersInRace.forEach(playerProfile => {
353
+ playerProfile.FadeInScreenUI?.FadeOut()
354
+ });
355
+
356
+ await mod.Wait(2)
357
+
358
+
359
+ await this.RaceStartCountdown()
360
+
361
+ this.playersInRace.forEach(playerProfile => {
362
+ playerProfile.UpdateWorldIconPosition();
363
+ });
364
+
365
+ currentRace.trackState = TrackState.running
366
+
367
+ currentRace.raceTime = mod.GetMatchTimeElapsed();
368
+
369
+ this.playersInRace.forEach(playerProfile => {
370
+ if (mod.IsPlayerValid(playerProfile.player) && mod.GetSoldierState(playerProfile.player, mod.SoldierStateBool.IsAlive)) {
371
+ mod.EnableAllInputRestrictions(playerProfile.player, false)
372
+ }
373
+
374
+ });
375
+
376
+ this.playersInRace.forEach(playerProfile => {
377
+ playerProfile.ScoreboardUI?.update()
378
+ })
379
+
380
+ this.DistanceCheckToCheckpoint();
381
+ this.SpawnAiEnemyVehicle();
382
+ this.UpdateScoreboardLoop();
383
+ this.StartFireworks();
384
+
385
+
386
+ }
387
+ async DebugLoop() {
388
+
389
+ while (true) {
390
+
391
+ currentRace.playersInRace.forEach(element => {
392
+
393
+ element.boosterDisabled = true
394
+ element.BoosterDisableUI?.Trigger()
395
+
396
+ });
397
+
398
+ await mod.Wait(5)
399
+ currentRace.playersInRace.forEach(element => {
400
+ element.boosterDisabled = false
401
+ element.BoosterDisableUI?.Trigger()
402
+
403
+ });
404
+ await mod.Wait(5)
405
+ }
406
+
407
+
408
+ }
409
+
410
+
411
+ async DistanceCheckToCheckpoint() {
412
+
413
+ while (this.trackState == TrackState.running || this.trackState == TrackState.winnerFound) {
414
+ this.playersInRace.forEach(playerProfile => {
415
+
416
+ if (playerProfile.completedTrack == true) {
417
+ console.log("Player have already completed track")
418
+ return;
419
+ }
420
+
421
+
422
+ if (!playerProfile.player) {
423
+ return;
424
+ }
425
+
426
+
427
+ if (!mod.GetSoldierState(playerProfile.player, mod.SoldierStateBool.IsAlive)) {
428
+ return
429
+ }
430
+
431
+ const playerPosition = mod.GetSoldierState(playerProfile.player, mod.SoldierStateVector.GetPosition)
432
+
433
+ if (!playerPosition) {
434
+ return
435
+ }
436
+
437
+ const targetCheckpoint = ConverVector3ToModVector(this.checkPoints[playerProfile.nextCheckpoint].position)
438
+
439
+ const distance = mod.DistanceBetween(playerPosition, targetCheckpoint)
440
+
441
+
442
+ if (distance <= playerProfile.checkpointCircleSize) {
443
+
444
+ playerProfile.checkpoint++;
445
+
446
+ const positionInRace = this.playersInRace.indexOf(playerProfile)
447
+ playerProfile.checkpointCircleSize = getCheckpointSize(positionInRace, currentRace.playersInRace.length);
448
+
449
+ //update checkpoint position
450
+ playerProfile.nextCheckpoint = (playerProfile.checkpoint) % this.checkPoints.length
451
+ playerProfile.checkPointPosition = ConverVector3ToModVector(this.checkPoints[playerProfile.nextCheckpoint].position)
452
+
453
+ const lapsCompl = Math.floor((playerProfile.checkpoint / this.checkPoints.length))
454
+
455
+ if (lapsCompl != playerProfile.lap) {
456
+ playerProfile.lap = lapsCompl;
457
+ console.log("lap" + playerProfile.lap)
458
+ }
459
+
460
+ if (playerProfile.lap >= this.laps && ((playerProfile.checkpoint % this.checkPoints.length) >= 1) && !playerProfile.completedTrack) {
461
+ this.PlayerCompletedTrack(playerProfile)
462
+ console.log("player win " + playerProfile.playerRaceTime)
463
+ }
464
+
465
+ playerProfile.UpdateWorldIconPosition();
466
+ this.UpdateOrder()
467
+ }
468
+
469
+ });
470
+ await mod.Wait(0.1)
471
+ }
472
+ }
473
+
474
+ StartFireworks() {
475
+ const fireworkArray = generateSpawnLine({ x: 121.062789916992, y: 177.110992431641, z: 163.706634521484 }, { x: 277.184600830078, y: 177.110992431641, z: 13.0980796813965 }, 6)
476
+
477
+ fireworkArray.forEach(firework => {
478
+ SpawnVFXAtPosition(ConverVector3ToModVector(firework.position), mod.RuntimeSpawn_Common.FX_Sparks)
479
+ });
480
+ }
481
+
482
+ async SpawnAiEnemyVehicle() {
483
+ console.log("start SpawnAi EnemyVehicle")
484
+
485
+ const vehicArray = [
486
+ mod.GetVehicleSpawner(1),
487
+ mod.GetVehicleSpawner(2),
488
+ mod.GetVehicleSpawner(3),
489
+ mod.GetVehicleSpawner(4),
490
+ mod.GetVehicleSpawner(5),
491
+ mod.GetVehicleSpawner(6),
492
+ mod.GetVehicleSpawner(7),
493
+ mod.GetVehicleSpawner(8),
494
+ mod.GetVehicleSpawner(9),
495
+ mod.GetVehicleSpawner(10),
496
+ mod.GetVehicleSpawner(11),
497
+ mod.GetVehicleSpawner(12),
498
+ mod.GetVehicleSpawner(13),
499
+ mod.GetVehicleSpawner(14),
500
+ ]
501
+
502
+
503
+ for (let index = 0; index < vehicArray.length; index++) {
504
+ mod.ForceVehicleSpawnerSpawn(vehicArray[index])
505
+ await mod.Wait(0.1)
506
+ }
507
+
508
+ const aiArray = [
509
+ mod.GetSpawner(1),
510
+ mod.GetSpawner(2),
511
+ mod.GetSpawner(3),
512
+ mod.GetSpawner(4),
513
+ mod.GetSpawner(5),
514
+ mod.GetSpawner(6),
515
+ mod.GetSpawner(7),
516
+ mod.GetSpawner(8),
517
+ mod.GetSpawner(9),
518
+ mod.GetSpawner(10),
519
+ mod.GetSpawner(11),
520
+ mod.GetSpawner(12),
521
+ mod.GetSpawner(13),
522
+ mod.GetSpawner(14),
523
+ ]
524
+
525
+ for (let index = 0; index < aiArray.length; index++) {
526
+ mod.SpawnAIFromAISpawner(aiArray[index], mod.SoldierClass.Assault, mod.GetTeam(9))
527
+ await mod.Wait(0.1)
528
+ }
529
+
530
+ console.log("completed SpawnAiEnemyVehicle")
531
+ }
532
+
533
+ async TimeLoop() {
534
+ console.log("Starting: TimeLoop")
535
+
536
+ while (currentRace.trackState == TrackState.running || currentRace.trackState == TrackState.winnerFound) {
537
+ let foundPlayerInRace = false;
538
+
539
+ for (let index = 0; index < this.playersInRace.length; index++) {
540
+ const pp = this.playersInRace[index];
541
+
542
+ if (pp) {
543
+ // pp.UITest.open();
544
+ }
545
+
546
+ if (pp.completedTrack) {
547
+ continue
548
+ }
549
+
550
+
551
+
552
+
553
+ let timeleft = pp.timeLeft - 0.1
554
+
555
+ if (timeleft < 0) {
556
+ pp.timeLeft = 0;
557
+ } else {
558
+ pp.timeLeft = timeleft;
559
+ }
560
+
561
+ if (pp.timeLeft == 0) {
562
+ continue;
563
+ }
564
+
565
+
566
+ if (pp.timeLeft > 0) {
567
+ foundPlayerInRace = true;
568
+ }
569
+ }
570
+
571
+
572
+ if (foundPlayerInRace == false) {
573
+ return
574
+ }
575
+
576
+ await mod.Wait(0.1)
577
+ }
578
+ }
579
+
580
+ async UpdateScoreboardLoop() {
581
+ console.log("Start UpdateScoreboard loop")
582
+ while (this.trackState == TrackState.running || this.trackState == TrackState.winnerFound) {
583
+ await mod.Wait(1.0)
584
+ this.UpdateOrder();
585
+ }
586
+ }
587
+
588
+
589
+ UpdateScoreboard() {
590
+ currentRace.playersInRace.forEach(playerProfile => {
591
+ playerProfile.ScoreboardUI?.update()
592
+ })
593
+
594
+ }
595
+
596
+
597
+ UpdateOrder() {
598
+
599
+ const oldarray = JSON.parse(JSON.stringify(this.playersInRace));
600
+
601
+ const updatedArray = sortRaceStanding(this.playersInRace);
602
+
603
+ let overtakingPlayers = detectOvertakes(oldarray, updatedArray);
604
+ for (let playerProfile of overtakingPlayers) {
605
+ playerProfile.OvertookPlayer();
606
+ }
607
+
608
+ if (hasOrderChangedByKey(oldarray, updatedArray)) {
609
+ console.log("Race Order Changed")
610
+ this.playersInRace = updatedArray
611
+
612
+ currentRace.playersInRace.forEach(playerProfile => {
613
+ playerProfile.ScoreboardUI?.update()
614
+ })
615
+
616
+ if (currentRace.playersInRace.length >= 2 && catchupMechanicSprintDisable) {
617
+ const players = currentRace.playersInRace;
618
+
619
+ // Calculate how many players should have sprint disabled (round up to at least 1)
620
+ const disableCount = Math.max(1, Math.ceil(players.length * 0.3));
621
+
622
+ for (let i = 0; i < players.length; i++) {
623
+ const disableSprint = i < disableCount; // First X players have sprint disabled
624
+ mod.EnableInputRestriction(players[i].player, mod.RestrictedInputs.Sprint, disableSprint);
625
+ players[i].boosterDisabled = disableSprint;
626
+ players[i].BoosterDisableUI?.Trigger()
627
+ }
628
+ } else if (currentRace.playersInRace.length <= 1 && catchupMechanicSprintDisable) {
629
+ // If a player leaves the game, and only one player is left. reactivate sprint.
630
+ const playerProf = currentRace.playersInRace[0];
631
+ if (playerProf && playerProf.boosterDisabled) {
632
+ mod.EnableInputRestriction(playerProf.player, mod.RestrictedInputs.Sprint, false);
633
+ playerProf.boosterDisabled = false;
634
+ playerProf.BoosterDisableUI?.Trigger()
635
+ }
636
+ }
637
+
638
+
639
+ }
640
+ }
641
+
642
+ }
643
+
644
+ function detectOvertakes(oldOrder: PlayerProfile[], newOrder: PlayerProfile[]) {
645
+ let overtakes: PlayerProfile[] = [];
646
+
647
+ for (let player of newOrder) {
648
+ const id = player.playerProfileId;
649
+ const oldPos = oldOrder.findIndex(p => p.playerProfileId === id);
650
+ const newPos = newOrder.findIndex(p => p.playerProfileId === id);
651
+
652
+ if (newPos < oldPos) {
653
+ overtakes.push(player);
654
+ }
655
+ }
656
+ return overtakes;
657
+ }
658
+
659
+ function hasOrderChangedByKey(prev: PlayerProfile[], current: PlayerProfile[]): boolean {
660
+ if (prev.length !== current.length) return true;
661
+ return prev.some((p, i) =>
662
+ p.playerProfileId !== current[i].playerProfileId
663
+ );
664
+ }
665
+
666
+ function compareRacers(a: PlayerProfile, b: PlayerProfile): number {
667
+
668
+
669
+ if (a.completedTrack && b.completedTrack) return a.playerRaceTime! - b.playerRaceTime!;
670
+ if (a.completedTrack) return -1;
671
+ if (b.completedTrack) return 1;
672
+
673
+
674
+ if (a.lap !== b.lap) return b.lap - a.lap;
675
+ if (a.checkpoint !== b.checkpoint) return b.checkpoint - a.checkpoint;
676
+
677
+
678
+ const aDistance = mod.DistanceBetween(mod.GetSoldierState(a.player, mod.SoldierStateVector.GetPosition), a.checkPointPosition)
679
+ const bDistance = mod.DistanceBetween(mod.GetSoldierState(b.player, mod.SoldierStateVector.GetPosition), b.checkPointPosition)
680
+
681
+ if (aDistance !== bDistance) return aDistance - bDistance;
682
+
683
+ return 0;
684
+ }
685
+
686
+
687
+ function sortRaceStanding(racers: PlayerProfile[]): PlayerProfile[] {
688
+ return [...racers].sort(compareRacers);
689
+ }
690
+
691
+ let currentRace: TrackData;
692
+
693
+ function PrepareRace(trackId: string) {
694
+ const track = getTrackById(trackId);
695
+ if (track) {
696
+ currentRace = new TrackData(track);
697
+ } else {
698
+ console.log("Could not find trackid")
699
+ }
700
+ }
701
+
702
+ export async function OnGameModeStarted() {
703
+
704
+ console.log("HoH Test Game Mode Started");
705
+
706
+ PrepareRace("track_02")
707
+
708
+ mod.SetAIToHumanDamageModifier(0.5)
709
+ mod.SetSpawnMode(mod.SpawnModes.AutoSpawn)
710
+ }
711
+
712
+ function MakeMessage(message: string, ...args: any[]) {
713
+ switch (args.length) {
714
+ case 0:
715
+ return mod.Message(message);
716
+ case 1:
717
+ return mod.Message(message, args[0]);
718
+ case 2:
719
+ return mod.Message(message, args[0], args[1]);
720
+ case 3:
721
+ return mod.Message(message, args[0], args[1], args[2]);
722
+ default:
723
+ throw new Error("Invalid number of arguments");
724
+ }
725
+ }
726
+
727
+ enum TrackState {
728
+ none = 0,
729
+ selected = 1,
730
+ starting = 2,
731
+ running = 3,
732
+ winnerFound = 4,
733
+ over = 5
734
+ }
735
+
736
+ let uniqueID: number = 0;
737
+
738
+ class PlayerProfile {
739
+
740
+ timeLeft: number = 30;
741
+ player: mod.Player;
742
+ checkpoint: number = 0;
743
+ nextCheckpoint: number = 1;
744
+ lap: number = 0;
745
+ currentTrackID: string = "";
746
+ playerRaceTime: number | null = null;
747
+ completedTrack: boolean = false;
748
+ checkPointPosition: mod.Vector = mod.CreateVector(0, 0, 0);
749
+ checkPointPositionLookDirection2: Vector3 = { x: 0, y: 0, z: 0 }
750
+ checkPointPosition2: Vector3 = { x: 0, y: 0, z: 0 }
751
+ selectedVehicle: mod.VehicleList = mod.VehicleList.F22;
752
+ playerProfileId: number = -1;
753
+ playerRacerNumber: number = -1;
754
+ readyUp: boolean = false;
755
+ checkpointCircleSize: number = 20;
756
+ boosterDisabled: boolean = false;
757
+
758
+ checkpointWorldIcons: mod.WorldIcon[] = [];
759
+ checkpointDirectionWorldIcons: mod.WorldIcon[] = [];
760
+
761
+ checkpointWorldIconsTwo: mod.WorldIcon[] = [];
762
+ checkpointDirectionWorldIconsTwo: mod.WorldIcon[] = [];
763
+
764
+ checkpointWorldIconsHolder: HoH_CheckpointWorldIconsHolder | undefined;
765
+ nextcheckpointWorldIconsHolder: HoH_CheckpointWorldIconsHolder | undefined;
766
+
767
+
768
+
769
+ VehicleShopUI: HoH_UIVehicleSelect | undefined;
770
+ FadeInScreenUI: HoH_UIBlackScreen | undefined;
771
+ StartCountDownUI: HoH_UIStartCountdown | undefined;
772
+ PlacementUI: HoH_UIPlacementHeader | undefined;
773
+ EndingCountDownUI: HoH_UIEndingGameCountdown | undefined;
774
+ OvertakeUI: HoH_BenjiOvertakeUI | undefined;
775
+ VersionNumberUI: HoH_Version | undefined;
776
+ BoosterDisableUI: HoH_BoosterDisabledUI | undefined;
777
+ ScoreboardUI: HoH_ScoreboardUI | undefined;
778
+
779
+ playerSpawnedVeh: mod.Vehicle | undefined;
780
+ playerSpawnedVehSpawner: mod.VehicleSpawner | undefined;
781
+
782
+ static playerInstances: mod.Player[] = [];
783
+
784
+ static #allHoHPlayers: { [key: number]: PlayerProfile } = {};
785
+
786
+
787
+ constructor(player: mod.Player) {
788
+ this.player = player;
789
+ this.playerProfileId = uniqueID++;
790
+
791
+ this.FadeInScreenUI = new HoH_UIBlackScreen(this);
792
+ this.StartCountDownUI = new HoH_UIStartCountdown(this);
793
+ this.PlacementUI = new HoH_UIPlacementHeader(this);
794
+ this.EndingCountDownUI = new HoH_UIEndingGameCountdown(this);
795
+ this.OvertakeUI = new HoH_BenjiOvertakeUI(this);
796
+ this.VersionNumberUI = new HoH_Version(this)
797
+ this.BoosterDisableUI = new HoH_BoosterDisabledUI(this)
798
+ this.ScoreboardUI = new HoH_ScoreboardUI(this)
799
+ }
800
+
801
+ CloseAllUI() {
802
+ this.FadeInScreenUI?.Close()
803
+ this.StartCountDownUI?.Close()
804
+ this.PlacementUI?.Close()
805
+ this.EndingCountDownUI?.Close()
806
+ this.OvertakeUI?.Close()
807
+ this.VersionNumberUI?.Close()
808
+ this.BoosterDisableUI?.Close()
809
+ this.ScoreboardUI?.Close()
810
+ this.VehicleShopUI?.close()
811
+ }
812
+
813
+
814
+ static get(player: mod.Player) {
815
+ if (mod.GetObjId(player) > -1) {
816
+ let index = mod.GetObjId(player);
817
+
818
+ let hohPlayer = this.#allHoHPlayers[index];
819
+ if (!hohPlayer) {
820
+ hohPlayer = new PlayerProfile(player);
821
+ if (debugPlayer) console.log("Creating Player Profile");
822
+ this.#allHoHPlayers[index] = hohPlayer;
823
+ this.playerInstances.push(player)
824
+ }
825
+ return hohPlayer;
826
+ }
827
+ if (debugPlayer) console.log("Error: could not finds an valid player object ID.");
828
+ return undefined;
829
+ }
830
+
831
+ async DeletePlayerWidgets() {
832
+
833
+ this.FadeInScreenUI?.Delete()
834
+ this.StartCountDownUI?.Delete()
835
+ this.PlacementUI?.Delete()
836
+ this.EndingCountDownUI?.Delete()
837
+ this.OvertakeUI?.Delete()
838
+ this.VersionNumberUI?.Delete()
839
+ this.BoosterDisableUI?.Delete()
840
+ this.ScoreboardUI?.Delete()
841
+
842
+
843
+ }
844
+
845
+ DestroyVehicle() {
846
+
847
+ if (this.playerSpawnedVeh) {
848
+ mod.DealDamage(this.playerSpawnedVeh, 9999)
849
+ }
850
+ if (this.playerSpawnedVehSpawner) {
851
+ mod.UnspawnObject(this.playerSpawnedVehSpawner)
852
+ this.playerSpawnedVehSpawner = undefined;
853
+ }
854
+ }
855
+
856
+ SetVehicle(vehicle: mod.VehicleList) {
857
+ this.selectedVehicle = vehicle
858
+ }
859
+
860
+ InitRacer() {
861
+
862
+ this.checkPointPosition = ConverVector3ToModVector(currentRace.checkPoints[0].position);
863
+ this.checkPointPosition2 = currentRace.checkPoints[0].position;
864
+
865
+ this.checkpointWorldIconsHolder = new HoH_CheckpointWorldIconsHolder(this, mod.CreateVector(0, 1, 0))
866
+ this.nextcheckpointWorldIconsHolder = new HoH_CheckpointWorldIconsHolder(this, mod.CreateVector(1, 0, 0))
867
+
868
+ VehicleHandler.RequestVehicle(this)
869
+ }
870
+
871
+ OpenVehicleOptionsUI() {
872
+
873
+ if (this.VehicleShopUI == undefined) {
874
+ this.VehicleShopUI = new HoH_UIVehicleSelect(this)
875
+ }
876
+ this.VehicleShopUI.open();
877
+ }
878
+
879
+
880
+ RefreshWorldIcons() {
881
+ this.checkpointWorldIconsHolder?.Refresh()
882
+ if ((this.checkpoint) < currentRace.laps * currentRace.checkPoints.length) {
883
+ this.nextcheckpointWorldIconsHolder?.Refresh()
884
+ }
885
+ }
886
+
887
+
888
+ RemoveWorldIcons() {
889
+
890
+ if (this.checkpointWorldIconsHolder) {
891
+ this.checkpointWorldIconsHolder.checkpointWorldIcons.forEach(element => {
892
+ mod.UnspawnObject(element)
893
+ });
894
+ }
895
+
896
+ if (this.nextcheckpointWorldIconsHolder) {
897
+ this.nextcheckpointWorldIconsHolder.checkpointWorldIcons.forEach(element => {
898
+ mod.UnspawnObject(element)
899
+ });
900
+ }
901
+
902
+ }
903
+
904
+ UpdateWorldIconPosition() {
905
+ if (this.completedTrack) {
906
+ this.checkpointWorldIconsHolder?.Hide()
907
+ this.nextcheckpointWorldIconsHolder?.Hide()
908
+ console.log("Hide ui since player completed track")
909
+ return;
910
+ }
911
+
912
+ this.checkPointPosition2 = currentRace.checkPoints[this.nextCheckpoint].position
913
+
914
+
915
+ this.checkPointPositionLookDirection2 = currentRace.checkPoints[(this.checkpoint + 1) % currentRace.checkPoints.length].position
916
+
917
+
918
+ if (!this.checkpointWorldIconsHolder) {
919
+ console.log("checkpointWorldIconsHolder not found")
920
+ }
921
+
922
+ this.checkpointWorldIconsHolder?.Update(this.checkPointPosition2, this.checkPointPositionLookDirection2, this.checkpointCircleSize)
923
+
924
+
925
+ //upcoming checkpoint after target checkpoint
926
+ const upcomingCheckpoint = (this.checkpoint + 1) % currentRace.checkPoints.length
927
+ const upcomingcheckpointPosition2 = currentRace.checkPoints[upcomingCheckpoint].position
928
+
929
+
930
+ this.nextcheckpointWorldIconsHolder?.Update(upcomingcheckpointPosition2, currentRace.checkPoints[(this.checkpoint + 2) % currentRace.checkPoints.length].position, this.checkpointCircleSize)
931
+
932
+ if (!this.nextcheckpointWorldIconsHolder) {
933
+ console.log("nextcheckpointWorldIconsHolder not found")
934
+ }
935
+
936
+ // Make the final checkpoint not have a next checkpoint.
937
+ if ((this.checkpoint) >= currentRace.laps * currentRace.checkPoints.length) {
938
+ this.nextcheckpointWorldIconsHolder?.Hide()
939
+ console.log("hide last checkpoint")
940
+ }
941
+
942
+ }
943
+
944
+
945
+ static removePlayer(player: mod.Player) {
946
+ let index = mod.GetObjId(player);
947
+
948
+ let hohPlayer = this.#allHoHPlayers[index];
949
+ if (hohPlayer) {
950
+ this.playerInstances.filter(item => item !== player);
951
+ delete this.#allHoHPlayers[index];
952
+ } else {
953
+ if (debugPlayer) console.log("Error: could not find player with profile to remove");
954
+ }
955
+ }
956
+
957
+
958
+ static removeInvalidPlayers() {
959
+ // Remove invalid from array
960
+ PlayerProfile.playerInstances = PlayerProfile.playerInstances.filter(player =>
961
+ this.isValidPlayer(player)
962
+ );
963
+
964
+ // Remove invalid from object
965
+ for (const id in PlayerProfile.#allHoHPlayers) {
966
+ const { player } = PlayerProfile.#allHoHPlayers[id];
967
+ if (!this.isValidPlayer(player)) {
968
+ delete PlayerProfile.#allHoHPlayers[id];
969
+ }
970
+ }
971
+ }
972
+
973
+ static isValidPlayer(player: mod.Player | null | undefined): boolean {
974
+ // Check for null/undefined before calling mod function
975
+ return player != null && mod.IsPlayerValid(player);
976
+ }
977
+
978
+
979
+
980
+ OvertookPlayer() {
981
+ this.OvertakeUI?.Trigger()
982
+ }
983
+
984
+ }
985
+
986
+ function getCheckpointSize(index: number, totalPlayers: number) {
987
+
988
+
989
+ if (index < 0 || index >= totalPlayers) {
990
+ return CIRCLE_MIN;
991
+ }
992
+
993
+
994
+ if (totalPlayers === 1) {
995
+ return CIRCLE_MIN
996
+ }
997
+
998
+
999
+ const normalized = index / (totalPlayers - 1);
1000
+
1001
+
1002
+ return CIRCLE_MIN + normalized * (CIRCLE_MAX - CIRCLE_MIN);
1003
+ }
1004
+
1005
+ class HoH_CheckpointWorldIconsHolder {
1006
+
1007
+ #playerprofile: PlayerProfile;
1008
+
1009
+ color: mod.Vector;
1010
+ circleSize: number = 40;
1011
+ directionForwardOffset: number = 35
1012
+ amountoficons: number = 8;
1013
+ checkpointWorldIcons: mod.WorldIcon[] = [];
1014
+
1015
+
1016
+
1017
+ constructor(playerprofile: PlayerProfile, color: mod.Vector) {
1018
+ this.#playerprofile = playerprofile;
1019
+ this.color = color;
1020
+
1021
+ const checkpointWidgets = generatePointsInACircle(this.#playerprofile.checkPointPosition2, this.#playerprofile.checkPointPositionLookDirection2, this.circleSize, this.amountoficons)
1022
+
1023
+
1024
+
1025
+ //Circle widgets
1026
+ checkpointWidgets.forEach(element => {
1027
+ const worldicon = mod.SpawnObject(mod.RuntimeSpawn_Common.WorldIcon, mod.CreateVector(element.x, element.y, element.z), mod.CreateVector(0, 0, 0))
1028
+ mod.SetWorldIconPosition(worldicon, mod.CreateVector(element.x, element.y, element.z))
1029
+ mod.SetWorldIconColor(worldicon, color)
1030
+ mod.SetWorldIconImage(worldicon, mod.WorldIconImages.Triangle)
1031
+ mod.EnableWorldIconImage(worldicon, false)
1032
+ mod.SetWorldIconOwner(worldicon, mod.GetTeam(this.#playerprofile.player))
1033
+ this.checkpointWorldIcons.push(worldicon)
1034
+ });
1035
+ }
1036
+
1037
+
1038
+ Hide() {
1039
+ this.checkpointWorldIcons.forEach(worldicon => {
1040
+ mod.EnableWorldIconImage(worldicon, false)
1041
+ });
1042
+ }
1043
+
1044
+ async Refresh() {
1045
+ if (!this.#playerprofile.completedTrack) {
1046
+ this.Hide()
1047
+
1048
+ await mod.Wait(1)
1049
+
1050
+ this.checkpointWorldIcons.forEach(worldicon => {
1051
+ mod.EnableWorldIconImage(worldicon, true)
1052
+ mod.GetObjectPosition(worldicon)
1053
+ });
1054
+ }
1055
+ }
1056
+
1057
+ Update(checkpointposition: Vector3, nextcheckpointPosition: Vector3, circleSize: number = this.circleSize) {
1058
+ console.log("Update world icon positions")
1059
+
1060
+ const checkpointWidgets = generatePointsInACircle(checkpointposition, nextcheckpointPosition, circleSize, this.amountoficons)
1061
+
1062
+ for (let index = 0; index < this.checkpointWorldIcons.length; index++) {
1063
+
1064
+ const position = checkpointWidgets[index]
1065
+ const widget = this.checkpointWorldIcons[index]
1066
+
1067
+ mod.SetWorldIconPosition(widget, mod.CreateVector(position.x, position.y, position.z))
1068
+ mod.EnableWorldIconImage(widget, true)
1069
+ }
1070
+
1071
+ }
1072
+ }
1073
+
1074
+ function getLookAtRotation(from: Vector3, to: Vector3): Vector3 {
1075
+ const dx = to.x - from.x;
1076
+ const dy = to.y - from.y;
1077
+ const dz = to.z - from.z;
1078
+
1079
+ const distanceXZ = Math.sqrt(dx * dx + dz * dz);
1080
+
1081
+ const pitch = Math.atan2(dy, distanceXZ);
1082
+ const yaw = Math.atan2(dx, dz);
1083
+ const roll = 0;
1084
+
1085
+ return { x: pitch, y: yaw, z: roll };
1086
+ }
1087
+
1088
+ async function SpawnVFXAtPosition(targetpoint: mod.Vector, vfx: any, rotation?: mod.Vector, scale?: mod.Vector) {
1089
+
1090
+
1091
+ const flareVFX = mod.SpawnObject(vfx, targetpoint, mod.CreateVector(0, 0, 0), mod.CreateVector(1, 1, 1));
1092
+
1093
+ mod.EnableVFX(flareVFX, true)
1094
+ }
1095
+
1096
+ export async function OnVehicleSpawned(eventVehicle: mod.Vehicle) {
1097
+ VehicleHandler.OnVehicleSpawned(eventVehicle)
1098
+ }
1099
+
1100
+ type VehicleAssignment = {
1101
+ player: mod.Player;
1102
+ position: mod.Vector;
1103
+ vehicleSpawner: mod.VehicleSpawner;
1104
+ vehicle?: mod.Vehicle;
1105
+ };
1106
+
1107
+ class VehicleHandler {
1108
+
1109
+ static playerNeedingVehicle: VehicleAssignment[] = [];
1110
+
1111
+
1112
+ static RequestVehicle(playerProfile: PlayerProfile) {
1113
+ if (currentRace?.trackState == TrackState.starting || currentRace?.trackState == TrackState.running || currentRace?.trackState == TrackState.winnerFound || currentRace?.trackState == TrackState.over) {
1114
+
1115
+ const currentCheckpoint = (playerProfile.checkpoint) % currentRace.checkPoints.length
1116
+ const checkPoint = currentRace.checkPoints[currentCheckpoint];
1117
+ if (!checkPoint) {
1118
+ return;
1119
+ }
1120
+
1121
+ const spawnPosition = generateSpawnLine(checkPoint.checkpointStart, checkPoint.checkpointEnd, MapPlayers, checkPoint.flipdir ? "right" : "left")
1122
+ const targetSpawnPoint = spawnPosition[playerProfile.playerRacerNumber]
1123
+
1124
+
1125
+ const vehSpawner = mod.SpawnObject(mod.RuntimeSpawn_Common.VehicleSpawner, ConverVector3ToModVector(targetSpawnPoint.position), ConverVector3ToModVector(getLookAtRotation(targetSpawnPoint.position, targetSpawnPoint.forwardPosition)))
1126
+
1127
+
1128
+ console.log("Target Spawn point " + targetSpawnPoint.position.x + " " + targetSpawnPoint.position.y + " " + targetSpawnPoint.position.z)
1129
+
1130
+ VehicleHandler.playerNeedingVehicle.push({ player: playerProfile.player, position: ConverVector3ToModVector(targetSpawnPoint.position), vehicleSpawner: vehSpawner })
1131
+
1132
+
1133
+ playerProfile.playerSpawnedVehSpawner = vehSpawner;
1134
+
1135
+ mod.SetVehicleSpawnerVehicleType(vehSpawner, playerProfile.selectedVehicle)
1136
+ mod.ForceVehicleSpawnerSpawn(vehSpawner)
1137
+ }
1138
+
1139
+ }
1140
+
1141
+ static async OnVehicleSpawned(eventVehicle: mod.Vehicle) {
1142
+
1143
+ console.log("OnVehicleSpawned")
1144
+
1145
+ if (VehicleHandler.playerNeedingVehicle.length == 0) {
1146
+ return;
1147
+ }
1148
+
1149
+ const vehiclePos = mod.GetVehicleState(eventVehicle, mod.VehicleStateVector.VehiclePosition)
1150
+ let closestDistance: number | null = null;
1151
+ let targetVehicleSpawner: VehicleAssignment | undefined;
1152
+
1153
+ for (const veh of VehicleHandler.playerNeedingVehicle) {
1154
+ const distance = mod.DistanceBetween(vehiclePos, veh.position);
1155
+ console.log("Distance between vehicle and point: " + distance)
1156
+ if (distance <= 25 && (closestDistance === null || distance < closestDistance)) {
1157
+ closestDistance = distance;
1158
+ targetVehicleSpawner = veh;
1159
+ }
1160
+ }
1161
+
1162
+ if (targetVehicleSpawner == undefined) {
1163
+ console.log("Could not find a vehicle close enough to the vehicle spawnpoint")
1164
+ return
1165
+ }
1166
+
1167
+ const index = VehicleHandler.playerNeedingVehicle.indexOf(targetVehicleSpawner);
1168
+ if (index !== -1) {
1169
+ VehicleHandler.playerNeedingVehicle.splice(index, 1);
1170
+ }
1171
+
1172
+
1173
+ while (currentRace.IsPlayerInRace(targetVehicleSpawner.player) && !mod.GetSoldierState(targetVehicleSpawner.player, mod.SoldierStateBool.IsAlive)) {
1174
+ await mod.Wait(0.1)
1175
+ }
1176
+
1177
+
1178
+ const pprofile = PlayerProfile.get(targetVehicleSpawner.player)
1179
+ if (!pprofile) {
1180
+ return
1181
+ }
1182
+
1183
+
1184
+ pprofile.playerSpawnedVeh = eventVehicle;
1185
+
1186
+ // If player disconected remove their vehicle we just spawned.
1187
+ if (!currentRace.IsPlayerInRace(targetVehicleSpawner.player)) {
1188
+ pprofile.DestroyVehicle()
1189
+ return
1190
+ }
1191
+
1192
+ const spawnOffset = mod.Add(vehiclePos, mod.CreateVector(0, 10, 0))
1193
+
1194
+ mod.Teleport(targetVehicleSpawner.player, spawnOffset, 0)
1195
+
1196
+ await mod.Wait(0.5)
1197
+ mod.ForcePlayerToSeat(targetVehicleSpawner.player, eventVehicle, 0)
1198
+
1199
+
1200
+ console.log("Vehicle ready: Seating player")
1201
+ }
1202
+ }
1203
+
1204
+ export function ConverVector3ToModVector(vector3: Vector3): mod.Vector {
1205
+ return mod.CreateVector(vector3.x, vector3.y, vector3.z)
1206
+ }
1207
+
1208
+ interface SpawnPoint {
1209
+ position: Vector3; // point on the line
1210
+ forwardPosition: Vector3; // 10 units to the left
1211
+ }
1212
+
1213
+ function generateSpawnLine(
1214
+ start: Vector3,
1215
+ end: Vector3,
1216
+ count: number,
1217
+ direction: "left" | "right" = "left",
1218
+ distance: number = 10,
1219
+ up: Vector3 = { x: 0, y: 1, z: 0 }
1220
+ ): SpawnPoint[] {
1221
+ if (count < 2) {
1222
+ throw new Error("Count must be at least 2 to generate a line.");
1223
+ }
1224
+
1225
+ const spawnPoints: SpawnPoint[] = [];
1226
+
1227
+ // Forward vector along line
1228
+ const dx = end.x - start.x;
1229
+ const dy = end.y - start.y;
1230
+ const dz = end.z - start.z;
1231
+ const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
1232
+ const forward = { x: dx / len, y: dy / len, z: dz / len };
1233
+
1234
+ // Compute left = up × forward (always "left", never flipped)
1235
+ const left = {
1236
+ x: up.y * forward.z - up.z * forward.y,
1237
+ y: up.z * forward.x - up.x * forward.z,
1238
+ z: up.x * forward.y - up.y * forward.x,
1239
+ };
1240
+
1241
+ // Scale left vector to desired distance
1242
+ const sideScaled = { x: left.x * distance, y: left.y * distance, z: left.z * distance };
1243
+
1244
+ // Step size
1245
+ const stepX = dx / (count - 1);
1246
+ const stepY = dy / (count - 1);
1247
+ const stepZ = dz / (count - 1);
1248
+
1249
+ for (let i = 0; i < count; i++) {
1250
+ const position = {
1251
+ x: start.x + stepX * i,
1252
+ y: start.y + stepY * i,
1253
+ z: start.z + stepZ * i,
1254
+ };
1255
+
1256
+ const forwardPosition = {
1257
+ x: position.x + sideScaled.x,
1258
+ y: position.y + sideScaled.y,
1259
+ z: position.z + sideScaled.z,
1260
+ };
1261
+
1262
+ spawnPoints.push({ position, forwardPosition });
1263
+ }
1264
+
1265
+ // Reverse array order if "right"
1266
+ if (direction === "right") {
1267
+ return spawnPoints.reverse();
1268
+ }
1269
+
1270
+ return spawnPoints;
1271
+ }
1272
+
1273
+ export async function OnPlayerLeaveGame(eventNumber: number) {
1274
+ console.log("Player left the game. Removing invalid players")
1275
+ currentRace?.PlayerLeftGame()
1276
+ PlayerProfile.removeInvalidPlayers()
1277
+ }
1278
+
1279
+
1280
+ export async function OnPlayerDeployed(player: mod.Player) {
1281
+
1282
+ try {
1283
+
1284
+ //Add remove weapons code
1285
+ mod.RemoveEquipment(player, mod.InventorySlots.PrimaryWeapon)
1286
+ mod.RemoveEquipment(player, mod.InventorySlots.SecondaryWeapon)
1287
+ mod.RemoveEquipment(player, mod.InventorySlots.GadgetOne)
1288
+ mod.RemoveEquipment(player, mod.InventorySlots.GadgetTwo)
1289
+ mod.RemoveEquipment(player, mod.InventorySlots.ClassGadget)
1290
+
1291
+ } catch (error) {
1292
+
1293
+ }
1294
+
1295
+ if (mod.GetSoldierState(player, mod.SoldierStateBool.IsAISoldier) == false) {
1296
+
1297
+ if (currentRace?.trackState == TrackState.selected) {
1298
+ currentRace.AddPlayerToTrack(player)
1299
+ currentRace.StartCountdown();
1300
+
1301
+ } else if (currentRace?.trackState == TrackState.running ||
1302
+ currentRace?.trackState == TrackState.winnerFound ||
1303
+ currentRace?.trackState == TrackState.over) {
1304
+
1305
+ const playerprofile = PlayerProfile.get(player)
1306
+
1307
+ if (!currentRace.IsPlayerInRace(player)) {
1308
+ currentRace.AddPlayerToTrack(player)
1309
+ playerprofile?.ScoreboardUI?.open()
1310
+ playerprofile?.InitRacer();
1311
+ playerprofile?.UpdateWorldIconPosition();
1312
+ playerprofile?.BoosterDisableUI?.Trigger()
1313
+ playerprofile?.RefreshWorldIcons()
1314
+
1315
+ currentRace.playersInRace.forEach(playerProfile => {
1316
+ playerProfile.ScoreboardUI?.update()
1317
+ })
1318
+ return
1319
+ }
1320
+
1321
+ if (playerprofile) {
1322
+ VehicleHandler.RequestVehicle(playerprofile)
1323
+ playerprofile.BoosterDisableUI?.Trigger()
1324
+ playerprofile.RefreshWorldIcons()
1325
+ }
1326
+
1327
+ }
1328
+
1329
+ } else {
1330
+
1331
+ if (currentRace?.trackState == TrackState.running || currentRace?.trackState == TrackState.winnerFound) {
1332
+
1333
+ const position = mod.GetSoldierState(player, mod.SoldierStateVector.GetPosition)
1334
+ const closesVeh = GetUnoccupiedVehicleInRange(position)
1335
+
1336
+ if (closesVeh) {
1337
+ mod.ForcePlayerToSeat(player, closesVeh, 0)
1338
+ TargetFirstPlayer(player)
1339
+ } else {
1340
+ //AiStingerAmmo(player)
1341
+ }
1342
+
1343
+
1344
+ } else {
1345
+ console.log("Debug Ai Spawned")
1346
+
1347
+ mod.AIEnableShooting(player, false)
1348
+ mod.AIEnableTargeting(player, false)
1349
+ currentRace.AddPlayerToTrack(player)
1350
+ const playerprofile = PlayerProfile.get(player)
1351
+ if (playerprofile) {
1352
+ playerprofile.readyUp = true;
1353
+ }
1354
+
1355
+ }
1356
+ }
1357
+
1358
+
1359
+
1360
+ }
1361
+
1362
+
1363
+ async function TargetFirstPlayer(player: mod.Player) {
1364
+ while (mod.IsPlayerValid(player) && currentRace.trackState == TrackState.running) {
1365
+
1366
+ mod.AISetTarget(player, currentRace.playersInRace[0].player)
1367
+
1368
+ await mod.Wait(0.1);
1369
+ const inputduration = 5; //getRandomFloatInRange(0.1,2);
1370
+ mod.AIForceFire(player, inputduration);
1371
+
1372
+ //mod.AIForceFire(player,10)
1373
+
1374
+ await mod.Wait(5)
1375
+ }
1376
+
1377
+ }
1378
+
1379
+ function GetUnoccupiedVehicleInRange(pos: mod.Vector): mod.Vehicle | undefined {
1380
+
1381
+ const allVeh = mod.AllVehicles();
1382
+
1383
+ for (let index = 0; index < mod.CountOf(allVeh); index++) {
1384
+ const veh = mod.ValueInArray(allVeh, index)
1385
+ if (!mod.IsVehicleOccupied(veh)) {
1386
+ const vehPos = mod.GetVehicleState(veh, mod.VehicleStateVector.VehiclePosition);
1387
+ if (mod.DistanceBetween(pos, vehPos) <= 15) {
1388
+ return veh;
1389
+ }
1390
+ }
1391
+ }
1392
+ console.log("Could not find vehicle close enough.")
1393
+ return undefined
1394
+ }
1395
+
1396
+ export async function OnPlayerDied(player: mod.Player) {
1397
+
1398
+ if (mod.GetSoldierState(player, mod.SoldierStateBool.IsAISoldier) == false) {
1399
+
1400
+ const playerProf = PlayerProfile.get(player);
1401
+
1402
+ if (playerProf) {
1403
+ playerProf.DestroyVehicle()
1404
+ playerProf.BoosterDisableUI?.Trigger()
1405
+ }
1406
+
1407
+ }
1408
+ }
1409
+
1410
+ function generatePointsInACircle(
1411
+ center: Vector3,
1412
+ lookAt: Vector3,
1413
+ radius: number,
1414
+ segments: number
1415
+ ): Vector3[] {
1416
+ const positions: Vector3[] = [];
1417
+
1418
+ // Compute forward direction vector
1419
+ const forward = normalizeVector(subtractVectors(lookAt, center));
1420
+
1421
+ // Choose arbitrary up vector
1422
+ const worldUp = { x: 0, y: 1, z: 0 };
1423
+
1424
+ // Compute right vector
1425
+ let right = crossProduct(worldUp, forward);
1426
+ if (length(right) < 0.0001) {
1427
+ // If forward is parallel to worldUp, use another up
1428
+ right = crossProduct({ x: 1, y: 0, z: 0 }, forward);
1429
+ }
1430
+ right = normalizeVector(right);
1431
+
1432
+ // Compute actual up vector perpendicular to forward and right
1433
+ const up = normalizeVector(crossProduct(forward, right));
1434
+
1435
+ for (let i = 0; i < segments; i++) {
1436
+ const angle = (i / segments) * Math.PI * 2;
1437
+ const x = Math.cos(angle) * radius;
1438
+ const y = Math.sin(angle) * radius;
1439
+
1440
+ const point = {
1441
+ x: center.x + right.x * x + up.x * y,
1442
+ y: center.y + right.y * x + up.y * y,
1443
+ z: center.z + right.z * x + up.z * y,
1444
+ };
1445
+
1446
+ positions.push(point);
1447
+ }
1448
+
1449
+ return positions;
1450
+ }
1451
+
1452
+ function subtractVectors(a: Vector3, b: Vector3): Vector3 {
1453
+ return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
1454
+ }
1455
+
1456
+ function normalizeVector(v: Vector3): Vector3 {
1457
+ const len = length(v);
1458
+ return len === 0 ? { x: 0, y: 0, z: 0 } : { x: v.x / len, y: v.y / len, z: v.z / len };
1459
+ }
1460
+
1461
+ function crossProduct(a: Vector3, b: Vector3): Vector3 {
1462
+ return {
1463
+ x: a.y * b.z - a.z * b.y,
1464
+ y: a.z * b.x - a.x * b.z,
1465
+ z: a.x * b.y - a.y * b.x,
1466
+ };
1467
+ }
1468
+
1469
+ function length(v: Vector3): number {
1470
+ return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
1471
+ }
1472
+
1473
+
1474
+ export async function OnPlayerUIButtonEvent(eventPlayer: mod.Player, eventUIWidget: mod.UIWidget, eventUIButtonEvent: mod.UIButtonEvent) {
1475
+
1476
+ const playerProfile = PlayerProfile.get(eventPlayer)
1477
+
1478
+ if (!playerProfile) {
1479
+ return
1480
+ }
1481
+
1482
+ playerProfile.VehicleShopUI?.OnButtonPressed(eventPlayer, eventUIWidget, eventUIButtonEvent)
1483
+ }
1484
+
1485
+
1486
+
1487
+ function Lerp(a: number, b: number, t: number): number {
1488
+ return a + (b - a) * t;
1489
+ }
1490
+
1491
+
1492
+
1493
+
1494
+
1495
+ // === acePursuitRawData.ts ===
1496
+
1497
+
1498
+
1499
+ const tracks: RaceTrack[] = [
1500
+ {
1501
+ trackId: "track_01",
1502
+ name: "Quad_Chaos",
1503
+ laps: 3,
1504
+ availableVehicles: [mod.VehicleList.Quadbike],
1505
+ gametype: GameType.race,
1506
+ checkPoints: [
1507
+
1508
+ ]
1509
+ },
1510
+ {
1511
+ trackId: "track_02",
1512
+ name: "Air_Lap",
1513
+ laps: 1,
1514
+ availableVehicles: [mod.VehicleList.F22, mod.VehicleList.F16, mod.VehicleList.JAS39],
1515
+ gametype: GameType.race,
1516
+ checkPoints: [
1517
+ { id: 106, checkpointStart: { x: 224.177551269531, y: 185.776763916016, z: -25.1318969726563 }, checkpointEnd: { x: 88.1495590209961, y: 185.776763916016, z: 105.332855224609 }, position: { x: 547.106201171875, y: 183.570999145508, z: 453.183319091797 } },
1518
+ { id: 107, checkpointStart: { x: 224.177551269531, y: 185.776763916016, z: -25.1318969726563 }, checkpointEnd: { x: 88.1495590209961, y: 185.776763916016, z: 105.332855224609 }, position: { x: 883.77880859375, y: 201.873992919922, z: 898.134033203125 } },
1519
+
1520
+ { id: 108, checkpointStart: { x: 1174.2579345, y: 187.2900, z: 1043.66088 }, checkpointEnd: { x: 1047.197265625, y: 186.30224609375, z: 1181.48742675781 }, position: { x: 1552.362, y: 201.8742, z: 1429.677 } },
1521
+ { id: 109, checkpointStart: { x: 1174.2579345, y: 187.2900, z: 1043.66088 }, checkpointEnd: { x: 1047.197265625, y: 186.30224609375, z: 1181.48742675781 }, position: { x: 2048.393, y: 208.4673, z: 1706.873 } },
1522
+
1523
+ { id: 110, checkpointStart: { x: 2296.06323242188, y: 188.086486816406, z: 1665.53356933594 }, checkpointEnd: { x: 2205.1943359375, y: 187.098724365234, z: 1829.49523925781 }, position: { x: 2676.97, y: 208.4673, z: 1946.0 } },
1524
+ { id: 111, checkpointStart: { x: 2296.06323242188, y: 188.086486816406, z: 1665.53356933594 }, checkpointEnd: { x: 2205.1943359375, y: 187.098724365234, z: 1829.49523925781 }, position: { x: 3299.96630859375, y: 261.307495117188, z: 2330.958984375 } },
1525
+
1526
+ { id: 112, checkpointStart: { x: 3409.69604492188, y: 187.067138671875, z: 2247.3193359375 }, checkpointEnd: { x: 3268.25463867188, y: 186.079376220703, z: 2370.34326171875 }, position: { x: 3939.844, y: 263.038, z: 2924.436 } },
1527
+ { id: 113, checkpointStart: { x: 3409.69604492188, y: 187.067138671875, z: 2247.3193359375 }, checkpointEnd: { x: 3268.25463867188, y: 186.079376220703, z: 2370.34326171875 }, position: { x: 4617.52392578125, y: 271.122009277344, z: 3677.669921875 } },
1528
+
1529
+ { id: 114, checkpointStart: { x: 4671.20703125, y: 310.601776123047, z: 4259.2900390625 }, checkpointEnd: { x: 4805.0439453125, y: 310.499969482422, z: 4372.10498046875 }, position: { x: 5288.721, y: 355.434, z: 3896.095 }, },
1530
+ { id: 115, checkpointStart: { x: 4671.20703125, y: 310.601776123047, z: 4259.2900390625 }, checkpointEnd: { x: 4805.0439453125, y: 310.499969482422, z: 4372.10498046875 }, position: { x: 5466.977, y: 423.387, z: 3535.378 }, },
1531
+
1532
+ { id: 116, checkpointStart: { x: 5552.3984375, y: 530.694274902344, z: 3811.77294921875 }, checkpointEnd: { x: 5708.0478515625, y: 529.706481933594, z: 3722.24780273438 }, position: { x: 5326.602, y: 557.992, z: 3181.616 } },
1533
+ { id: 117, checkpointStart: { x: 5552.3984375, y: 530.694274902344, z: 3811.77294921875 }, checkpointEnd: { x: 5708.0478515625, y: 529.706481933594, z: 3722.24780273438 }, position: { x: 4515.22607421875, y: 441.529144287109, z: 2739.50341796875 } },
1534
+
1535
+ { id: 118, checkpointStart: { x: 4541.82080078125, y: 328.444671630859, z: 2850.97265625 }, checkpointEnd: { x: 4629.31396484375, y: 327.456909179688, z: 2685.18530273438 }, position: { x: 3970.04, y: 334.594, z: 2572.894 } },
1536
+ { id: 119, checkpointStart: { x: 4541.82080078125, y: 328.444671630859, z: 2850.97265625 }, checkpointEnd: { x: 4629.31396484375, y: 327.456909179688, z: 2685.18530273438 }, position: { x: 3342.115, y: 260.9633, z: 2219.49 } },
1537
+
1538
+ { id: 120, checkpointStart: { x: 3309.59326171875, y: 187.919281005859, z: 2412.44750976563 }, checkpointEnd: { x: 3450.93359375, y: 186.931518554688, z: 2289.3076171875 }, position: { x: 2624.016, y: 293.6768, z: 2070.868 }, flipdir: true },
1539
+ { id: 121, checkpointStart: { x: 3309.59326171875, y: 187.919281005859, z: 2412.44750976563 }, checkpointEnd: { x: 3450.93359375, y: 186.931518554688, z: 2289.3076171875 }, position: { x: 2050.632, y: 311.7585, z: 1620.939 }, flipdir: true },
1540
+
1541
+ { id: 1210, checkpointStart: { x: 2259.86279296875, y: 187.481216430664, z: 1861.27551269531 }, checkpointEnd: { x: 2349.51904296875, y: 186.493453979492, z: 1696.64758300781 }, position: { x: 1518.23, y: 323.8511, z: 1482.588 }, flipdir: true },
1542
+ { id: 122, checkpointStart: { x: 2259.86279296875, y: 187.481216430664, z: 1861.27551269531 }, checkpointEnd: { x: 2349.51904296875, y: 186.493453979492, z: 1696.64758300781 }, position: { x: 1042.26672363281, y: 292.134704589844, z: 854.870483398438 }, flipdir: true },
1543
+
1544
+ { id: 123, checkpointStart: { x: 1088.71118164063, y: 186.349456787109, z: 1222.66943359375 }, checkpointEnd: { x: 1216.44152832031, y: 185.361694335938, z: 1085.46325683594 }, position: { x: 393.625457763672, y: 350.244506835938, z: 96.0521240234375 }, flipdir: true },
1545
+
1546
+ { id: 1230, checkpointStart: { x: 127.68977355957, y: 185.05647277832, z: 148.481643676758 }, checkpointEnd: { x: 262.190673828125, y: 183.409637451172, z: 17.2779006958008 }, position: { x: -78.9294662475586, y: 213.880004882813, z: -173.546005249023 }, flipdir: true },
1547
+ { id: 124, checkpointStart: { x: 127.68977355957, y: 185.05647277832, z: 148.481643676758 }, checkpointEnd: { x: 262.190673828125, y: 183.409637451172, z: 17.2779006958008 }, position: { x: -530.65, y: 301.9926, z: -101.325 }, flipdir: true },
1548
+ { id: 125, checkpointStart: { x: 127.68977355957, y: 185.05647277832, z: 148.481643676758 }, checkpointEnd: { x: 262.190673828125, y: 183.409637451172, z: 17.2779006958008 }, position: { x: -1235.055, y: 251.483, z: -262.1271 }, flipdir: true },
1549
+
1550
+ { id: 126, checkpointStart: { x: -2109.8779296875, y: 483.681701660156, z: -280.909484863281 }, checkpointEnd: { x: -2105.28930664063, y: 482.693939208984, z: -468.311492919922 }, position: { x: -2510.07, y: 675.4128, z: -367.911 } },
1551
+ { id: 127, checkpointStart: { x: -2109.8779296875, y: 483.681701660156, z: -280.909484863281 }, checkpointEnd: { x: -2105.28930664063, y: 482.693939208984, z: -468.311492919922 }, position: { x: -3051.823, y: 992.7201, z: -134.6379 } },
1552
+ { id: 1280, checkpointStart: { x: -2109.8779296875, y: 483.681701660156, z: -280.909484863281 }, checkpointEnd: { x: -2105.28930664063, y: 482.693939208984, z: -468.311492919922 }, position: { x: -3324.713, y: 924.931, z: 221.4012 } },
1553
+
1554
+ { id: 128, checkpointStart: { x: -2947.99340820313, y: 739.220336914063, z: 93.411865234375 }, checkpointEnd: { x: -3132.34106445313, y: 738.232543945313, z: 59.4026794433594 }, position: { x: -3465.781, y: 875.6663, z: 683.3296 } },
1555
+ { id: 129, checkpointStart: { x: -2947.99340820313, y: 739.220336914063, z: 93.411865234375 }, checkpointEnd: { x: -3132.34106445313, y: 738.232543945313, z: 59.4026794433594 }, position: { x: -3273.344, y: 829.738, z: 973.0317 } },
1556
+ { id: 130, checkpointStart: { x: -2947.99340820313, y: 739.220336914063, z: 93.411865234375 }, checkpointEnd: { x: -3132.34106445313, y: 738.232543945313, z: 59.4026794433594 }, position: { x: -2918.56, y: 751.904, z: 941.331 } },
1557
+
1558
+ { id: 1300, checkpointStart: { x: -2819.57739257813, y: 607.91748046875, z: 672.408569335938 }, checkpointEnd: { x: -2648.84423828125, y: 606.9296875, z: 749.808410644531 }, position: { x: -2617.48413085938, y: 633.155151367188, z: 378.069183349609 } },
1559
+ { id: 131, checkpointStart: { x: -2819.57739257813, y: 607.91748046875, z: 672.408569335938 }, checkpointEnd: { x: -2648.84423828125, y: 606.9296875, z: 749.808410644531 }, position: { x: -2477.86, y: 537.821, z: 174.285 } },
1560
+
1561
+ { id: 132, checkpointStart: { x: -2157.56713867188, y: 482.034240722656, z: -155.647933959961 }, checkpointEnd: { x: -2166.33764648438, y: 481.046478271484, z: 31.6049041748047 }, position: { x: -1207.372, y: 354.4803, z: -96.10004 } },
1562
+ { id: 133, checkpointStart: { x: -2157.56713867188, y: 482.034240722656, z: -155.647933959961 }, checkpointEnd: { x: -2166.33764648438, y: 481.046478271484, z: 31.6049041748047 }, position: { x: -80.90897, y: 260.4618, z: 68.27824 } },
1563
+
1564
+ ]
1565
+ }
1566
+ ];
1567
+
1568
+
1569
+ // === UI_BenjiOvertake.ts ===
1570
+
1571
+
1572
+ class HoH_BenjiOvertakeUI {
1573
+
1574
+ #playerprofile: PlayerProfile;
1575
+
1576
+ rootwidgets: mod.UIWidget[] = [];
1577
+
1578
+ constructor(playerProfile: PlayerProfile) {
1579
+ this.#playerprofile = playerProfile;
1580
+ this.#Create();
1581
+ }
1582
+
1583
+ Delete() {
1584
+
1585
+ for (let index = 0; index < this.rootwidgets.length; index++) {
1586
+ const element = this.rootwidgets[index];
1587
+ mod.DeleteUIWidget(element)
1588
+ }
1589
+
1590
+ }
1591
+
1592
+ Close(){
1593
+ this.rootwidgets.forEach(rootwidget => {
1594
+ mod.SetUIWidgetVisible(rootwidget, false)
1595
+ });
1596
+ }
1597
+
1598
+ #Create() {
1599
+ const coolahhuiname: string = "overtake_message_" + this.#playerprofile.playerProfileId;
1600
+ mod.AddUIText(coolahhuiname, mod.CreateVector(0, 100, 0), mod.CreateVector(200, 40, 0), mod.UIAnchor.TopCenter, MakeMessage(mod.stringkeys.overtakenmessage), this.#playerprofile.player);
1601
+ const widget = mod.FindUIWidgetWithName(coolahhuiname);
1602
+ mod.SetUITextColor(widget, mod.CreateVector(0, 0, 0));
1603
+ mod.SetUITextSize(widget, 40);
1604
+ mod.SetUITextAnchor(widget, mod.UIAnchor.Center);
1605
+ mod.SetUIWidgetPadding(widget, -100);
1606
+ mod.SetUIWidgetVisible(widget, true);
1607
+ mod.SetUIWidgetBgFill(widget, mod.UIBgFill.Solid);
1608
+ mod.SetUIWidgetBgColor(widget, mod.CreateVector(0.678, 0.753, 0.800));
1609
+ mod.SetUIWidgetBgAlpha(widget, 0.9);
1610
+ mod.SetUIWidgetVisible(widget, false);
1611
+ this.rootwidgets.push(widget)
1612
+
1613
+ this.rootwidgets.push(this.CreateFadeLineUI(true))
1614
+ this.rootwidgets.push(this.CreateFadeLineUI(false))
1615
+ }
1616
+
1617
+
1618
+ CreateFadeLineUI(right: boolean): mod.UIWidget {
1619
+ const coolahhuiname: string = "overtake_message_line_" + right + "_" + this.#playerprofile.playerProfileId;
1620
+ let horizontalOffset: number = right ? 175 : -175;
1621
+ mod.AddUIContainer(coolahhuiname, mod.CreateVector(horizontalOffset, 100, 0), mod.CreateVector(150, 40, 0), mod.UIAnchor.TopCenter, this.#playerprofile.player);
1622
+ let widget = mod.FindUIWidgetWithName(coolahhuiname);
1623
+ mod.SetUIWidgetPadding(widget, 1);
1624
+ right ? mod.SetUIWidgetBgFill(widget, mod.UIBgFill.GradientLeft) : mod.SetUIWidgetBgFill(widget, mod.UIBgFill.GradientRight);
1625
+ mod.SetUIWidgetBgColor(widget, mod.CreateVector(0.678, 0.753, 0.800));
1626
+ mod.SetUIWidgetBgAlpha(widget, 0.9);
1627
+ mod.SetUIWidgetVisible(widget, false);
1628
+
1629
+ return widget;
1630
+ }
1631
+
1632
+
1633
+ async Trigger() {
1634
+
1635
+ if (this.FeedbackBeingShown) {
1636
+ return;
1637
+ }
1638
+ this.FeedbackBeingShown = true;
1639
+
1640
+
1641
+ mod.SetUIWidgetVisible(this.rootwidgets[0], true);
1642
+ mod.SetUIWidgetBgAlpha(this.rootwidgets[0], 1);
1643
+ mod.SetUITextAlpha(this.rootwidgets[0], 1);
1644
+
1645
+ mod.SetUIWidgetVisible(this.rootwidgets[1], true);
1646
+ mod.SetUIWidgetVisible(this.rootwidgets[2], true);
1647
+ mod.SetUIWidgetBgAlpha(this.rootwidgets[1], 1);
1648
+ mod.SetUIWidgetBgAlpha(this.rootwidgets[2], 1);
1649
+
1650
+ await mod.Wait(2.0);
1651
+ this.InterpFeedback();
1652
+ await mod.Wait(5.0);
1653
+
1654
+ this.FeedbackBeingShown = false;
1655
+ mod.SetUIWidgetVisible(this.rootwidgets[0], false);
1656
+ mod.SetUIWidgetVisible(this.rootwidgets[1], false);
1657
+ mod.SetUIWidgetVisible(this.rootwidgets[2], false);
1658
+ }
1659
+ FeedbackBeingShown: boolean = false;
1660
+ FeedbackQueued: boolean = false;
1661
+
1662
+ async InterpFeedback() {
1663
+
1664
+
1665
+ let currentLerpvalue: number = 0;
1666
+ let lerpIncrement: number = 0;
1667
+ while (currentLerpvalue < 1.0) {
1668
+ if (!this.FeedbackBeingShown) break;
1669
+ lerpIncrement = lerpIncrement + 0.1;
1670
+ currentLerpvalue = Lerp(currentLerpvalue, 1, lerpIncrement);
1671
+ mod.SetUIWidgetBgAlpha(this.rootwidgets[0], 1 - currentLerpvalue);
1672
+ mod.SetUITextAlpha(this.rootwidgets[0], 1 - currentLerpvalue);
1673
+ mod.SetUIWidgetBgAlpha(this.rootwidgets[1], 1 - currentLerpvalue);
1674
+ mod.SetUIWidgetBgAlpha(this.rootwidgets[2], 1 - currentLerpvalue);
1675
+ await mod.Wait(0.1);
1676
+ }
1677
+
1678
+ }
1679
+
1680
+ }
1681
+
1682
+ // === UI_BoosterDisabled.ts ===
1683
+
1684
+
1685
+ class HoH_BoosterDisabledUI {
1686
+ private currentRunId = 0;
1687
+ #playerprofile: PlayerProfile;
1688
+ private WidgetON = false;
1689
+ rootwidgets: mod.UIWidget[] = [];
1690
+ constructor(playerProfile: PlayerProfile) {
1691
+ this.#playerprofile = playerProfile;
1692
+ this.#Create();
1693
+ }
1694
+ Delete() {
1695
+
1696
+ for (let index = 0; index < this.rootwidgets.length; index++) {
1697
+ const element = this.rootwidgets[index];
1698
+ mod.DeleteUIWidget(element)
1699
+ }
1700
+
1701
+ }
1702
+
1703
+ Close() {
1704
+ this.rootwidgets.forEach(rootwidget => {
1705
+ mod.SetUIWidgetVisible(rootwidget, false)
1706
+ });
1707
+ }
1708
+
1709
+ widgetHeightPos = 120;
1710
+ widgetHorisontalPos = -170;
1711
+
1712
+ #Create() {
1713
+ const coolahhuiname: string = "booster_disable_message_" + this.#playerprofile.playerProfileId;
1714
+ mod.AddUIText(coolahhuiname, mod.CreateVector(this.widgetHorisontalPos, this.widgetHeightPos, 0), mod.CreateVector(205, 36, 0), mod.UIAnchor.BottomCenter, MakeMessage(mod.stringkeys.boosterdisabled), this.#playerprofile.player);
1715
+ const widget = mod.FindUIWidgetWithName(coolahhuiname);
1716
+ mod.SetUITextColor(widget, mod.CreateVector(1, 1, 1));
1717
+ mod.SetUITextSize(widget, 14);
1718
+ mod.SetUITextAnchor(widget, mod.UIAnchor.Center);
1719
+ mod.SetUIWidgetPadding(widget, -100);
1720
+ mod.SetUIWidgetVisible(widget, true);
1721
+ mod.SetUIWidgetBgFill(widget, mod.UIBgFill.Solid);
1722
+ mod.SetUIWidgetBgColor(widget, mod.CreateVector(0.68, 0, 0));
1723
+ mod.SetUIWidgetBgAlpha(widget, 1.0);
1724
+ mod.SetUIWidgetDepth(widget, mod.UIDepth.AboveGameUI)
1725
+ mod.SetUIWidgetVisible(widget, false);
1726
+ this.rootwidgets.push(widget)
1727
+
1728
+ }
1729
+
1730
+
1731
+ async Trigger() {
1732
+
1733
+ if (!mod.IsPlayerValid(this.#playerprofile.player)) return;
1734
+
1735
+ const isAlive = mod.GetSoldierState(
1736
+ this.#playerprofile.player,
1737
+ mod.SoldierStateBool.IsAlive
1738
+ );
1739
+ if (!isAlive) {
1740
+ mod.SetUIWidgetVisible(this.rootwidgets[0], false);
1741
+ return;
1742
+ }
1743
+
1744
+ if (this.#playerprofile.boosterDisabled) {
1745
+ mod.SetUIWidgetVisible(this.rootwidgets[0], true);
1746
+ } else {
1747
+ mod.SetUIWidgetVisible(this.rootwidgets[0], false);
1748
+ }
1749
+
1750
+ }
1751
+
1752
+ }
1753
+
1754
+ // === UI_EndingGameCountdown.ts ===
1755
+
1756
+
1757
+
1758
+ class HoH_UIEndingGameCountdown {
1759
+ #Player: PlayerProfile
1760
+
1761
+ headerWidget: mod.UIWidget | undefined
1762
+ textWidget: mod.UIWidget | undefined
1763
+
1764
+ constructor(playerProf: PlayerProfile) {
1765
+ this.#Player = playerProf;
1766
+
1767
+ const bfBlueColor = [0.678, 0.753, 0.800]
1768
+ const height = 35;
1769
+ const width = 240;
1770
+ this.headerWidget = ParseUI(
1771
+ {
1772
+ type: "Container",
1773
+ size: [width, height],
1774
+ position: [0, 155],
1775
+ name: "ending_countdown_" + this.#Player.playerProfileId,
1776
+ anchor: mod.UIAnchor.TopCenter,
1777
+ bgFill: mod.UIBgFill.Blur,
1778
+ bgColor: [0.2, 0.2, 0.3],
1779
+ bgAlpha: 0.8,
1780
+ playerId: playerProf.player,
1781
+ visible: false,
1782
+ children: [
1783
+ {
1784
+ type: "Text",
1785
+ name: "ending_countdown_text_" + this.#Player.playerProfileId,
1786
+ size: [width, height],
1787
+ position: [0, 0],
1788
+ anchor: mod.UIAnchor.Center,
1789
+ bgFill: mod.UIBgFill.None,
1790
+ textColor: bfBlueColor,
1791
+ textAnchor: mod.UIAnchor.Center,
1792
+ textLabel: MakeMessage(mod.stringkeys.gameending, 12),
1793
+ textSize: 25
1794
+ },
1795
+ ]
1796
+ }
1797
+ )
1798
+ this.textWidget = mod.FindUIWidgetWithName("ending_countdown_text_" + this.#Player.playerProfileId)
1799
+ }
1800
+
1801
+
1802
+ Delete() {
1803
+ this.headerWidget && mod.DeleteUIWidget(this.headerWidget)
1804
+ // this.textWidget && mod.DeleteUIWidget(this.textWidget)
1805
+ }
1806
+
1807
+
1808
+ Open(timeleft: number) {
1809
+ this.headerWidget && mod.SetUIWidgetVisible(this.headerWidget, true)
1810
+ this.textWidget && mod.SetUITextLabel(this.textWidget, MakeMessage(mod.stringkeys.gameending, timeleft))
1811
+ }
1812
+
1813
+
1814
+ update(timeleft: number) {
1815
+ this.textWidget && mod.SetUITextLabel(this.textWidget, MakeMessage(mod.stringkeys.gameending, timeleft))
1816
+ }
1817
+
1818
+ async Close(delayclose: number = 0) {
1819
+ await mod.Wait(delayclose)
1820
+ this.headerWidget && mod.SetUIWidgetVisible(this.headerWidget, false)
1821
+ }
1822
+ }
1823
+
1824
+
1825
+ // === UI_FadeInBlackScreen.ts ===
1826
+
1827
+
1828
+
1829
+ class HoH_UIBlackScreen {
1830
+ #Player: PlayerProfile
1831
+
1832
+ #black_screen_widget: mod.UIWidget | undefined;
1833
+
1834
+ fadeinTime: number = 1;
1835
+ fadeOutTime: number = 1;
1836
+
1837
+ constructor(playerProf: PlayerProfile) {
1838
+ this.#Player = playerProf
1839
+
1840
+ this.#black_screen_widget = ParseUI(
1841
+ {
1842
+ type: "Container",
1843
+ size: [2500, 2500],
1844
+ position: [0, 0],
1845
+ name: "fadeScreen_" + this.#Player.playerProfileId,
1846
+ anchor: mod.UIAnchor.Center,
1847
+ bgFill: mod.UIBgFill.Solid,
1848
+ bgColor: [0, 0, 0],
1849
+ bgAlpha: 0.0,
1850
+ playerId: playerProf.player
1851
+ }
1852
+ )
1853
+ }
1854
+
1855
+ Close() {
1856
+ this.#black_screen_widget && mod.SetUIWidgetVisible(this.#black_screen_widget, false)
1857
+ }
1858
+
1859
+ Delete() {
1860
+ this.#black_screen_widget && mod.DeleteUIWidget(this.#black_screen_widget)
1861
+ }
1862
+
1863
+ async FadeIn() {
1864
+
1865
+ if (this.#black_screen_widget) {
1866
+
1867
+ this.#black_screen_widget && mod.SetUIWidgetVisible(this.#black_screen_widget, true)
1868
+ mod.SetUIWidgetBgAlpha(this.#black_screen_widget, 0);
1869
+
1870
+
1871
+ let time = 0;
1872
+
1873
+ while (time < 1) {
1874
+
1875
+ time += 0.1
1876
+
1877
+
1878
+ if (time > 1) {
1879
+ time = 1;
1880
+ }
1881
+
1882
+ mod.SetUIWidgetBgAlpha(this.#black_screen_widget, time)
1883
+ await mod.Wait(0.1)
1884
+
1885
+ }
1886
+
1887
+ mod.SetUIWidgetBgAlpha(this.#black_screen_widget, 1)
1888
+
1889
+ }
1890
+ }
1891
+
1892
+ async FadeOut() {
1893
+
1894
+ if (this.#black_screen_widget) {
1895
+
1896
+ mod.SetUIWidgetBgAlpha(this.#black_screen_widget, 1);
1897
+
1898
+ let time = 1;
1899
+
1900
+ while (time > 0) {
1901
+
1902
+ time -= 0.1
1903
+
1904
+ if (time < 0) {
1905
+ time = 0;
1906
+ }
1907
+
1908
+ mod.SetUIWidgetBgAlpha(this.#black_screen_widget, time)
1909
+ await mod.Wait(0.1)
1910
+
1911
+ }
1912
+
1913
+ mod.SetUIWidgetBgAlpha(this.#black_screen_widget, 0);
1914
+ this.#black_screen_widget && mod.SetUIWidgetVisible(this.#black_screen_widget, false)
1915
+ }
1916
+ }
1917
+ }
1918
+
1919
+ // === UI_GlobalHelpers.ts ===
1920
+
1921
+
1922
+
1923
+ type UIVector = mod.Vector | number[];
1924
+
1925
+ interface UIParams {
1926
+ name: string;
1927
+ type: string;
1928
+ position: any;
1929
+ size: any;
1930
+ anchor: mod.UIAnchor;
1931
+ parent: mod.UIWidget;
1932
+ visible: boolean;
1933
+ textLabel: string;
1934
+ textColor: UIVector;
1935
+ textAlpha: number;
1936
+ textSize: number;
1937
+ textAnchor: mod.UIAnchor;
1938
+ padding: number;
1939
+ bgColor: UIVector;
1940
+ bgAlpha: number;
1941
+ bgFill: mod.UIBgFill;
1942
+ imageType: mod.UIImageType;
1943
+ imageColor: UIVector;
1944
+ imageAlpha: number;
1945
+ teamId?: mod.Team;
1946
+ playerId?: mod.Player;
1947
+ children?: any[];
1948
+ buttonEnabled: boolean;
1949
+ buttonColorBase: UIVector;
1950
+ buttonAlphaBase: number;
1951
+ buttonColorDisabled: UIVector;
1952
+ buttonAlphaDisabled: number;
1953
+ buttonColorPressed: UIVector;
1954
+ buttonAlphaPressed: number;
1955
+ buttonColorHover: UIVector;
1956
+ buttonAlphaHover: number;
1957
+ buttonColorFocused: UIVector;
1958
+ buttonAlphaFocused: number;
1959
+ }
1960
+
1961
+ function __asModVector(param: number[] | mod.Vector) {
1962
+ if (Array.isArray(param))
1963
+ return mod.CreateVector(param[0], param[1], param.length == 2 ? 0 : param[2]);
1964
+ else
1965
+ return param;
1966
+ }
1967
+
1968
+ function __asModMessage(param: string | mod.Message) {
1969
+ if (typeof (param) === "string")
1970
+ return mod.Message(param);
1971
+ return param;
1972
+ }
1973
+
1974
+ function __fillInDefaultArgs(params: UIParams) {
1975
+ if (!params.hasOwnProperty('name'))
1976
+ params.name = "";
1977
+ if (!params.hasOwnProperty('position'))
1978
+ params.position = mod.CreateVector(0, 0, 0);
1979
+ if (!params.hasOwnProperty('size'))
1980
+ params.size = mod.CreateVector(100, 100, 0);
1981
+ if (!params.hasOwnProperty('anchor'))
1982
+ params.anchor = mod.UIAnchor.TopLeft;
1983
+ if (!params.hasOwnProperty('parent'))
1984
+ params.parent = mod.GetUIRoot();
1985
+ if (!params.hasOwnProperty('visible'))
1986
+ params.visible = true;
1987
+ if (!params.hasOwnProperty('padding'))
1988
+ params.padding = (params.type == "Container") ? 0 : 8;
1989
+ if (!params.hasOwnProperty('bgColor'))
1990
+ params.bgColor = mod.CreateVector(0.25, 0.25, 0.25);
1991
+ if (!params.hasOwnProperty('bgAlpha'))
1992
+ params.bgAlpha = 0.5;
1993
+ if (!params.hasOwnProperty('bgFill'))
1994
+ params.bgFill = mod.UIBgFill.Solid;
1995
+ }
1996
+
1997
+ function __setNameAndGetWidget(uniqueName: any, params: any) {
1998
+ let widget = mod.FindUIWidgetWithName(uniqueName) as mod.UIWidget;
1999
+ mod.SetUIWidgetName(widget, params.name);
2000
+ return widget;
2001
+ }
2002
+
2003
+ const __cUniqueName = "----uniquename----";
2004
+
2005
+ function __addUIContainer(params: UIParams) {
2006
+ __fillInDefaultArgs(params);
2007
+ let restrict = params.teamId ?? params.playerId;
2008
+ if (restrict) {
2009
+ mod.AddUIContainer(__cUniqueName,
2010
+ __asModVector(params.position),
2011
+ __asModVector(params.size),
2012
+ params.anchor,
2013
+ params.parent,
2014
+ params.visible,
2015
+ params.padding,
2016
+ __asModVector(params.bgColor),
2017
+ params.bgAlpha,
2018
+ params.bgFill,
2019
+ restrict);
2020
+ } else {
2021
+ mod.AddUIContainer(__cUniqueName,
2022
+ __asModVector(params.position),
2023
+ __asModVector(params.size),
2024
+ params.anchor,
2025
+ params.parent,
2026
+ params.visible,
2027
+ params.padding,
2028
+ __asModVector(params.bgColor),
2029
+ params.bgAlpha,
2030
+ params.bgFill);
2031
+ }
2032
+ let widget = __setNameAndGetWidget(__cUniqueName, params);
2033
+ if (params.children) {
2034
+ params.children.forEach((childParams: any) => {
2035
+ childParams.parent = widget;
2036
+ __addUIWidget(childParams);
2037
+ });
2038
+ }
2039
+ return widget;
2040
+ }
2041
+
2042
+ function __fillInDefaultTextArgs(params: UIParams) {
2043
+ if (!params.hasOwnProperty('textLabel'))
2044
+ params.textLabel = "";
2045
+ if (!params.hasOwnProperty('textSize'))
2046
+ params.textSize = 0;
2047
+ if (!params.hasOwnProperty('textColor'))
2048
+ params.textColor = mod.CreateVector(1, 1, 1);
2049
+ if (!params.hasOwnProperty('textAlpha'))
2050
+ params.textAlpha = 1;
2051
+ if (!params.hasOwnProperty('textAnchor'))
2052
+ params.textAnchor = mod.UIAnchor.CenterLeft;
2053
+ }
2054
+
2055
+ function __addUIText(params: UIParams) {
2056
+ __fillInDefaultArgs(params);
2057
+ __fillInDefaultTextArgs(params);
2058
+ let restrict = params.teamId ?? params.playerId;
2059
+ if (restrict) {
2060
+ mod.AddUIText(__cUniqueName,
2061
+ __asModVector(params.position),
2062
+ __asModVector(params.size),
2063
+ params.anchor,
2064
+ params.parent,
2065
+ params.visible,
2066
+ params.padding,
2067
+ __asModVector(params.bgColor),
2068
+ params.bgAlpha,
2069
+ params.bgFill,
2070
+ __asModMessage(params.textLabel),
2071
+ params.textSize,
2072
+ __asModVector(params.textColor),
2073
+ params.textAlpha,
2074
+ params.textAnchor,
2075
+ restrict);
2076
+ } else {
2077
+ mod.AddUIText(__cUniqueName,
2078
+ __asModVector(params.position),
2079
+ __asModVector(params.size),
2080
+ params.anchor,
2081
+ params.parent,
2082
+ params.visible,
2083
+ params.padding,
2084
+ __asModVector(params.bgColor),
2085
+ params.bgAlpha,
2086
+ params.bgFill,
2087
+ __asModMessage(params.textLabel),
2088
+ params.textSize,
2089
+ __asModVector(params.textColor),
2090
+ params.textAlpha,
2091
+ params.textAnchor);
2092
+ }
2093
+ return __setNameAndGetWidget(__cUniqueName, params);
2094
+ }
2095
+
2096
+ function __fillInDefaultImageArgs(params: any) {
2097
+ if (!params.hasOwnProperty('imageType'))
2098
+ params.imageType = mod.UIImageType.None;
2099
+ if (!params.hasOwnProperty('imageColor'))
2100
+ params.imageColor = mod.CreateVector(1, 1, 1);
2101
+ if (!params.hasOwnProperty('imageAlpha'))
2102
+ params.imageAlpha = 1;
2103
+ }
2104
+
2105
+ function __addUIImage(params: UIParams) {
2106
+ __fillInDefaultArgs(params);
2107
+ __fillInDefaultImageArgs(params);
2108
+ let restrict = params.teamId ?? params.playerId;
2109
+ if (restrict) {
2110
+ mod.AddUIImage(__cUniqueName,
2111
+ __asModVector(params.position),
2112
+ __asModVector(params.size),
2113
+ params.anchor,
2114
+ params.parent,
2115
+ params.visible,
2116
+ params.padding,
2117
+ __asModVector(params.bgColor),
2118
+ params.bgAlpha,
2119
+ params.bgFill,
2120
+ params.imageType,
2121
+ __asModVector(params.imageColor),
2122
+ params.imageAlpha,
2123
+ restrict);
2124
+ } else {
2125
+ mod.AddUIImage(__cUniqueName,
2126
+ __asModVector(params.position),
2127
+ __asModVector(params.size),
2128
+ params.anchor,
2129
+ params.parent,
2130
+ params.visible,
2131
+ params.padding,
2132
+ __asModVector(params.bgColor),
2133
+ params.bgAlpha,
2134
+ params.bgFill,
2135
+ params.imageType,
2136
+ __asModVector(params.imageColor),
2137
+ params.imageAlpha);
2138
+ }
2139
+ return __setNameAndGetWidget(__cUniqueName, params);
2140
+ }
2141
+
2142
+ function __fillInDefaultArg(params: any, argName: any, defaultValue: any) {
2143
+ if (!params.hasOwnProperty(argName))
2144
+ params[argName] = defaultValue;
2145
+ }
2146
+
2147
+ function __fillInDefaultButtonArgs(params: any) {
2148
+ if (!params.hasOwnProperty('buttonEnabled'))
2149
+ params.buttonEnabled = true;
2150
+ if (!params.hasOwnProperty('buttonColorBase'))
2151
+ params.buttonColorBase = mod.CreateVector(0.7, 0.7, 0.7);
2152
+ if (!params.hasOwnProperty('buttonAlphaBase'))
2153
+ params.buttonAlphaBase = 1;
2154
+ if (!params.hasOwnProperty('buttonColorDisabled'))
2155
+ params.buttonColorDisabled = mod.CreateVector(0.2, 0.2, 0.2);
2156
+ if (!params.hasOwnProperty('buttonAlphaDisabled'))
2157
+ params.buttonAlphaDisabled = 0.5;
2158
+ if (!params.hasOwnProperty('buttonColorPressed'))
2159
+ params.buttonColorPressed = mod.CreateVector(0.25, 0.25, 0.25);
2160
+ if (!params.hasOwnProperty('buttonAlphaPressed'))
2161
+ params.buttonAlphaPressed = 1;
2162
+ if (!params.hasOwnProperty('buttonColorHover'))
2163
+ params.buttonColorHover = mod.CreateVector(1, 1, 1);
2164
+ if (!params.hasOwnProperty('buttonAlphaHover'))
2165
+ params.buttonAlphaHover = 1;
2166
+ if (!params.hasOwnProperty('buttonColorFocused'))
2167
+ params.buttonColorFocused = mod.CreateVector(1, 1, 1);
2168
+ if (!params.hasOwnProperty('buttonAlphaFocused'))
2169
+ params.buttonAlphaFocused = 1;
2170
+ }
2171
+
2172
+ function __addUIButton(params: UIParams) {
2173
+ __fillInDefaultArgs(params);
2174
+ __fillInDefaultButtonArgs(params);
2175
+ let restrict = params.teamId ?? params.playerId;
2176
+ if (restrict) {
2177
+ mod.AddUIButton(__cUniqueName,
2178
+ __asModVector(params.position),
2179
+ __asModVector(params.size),
2180
+ params.anchor,
2181
+ params.parent,
2182
+ params.visible,
2183
+ params.padding,
2184
+ __asModVector(params.bgColor),
2185
+ params.bgAlpha,
2186
+ params.bgFill,
2187
+ params.buttonEnabled,
2188
+ __asModVector(params.buttonColorBase), params.buttonAlphaBase,
2189
+ __asModVector(params.buttonColorDisabled), params.buttonAlphaDisabled,
2190
+ __asModVector(params.buttonColorPressed), params.buttonAlphaPressed,
2191
+ __asModVector(params.buttonColorHover), params.buttonAlphaHover,
2192
+ __asModVector(params.buttonColorFocused), params.buttonAlphaFocused,
2193
+ restrict);
2194
+ } else {
2195
+ mod.AddUIButton(__cUniqueName,
2196
+ __asModVector(params.position),
2197
+ __asModVector(params.size),
2198
+ params.anchor,
2199
+ params.parent,
2200
+ params.visible,
2201
+ params.padding,
2202
+ __asModVector(params.bgColor),
2203
+ params.bgAlpha,
2204
+ params.bgFill,
2205
+ params.buttonEnabled,
2206
+ __asModVector(params.buttonColorBase), params.buttonAlphaBase,
2207
+ __asModVector(params.buttonColorDisabled), params.buttonAlphaDisabled,
2208
+ __asModVector(params.buttonColorPressed), params.buttonAlphaPressed,
2209
+ __asModVector(params.buttonColorHover), params.buttonAlphaHover,
2210
+ __asModVector(params.buttonColorFocused), params.buttonAlphaFocused);
2211
+ }
2212
+ return __setNameAndGetWidget(__cUniqueName, params);
2213
+ }
2214
+
2215
+ function __addUIWidget(params: UIParams) {
2216
+ if (params == null)
2217
+ return undefined;
2218
+ if (params.type == "Container")
2219
+ return __addUIContainer(params);
2220
+ else if (params.type == "Text")
2221
+ return __addUIText(params);
2222
+ else if (params.type == "Image")
2223
+ return __addUIImage(params);
2224
+ else if (params.type == "Button")
2225
+ return __addUIButton(params);
2226
+ return undefined;
2227
+ }
2228
+
2229
+ function ParseUI(...params: any[]) {
2230
+ let widget: mod.UIWidget | undefined;
2231
+ for (let a = 0; a < params.length; a++) {
2232
+ widget = __addUIWidget(params[a] as UIParams);
2233
+ }
2234
+ return widget;
2235
+ }
2236
+
2237
+ // === UI_PlacementHeader.ts ===
2238
+
2239
+
2240
+
2241
+ class HoH_UIPlacementHeader {
2242
+ #Player: PlayerProfile
2243
+
2244
+ headerWidget: mod.UIWidget | undefined
2245
+ textWidget: mod.UIWidget | undefined
2246
+
2247
+ constructor(playerProf: PlayerProfile) {
2248
+ this.#Player = playerProf;
2249
+
2250
+ const bfBlueColor = [0.678, 0.753, 0.800]
2251
+ const height = 100;
2252
+ const width = 500;
2253
+ this.headerWidget = ParseUI(
2254
+ {
2255
+ type: "Container",
2256
+ size: [width, height],
2257
+ position: [0, 50],
2258
+ name: "placement_" + this.#Player.playerProfileId,
2259
+ anchor: mod.UIAnchor.TopCenter,
2260
+ bgFill: mod.UIBgFill.Blur,
2261
+ bgColor: [0.2, 0.2, 0.3],
2262
+ bgAlpha: 0.9,
2263
+ playerId: playerProf.player,
2264
+ visible: false,
2265
+ children: [
2266
+ {
2267
+ type: "Container",
2268
+ name: "placement_line_right_" + this.#Player.playerProfileId,
2269
+ size: [2, height],
2270
+ position: [width / 2, 0],
2271
+ anchor: mod.UIAnchor.Center,
2272
+ bgFill: mod.UIBgFill.Solid,
2273
+ bgColor: bfBlueColor,
2274
+ bgAlpha: 1
2275
+ },
2276
+ {
2277
+ type: "Container",
2278
+ name: "placement_line_left_" + this.#Player.playerProfileId,
2279
+ size: [2, height],
2280
+ position: [-width / 2, 0],
2281
+ anchor: mod.UIAnchor.Center,
2282
+ bgFill: mod.UIBgFill.Solid,
2283
+ bgColor: bfBlueColor,
2284
+ bgAlpha: 1
2285
+ },
2286
+ {
2287
+ type: "Text",
2288
+ name: "placement_text" + this.#Player.playerProfileId,
2289
+ size: [width, height],
2290
+ position: [0, 0],
2291
+ anchor: mod.UIAnchor.Center,
2292
+ bgFill: mod.UIBgFill.None,
2293
+ textColor: bfBlueColor,
2294
+ textAnchor: mod.UIAnchor.Center,
2295
+ textLabel: MakeMessage(mod.stringkeys.header_placement, 0),
2296
+ textSize: 45
2297
+ },
2298
+ ]
2299
+ }
2300
+ )
2301
+ this.textWidget = mod.FindUIWidgetWithName("placement_text" + this.#Player.playerProfileId)
2302
+ }
2303
+
2304
+
2305
+ Delete() {
2306
+ this.headerWidget && mod.DeleteUIWidget(this.headerWidget)
2307
+ //this.textWidget && mod.DeleteUIWidget(this.textWidget)
2308
+ }
2309
+
2310
+ Open(text: string, placement: number, scale: number) {
2311
+ this.headerWidget && mod.SetUIWidgetVisible(this.headerWidget, true)
2312
+ this.textWidget && mod.SetUITextLabel(this.textWidget, MakeMessage(text, placement))
2313
+ this.textWidget && mod.SetUITextSize(this.textWidget, scale)
2314
+ }
2315
+
2316
+ update() {
2317
+ }
2318
+
2319
+ async Close(delayclose: number = 0) {
2320
+ await mod.Wait(delayclose)
2321
+ this.headerWidget && mod.SetUIWidgetVisible(this.headerWidget, false)
2322
+ }
2323
+
2324
+ }
2325
+
2326
+ // === UI_Scoreboard.ts ===
2327
+
2328
+
2329
+
2330
+ class HoH_ScoreboardUI {
2331
+
2332
+ #playerProfile: PlayerProfile;
2333
+ #CoreWidget: mod.UIWidget | undefined;
2334
+
2335
+ #scoreboardPlacement_text: mod.UIWidget | undefined
2336
+
2337
+ #ScoreboardplayerName: mod.UIWidget[] = []
2338
+ #ScoreboardPlacement: mod.UIWidget[] = []
2339
+ #ScoreboardTimeOne: mod.UIWidget[] = []
2340
+ #ScoreboardTimeTwo: mod.UIWidget[] = []
2341
+
2342
+ constructor(playerprofile: PlayerProfile) {
2343
+ this.#playerProfile = playerprofile
2344
+ this.Create()
2345
+ }
2346
+
2347
+
2348
+ Delete() {
2349
+ this.#CoreWidget && mod.DeleteUIWidget(this.#CoreWidget)
2350
+ }
2351
+
2352
+
2353
+ update() {
2354
+ console.log("Update Scoreboard positions")
2355
+
2356
+ this.#CoreWidget && mod.SetUIWidgetVisible(this.#CoreWidget, true)
2357
+
2358
+ const playerPositionInRace = currentRace.playersInRace.indexOf(this.#playerProfile)
2359
+
2360
+ this.#scoreboardPlacement_text && mod.SetUITextLabel(this.#scoreboardPlacement_text, MakeMessage(mod.stringkeys.position_in_race, playerPositionInRace + 1, currentRace.playersInRace.length))
2361
+
2362
+ for (let index = 0; index < this.#ScoreboardplayerName.length; index++) {
2363
+ const playerInPos = this.indexExists(currentRace.playersInRace, index)
2364
+
2365
+
2366
+ if (playerInPos) {
2367
+
2368
+ const playerProf = currentRace.playersInRace[index];
2369
+ const playerTime = playerProf.playerRaceTime;
2370
+
2371
+ if (playerTime) {
2372
+ const formatTime = this.FormatTime(playerTime - currentRace.raceTime)
2373
+
2374
+ mod.SetUITextLabel(this.#ScoreboardTimeOne[index], MakeMessage(mod.stringkeys.scoreboard_1_time_1, formatTime[0], formatTime[1]))
2375
+ mod.SetUITextLabel(this.#ScoreboardTimeTwo[index], MakeMessage(mod.stringkeys.scoreboard_1_time_2, formatTime[2], formatTime[3], formatTime[4]))
2376
+
2377
+ mod.SetUIWidgetVisible(this.#ScoreboardTimeOne[index], true)
2378
+ mod.SetUIWidgetVisible(this.#ScoreboardTimeTwo[index], true)
2379
+ } else {
2380
+ mod.SetUIWidgetVisible(this.#ScoreboardTimeOne[index], false)
2381
+ mod.SetUIWidgetVisible(this.#ScoreboardTimeTwo[index], false)
2382
+ }
2383
+
2384
+ mod.SetUIWidgetVisible(this.#ScoreboardplayerName[index], true)
2385
+ mod.SetUIWidgetVisible(this.#ScoreboardPlacement[index], true)
2386
+
2387
+
2388
+ mod.SetUITextLabel(this.#ScoreboardplayerName[index], MakeMessage(mod.stringkeys.scoreboard_1_name, currentRace.playersInRace[index].player))
2389
+
2390
+
2391
+ if (currentRace.trackState == TrackState.selected) {
2392
+ //Makes the players name green when ready
2393
+ if (playerProf.readyUp) {
2394
+ mod.SetUITextColor(this.#ScoreboardplayerName[index], mod.CreateVector(0.4196, 0.9098, 0.0745))
2395
+ mod.SetUITextColor(this.#ScoreboardPlacement[index], mod.CreateVector(0.4196, 0.9098, 0.0745))
2396
+ mod.SetUITextColor(this.#ScoreboardTimeOne[index], mod.CreateVector(0.4196, 0.9098, 0.0745))
2397
+ mod.SetUITextColor(this.#ScoreboardTimeTwo[index], mod.CreateVector(0.4196, 0.9098, 0.0745))
2398
+ } else {
2399
+ mod.SetUITextColor(this.#ScoreboardplayerName[index], mod.CreateVector(0.678, 0.753, 0.800))
2400
+ mod.SetUITextColor(this.#ScoreboardPlacement[index], mod.CreateVector(0.678, 0.753, 0.800))
2401
+ mod.SetUITextColor(this.#ScoreboardTimeOne[index], mod.CreateVector(0.678, 0.753, 0.800))
2402
+ mod.SetUITextColor(this.#ScoreboardTimeTwo[index], mod.CreateVector(0.678, 0.753, 0.800))
2403
+ }
2404
+
2405
+ } else if (index == playerPositionInRace) {
2406
+ mod.SetUITextColor(this.#ScoreboardplayerName[index], mod.CreateVector(1, 1, 0))
2407
+ mod.SetUITextColor(this.#ScoreboardPlacement[index], mod.CreateVector(1, 1, 0))
2408
+ mod.SetUITextColor(this.#ScoreboardTimeOne[index], mod.CreateVector(1, 1, 0))
2409
+ mod.SetUITextColor(this.#ScoreboardTimeTwo[index], mod.CreateVector(1, 1, 0))
2410
+ } else {
2411
+ mod.SetUITextColor(this.#ScoreboardplayerName[index], mod.CreateVector(0.678, 0.753, 0.800))
2412
+ mod.SetUITextColor(this.#ScoreboardPlacement[index], mod.CreateVector(0.678, 0.753, 0.800))
2413
+ mod.SetUITextColor(this.#ScoreboardTimeOne[index], mod.CreateVector(0.678, 0.753, 0.800))
2414
+ mod.SetUITextColor(this.#ScoreboardTimeTwo[index], mod.CreateVector(0.678, 0.753, 0.800))
2415
+ }
2416
+
2417
+ } else {
2418
+
2419
+ mod.SetUIWidgetVisible(this.#ScoreboardplayerName[index], false)
2420
+ mod.SetUIWidgetVisible(this.#ScoreboardPlacement[index], false)
2421
+ mod.SetUIWidgetVisible(this.#ScoreboardTimeOne[index], false)
2422
+ mod.SetUIWidgetVisible(this.#ScoreboardTimeTwo[index], false)
2423
+ }
2424
+
2425
+ }
2426
+ }
2427
+
2428
+ indexExists<T>(array: T[], index: number): boolean {
2429
+ return index >= 0 && index < array.length;
2430
+ }
2431
+
2432
+ FormatTime(time: number,): number[] {
2433
+
2434
+
2435
+ const minutes = Math.floor(time / 60);
2436
+ const seconds = Math.floor(time % 60);
2437
+ const tenths = Math.floor((time % 1) * 10);
2438
+
2439
+ const result: number[] = [];
2440
+
2441
+ // Ensure minutes are always 2 digits
2442
+ result.push(Math.floor(minutes / 10));
2443
+ result.push(minutes % 10);
2444
+
2445
+ // Ensure seconds are always 2 digits
2446
+ result.push(Math.floor(seconds / 10));
2447
+ result.push(seconds % 10);
2448
+
2449
+ // Tenths is always 1 digit
2450
+ result.push(tenths);
2451
+
2452
+ return result;
2453
+ }
2454
+
2455
+ Create() {
2456
+
2457
+ console.log("Creating Scoreboard UI")
2458
+ let children = []
2459
+
2460
+ const ScoreboardplayerPlacement = `Scoreboard2Placement_text_${this.#playerProfile.playerProfileId}`
2461
+
2462
+ const Scoreboardlaps = `Scoreboard2laps_text_${this.#playerProfile.playerProfileId}`
2463
+
2464
+ children.push(
2465
+ {
2466
+ type: "Text",
2467
+ name: ScoreboardplayerPlacement,
2468
+ textLabel: MakeMessage(mod.stringkeys.position_in_race, 1, MapPlayers),
2469
+ position: [0, -180, 1],
2470
+ size: [170, 140, 0],
2471
+ textSize: 110,
2472
+ bgFill: mod.UIBgFill.Blur,
2473
+ textColor: [0.678, 0.753, 0.800],
2474
+ textAnchor: mod.UIAnchor.Center,
2475
+ anchor: mod.UIAnchor.TopLeft,
2476
+ visible: true
2477
+ }
2478
+ )
2479
+
2480
+ for (let index = 0; index < MapPlayers; index++) {
2481
+
2482
+ const ScoreboardplayerName = `Scoreboard2playerName${index}text_${this.#playerProfile.playerProfileId}`
2483
+ const ScoreboardPlacement = `Scoreboard2Placement${index}text_${this.#playerProfile.playerProfileId}`
2484
+ const ScoreboardTimeOne = `Scoreboard2TimeOne${index}text_${this.#playerProfile.playerProfileId}`
2485
+ const ScoreboardTimeTwo = `Scoreboard2TimeTwo${index}text_${this.#playerProfile.playerProfileId}`
2486
+
2487
+ const rowPadding = 30;
2488
+ const startY = -125
2489
+ const y = startY + index * rowPadding;
2490
+
2491
+ const textBoxHeight = 30;
2492
+
2493
+ const textSize = 20;
2494
+ const textNameSize = 15;
2495
+ const timeSize = 15;
2496
+
2497
+ children.push(
2498
+ {
2499
+ type: "Text",
2500
+ name: ScoreboardPlacement,
2501
+ textLabel: MakeMessage(mod.stringkeys.scoreboard_1_name, index + 1),
2502
+ position: [-15, y, 1],
2503
+ size: [30, textBoxHeight, 0],
2504
+ textSize: textSize,
2505
+ bgFill: mod.UIBgFill.Blur,
2506
+ textColor: [0.678, 0.753, 0.800],
2507
+ textAnchor: mod.UIAnchor.CenterLeft,
2508
+ anchor: mod.UIAnchor.CenterLeft,
2509
+ visible: true
2510
+ },
2511
+ {
2512
+ type: "Text",
2513
+ name: ScoreboardplayerName,
2514
+ textLabel: MakeMessage(mod.stringkeys.X),
2515
+ position: [20, y, 1],
2516
+ size: [200, textBoxHeight, 0],
2517
+ textSize: textNameSize,
2518
+ bgFill: mod.UIBgFill.Blur,
2519
+ textColor: [0.678, 0.753, 0.800],
2520
+ textAnchor: mod.UIAnchor.CenterLeft,
2521
+ anchor: mod.UIAnchor.CenterLeft,
2522
+ visible: true
2523
+ },
2524
+ {
2525
+ type: "Text",
2526
+ name: ScoreboardTimeOne,
2527
+ textLabel: MakeMessage(mod.stringkeys.X),
2528
+ position: [160, y, 1],
2529
+ size: [250, textBoxHeight, 0],
2530
+ textSize: timeSize,
2531
+ bgFill: mod.UIBgFill.None,
2532
+ textColor: [0.678, 0.753, 0.800],
2533
+ textAnchor: mod.UIAnchor.CenterLeft,
2534
+ anchor: mod.UIAnchor.CenterLeft,
2535
+ visible: true
2536
+
2537
+ },
2538
+ {
2539
+ type: "Text",
2540
+ name: ScoreboardTimeTwo,
2541
+ textLabel: MakeMessage(mod.stringkeys.X),
2542
+ position: [180, y, 1],
2543
+ size: [250, textBoxHeight, 0],
2544
+ textSize: timeSize,
2545
+ bgFill: mod.UIBgFill.None,
2546
+ textColor: [0.678, 0.753, 0.800],
2547
+ textAnchor: mod.UIAnchor.CenterLeft,
2548
+ anchor: mod.UIAnchor.CenterLeft,
2549
+ visible: true
2550
+
2551
+ }
2552
+ )
2553
+
2554
+ }
2555
+
2556
+ const scoreboardContainerWidget = ParseUI({
2557
+ type: "Container",
2558
+ name: `scoreboard_Container_${this.#playerProfile.playerProfileId}`,
2559
+ position: [0, -200, 0],
2560
+ size: [250, 300, 0],
2561
+ anchor: mod.UIAnchor.CenterLeft,
2562
+ bgColor: [1, 1, 1],
2563
+ bgFill: mod.UIBgFill.None,
2564
+ bgAlpha: 1,
2565
+ padding: 20,
2566
+ children: children,
2567
+ playerId: this.#playerProfile.player,
2568
+ visible: false
2569
+ })
2570
+
2571
+
2572
+ this.#CoreWidget = scoreboardContainerWidget;
2573
+ this.#scoreboardPlacement_text = mod.FindUIWidgetWithName(ScoreboardplayerPlacement)
2574
+
2575
+ for (let index = 0; index < MapPlayers; index++) {
2576
+
2577
+ const ScoreboardPlacement = `Scoreboard2Placement${index}text_${this.#playerProfile.playerProfileId}`
2578
+ const ScoreboardplayerName = `Scoreboard2playerName${index}text_${this.#playerProfile.playerProfileId}`
2579
+ const ScoreboardTimeOne = `Scoreboard2TimeOne${index}text_${this.#playerProfile.playerProfileId}`
2580
+ const ScoreboardTimeTwo = `Scoreboard2TimeTwo${index}text_${this.#playerProfile.playerProfileId}`
2581
+
2582
+ this.#ScoreboardPlacement.push(mod.FindUIWidgetWithName(ScoreboardPlacement))
2583
+ this.#ScoreboardplayerName.push(mod.FindUIWidgetWithName(ScoreboardplayerName))
2584
+ this.#ScoreboardTimeOne.push(mod.FindUIWidgetWithName(ScoreboardTimeOne))
2585
+ this.#ScoreboardTimeTwo.push(mod.FindUIWidgetWithName(ScoreboardTimeTwo))
2586
+ }
2587
+
2588
+
2589
+ //Extra settings
2590
+ this.#CoreWidget && mod.SetUIWidgetDepth(this.#CoreWidget, mod.UIDepth.BelowGameUI)
2591
+ }
2592
+
2593
+ open() {
2594
+ if (this.#CoreWidget == undefined) {
2595
+ this.Create()
2596
+ }
2597
+
2598
+ this.update()
2599
+
2600
+ this.#CoreWidget && mod.SetUIWidgetVisible(this.#CoreWidget, true)
2601
+ }
2602
+
2603
+
2604
+ Close() {
2605
+ this.#CoreWidget && mod.SetUIWidgetVisible(this.#CoreWidget, false)
2606
+ }
2607
+
2608
+ }
2609
+
2610
+ // === UI_StartCountdown.ts ===
2611
+
2612
+
2613
+
2614
+ class HoH_UIStartCountdown {
2615
+ #Player: PlayerProfile
2616
+
2617
+ headerWidget: mod.UIWidget | undefined
2618
+ textWidget: mod.UIWidget | undefined
2619
+
2620
+ constructor(playerProf: PlayerProfile) {
2621
+ this.#Player = playerProf;
2622
+
2623
+ const bfBlueColor = [0.678, 0.753, 0.800]
2624
+ const height = 125;
2625
+ this.headerWidget = ParseUI(
2626
+ {
2627
+ type: "Container",
2628
+ size: [150, height],
2629
+ position: [0, 50],
2630
+ name: "start_countdown_" + this.#Player.playerProfileId,
2631
+ anchor: mod.UIAnchor.TopCenter,
2632
+ bgFill: mod.UIBgFill.Blur,
2633
+ bgColor: [0.2, 0.2, 0.3],
2634
+ bgAlpha: 0.9,
2635
+ playerId: playerProf.player,
2636
+ visible: false,
2637
+ children: [
2638
+ {
2639
+ type: "Container",
2640
+ name: "start_countdown_line_right_" + this.#Player.playerProfileId,
2641
+ size: [2, height],
2642
+ position: [75, 0],
2643
+ anchor: mod.UIAnchor.Center,
2644
+ bgFill: mod.UIBgFill.Solid,
2645
+ bgColor: bfBlueColor,
2646
+ bgAlpha: 1
2647
+ },
2648
+ {
2649
+ type: "Container",
2650
+ name: "start_countdown_line_left_" + this.#Player.playerProfileId,
2651
+ size: [2, height],
2652
+ position: [-75, 0],
2653
+ anchor: mod.UIAnchor.Center,
2654
+ bgFill: mod.UIBgFill.Solid,
2655
+ bgColor: bfBlueColor,
2656
+ bgAlpha: 1
2657
+ },
2658
+ {
2659
+ type: "Text",
2660
+ name: "start_countdown_text" + this.#Player.playerProfileId,
2661
+ size: [100, height],
2662
+ position: [0, 0],
2663
+ anchor: mod.UIAnchor.Center,
2664
+ bgFill: mod.UIBgFill.None,
2665
+ textColor: bfBlueColor,
2666
+ textAnchor: mod.UIAnchor.Center,
2667
+ textLabel: MakeMessage(mod.stringkeys.scoreboard_1_name),
2668
+ textSize: 85
2669
+ }
2670
+ ]
2671
+ }
2672
+ )
2673
+ this.textWidget = mod.FindUIWidgetWithName("start_countdown_text" + this.#Player.playerProfileId)
2674
+ }
2675
+
2676
+
2677
+ Delete() {
2678
+ this.headerWidget && mod.DeleteUIWidget(this.headerWidget)
2679
+ //this.textWidget && mod.DeleteUIWidget(this.textWidget)
2680
+ }
2681
+
2682
+ Open(text: string, countdowntime: number) {
2683
+ this.headerWidget && mod.SetUIWidgetVisible(this.headerWidget, true)
2684
+ this.textWidget && mod.SetUITextLabel(this.textWidget, MakeMessage(text, countdowntime))
2685
+ }
2686
+
2687
+
2688
+ update(text: string, countdowntime: number) {
2689
+ this.textWidget && mod.SetUITextLabel(this.textWidget, MakeMessage(text, countdowntime))
2690
+ }
2691
+
2692
+ async Close(delayclose: number = 0) {
2693
+ await mod.Wait(delayclose)
2694
+ this.headerWidget && mod.SetUIWidgetVisible(this.headerWidget, false)
2695
+ }
2696
+
2697
+ }
2698
+
2699
+ // === UI_VehicleSelect.ts ===
2700
+
2701
+
2702
+
2703
+ type HoH_ClickFunction = () => void;
2704
+ type HoH_FocusFunction = (focusIn: boolean) => void;
2705
+
2706
+
2707
+ const VehiclePositions = [
2708
+ { x: 217.841003417969, y: 227.279006958008, z: 558.077026367188 },
2709
+ { x: 242.748123168945, y: 228.753005981445, z: 563.011169433594 },
2710
+ { x: 268.551696777344, y: 228.162994384766, z: 568.041442871094 }
2711
+ ]
2712
+
2713
+ const playerVehicleSelectPositions = [
2714
+ [
2715
+ { x: 214.76530456543, y: 227.879440307617, z: 571.411010742188 },
2716
+ { x: 228.050430297852, y: 227.879440307617, z: 573.903076171875 }
2717
+ ],
2718
+ [
2719
+ { x: 241.145736694336, y: 227.879440307617, z: 577.202758789063 },
2720
+ { x: 254.34407043457, y: 227.879440307617, z: 580.119812011719 }
2721
+ ],
2722
+ [
2723
+ { x: 265.234405517578, y: 227.879440307617, z: 581.415283203125 },
2724
+ { x: 278.574584960938, y: 227.879440307617, z: 583.592895507813 }
2725
+ ]
2726
+
2727
+ ]
2728
+
2729
+ class HoH_UIButtonHolder {
2730
+
2731
+ playerProfile: PlayerProfile;
2732
+
2733
+ button_id: string
2734
+ button_widget: mod.UIWidget
2735
+
2736
+ button_text_id: string
2737
+ button_text_widget: mod.UIWidget
2738
+
2739
+ select_text_id: string | undefined
2740
+ select_widget: mod.UIWidget | undefined
2741
+
2742
+ click: HoH_ClickFunction;
2743
+ focus: HoH_FocusFunction;
2744
+
2745
+ constructor(playerProfile: PlayerProfile, uniqueButtonName: string, uniqueTextName: string, uniqueSelectName?: string, clickFunction?: HoH_ClickFunction, focusFunction?: HoH_FocusFunction) {
2746
+ this.button_id = uniqueButtonName;
2747
+ this.button_widget = mod.FindUIWidgetWithName(uniqueButtonName);
2748
+ this.button_text_id = uniqueTextName;
2749
+ this.button_text_widget = mod.FindUIWidgetWithName(uniqueTextName);
2750
+ this.playerProfile = playerProfile;
2751
+
2752
+ if (uniqueSelectName) {
2753
+ this.select_text_id = uniqueSelectName;
2754
+ this.select_widget = mod.FindUIWidgetWithName(uniqueSelectName)
2755
+ }
2756
+
2757
+ if (focusFunction) {
2758
+ mod.EnableUIButtonEvent(this.button_widget, mod.UIButtonEvent.FocusIn, true)
2759
+ mod.EnableUIButtonEvent(this.button_widget, mod.UIButtonEvent.FocusOut, true)
2760
+ }
2761
+
2762
+ this.focus = focusFunction?.bind(this) ?? this.defaultFocus;
2763
+ this.click = clickFunction?.bind(this) ?? this.defaultClick;
2764
+ }
2765
+
2766
+ defaultClick() { console.log("default Click") }
2767
+ defaultFocus() { console.log("default focus") }
2768
+
2769
+ }
2770
+
2771
+ class HoH_UIVehicleSelect {
2772
+
2773
+ #Left_Button_Holder: HoH_UIButtonHolder | undefined = undefined;
2774
+ #Right_Button_Holder: HoH_UIButtonHolder | undefined = undefined;
2775
+
2776
+ #Readyup_Button_Holder: HoH_UIButtonHolder | undefined = undefined;
2777
+
2778
+ #LEFT_BUTTON: string
2779
+ #LEFT_BUTTON_TEXT: string
2780
+ #LEFT_BUTTON_SELECTED: string
2781
+
2782
+ #RIGHT_BUTTON: string
2783
+ #RIGHT_BUTTON_TEXT: string
2784
+ #RIGHT_BUTTON_SELECTED: string
2785
+
2786
+ #RootWidgets: mod.UIWidget[] = []
2787
+
2788
+ #PLAYERS_WAITING_TEXT: string;
2789
+ #players_ready_text_widget: mod.UIWidget | undefined;
2790
+
2791
+ #PLAYERS_READY_COUNT_TEXT: string
2792
+ #players_ready_count_widget: mod.UIWidget | undefined;
2793
+
2794
+ #CURRENT_SELECT_VEH_TEXT: string;
2795
+ #current_select_veh_Widget: mod.UIWidget | undefined;
2796
+
2797
+ #READYUP_BUTTON: string
2798
+ #READYUP_BUTTON_TEXT: string
2799
+ #READYUP_BUTTON_SELECT: string
2800
+
2801
+ #readyup_button_widget: mod.UIWidget | undefined;
2802
+
2803
+ #playerProfile: PlayerProfile;
2804
+
2805
+ UIOpen: boolean = false;
2806
+
2807
+ selectVehNb: number = 0
2808
+
2809
+
2810
+ constructor(player: PlayerProfile) {
2811
+ this.#playerProfile = player;
2812
+
2813
+ this.#LEFT_BUTTON = `left_veh_button_${this.#playerProfile.playerProfileId}`
2814
+ this.#LEFT_BUTTON_TEXT = `left_veh_button_text_${this.#playerProfile.playerProfileId}`
2815
+ this.#LEFT_BUTTON_SELECTED = `left_veh_button_selected_${this.#playerProfile.playerProfileId}`
2816
+
2817
+ this.#RIGHT_BUTTON = `right_veh_button_${this.#playerProfile.playerProfileId}`
2818
+ this.#RIGHT_BUTTON_TEXT = `right_veh_button_text_${this.#playerProfile.playerProfileId}`
2819
+ this.#RIGHT_BUTTON_SELECTED = `right_veh_button_select_${this.#playerProfile.playerProfileId}`
2820
+
2821
+ this.#READYUP_BUTTON = `readyup_veh_button_${this.#playerProfile.playerProfileId}`
2822
+ this.#READYUP_BUTTON_TEXT = `readyup_veh_button_text_${this.#playerProfile.playerProfileId}`
2823
+ this.#READYUP_BUTTON_SELECT = `readyup_veh_button_select_text_${this.#playerProfile.playerProfileId}`
2824
+
2825
+ this.#PLAYERS_READY_COUNT_TEXT = `players_ready_count_text_${this.#playerProfile.playerProfileId}`
2826
+ this.#CURRENT_SELECT_VEH_TEXT = `current_select_veh_text_${this.#playerProfile.playerProfileId}`
2827
+ this.#PLAYERS_WAITING_TEXT = `players_waiting_text_${this.#playerProfile.playerProfileId}`
2828
+
2829
+ this.selectVehNb = currentRace.availableVehicles.indexOf(this.#playerProfile.selectedVehicle)
2830
+ }
2831
+
2832
+ UIUpdatePlayersReady() {
2833
+ const { ready, total } = currentRace.GetPlayersReady();
2834
+ this.#players_ready_count_widget && mod.SetUITextLabel(this.#players_ready_count_widget, MakeMessage(mod.stringkeys.readyplayers, ready, total))
2835
+
2836
+ if (total < MinimumPlayerToStart) {
2837
+ this.#players_ready_text_widget && mod.SetUITextLabel(this.#players_ready_text_widget, MakeMessage(mod.stringkeys.waitingforplayersX, total, MapPlayers, MinimumPlayerToStart))
2838
+
2839
+ }
2840
+
2841
+ this.#players_ready_text_widget && mod.SetUITextLabel(this.#players_ready_text_widget, MakeMessage(mod.stringkeys.startsin, currentRace.readyupCountDown))
2842
+
2843
+
2844
+ currentRace.UpdateScoreboard()
2845
+ }
2846
+
2847
+ EnableSwitchButtons(enabled: boolean) {
2848
+ this.#Left_Button_Holder && mod.SetUIButtonEnabled(this.#Left_Button_Holder.button_widget, enabled)
2849
+ this.#Right_Button_Holder && mod.SetUIButtonEnabled(this.#Right_Button_Holder.button_widget, enabled)
2850
+
2851
+ if (enabled) {
2852
+ this.#Readyup_Button_Holder && mod.SetUIWidgetBgColor(this.#Readyup_Button_Holder.button_widget, mod.CreateVector(1, 1, 1))
2853
+ this.#Left_Button_Holder && mod.SetUIWidgetVisible(this.#Left_Button_Holder.button_widget, true)
2854
+ this.#Right_Button_Holder && mod.SetUIWidgetVisible(this.#Right_Button_Holder.button_widget, true)
2855
+ } else {
2856
+ this.#Readyup_Button_Holder && mod.SetUIWidgetBgColor(this.#Readyup_Button_Holder.button_widget, mod.CreateVector(0.4196, 0.9098, 0.0745))
2857
+ this.#Left_Button_Holder && mod.SetUIWidgetVisible(this.#Left_Button_Holder.button_widget, false)
2858
+ this.#Right_Button_Holder && mod.SetUIWidgetVisible(this.#Right_Button_Holder.button_widget, false)
2859
+ }
2860
+ }
2861
+
2862
+ VehicleSelectIncrease() {
2863
+ if (this.selectVehNb > 0) {
2864
+
2865
+ this.selectVehNb--;
2866
+ this.cameraTeleport();
2867
+ this.refresh();
2868
+ }
2869
+ }
2870
+
2871
+ VehicleSelectDecrease() {
2872
+ if (this.selectVehNb < currentRace.availableVehicles.length - 1) {
2873
+
2874
+ this.selectVehNb++;
2875
+ this.cameraTeleport()
2876
+ this.refresh();
2877
+ }
2878
+ }
2879
+
2880
+ #create() {
2881
+
2882
+ const { ready, total } = currentRace.GetPlayersReady();
2883
+
2884
+ const bfBlueColor = [0.678, 0.753, 0.800]
2885
+ const buttonBgColor = [1, 1, 1]
2886
+
2887
+ this.#RootWidgets.push(ParseUI({
2888
+ type: "Container",
2889
+ size: [500, 110],
2890
+ position: [0, 75],
2891
+ anchor: mod.UIAnchor.TopCenter,
2892
+ bgFill: mod.UIBgFill.Blur,
2893
+ bgColor: [0.2, 0.2, 0.3],
2894
+ bgAlpha: 0.9,
2895
+ playerId: this.#playerProfile.player,
2896
+ children: [{
2897
+ type: "Text",
2898
+ name: this.#PLAYERS_WAITING_TEXT,
2899
+ size: [500, 110],
2900
+ position: [0, -28],
2901
+ anchor: mod.UIAnchor.Center,
2902
+ bgFill: mod.UIBgFill.None,
2903
+ textColor: bfBlueColor,
2904
+ textAnchor: mod.UIAnchor.Center,
2905
+ textLabel: MakeMessage(mod.stringkeys.startsin, currentRace.readyupCountDown),
2906
+ textSize: 38
2907
+ },
2908
+ {
2909
+ type: "Text",
2910
+ name: this.#PLAYERS_READY_COUNT_TEXT,
2911
+ size: [250, 110],
2912
+ position: [0, 25],
2913
+ anchor: mod.UIAnchor.Center,
2914
+ textColor: bfBlueColor,
2915
+ bgFill: mod.UIBgFill.None,
2916
+ textAnchor: mod.UIAnchor.Center,
2917
+ textLabel: MakeMessage(mod.stringkeys.readyplayers, ready, total),
2918
+ textSize: 24
2919
+ },
2920
+ {
2921
+ type: "Container",
2922
+ name: this.#PLAYERS_READY_COUNT_TEXT + "_left_side_line",
2923
+ size: [1, 110],
2924
+ position: [-252, 0],
2925
+ anchor: mod.UIAnchor.Center,
2926
+ bgFill: mod.UIBgFill.Solid,
2927
+ bgColor: bfBlueColor,
2928
+ bgAlpha: 1
2929
+ }, {
2930
+ type: "Container",
2931
+ name: this.#PLAYERS_READY_COUNT_TEXT + "_right_side_line",
2932
+ size: [1, 110],
2933
+ position: [251, 0],
2934
+ anchor: mod.UIAnchor.Center,
2935
+ bgFill: mod.UIBgFill.Solid,
2936
+ bgColor: bfBlueColor,
2937
+ bgAlpha: 1
2938
+ },
2939
+ {
2940
+ type: "Container",
2941
+ name: this.#PLAYERS_READY_COUNT_TEXT + "_middle_line",
2942
+ size: [425, 3],
2943
+ position: [0, 0],
2944
+ anchor: mod.UIAnchor.Center,
2945
+ bgFill: mod.UIBgFill.Solid,
2946
+ bgColor: bfBlueColor,
2947
+ bgAlpha: 1
2948
+ },
2949
+ ]
2950
+ }) as mod.UIWidget)
2951
+
2952
+
2953
+ this.#players_ready_text_widget = mod.FindUIWidgetWithName(this.#PLAYERS_WAITING_TEXT)
2954
+ this.#players_ready_count_widget = mod.FindUIWidgetWithName(this.#PLAYERS_READY_COUNT_TEXT)
2955
+
2956
+
2957
+
2958
+ this.#RootWidgets.push(ParseUI({
2959
+ type: "Container",
2960
+ size: [500, 100],
2961
+ position: [0, 75],
2962
+ anchor: mod.UIAnchor.BottomCenter,
2963
+ bgFill: mod.UIBgFill.None,
2964
+ bgColor: mod.CreateVector(1, 1, 1),
2965
+ bgAlpha: 0.0,
2966
+ playerId: this.#playerProfile.player,
2967
+ children: [
2968
+ {
2969
+ type: "Container",
2970
+ name: this.#LEFT_BUTTON_SELECTED,
2971
+ size: [60, 60],
2972
+ position: [-150, 0],
2973
+ anchor: mod.UIAnchor.Center,
2974
+ bgFill: mod.UIBgFill.OutlineThin,
2975
+ bgColor: bfBlueColor,
2976
+ bgAlpha: 0.8,
2977
+ visible: false
2978
+ },
2979
+ {
2980
+ type: "Button",
2981
+ name: this.#LEFT_BUTTON,
2982
+ size: [50, 50],
2983
+ position: [-150, 0],
2984
+ anchor: mod.UIAnchor.Center,
2985
+ bgFill: mod.UIBgFill.Blur,
2986
+ buttonColorHover: [1, 1, 1],
2987
+ bgColor: [1, 1, 1],
2988
+ bgAlpha: 1.0,
2989
+ }, {
2990
+ type: "Text",
2991
+ parent: this.#LEFT_BUTTON,
2992
+ name: this.#LEFT_BUTTON_TEXT,
2993
+ size: [50, 50],
2994
+ position: [-150, 0],
2995
+ anchor: mod.UIAnchor.Center,
2996
+ bgFill: mod.UIBgFill.None,
2997
+ textColor: bfBlueColor,
2998
+ textAnchor: mod.UIAnchor.Center,
2999
+ textLabel: MakeMessage(mod.stringkeys.leftArrow),
3000
+ textSize: 35,
3001
+
3002
+ },
3003
+
3004
+
3005
+ {
3006
+ type: "Container",
3007
+ name: this.#READYUP_BUTTON_SELECT,
3008
+ size: [240, 60],
3009
+ position: [0, 0],
3010
+ anchor: mod.UIAnchor.Center,
3011
+ bgFill: mod.UIBgFill.OutlineThin,
3012
+ bgColor: bfBlueColor,
3013
+ bgAlpha: 0.8,
3014
+ visible: false
3015
+ },
3016
+ {
3017
+ type: "Button",
3018
+ name: this.#READYUP_BUTTON,
3019
+ size: [230, 50],
3020
+ position: [0, 0],
3021
+ anchor: mod.UIAnchor.Center,
3022
+ bgFill: mod.UIBgFill.Blur,
3023
+ buttonColorHover: [1, 1, 1],
3024
+ bgColor: [1, 1, 1],
3025
+ bgAlpha: 1.0
3026
+ },
3027
+ {
3028
+ type: "Text",
3029
+ parent: this.#READYUP_BUTTON,
3030
+ name: this.#READYUP_BUTTON_TEXT,
3031
+ size: [230, 50],
3032
+ position: [0, 0],
3033
+ anchor: mod.UIAnchor.Center,
3034
+ bgFill: mod.UIBgFill.None,
3035
+ textColor: bfBlueColor,
3036
+ textAnchor: mod.UIAnchor.Center,
3037
+ textLabel: MakeMessage(mod.stringkeys.ready),
3038
+ textSize: 25
3039
+ },
3040
+
3041
+ {
3042
+ type: "Container",
3043
+ name: this.#RIGHT_BUTTON_SELECTED,
3044
+ size: [60, 60],
3045
+ position: [150, 0],
3046
+ anchor: mod.UIAnchor.Center,
3047
+ bgFill: mod.UIBgFill.OutlineThin,
3048
+ bgColor: bfBlueColor,
3049
+ bgAlpha: 0.8,
3050
+ visible: false
3051
+ },
3052
+ {
3053
+ type: "Button",
3054
+ name: this.#RIGHT_BUTTON,
3055
+ size: [50, 50],
3056
+ position: [150, 0],
3057
+ anchor: mod.UIAnchor.Center,
3058
+ bgFill: mod.UIBgFill.Blur,
3059
+ buttonColorHover: [1, 1, 1],
3060
+ bgColor: [1, 1, 1],
3061
+ bgAlpha: 1.0
3062
+ }, {
3063
+
3064
+ type: "Text",
3065
+ parent: this.#RIGHT_BUTTON,
3066
+ name: this.#RIGHT_BUTTON_TEXT,
3067
+ size: [50, 50],
3068
+ position: [150, 0],
3069
+ anchor: mod.UIAnchor.Center,
3070
+ bgFill: mod.UIBgFill.None,
3071
+ textColor: bfBlueColor,
3072
+ textAnchor: mod.UIAnchor.Center,
3073
+ textLabel: MakeMessage(mod.stringkeys.rightArrow),
3074
+ textSize: 35
3075
+ },
3076
+
3077
+ {
3078
+ type: "Container",
3079
+ name: this.#CURRENT_SELECT_VEH_TEXT + "_line",
3080
+ size: [300, 3],
3081
+ position: [0, -35],
3082
+ anchor: mod.UIAnchor.Center,
3083
+ bgFill: mod.UIBgFill.Solid,
3084
+ bgColor: bfBlueColor,
3085
+ bgAlpha: 0.5
3086
+ },
3087
+ {
3088
+ type: "Text",
3089
+ name: this.#CURRENT_SELECT_VEH_TEXT,
3090
+ size: [350, 80],
3091
+ position: [0, -70],
3092
+ anchor: mod.UIAnchor.Center,
3093
+ bgFill: mod.UIBgFill.None,
3094
+ textColor: bfBlueColor,
3095
+ textAnchor: mod.UIAnchor.Center,
3096
+ textLabel: MakeMessage(mod.stringkeys.selectVeh),
3097
+ textSize: 60
3098
+ },
3099
+ {
3100
+ type: "Text",
3101
+ name: this.#CURRENT_SELECT_VEH_TEXT + "_chosen_veh",
3102
+ size: [300, 50],
3103
+ position: [0, -105],
3104
+ anchor: mod.UIAnchor.Center,
3105
+ bgFill: mod.UIBgFill.None,
3106
+ textColor: bfBlueColor,
3107
+ textAnchor: mod.UIAnchor.Center,
3108
+ textLabel: MakeMessage(mod.stringkeys.chosenvehicle),
3109
+ textSize: 20
3110
+ }
3111
+ ]
3112
+ }) as mod.UIWidget)
3113
+
3114
+ this.#Left_Button_Holder = new HoH_UIButtonHolder(
3115
+ this.#playerProfile,
3116
+ this.#LEFT_BUTTON,
3117
+ this.#LEFT_BUTTON_TEXT,
3118
+ this.#LEFT_BUTTON_SELECTED,
3119
+ function (this: HoH_UIButtonHolder) {
3120
+ this.playerProfile.VehicleShopUI?.VehicleSelectIncrease();
3121
+ },
3122
+ function (this: HoH_UIButtonHolder, focusIn: boolean) {
3123
+ if (focusIn) {
3124
+ mod.SetUIWidgetBgFill(this.button_widget, mod.UIBgFill.Solid)
3125
+ mod.SetUITextColor(this.button_text_widget, mod.CreateVector(0.2, 0.2, 0.2))
3126
+ mod.SetUIWidgetBgColor(this.button_widget, mod.CreateVector(0.678, 0.753, 0.800))
3127
+ this.select_widget && mod.SetUIWidgetVisible(this.select_widget, true)
3128
+ } else {
3129
+ mod.SetUIWidgetBgFill(this.button_widget, mod.UIBgFill.Blur)
3130
+ mod.SetUITextColor(this.button_text_widget, mod.CreateVector(0.678, 0.753, 0.800))
3131
+ mod.SetUIWidgetBgColor(this.button_widget, mod.CreateVector(1, 1, 1))
3132
+ this.select_widget && mod.SetUIWidgetVisible(this.select_widget, false)
3133
+ }
3134
+ }
3135
+ );
3136
+
3137
+ this.#Right_Button_Holder = new HoH_UIButtonHolder(
3138
+ this.#playerProfile,
3139
+ this.#RIGHT_BUTTON,
3140
+ this.#RIGHT_BUTTON_TEXT,
3141
+ this.#RIGHT_BUTTON_SELECTED,
3142
+ function (this: HoH_UIButtonHolder) {
3143
+ this.playerProfile.VehicleShopUI?.VehicleSelectDecrease()
3144
+
3145
+ },
3146
+ function (this: HoH_UIButtonHolder, focusIn: boolean) {
3147
+ if (focusIn) {
3148
+ mod.SetUIWidgetBgFill(this.button_widget, mod.UIBgFill.Solid)
3149
+ mod.SetUITextColor(this.button_text_widget, mod.CreateVector(0.2, 0.2, 0.2))
3150
+ mod.SetUIWidgetBgColor(this.button_widget, mod.CreateVector(0.678, 0.753, 0.800))
3151
+ this.select_widget && mod.SetUIWidgetVisible(this.select_widget, true)
3152
+ } else {
3153
+ mod.SetUIWidgetBgFill(this.button_widget, mod.UIBgFill.Blur)
3154
+ mod.SetUITextColor(this.button_text_widget, mod.CreateVector(0.678, 0.753, 0.800))
3155
+ mod.SetUIWidgetBgColor(this.button_widget, mod.CreateVector(1, 1, 1))
3156
+ this.select_widget && mod.SetUIWidgetVisible(this.select_widget, false)
3157
+ }
3158
+ }
3159
+ )
3160
+
3161
+ this.#Readyup_Button_Holder = new HoH_UIButtonHolder(
3162
+ this.#playerProfile,
3163
+ this.#READYUP_BUTTON,
3164
+ this.#READYUP_BUTTON_TEXT,
3165
+ this.#READYUP_BUTTON_SELECT,
3166
+ function (this: HoH_UIButtonHolder) {
3167
+ if (this.playerProfile.readyUp) {
3168
+ this.playerProfile.VehicleShopUI?.EnableSwitchButtons(false)
3169
+
3170
+ } else {
3171
+ this.playerProfile.VehicleShopUI?.EnableSwitchButtons(true)
3172
+
3173
+ }
3174
+ },
3175
+ function (this: HoH_UIButtonHolder, focusIn: boolean) {
3176
+
3177
+
3178
+ if (focusIn) {
3179
+ mod.SetUIWidgetBgFill(this.button_widget, mod.UIBgFill.Solid)
3180
+ mod.SetUITextColor(this.button_text_widget, mod.CreateVector(0.2, 0.2, 0.2))
3181
+ mod.SetUIWidgetBgColor(this.button_widget, mod.CreateVector(0.678, 0.753, 0.800))
3182
+ this.select_widget && mod.SetUIWidgetVisible(this.select_widget, true)
3183
+ } else {
3184
+ mod.SetUIWidgetBgFill(this.button_widget, mod.UIBgFill.Blur)
3185
+ mod.SetUITextColor(this.button_text_widget, mod.CreateVector(0.678, 0.753, 0.800))
3186
+ mod.SetUIWidgetBgColor(this.button_widget, mod.CreateVector(1, 1, 1))
3187
+ this.select_widget && mod.SetUIWidgetVisible(this.select_widget, false)
3188
+ }
3189
+ }
3190
+ )
3191
+
3192
+ this.#current_select_veh_Widget = mod.FindUIWidgetWithName(this.#CURRENT_SELECT_VEH_TEXT)
3193
+
3194
+
3195
+
3196
+ // CatchupMechanic Information
3197
+
3198
+
3199
+
3200
+ const PLAYER_CATCHUP_MECHANIC_TITLE = `players_catchup_mechanic_title_${this.#playerProfile.playerProfileId}`
3201
+ const PLAYER_CATCHUP_MECHANIC_DESC = `players_catchup_mechanic_desc_${this.#playerProfile.playerProfileId}`
3202
+
3203
+ this.#RootWidgets.push(ParseUI({
3204
+ type: "Container",
3205
+ size: [500, 200],
3206
+ position: [50, 350],
3207
+ anchor: mod.UIAnchor.TopRight,
3208
+ bgFill: mod.UIBgFill.Blur,
3209
+ bgColor: [0.2, 0.2, 0.3],
3210
+ bgAlpha: 0.6,
3211
+ playerId: this.#playerProfile.player,
3212
+ children: [{
3213
+ type: "Text",
3214
+ name: PLAYER_CATCHUP_MECHANIC_TITLE,
3215
+ size: [500, 200],
3216
+ position: [0, 0],
3217
+ anchor: mod.UIAnchor.Center,
3218
+ bgFill: mod.UIBgFill.None,
3219
+ textColor: bfBlueColor,
3220
+ textAnchor: mod.UIAnchor.TopLeft,
3221
+ textLabel: MakeMessage(mod.stringkeys.afterburnerExplainedTitle),
3222
+ textSize: 40
3223
+ },
3224
+ {
3225
+ type: "Text",
3226
+ name: PLAYER_CATCHUP_MECHANIC_DESC,
3227
+ size: [500, 200],
3228
+ position: [0, 20],
3229
+ anchor: mod.UIAnchor.Center,
3230
+ bgFill: mod.UIBgFill.None,
3231
+ textColor: bfBlueColor,
3232
+ textAnchor: mod.UIAnchor.CenterLeft,
3233
+ textLabel: MakeMessage(mod.stringkeys.afterburnerExplained),
3234
+ textSize: 30
3235
+ },
3236
+ ]
3237
+ }) as mod.UIWidget)
3238
+
3239
+ }
3240
+
3241
+ cameraTeleport() {
3242
+
3243
+ let startPos = playerVehicleSelectPositions[this.selectVehNb][0]
3244
+ let endPod = playerVehicleSelectPositions[this.selectVehNb][1]
3245
+
3246
+ const spawnPositions = generateSpawnLine(startPos, endPod, MapPlayers, "right")
3247
+ const spawnposition = spawnPositions[this.#playerProfile.playerRacerNumber].position
3248
+
3249
+ const lookrotation = this.lookAtYaw(spawnposition, VehiclePositions[this.selectVehNb])
3250
+ mod.Teleport(this.#playerProfile.player, mod.CreateVector(spawnposition.x, spawnposition.y, spawnposition.z), lookrotation)
3251
+
3252
+ }
3253
+
3254
+ lookAtYaw(from: Vector3, to: Vector3): number {
3255
+ const dx = to.x - from.x;
3256
+ const dz = to.z - from.z;
3257
+ return Math.atan2(dx, dz); // radians
3258
+ }
3259
+
3260
+
3261
+ refresh() {
3262
+
3263
+ this.#playerProfile.SetVehicle(currentRace.availableVehicles[this.selectVehNb])
3264
+ const vehicleEnumValue = currentRace.availableVehicles[this.selectVehNb];
3265
+
3266
+ this.#current_select_veh_Widget && mod.SetUITextLabel(this.#current_select_veh_Widget, MakeMessage(this.getVehName(vehicleEnumValue)))
3267
+ }
3268
+
3269
+ getVehName(veh: mod.VehicleList) {
3270
+ if (veh == mod.VehicleList.F22) {
3271
+ return mod.stringkeys.vehicle_1
3272
+ } else if (veh == mod.VehicleList.F16) {
3273
+ return mod.stringkeys.vehicle_2
3274
+ } else if (veh == mod.VehicleList.JAS39) {
3275
+ return mod.stringkeys.vehicle_3
3276
+ }
3277
+ return ""
3278
+ }
3279
+
3280
+ async open() {
3281
+ if (!this.#players_ready_count_widget)
3282
+ this.#create();
3283
+
3284
+ if (this.UIOpen == true) {
3285
+ return
3286
+ }
3287
+
3288
+ this.UIOpen = true;
3289
+
3290
+ this.#RootWidgets.forEach(Rootwidget => {
3291
+ mod.SetUIWidgetVisible(Rootwidget, true)
3292
+ });
3293
+
3294
+
3295
+ mod.EnableUIInputMode(true, this.#playerProfile.player)
3296
+
3297
+ this.refresh()
3298
+ this.UIUpdatePlayersReady();
3299
+ this.cameraTeleport();
3300
+ await mod.Wait(1)
3301
+ this.cameraTeleport();
3302
+ }
3303
+
3304
+ close() {
3305
+ if (this.UIOpen == false) {
3306
+ return
3307
+ }
3308
+
3309
+ this.UIOpen = false;
3310
+ this.#RootWidgets.forEach(Rootwidget => {
3311
+ mod.SetUIWidgetVisible(Rootwidget, false)
3312
+ });
3313
+
3314
+ mod.EnableUIInputMode(false, this.#playerProfile.player)
3315
+ }
3316
+
3317
+ OnButtonPressed(eventPlayer: mod.Player, eventUIWidget: mod.UIWidget, eventUIButtonEvent: mod.UIButtonEvent) {
3318
+ if (mod.Equals(eventUIButtonEvent, mod.UIButtonEvent.FocusIn)) {
3319
+ //console.log("FocusIn")
3320
+ this.buttonFocused(mod.GetUIWidgetName(eventUIWidget), true)
3321
+ } else if (mod.Equals(eventUIButtonEvent, mod.UIButtonEvent.FocusOut)) {
3322
+ //console.log("FocusOut ")
3323
+ this.buttonFocused(mod.GetUIWidgetName(eventUIWidget), false)
3324
+ } else if (mod.Equals(eventUIButtonEvent, mod.UIButtonEvent.ButtonDown)) {
3325
+ // console.log("ButtonDown")
3326
+ this.buttonPressed(mod.GetUIWidgetName(eventUIWidget))
3327
+ } else if (mod.Equals(eventUIButtonEvent, mod.UIButtonEvent.ButtonUp)) {
3328
+ // console.log("ButtonUp")
3329
+ } else if (mod.Equals(eventUIButtonEvent, mod.UIButtonEvent.HoverIn)) {
3330
+ // console.log("HoverIn")
3331
+ } else if (mod.Equals(eventUIButtonEvent, mod.UIButtonEvent.HoverOut)) {
3332
+ //console.log("HoverOut")
3333
+ }
3334
+
3335
+ }
3336
+
3337
+ buttonFocused(widgetName: string, focusIn: boolean = false) {
3338
+ if (widgetName == this.#LEFT_BUTTON && this.#Left_Button_Holder) {
3339
+ this.#Left_Button_Holder.focus(focusIn);
3340
+ } else if (widgetName == this.#RIGHT_BUTTON && this.#Right_Button_Holder) {
3341
+ this.#Right_Button_Holder.focus(focusIn);
3342
+ } else if (widgetName == this.#READYUP_BUTTON && this.#Readyup_Button_Holder) {
3343
+ this.#Readyup_Button_Holder.focus(focusIn);
3344
+ }
3345
+ }
3346
+
3347
+ buttonPressed(widgetName: string) {
3348
+
3349
+ if (widgetName == this.#LEFT_BUTTON) {
3350
+
3351
+ this.#Left_Button_Holder?.click()
3352
+
3353
+
3354
+ } else if (widgetName == this.#READYUP_BUTTON) {
3355
+
3356
+ if (!this.#playerProfile.readyUp) {
3357
+ this.#playerProfile.readyUp = true;
3358
+
3359
+ } else {
3360
+ this.#playerProfile.readyUp = false;
3361
+ }
3362
+
3363
+ this.#Readyup_Button_Holder?.click()
3364
+
3365
+ currentRace.playersInRace.forEach(pp => {
3366
+ pp.VehicleShopUI?.UIUpdatePlayersReady()
3367
+ });
3368
+
3369
+ } else if (widgetName == this.#RIGHT_BUTTON) {
3370
+
3371
+ this.#Right_Button_Holder?.click()
3372
+
3373
+ }
3374
+ }
3375
+ }
3376
+
3377
+ // === UI_VersionNB.ts ===
3378
+
3379
+
3380
+
3381
+ class HoH_Version {
3382
+
3383
+ VersionWidget: mod.UIWidget | undefined;
3384
+
3385
+ constructor(playerProfile: PlayerProfile) {
3386
+ const bfBlueColor = [0.678, 0.753, 0.800]
3387
+
3388
+ this.VersionWidget = ParseUI({
3389
+ type: "Container",
3390
+ size: [200, 25],
3391
+ position: [0, 0],
3392
+ anchor: mod.UIAnchor.BottomRight,
3393
+ bgFill: mod.UIBgFill.Blur,
3394
+ bgColor: [0.2, 0.2, 0.3],
3395
+ bgAlpha: 0.7,
3396
+ playerId: playerProfile.player,
3397
+ children: [{
3398
+ type: "Text",
3399
+ name: "game_version_" + playerProfile.playerProfileId,
3400
+ size: [200, 25],
3401
+ position: [0, 0],
3402
+ anchor: mod.UIAnchor.Center,
3403
+ bgFill: mod.UIBgFill.None,
3404
+ textColor: bfBlueColor,
3405
+ textAnchor: mod.UIAnchor.Center,
3406
+ textLabel: MakeMessage(mod.stringkeys.modversion, VERSION[0], VERSION[1], VERSION[2]),
3407
+ textSize: 20
3408
+ }]
3409
+ })
3410
+ }
3411
+
3412
+ Close() {
3413
+ this.VersionWidget && mod.SetUIWidgetVisible(this.VersionWidget, false)
3414
+ }
3415
+
3416
+ Delete() {
3417
+ this.VersionWidget && mod.DeleteUIWidget(this.VersionWidget)
3418
+ }
3419
+
3420
+ }
3421
+