hytopia 0.3.8 → 0.3.10

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.
@@ -457,7 +457,7 @@ Called when the controlled entity is spawned. In PlayerEntityController, this fu
457
457
 
458
458
  </td><td>
459
459
 
460
- Ticks the player movement for the entity controller, overriding the default implementation.
460
+ Ticks the player movement for the entity controller, overriding the default implementation. If the entity to tick is a child entity, only the event will be emitted but the default movement logic will not be applied.
461
461
 
462
462
 
463
463
  </td></tr>
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## PlayerEntityController.tickWithPlayerInput() method
6
6
 
7
- Ticks the player movement for the entity controller, overriding the default implementation.
7
+ Ticks the player movement for the entity controller, overriding the default implementation. If the entity to tick is a child entity, only the event will be emitted but the default movement logic will not be applied.
8
8
 
9
9
  **Signature:**
10
10
 
@@ -9,7 +9,7 @@ Creates and adds a child collider to the rigid body for the simulation it belong
9
9
  **Signature:**
10
10
 
11
11
  ```typescript
12
- createAndAddChildCollider(colliderOptions: ColliderOptions): Collider;
12
+ createAndAddChildCollider(colliderOptions: ColliderOptions): Collider | null;
13
13
  ```
14
14
 
15
15
  ## Parameters
@@ -49,9 +49,9 @@ The options for the child collider to add.
49
49
  </tbody></table>
50
50
  **Returns:**
51
51
 
52
- [Collider](./server.collider.md)
52
+ [Collider](./server.collider.md) \| null
53
53
 
54
- The child collider that was added to the rigid body.
54
+ The child collider that was added to the rigid body, or null if failed.
55
55
 
56
56
  ## Remarks
57
57
 
@@ -25,7 +25,7 @@
25
25
 
26
26
  <!-- Leaderboard -->
27
27
  <div class="leaderboard">
28
- <div class="leaderboard-title">HyGrounds Deathmatch</div>
28
+ <div class="leaderboard-title">Deathmatch</div>
29
29
  <div class="leaderboard-timer">0:00</div>
30
30
  <div class="leaderboard-players-count">Players: 0</div>
31
31
  <div class="leaderboard-header">
@@ -61,6 +61,26 @@
61
61
  </div>
62
62
  </div>
63
63
 
64
+ <div class="mobile-buttons-container">
65
+ <div id="mobile-reload-button" class="mobile-button">
66
+ <img src="{{CDN_ASSETS_URL}}/icons/mobile-reload.png" />
67
+ </div>
68
+
69
+ <div id="mobile-interact-button" class="mobile-button">E</div>
70
+
71
+ <div id="mobile-jump-button" class="mobile-button">
72
+ <img src="{{CDN_ASSETS_URL}}/icons/mobile-jump.png" />
73
+ </div>
74
+
75
+ <div id="mobile-attack-button" class="mobile-button">
76
+ <img src="{{CDN_ASSETS_URL}}/icons/mobile-shoot.png" />
77
+ </div>
78
+
79
+ <div id="mobile-place-block-button" class="mobile-button">
80
+ <img src="{{CDN_ASSETS_URL}}/icons/mobile-place-block.png" />
81
+ </div>
82
+ </div>
83
+
64
84
  <div class="materials-counter">
65
85
  <img src="{{CDN_ASSETS_URL}}/icons/block.png" alt="Materials Icon" class="materials-icon">
66
86
  <span class="materials-amount">0</span>
@@ -504,6 +524,73 @@
504
524
  timerInterval = setInterval(updateTimer, 1000);
505
525
  }
506
526
  });
