@hytopia.com/examples 1.0.48 → 1.0.50

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,
@@ -18,7 +18,6 @@ import {
18
18
  ITEM_SPAWNS,
19
19
  ITEM_SPAWNS_AT_START,
20
20
  ITEM_SPAWN_ITEMS,
21
- MINIMUM_PLAYERS_TO_START,
22
21
  SPAWN_REGION_AABB,
23
22
  RANK_WIN_EXP,
24
23
  } from '../gameConfig';
@@ -26,6 +25,7 @@ import {
26
25
  import GamePlayerEntity from './GamePlayerEntity';
27
26
  import ChestEntity from './ChestEntity';
28
27
  import ItemFactory from './ItemFactory';
28
+ import BotPlayerEntity from './BotPlayerEntity';
29
29
 
30
30
  export default class GameManager {
31
31
  public static readonly instance = new GameManager();
@@ -54,6 +54,7 @@ export default class GameManager {
54
54
  this.world = world;
55
55
  this._spawnBedrock(world);
56
56
  this._waitForPlayersToStart();
57
+ BotPlayerEntity.setWorldActive(world, false);
57
58
  }
58
59
 
59
60
  /**
@@ -67,6 +68,7 @@ export default class GameManager {
67
68
 
68
69
  // Set game as active
69
70
  this._gameActive = true;
71
+ BotPlayerEntity.setWorldActive(this.world, true);
70
72
  this._gameStartAt = Date.now();
71
73
 
72
74
  // Spawn initial game elements
@@ -86,6 +88,7 @@ export default class GameManager {
86
88
 
87
89
  // Sync UI for all players
88
90
  this._syncAllPlayersUI();
91
+ this.onPlayerPopulationChanged();
89
92
  }
90
93
 
91
94
  /**
@@ -95,9 +98,11 @@ export default class GameManager {
95
98
  if (!this.world || !this._gameActive) return;
96
99
 
97
100
  this._gameActive = false;
101
+ BotPlayerEntity.setWorldActive(this.world, false);
98
102
  this.world.chatManager.sendBroadcastMessage('Game over! Starting the next round in 10 seconds...', 'FF0000');
99
103
 
100
104
  this._identifyWinningPlayer();
105
+ this.refreshPlayerCount();
101
106
 
102
107
  // Clear any existing restart timer
103
108
  if (this._restartTimer) {
@@ -129,6 +134,8 @@ export default class GameManager {
129
134
 
130
135
  // Load player's data
131
136
  playerEntity.loadPersistedData();
137
+
138
+ this.onPlayerPopulationChanged();
132
139
  }
133
140
 
134
141
  /**
@@ -246,6 +253,25 @@ export default class GameManager {
246
253
 
247
254
  // Reset leaderboard
248
255
  this.resetLeaderboard();
256
+
257
+ this.onPlayerPopulationChanged();
258
+ }
259
+
260
+ public refreshPlayerCount(): void {
261
+ if (!this.world) return;
262
+
263
+ this.playerCount = this.world.entityManager.getAllPlayerEntities().length;
264
+ }
265
+
266
+ public onPlayerPopulationChanged(): void {
267
+ this._syncBots();
268
+ this.refreshPlayerCount();
269
+ }
270
+
271
+ private _syncBots(): void {
272
+ if (!this.world) return;
273
+
274
+ BotPlayerEntity.ensureForWorld(this.world);
249
275
  }
250
276
 
251
277
  public _identifyWinningPlayer() {
@@ -422,12 +448,21 @@ export default class GameManager {
422
448
  private _waitForPlayersToStart() {
423
449
  if (!this.world) return;
424
450
 
425
- const connectedPlayers = GameServer.instance.playerManager.getConnectedPlayersByWorld(this.world).length;
451
+ const connectedPlayers = this._getHumanPlayerCount();
426
452
 
427
- if (connectedPlayers >= MINIMUM_PLAYERS_TO_START) {
453
+ if (connectedPlayers >= 1) {
428
454
  this.startGame();
429
455
  } else {
430
456
  setTimeout(() => this._waitForPlayersToStart(), 1000);
431
457
  }
432
458
  }
459
+
460
+ private _getHumanPlayerCount(): number {
461
+ if (!this.world) return 0;
462
+
463
+ return this.world.entityManager
464
+ .getAllPlayerEntities()
465
+ .filter(entity => !(entity instanceof BotPlayerEntity))
466
+ .length;
467
+ }
433
468
  }
@@ -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.50",
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":[]}}