@hytopia.com/examples 1.0.48 → 1.0.49

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.
@@ -5,6 +5,7 @@ import {
5
5
  Entity,
6
6
  ModelEntityOptions,
7
7
  QuaternionLike,
8
+ RigidBodyType,
8
9
  SceneUI,
9
10
  Vector3Like,
10
11
  World,
@@ -24,6 +25,7 @@ export default class ChestEntity extends Entity {
24
25
  modelScale: 1,
25
26
  name: 'Item Chest',
26
27
  rigidBodyOptions: {
28
+ type: RigidBodyType.DYNAMIC,
27
29
  additionalMass: 10000,
28
30
  enabledPositions: { x: false, y: true, z: false },
29
31
  enabledRotations: { x: false, y: false, z: false },
@@ -83,6 +85,10 @@ export default class ChestEntity extends Entity {
83
85
  this._labelSceneUI.load(world);
84
86
  }
85
87
 
88
+ public get isOpened(): boolean {
89
+ return this._opened;
90
+ }
91
+
86
92
  private _createLabelUI(): SceneUI {
87
93
  return new SceneUI({
88
94
  attachedToEntity: this,
@@ -26,6 +26,7 @@ import {
26
26
  import GamePlayerEntity from './GamePlayerEntity';
27
27
  import ChestEntity from './ChestEntity';
28
28
  import ItemFactory from './ItemFactory';
29
+ import BotPlayerEntity from './BotPlayerEntity';
29
30
 
30
31
  export default class GameManager {
31
32
  public static readonly instance = new GameManager();
@@ -54,6 +55,7 @@ export default class GameManager {
54
55
  this.world = world;
55
56
  this._spawnBedrock(world);
56
57
  this._waitForPlayersToStart();
58
+ BotPlayerEntity.setWorldActive(world, false);
57
59
  }
58
60
 
59
61
  /**
@@ -67,6 +69,7 @@ export default class GameManager {
67
69
 
68
70
  // Set game as active
69
71
  this._gameActive = true;
72
+ BotPlayerEntity.setWorldActive(this.world, true);
70
73
  this._gameStartAt = Date.now();
71
74
 
72
75
  // Spawn initial game elements
@@ -86,6 +89,7 @@ export default class GameManager {
86
89
 
87
90
  // Sync UI for all players
88
91
  this._syncAllPlayersUI();
92
+ this.onPlayerPopulationChanged();
89
93
  }
90
94
 
91
95
  /**
@@ -95,10 +99,14 @@ export default class GameManager {
95
99
  if (!this.world || !this._gameActive) return;
96
100
 
97
101
  this._gameActive = false;
102
+ BotPlayerEntity.setWorldActive(this.world, false);
98
103
  this.world.chatManager.sendBroadcastMessage('Game over! Starting the next round in 10 seconds...', 'FF0000');
99
104
 
100
105
  this._identifyWinningPlayer();
101
106
 
107
+ BotPlayerEntity.despawnAll(this.world);
108
+ this.refreshPlayerCount();
109
+
102
110
  // Clear any existing restart timer
103
111
  if (this._restartTimer) {
104
112
  clearTimeout(this._restartTimer);
@@ -129,6 +137,8 @@ export default class GameManager {
129
137
 
130
138
  // Load player's data
131
139
  playerEntity.loadPersistedData();
140
+
141
+ this.onPlayerPopulationChanged();
132
142
  }
133
143
 
134
144
  /**
@@ -204,6 +214,8 @@ export default class GameManager {
204
214
  private _cleanup() {
205
215
  if (!this.world) return;
206
216
 
217
+ BotPlayerEntity.despawnAll(this.world);
218
+
207
219
  // Reset map to initial state
208
220
  this.world.loadMap(worldMap);
209
221
  this._spawnBedrock(this.world);
@@ -246,6 +258,25 @@ export default class GameManager {
246
258
 
247
259
  // Reset leaderboard
248
260
  this.resetLeaderboard();
261
+
262
+ this.onPlayerPopulationChanged();
263
+ }
264
+
265
+ public refreshPlayerCount(): void {
266
+ if (!this.world) return;
267
+
268
+ this.playerCount = this.world.entityManager.getAllPlayerEntities().length;
269
+ }
270
+
271
+ public onPlayerPopulationChanged(): void {
272
+ this._syncBots();
273
+ this.refreshPlayerCount();
274
+ }
275
+
276
+ private _syncBots(): void {
277
+ if (!this.world) return;
278
+
279
+ BotPlayerEntity.ensureForWorld(this.world);
249
280
  }
250
281
 
251
282
  public _identifyWinningPlayer() {
@@ -422,12 +453,21 @@ export default class GameManager {
422
453
  private _waitForPlayersToStart() {
423
454
  if (!this.world) return;
424
455
 
425
- const connectedPlayers = GameServer.instance.playerManager.getConnectedPlayersByWorld(this.world).length;
456
+ const connectedPlayers = this._getHumanPlayerCount();
426
457
 
427
- if (connectedPlayers >= MINIMUM_PLAYERS_TO_START) {
458
+ if (connectedPlayers >= 1) {
428
459
  this.startGame();
429
460
  } else {
430
461
  setTimeout(() => this._waitForPlayersToStart(), 1000);
431
462
  }
432
463
  }
464
+
465
+ private _getHumanPlayerCount(): number {
466
+ if (!this.world) return 0;
467
+
468
+ return this.world.entityManager
469
+ .getAllPlayerEntities()
470
+ .filter(entity => !(entity instanceof BotPlayerEntity))
471
+ .length;
472
+ }
433
473
  }
@@ -79,6 +79,16 @@ export default class GamePlayerEntity extends DefaultPlayerEntity {
79
79
 
80
80
  public get isDead(): boolean { return this._dead; }
81
81
 
82
+ /** The currently active inventory item, if any. */
83
+ public get activeInventoryItem(): ItemEntity | undefined {
84
+ return this._inventory[this._inventoryActiveSlotIndex];
85
+ }
86
+
87
+ /** A snapshot of the inventory items. */
88
+ public get inventoryItems(): ReadonlyArray<ItemEntity | undefined> {
89
+ return this._inventory.slice();
90
+ }
91
+
82
92
  public constructor(player: Player) {
83
93
  super({
84
94
  player,
@@ -88,7 +98,7 @@ export default class GamePlayerEntity extends DefaultPlayerEntity {
88
98
  });
89
99
 
90
100
  this._setupPlayerController();
91
- this._setupPlayerUI();
101
+ this.setupPlayerUI();
92
102
  this._setupPlayerCamera();
93
103
  this._setupPlayerHeadshotCollider();
94
104
 
@@ -396,7 +406,7 @@ export default class GamePlayerEntity extends DefaultPlayerEntity {
396
406
  pickaxe.pickup(this);
397
407
  }
398
408
 
399
- private _setupPlayerUI(): void {
409
+ public setupPlayerUI(): void {
400
410
  this.nametagSceneUI.setViewDistance(8); // lessen view distance so you only see player names when close
401
411
  this.player.ui.load('ui/index.html');
402
412
 
@@ -185,6 +185,35 @@ export default abstract class GunEntity extends ItemEntity {
185
185
  }
186
186
  }
187
187
 
188
+ /** The amount of ammo currently in the clip. */
189
+ public getClipAmmo(): number {
190
+ return this.ammo;
191
+ }
192
+
193
+ /** The remaining reserve ammo for this gun. */
194
+ public getReserveAmmo(): number {
195
+ return this.totalAmmo;
196
+ }
197
+
198
+ /** Whether the gun has ammo available in the clip or reserves. */
199
+ public hasUsableAmmo(): boolean {
200
+ return this.ammo > 0 || this.totalAmmo > 0;
201
+ }
202
+
203
+ public getReloadTimeMs(): number {
204
+ return this.reloadTimeMs;
205
+ }
206
+
207
+ /** The effective range (in blocks) of the gun. */
208
+ public getEffectiveRange(): number {
209
+ return this.range;
210
+ }
211
+
212
+ /** Whether the gun is currently reloading. */
213
+ public isReloading(): boolean {
214
+ return this._reloading;
215
+ }
216
+
188
217
  private _createMuzzleFlash(): void {
189
218
  if (!this.isSpawned || !this.world) return;
190
219
 
@@ -6,6 +6,7 @@ import {
6
6
  import GameManager from './classes/GameManager';
7
7
 
8
8
  import worldMap from './assets/map.json' with { type: 'json' } ;
9
+ import GamePlayerEntity from './classes/GamePlayerEntity';
9
10
 
10
11
  startServer(world => {
11
12
  // Load the game map
@@ -20,7 +21,6 @@ startServer(world => {
20
21
  // Handle player joining the game
21
22
  world.on(PlayerEvent.JOINED_WORLD, ({ player }) => {
22
23
  GameManager.instance.spawnPlayerEntity(player);
23
- GameManager.instance.playerCount++;
24
24
  });
25
25
 
26
26
  // Handle player leaving the game
@@ -30,7 +30,15 @@ startServer(world => {
30
30
  .getPlayerEntitiesByPlayer(player)
31
31
  .forEach(entity => entity.despawn());
32
32
 
33
- GameManager.instance.playerCount--;
33
+ GameManager.instance.onPlayerPopulationChanged();
34
+ });
35
+
36
+ world.on(PlayerEvent.RECONNECTED_WORLD, ({ player }) => {
37
+ world.entityManager.getPlayerEntitiesByPlayer(player).forEach(entity => {
38
+ if (entity instanceof GamePlayerEntity) {
39
+ entity.setupPlayerUI();
40
+ }
41
+ });
34
42
  });
35
43
  });
36
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hytopia.com/examples",
3
- "version": "1.0.48",
3
+ "version": "1.0.49",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -1 +0,0 @@
1
- {"__version":31,"health":92,"currentRegionId":"hearthwilds","currentRegionSpawnFacingAngle":90,"currentRegionSpawnPoint":{"x":249,"y":22,"z":-89},"skillExperience":[],"backpack":{"items":[]},"hotbar":{"items":[{"position":0,"itemId":"toy_sword"}]},"questLog":{"quests":[]},"storage":{"items":[]},"wearables":{"items":[]}}
@@ -1 +0,0 @@
1
- {"__version":11,"health":100,"currentRegionId":"stalkhaven","currentRegionSpawnFacingAngle":90,"currentRegionSpawnPoint":{"x":32,"y":2,"z":1},"skillExperience":[],"backpack":{"items":[]},"hotbar":{"items":[{"position":0,"itemId":"toy_sword"}]},"questLog":{"quests":[]},"storage":{"items":[]},"wearables":{"items":[]}}
@@ -1 +0,0 @@
1
- {"__version":11,"health":100,"currentRegionId":"stalkhaven","currentRegionSpawnFacingAngle":90,"currentRegionSpawnPoint":{"x":32,"y":2,"z":1},"skillExperience":[],"backpack":{"items":[]},"hotbar":{"items":[{"position":0,"itemId":"toy_sword"}]},"questLog":{"quests":[]},"storage":{"items":[]},"wearables":{"items":[]}}
@@ -1 +0,0 @@
1
- {"__version":11,"health":100,"currentRegionId":"stalkhaven","currentRegionSpawnFacingAngle":90,"currentRegionSpawnPoint":{"x":32,"y":2,"z":1},"skillExperience":[],"backpack":{"items":[]},"hotbar":{"items":[{"position":0,"itemId":"toy_sword"}]},"questLog":{"quests":[]},"storage":{"items":[]},"wearables":{"items":[]}}
@@ -1 +0,0 @@
1
- {"__version":11,"health":100,"currentRegionId":"stalkhaven","currentRegionSpawnFacingAngle":90,"currentRegionSpawnPoint":{"x":32,"y":2,"z":1},"skillExperience":[],"backpack":{"items":[]},"hotbar":{"items":[{"position":0,"itemId":"toy_sword"}]},"questLog":{"quests":[]},"storage":{"items":[]},"wearables":{"items":[]}}
@@ -1 +0,0 @@
1
- {"__version":11,"health":100,"currentRegionId":"stalkhaven","currentRegionSpawnFacingAngle":90,"currentRegionSpawnPoint":{"x":32,"y":2,"z":1},"skillExperience":[],"backpack":{"items":[]},"hotbar":{"items":[{"position":0,"itemId":"toy_sword"}]},"questLog":{"quests":[]},"storage":{"items":[]},"wearables":{"items":[]}}
@@ -1 +0,0 @@
1
- {"__version":11,"health":100,"currentRegionId":"stalkhaven","currentRegionSpawnFacingAngle":90,"currentRegionSpawnPoint":{"x":32,"y":2,"z":1},"skillExperience":[],"backpack":{"items":[]},"hotbar":{"items":[{"position":0,"itemId":"toy_sword"}]},"questLog":{"quests":[]},"storage":{"items":[]},"wearables":{"items":[]}}