527
+
528
+ // Mobile UI Controls
529
+ const inventorySlots = document.querySelectorAll('.inventory-slot');
530
+
531
+ inventorySlots.forEach((slot, index) => {
532
+ slot.addEventListener('click', () => {
533
+ hytopia.sendData({
534
+ type: 'inventory-select',
535
+ index: index,
536
+ })
537
+ });
538
+ });
539
+
540
+ const mobileInteractButton = document.getElementById('mobile-interact-button');
541
+ mobileInteractButton.addEventListener('touchstart', () => {
542
+ mobileInteractButton.classList.add('active');
543
+ hytopia.pressInput('e', true);
544
+ });
545
+
546
+ mobileInteractButton.addEventListener('touchend', () => {
547
+ mobileInteractButton.classList.remove('active');
548
+ hytopia.pressInput('e', false);
549
+ });
550
+
551
+ const mobileReloadButton = document.getElementById('mobile-reload-button');
552
+ mobileReloadButton.addEventListener('touchstart', () => {
553
+ mobileReloadButton.classList.add('active');
554
+ hytopia.pressInput('r', true);
555
+ });
556
+
557
+ mobileReloadButton.addEventListener('touchend', () => {
558
+ mobileReloadButton.classList.remove('active');
559
+ hytopia.pressInput('r', false);
560
+ });
561
+
562
+ const mobileJumpButton = document.getElementById('mobile-jump-button');
563
+ mobileJumpButton.addEventListener('touchstart', () => {
564
+ mobileJumpButton.classList.add('active');
565
+ hytopia.pressInput(' ', true);
566
+ });
567
+
568
+ mobileJumpButton.addEventListener('touchend', () => {
569
+ mobileJumpButton.classList.remove('active');
570
+ hytopia.pressInput(' ', false);
571
+ });
572
+
573
+ const mobileAttackButton = document.getElementById('mobile-attack-button');
574
+ mobileAttackButton.addEventListener('touchstart', () => {
575
+ mobileAttackButton.classList.add('active');
576
+ hytopia.pressInput('ml', true);
577
+ }, { passive: true });
578
+
579
+ mobileAttackButton.addEventListener('touchend', () => {
580
+ mobileAttackButton.classList.remove('active');
581
+ hytopia.pressInput('ml', false);
582
+ }, { passive: true });
583
+
584
+ const mobilePlaceBlockButton = document.getElementById('mobile-place-block-button');
585
+ mobilePlaceBlockButton.addEventListener('touchstart', () => {
586
+ mobilePlaceBlockButton.classList.add('active');
587
+ hytopia.pressInput('mr', true);
588
+ });
589
+
590
+ mobilePlaceBlockButton.addEventListener('touchend', () => {
591
+ mobilePlaceBlockButton.classList.remove('active');
592
+ hytopia.pressInput('mr', false);
593
+ });
507
594
  </script>
508
595
 
509
596
  <!-- UI Styles -->
@@ -737,7 +824,7 @@
737
824
  right: 20px;
738
825
  display: flex;
739
826
  gap: 10px;
740
- z-index: 99;
827
+ z-index: 999;
741
828
  }
742
829
 
743
830
  .inventory-slot {
@@ -1028,7 +1115,9 @@
1028
1115
  background: rgba(0, 0, 0, 0.7);
1029
1116
  border-radius: 5px;
1030
1117
  padding: 10px;
1031
- width: 200px;
1118
+ width: 15%;
1119
+ max-width: 200px;
1120
+ max-height: 40%;
1032
1121
  font-family: 'Arial', sans-serif;
1033
1122
  color: white;
1034
1123
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
@@ -1119,4 +1208,131 @@
1119
1208
  max-width: 160px;
1120
1209
  text-overflow: ellipsis;
1121
1210
  }
1211
+
1212
+ .mobile-buttons-container {
1213
+ display: none;
1214
+ }
1215
+
1216
+ /* Mobile UI */
1217
+ body.mobile .hud {
1218
+ left: 50%;
1219
+ transform: translateX(-50%);
1220
+ bottom: 35px;
1221
+ right: auto;
1222
+ }
1223
+
1224
+ body.mobile .inventory-hud {
1225
+ bottom: 30px;
1226
+ }
1227
+
1228
+ body.mobile .inventory-slot {
1229
+ width: 45px;
1230
+ height: 45px;
1231
+ }
1232
+
1233
+ body.mobile .slot-number {
1234
+ font-size: 12px;
1235
+ top: -16px;
1236
+ }
1237
+
1238
+ body.mobile .slot-name {
1239
+ font-size: 8px;
1240
+ bottom: -14px;
1241
+ }
1242
+
1243
+ body.mobile .slot-quantity {
1244
+ font-size: 10px;
1245
+ }
1246
+
1247
+ body.mobile .shield-bar,
1248
+ body.mobile .health-bar {
1249
+ width: 120px;
1250
+ height: 16px;
1251
+ }
1252
+
1253
+ body.mobile .shield-text,
1254
+ body.mobile .health-text {
1255
+ font-size: 0.7em;
1256
+ }
1257
+
1258
+ body.mobile .shield-icon,
1259
+ body.mobile .health-icon {
1260
+ width: 10px;
1261
+ height: 10px;
1262
+ }
1263
+
1264
+ body.mobile .leaderboard-title {
1265
+ font-size: 14px;
1266
+ }
1267
+
1268
+ body.mobile .leaderboard-timer {
1269
+ font-size: 12px;
1270
+ }
1271
+
1272
+ body.mobile .leaderboard-players-count {
1273
+ font-size: 12px;
1274
+ }
1275
+
1276
+ body.mobile .leaderboard-header {
1277
+ font-size: 10px;
1278
+ }
1279
+
1280
+ body.mobile .leaderboard-player {
1281
+ font-size: 10px;
1282
+ }
1283
+
1284
+ body.mobile .materials-counter {
1285
+ right: 20px;
1286
+ bottom: 115px;
1287
+ font-size: 12px;
1288
+ }
1289
+
1290
+ body.mobile .floating-number {
1291
+ font-size: 14px;
1292
+ }
1293
+
1294
+ body.mobile div > canvas { /* hide three debugger panels */
1295
+ display: none !important;
1296
+ }
1297
+
1298
+ body.mobile .mobile-buttons-container {
1299
+ display: flex;
1300
+ gap: 10px;
1301
+ position: fixed;
1302
+ bottom: 113px;
1303
+ right: 118px;
1304
+ user-select: none;
1305
+ }
1306
+
1307
+ body.mobile .mobile-button {
1308
+ background-color: rgba(0, 0, 0, 0.5);
1309
+ border-radius: 50%;
1310
+ align-items: center;
1311
+ justify-content: center;
1312
+ display: flex;
1313
+ width: 45px;
1314
+ height: 45px;
1315
+ -webkit-tap-highlight-color: transparent;
1316
+ touch-action: manipulation;
1317
+ user-select: none;
1318
+ transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
1319
+ will-change: transform, background-color;
1320
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
1321
+ font-family: 'Inter', sans-serif;
1322
+ font-size: 14px;
1323
+ font-weight: bold;
1324
+ color: rgba(255, 255, 255, 0.8);
1325
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
1326
+ }
1327
+
1328
+ body.mobile .mobile-button.active {
1329
+ transform: scale(0.92);
1330
+ background-color: rgba(0, 0, 0, 0.75);
1331
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
1332
+ }
1333
+
1334
+ body.mobile .mobile-button img {
1335
+ width: 25px;
1336
+ height: 25px;
1337
+ }
1122
1338
  </style>
@@ -9,6 +9,7 @@ import {
9
9
  QuaternionLike,
10
10
  World,
11
11
  PlayerEntityController,
12
+ PlayerUIEvent,
12
13
  } from 'hytopia';
13
14
 
14
15
  import ChestEntity from './ChestEntity';
@@ -337,6 +338,15 @@ export default class GamePlayerEntity extends PlayerEntity {
337
338
  private _setupPlayerUI(): void {
338
339
  this.nametagSceneUI.setViewDistance(8); // lessen view distance so you only see player names when close
339
340
  this.player.ui.load('ui/index.html');
341
+
342
+ // Handle inventory selection from mobile UI
343
+ this.player.ui.on(PlayerUIEvent.DATA, (payload) => {
344
+ const { data } = payload;
345
+
346
+ if (data.type === 'inventory-select') {
347
+ this.setActiveInventorySlotIndex(data.index);
348
+ }
349
+ });
340
350
  }
341
351
 
342
352
  private _setupPlayerCamera(): void {
@@ -244,6 +244,10 @@ export default abstract class GunEntity extends ItemEntity {
244
244
  }
245
245
 
246
246
  public updateAmmoIndicatorUI(reloading: boolean = false): void {
247
+ if (!this.parent) {
248
+ return;
249
+ }
250
+
247
251
  const player = this.parent as GamePlayerEntity;
248
252
 
249
253
  player.player.ui.sendData(reloading ? {
@@ -90,6 +90,7 @@ export default class ItemEntity extends Entity {
90
90
  this.parent.dropActiveInventoryItem();
91
91
  setTimeout(() => {
92
92
  this.despawn();
93
+ this.stopDespawnTimer();
93
94
  }, 0);
94
95
  }
95
96
 
@@ -167,7 +168,9 @@ export default class ItemEntity extends Entity {
167
168
  if (this._despawnTimer) return;
168
169
 
169
170
  this._despawnTimer = setTimeout(() => {
170
- this.despawn();
171
+ if (this.isSpawned) {
172
+ this.despawn();
173
+ }
171
174
  }, ITEM_DESPAWN_TIME_MS);
172
175
  }
173
176
 
@@ -16,7 +16,7 @@ const DEFAULT_AK47_OPTIONS: GunEntityOptions = {
16
16
  scopeZoom: 2,
17
17
  modelUri: 'models/items/ak-47.glb',
18
18
  modelScale: 1.3,
19
- range: 70,
19
+ range: 80,
20
20
  reloadAudioUri: 'audio/sfx/rifle-reload.mp3',
21
21
  reloadTimeMs: 2200,
22
22
  shootAudioUri: 'audio/sfx/rifle-shoot.mp3',
@@ -15,7 +15,7 @@ const DEFAULT_AUTO_SHOTGUN_OPTIONS: GunEntityOptions = {
15
15
  totalAmmo: 30,
16
16
  modelUri: 'models/items/auto-shotgun.glb',
17
17
  modelScale: 1.2,
18
- range: 8,
18
+ range: 10,
19
19
  reloadAudioUri: 'audio/sfx/shotgun-reload.mp3',
20
20
  reloadTimeMs: 3500,
21
21
  shootAudioUri: 'audio/sfx/shotgun-shoot.mp3',
@@ -11,8 +11,8 @@ const DEFAULT_AUTO_SNIPER_OPTIONS: GunEntityOptions = {
11
11
  idleAnimation: 'idle_gun_both',
12
12
  mlAnimation: 'shoot_gun_both',
13
13
  name: 'Auto Sniper',
14
- maxAmmo: 1,
15
- totalAmmo: 12,
14
+ maxAmmo: 10,
15
+ totalAmmo: 20,
16
16
  scopeZoom: 5,
17
17
  modelUri: 'models/items/auto-sniper.glb',
18
18
  modelScale: 1.3,
@@ -16,7 +16,7 @@ const DEFAULT_LIGHT_MACHINE_GUN_OPTIONS: GunEntityOptions = {
16
16
  scopeZoom: 1.35,
17
17
  modelUri: 'models/items/light-machine-gun.glb',
18
18
  modelScale: 1.3,
19
- range: 50,
19
+ range: 60,
20
20
  reloadAudioUri: 'audio/sfx/machine-gun-reload.mp3',
21
21
  reloadTimeMs: 4200,
22
22
  shootAudioUri: 'audio/sfx/machine-gun-shoot.mp3',
@@ -16,7 +16,7 @@ const DEFAULT_SUBMACHINE_GUN_OPTIONS: GunEntityOptions = {
16
16
  scopeZoom: 1.35,
17
17
  modelUri: 'models/items/submachine-gun.glb',
18
18
  modelScale: 1.3,
19
- range: 35,
19
+ range: 40,
20
20
  reloadAudioUri: 'audio/sfx/rifle-reload.mp3',
21
21
  reloadTimeMs: 1500,
22
22
  shootAudioUri: 'audio/sfx/pistol-shoot.mp3',
@@ -60,11 +60,11 @@ export const CHEST_OPEN_DESPAWN_MS = 20 * 1000; // 20 seconds
60
60
  export const CHEST_DROP_ITEMS = [
61
61
  {
62
62
  itemId: 'ak47',
63
- pickWeight: 0.5,
63
+ pickWeight: 0.7,
64
64
  },
65
65
  {
66
66
  itemId: 'auto-shotgun',
67
- pickWeight: 0.5,
67
+ pickWeight: 0.6,
68
68
  },
69
69
  {
70
70
  itemId: 'auto-sniper',
@@ -76,43 +76,43 @@ export const CHEST_DROP_ITEMS = [
76
76
  },
77
77
  {
78
78
  itemId: 'gravity-potion',
79
- pickWeight: 0.4,
79
+ pickWeight: 0.3,
80
80
  },
81
81
  {
82
82
  itemId: 'light-machine-gun',
83
- pickWeight: 0.5,
83
+ pickWeight: 0.6,
84
84
  },
85
85
  {
86
86
  itemId: 'medpack',
87
- pickWeight: 1,
87
+ pickWeight: 1.2,
88
88
  },
89
89
  {
90
90
  itemId: 'mining-drill',
91
- pickWeight: 0.5,
91
+ pickWeight: 0.8,
92
92
  },
93
93
  {
94
94
  itemId: 'pistol',
95
- pickWeight: 1,
95
+ pickWeight: 1.5,
96
96
  },
97
97
  {
98
98
  itemId: 'revolver',
99
- pickWeight: 0.5,
99
+ pickWeight: 0.9,
100
100
  },
101
101
  {
102
102
  itemId: 'rocket-launcher',
103
- pickWeight: 0.4,
103
+ pickWeight: 0.3,
104
104
  },
105
105
  {
106
106
  itemId: 'shotgun',
107
- pickWeight: 0.5,
107
+ pickWeight: 0.8,
108
108
  },
109
109
  {
110
110
  itemId: 'shield-potion',
111
- pickWeight: 1,
111
+ pickWeight: 1.2,
112
112
  },
113
113
  {
114
114
  itemId: 'submachine-gun',
115
- pickWeight: 0.5,
115
+ pickWeight: 1.0,
116
116
  },
117
117
  ]
118
118
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hytopia",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "The HYTOPIA SDK makes it easy for developers to create massively multiplayer games using JavaScript or TypeScript.",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.api.json CHANGED
@@ -27166,7 +27166,7 @@
27166
27166
  {
27167
27167
  "kind": "Method",
27168
27168
  "canonicalReference": "server!PlayerEntityController#tickWithPlayerInput:member(1)",
27169
- "docComment": "/**\n * Ticks the player movement for the entity controller, overriding the default implementation.\n *\n * @param entity - The entity to tick.\n *\n * @param input - The current input state of the player.\n *\n * @param cameraOrientation - The current camera orientation state of the player.\n *\n * @param deltaTimeMs - The delta time in milliseconds since the last tick.\n */\n",
27169
+ "docComment": "/**\n * Ticks the player movement for the entity controller, overriding the default implementation. If the entity to tick is a child entity, only the event will be emitted but the default movement logic will not be applied.\n *\n * @param entity - The entity to tick.\n *\n * @param input - The current input state of the player.\n *\n * @param cameraOrientation - The current camera orientation state of the player.\n *\n * @param deltaTimeMs - The delta time in milliseconds since the last tick.\n */\n",
27170
27170
  "excerptTokens": [
27171
27171
  {
27172
27172
  "kind": "Content",
@@ -31498,7 +31498,7 @@
31498
31498
  {
31499
31499
  "kind": "Method",
31500
31500
  "canonicalReference": "server!RigidBody#createAndAddChildCollider:member(1)",
31501
- "docComment": "/**\n * Creates and adds a child collider to the rigid body for the simulation it belongs to.\n *\n * @remarks\n *\n * If the rigid body is not simulated, the collider will be added to the rigid body as a pending child collider and also simulated when the rigid body is simulated.\n *\n * @param colliderOptions - The options for the child collider to add.\n *\n * @returns The child collider that was added to the rigid body.\n */\n",
31501
+ "docComment": "/**\n * Creates and adds a child collider to the rigid body for the simulation it belongs to.\n *\n * @remarks\n *\n * If the rigid body is not simulated, the collider will be added to the rigid body as a pending child collider and also simulated when the rigid body is simulated.\n *\n * @param colliderOptions - The options for the child collider to add.\n *\n * @returns The child collider that was added to the rigid body, or null if failed.\n */\n",
31502
31502
  "excerptTokens": [
31503
31503
  {
31504
31504
  "kind": "Content",
@@ -31518,6 +31518,10 @@
31518
31518
  "text": "Collider",
31519
31519
  "canonicalReference": "server!Collider:class"
31520
31520
  },
31521
+ {
31522
+ "kind": "Content",
31523
+ "text": " | null"
31524
+ },
31521
31525
  {
31522
31526
  "kind": "Content",
31523
31527
  "text": ";"
@@ -31526,7 +31530,7 @@
31526
31530
  "isStatic": false,
31527
31531
  "returnTypeTokenRange": {
31528
31532
  "startIndex": 3,
31529
- "endIndex": 4
31533
+ "endIndex": 5
31530
31534
  },
31531
31535
  "releaseTag": "Public",
31532
31536
  "isProtected": false,
package/server.d.ts CHANGED
@@ -1084,6 +1084,8 @@ export declare class Collider extends EventRouter {
1084
1084
 
1085
1085
 
1086
1086
 
1087
+ private _requireUnsimulated;
1088
+ private _requireNotRemoved;
1087
1089
 
1088
1090
  }
1089
1091
 
@@ -3511,7 +3513,9 @@ export declare class PlayerEntityController extends BaseEntityController {
3511
3513
  spawn(entity: Entity): void;
3512
3514
  /**
3513
3515
  * Ticks the player movement for the entity controller,
3514
- * overriding the default implementation.
3516
+ * overriding the default implementation. If the entity to tick
3517
+ * is a child entity, only the event will be emitted but the default
3518
+ * movement logic will not be applied.
3515
3519
  *
3516
3520
  * @param entity - The entity to tick.
3517
3521
  * @param input - The current input state of the player.
@@ -4252,9 +4256,9 @@ export declare class RigidBody extends EventRouter {
4252
4256
  * and also simulated when the rigid body is simulated.
4253
4257
  *
4254
4258
  * @param colliderOptions - The options for the child collider to add.
4255
- * @returns The child collider that was added to the rigid body.
4259
+ * @returns The child collider that was added to the rigid body, or null if failed.
4256
4260
  */
4257
- createAndAddChildCollider(colliderOptions: ColliderOptions): Collider;
4261
+ createAndAddChildCollider(colliderOptions: ColliderOptions): Collider | null;
4258
4262
  /**
4259
4263
  * Creates and adds multiple child colliders to the rigid body for the simulation it belongs to.
4260
4264
  * @param colliderOptions - The options for the child colliders to add to the rigid body.