hytopia 0.1.72 → 0.1.73

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.
@@ -11,179 +11,46 @@ import {
11
11
  PlayerEntity,
12
12
  SceneUI,
13
13
  World,
14
+ CollisionGroup,
14
15
  } from 'hytopia';
15
16
 
17
+ import GAME_WALL_SHAPES from './wall-shapes';
18
+
16
19
  import worldMap from './assets/map.json';
17
20
 
21
+ // Game constants
18
22
  enum GameWallDirection {
19
23
  NORTH = 'NORTH',
20
24
  SOUTH = 'SOUTH',
21
- WEST = 'WEST',
25
+ WEST = 'WEST',
22
26
  EAST = 'EAST',
23
27
  }
24
28
 
25
- const GAME_LEVEL_DURATION_SECONDS = 10;
26
- const GAME_START_POSITION = { x: 1, y: 25, z: 1 };
27
- const GAME_START_DELAY_SECONDS = 3;
28
- const GAME_WALL_DESPAWN_DISTANCE = 18;
29
- const GAME_WALL_SPAWN_POSITIONS = {
30
- [GameWallDirection.NORTH]: { x: 1, y: 20, z: -10 }, // North (x width)
31
- [GameWallDirection.SOUTH]: { x: 1, y: 20, z: 12 }, // South (x width)
32
- [GameWallDirection.WEST]: { x: -10, y: 20, z: 1 }, // West (z width)
33
- [GameWallDirection.EAST]: { x: 12, y: 20, z: 1 }, // East (z width)
34
- }
35
- const GAME_WALL_VELOCITY_DIRECTIONS = {
36
- [GameWallDirection.NORTH]: { x: 0, y: 0, z: 1 },
37
- [GameWallDirection.SOUTH]: { x: 0, y: 0, z: -1 },
38
- [GameWallDirection.WEST]: { x: 1, y: 0, z: 0 },
39
- [GameWallDirection.EAST]: { x: -1, y: 0, z: 0 },
40
- }
41
- const GAME_WALL_SHAPES = [
42
- // lol shape
43
- [
44
- [0, 1, 0, 1, 1, 1, 0, 1],
45
- [0, 1, 0, 1, 0, 1, 0, 1],
46
- [0, 1, 0, 1, 1, 1, 0, 1],
47
- ],
48
- // stairs
49
- [
50
- [0, 0, 0, 0, 0, 0, 0, 1],
51
- [0, 0, 0, 0, 0, 1, 1, 1],
52
- [0, 0, 0, 1, 1, 1, 1, 1],
53
- [0, 1, 1, 1, 1, 1, 1, 1],
54
- [1, 1, 1, 1, 1, 1, 1, 1],
55
- ],
56
- // horizontal bar
57
- [
58
- [1, 1, 1, 1, 1, 1, 1, 1],
59
- [1, 1, 1, 1, 1, 1, 1, 1],
60
- ],
61
- // vertical bars
62
- [
63
- [1, 0, 1, 0, 1, 0, 1, 0],
64
- [1, 0, 1, 0, 1, 0, 1, 0],
65
- [1, 0, 1, 0, 1, 0, 1, 0],
66
- [1, 0, 1, 0, 1, 0, 1, 0],
67
- ],
68
- // lattice
69
- [
70
- [1, 0, 1, 0, 1, 0, 1, 0],
71
- [0, 1, 0, 1, 0, 1, 0, 1],
72
- [1, 0, 1, 0, 1, 0, 1, 0],
73
- [0, 1, 0, 1, 0, 1, 0, 1],
74
- [1, 0, 1, 0, 1, 0, 1, 0],
75
- [0, 0, 0, 1, 0, 1, 0, 0],
76
- [1, 0, 1, 0, 1, 0, 1, 0],
77
- ],
78
- // platforming hole
79
- [
80
- [1, 1, 1, 0, 0, 1, 1, 1],
81
- [1, 1, 1, 0, 0, 1, 1, 1],
82
- [1, 1, 1, 2, 2, 1, 1, 1],
83
- [1, 1, 2, 1, 1, 2, 1, 1],
84
- [1, 2, 1, 1, 1, 1, 2, 1],
85
- [2, 1, 1, 1, 1, 1, 1, 2],
86
- ],
87
- // 3d stairs
88
- [
89
- [1, 1, 1, 1, 1, 1, 1, 1],
90
- [2, 1, 2, 1, 1, 2, 1, 2],
91
- [2, 3, 2, 3, 3, 2, 3, 2],
92
- [3, 3, 3, 3, 3, 3, 3, 3],
93
- ],
94
- // 3d lattice
95
- [
96
- [0, 2, 0, 2, 0, 2, 0, 2],
97
- [1, 0, 1, 0, 1, 0, 1, 0],
98
- [0, 2, 0, 2, 0, 2, 0, 2],
99
- [1, 0, 2, 0, 2, 0, 1, 0],
100
- [1, 0, 1, 0, 1, 0, 1, 0],
101
- [0, 2, 0, 2, 0, 2, 0, 2],
102
- [1, 0, 3, 0, 3, 0, 1, 0],
103
- ],
104
- // full wall
105
- [
106
- [1, 1, 1, 1, 1, 1, 1, 1],
107
- [1, 1, 1, 1, 1, 1, 1, 1],
108
- [1, 1, 1, 1, 1, 1, 1, 1],
109
- [1, 1, 1, 1, 1, 1, 1, 1],
110
- ],
111
- // zig zag challenge
112
- [
113
- [1, 1, 1, 0, 0, 0, 0, 0],
114
- [0, 0, 1, 1, 1, 0, 0, 0],
115
- [0, 0, 0, 0, 1, 1, 1, 0],
116
- [0, 0, 0, 0, 0, 0, 1, 1],
117
- ],
118
- // triple jump
119
- [
120
- [0, 0, 1, 0, 0, 1, 0, 0],
121
- [0, 0, 1, 0, 0, 1, 0, 0],
122
- [1, 1, 1, 0, 0, 1, 1, 1],
123
- ],
124
- // layered tunnel
125
- [
126
- [1, 1, 1, 1, 1, 1, 1, 1],
127
- [1, 0, 0, 2, 2, 0, 0, 1],
128
- [1, 0, 0, 3, 3, 0, 0, 1],
129
- [1, 1, 1, 1, 1, 1, 1, 1],
130
- ],
131
- // diagonal dash
132
- [
133
- [1, 1, 0, 0, 0, 0, 0, 0],
134
- [0, 1, 1, 0, 0, 0, 0, 0],
135
- [0, 0, 1, 1, 0, 0, 0, 0],
136
- [0, 0, 0, 1, 1, 0, 0, 0],
137
- [0, 0, 0, 0, 1, 1, 0, 0],
138
- ],
139
- // wave rider
140
- [
141
- [1, 0, 0, 0, 0, 0, 0, 1],
142
- [1, 1, 0, 0, 0, 0, 1, 1],
143
- [0, 1, 1, 0, 0, 1, 1, 0],
144
- [0, 0, 1, 1, 1, 1, 0, 0],
145
- ],
146
- // forward momentum
147
- [
148
- [1, 1, 2, 2, 3, 3, 3, 3],
149
- [1, 1, 2, 2, 3, 3, 3, 3],
150
- [1, 1, 2, 2, 3, 3, 3, 3],
151
- ],
152
- // spiral staircase
153
- [
154
- [1, 1, 1, 1, 2, 2, 3, 3],
155
- [3, 3, 1, 1, 1, 2, 2, 2],
156
- [2, 3, 3, 1, 1, 1, 1, 2],
157
- [2, 2, 2, 3, 3, 1, 1, 1],
158
- ],
159
- // hopscotch
160
- [
161
- [0, 1, 0, 1, 0, 1, 0, 1],
162
- [0, 2, 0, 2, 0, 2, 0, 2],
163
- [1, 0, 1, 0, 1, 0, 1, 0],
164
- [2, 0, 2, 0, 2, 0, 2, 0],
165
- ],
166
- // snake path
167
- [
168
- [1, 1, 1, 1, 0, 0, 0, 0],
169
- [0, 0, 0, 1, 0, 0, 0, 0],
170
- [0, 0, 0, 2, 0, 0, 0, 0],
171
- [0, 0, 0, 3, 3, 3, 3, 3],
172
- ],
173
- // diamond gate
174
- [
175
- [1, 1, 1, 0, 0, 1, 1, 1],
176
- [1, 1, 0, 0, 0, 0, 1, 1],
177
- [1, 0, 2, 0, 0, 2, 0, 1],
178
- [0, 0, 0, 3, 3, 0, 0, 0],
179
- [1, 0, 2, 0, 0, 2, 0, 1],
180
- [1, 1, 0, 0, 0, 0, 1, 1],
181
- [1, 1, 1, 0, 0, 1, 1, 1],
182
- ],
183
- ];
184
-
185
- const JOIN_NPC_SPAWN_POSITION = { x: 1, y: 35, z: -7 };
186
- const PLAYER_SPAWN_POSITION = { x: 1, y: 35, z: 2 };
29
+ const GAME_CONFIG = {
30
+ LEVEL_DURATION: 10,
31
+ START_DELAY: 30,
32
+ WALL_COLLISION_GROUP: CollisionGroup.GROUP_1,
33
+ WALL_DESPAWN_DISTANCE: 18,
34
+ POSITIONS: {
35
+ START: { x: 1, y: 22, z: 1 },
36
+ JOIN_NPC: { x: 1, y: 35, z: -7 },
37
+ PLAYER_SPAWN: { x: 1, y: 35, z: 2 },
38
+ WALLS: {
39
+ [GameWallDirection.NORTH]: { x: 1, y: 20, z: -10 },
40
+ [GameWallDirection.SOUTH]: { x: 1, y: 20, z: 12 },
41
+ [GameWallDirection.WEST]: { x: -10, y: 20, z: 1 },
42
+ [GameWallDirection.EAST]: { x: 12, y: 20, z: 1 },
43
+ }
44
+ },
45
+ WALL_VELOCITIES: {
46
+ [GameWallDirection.NORTH]: { x: 0, y: 0, z: 1 },
47
+ [GameWallDirection.SOUTH]: { x: 0, y: 0, z: -1 },
48
+ [GameWallDirection.WEST]: { x: 1, y: 0, z: 0 },
49
+ [GameWallDirection.EAST]: { x: -1, y: 0, z: 0 },
50
+ }
51
+ };
52
+
53
+ // Game state
187
54
  const QUEUED_PLAYER_ENTITIES = new Set<PlayerEntity>();
188
55
  const GAME_PLAYER_ENTITIES = new Set<PlayerEntity>();
189
56
 
@@ -194,13 +61,14 @@ let gameInterval: NodeJS.Timer;
194
61
  let gameStartTime: number | null = null;
195
62
  let gameUiState: object = {};
196
63
 
197
- let gameActiveAudio = new Audio({
64
+ // Audio
65
+ const gameActiveAudio = new Audio({
198
66
  uri: 'audio/music.mp3',
199
67
  loop: true,
200
68
  volume: 0.2,
201
69
  });
202
70
 
203
- let gameInactiveAudio = new Audio({
71
+ const gameInactiveAudio = new Audio({
204
72
  uri: 'audio/music/overworld.mp3',
205
73
  loop: true,
206
74
  volume: 0.2,
@@ -212,7 +80,6 @@ startServer(world => {
212
80
  world.onPlayerLeave = player => onPlayerLeave(world, player);
213
81
 
214
82
  spawnJoinNpc(world);
215
-
216
83
  gameInactiveAudio.play(world);
217
84
  });
218
85
 
@@ -224,27 +91,33 @@ function onPlayerJoin(world: World, player: Player) {
224
91
  player,
225
92
  name: 'Player',
226
93
  modelUri: 'models/player.gltf',
227
- modelLoopedAnimations: [ 'idle' ],
94
+ modelLoopedAnimations: ['idle'],
228
95
  modelScale: 0.5,
229
96
  });
230
97
 
231
- playerEntity.spawn(world, PLAYER_SPAWN_POSITION);
98
+ playerEntity.spawn(world, GAME_CONFIG.POSITIONS.PLAYER_SPAWN);
232
99
 
100
+ playerEntity.setCollisionGroupsForSolidColliders({
101
+ belongsTo: [CollisionGroup.ENTITY, CollisionGroup.PLAYER],
102
+ collidesWith: [CollisionGroup.BLOCK, CollisionGroup.ENTITY_SENSOR, GAME_CONFIG.WALL_COLLISION_GROUP],
103
+ });
104
+
105
+ playerEntity.setCollisionGroupsForSensorColliders({
106
+ belongsTo: [CollisionGroup.ENTITY_SENSOR],
107
+ collidesWith: [CollisionGroup.BLOCK, GAME_CONFIG.WALL_COLLISION_GROUP],
108
+ });
109
+
233
110
  playerEntity.onTick = () => {
234
111
  if (playerEntity.position.y < 5) {
235
112
  killPlayer(playerEntity);
236
113
  }
237
114
  }
238
-
239
115
  }
240
116
 
241
117
  function onPlayerLeave(world: World, player: Player) {
242
118
  world.entityManager.getAllPlayerEntities(player).forEach(entity => {
243
- // remove from game state
244
119
  removePlayerFromQueue(entity);
245
120
  killPlayer(entity);
246
-
247
- // despawn player entity
248
121
  entity.despawn();
249
122
  });
250
123
  }
@@ -253,7 +126,7 @@ function spawnJoinNpc(world: World) {
253
126
  const joinNpc = new Entity({
254
127
  name: 'Join NPC',
255
128
  modelUri: 'models/mindflayer.gltf',
256
- modelLoopedAnimations: [ 'idle' ],
129
+ modelLoopedAnimations: ['idle'],
257
130
  modelScale: 0.4,
258
131
  rigidBodyOptions: {
259
132
  rotation: { x: 0, y: 1, z: 0, w: 0 },
@@ -274,9 +147,9 @@ function spawnJoinNpc(world: World) {
274
147
  }
275
148
  ],
276
149
  },
277
- });
150
+ });
278
151
 
279
- joinNpc.spawn(world, JOIN_NPC_SPAWN_POSITION);
152
+ joinNpc.spawn(world, GAME_CONFIG.POSITIONS.JOIN_NPC);
280
153
 
281
154
  const sceneUi = new SceneUI({
282
155
  templateId: 'join-npc-ui',
@@ -289,6 +162,7 @@ function spawnJoinNpc(world: World) {
289
162
 
290
163
  function addPlayerEntityToQueue(world: World, playerEntity: PlayerEntity) {
291
164
  if (QUEUED_PLAYER_ENTITIES.has(playerEntity)) return;
165
+
292
166
  QUEUED_PLAYER_ENTITIES.add(playerEntity);
293
167
  world.chatManager.sendPlayerMessage(playerEntity.player, 'You have joined the next game queue!', '00FF00');
294
168
  uiUpdate({ queueCount: QUEUED_PLAYER_ENTITIES.size });
@@ -313,23 +187,23 @@ function queueGame(world: World) {
313
187
  uiUpdate({
314
188
  gameState,
315
189
  gameCountdownStartTime,
316
- countdown: GAME_START_DELAY_SECONDS,
190
+ countdown: GAME_CONFIG.START_DELAY,
317
191
  });
318
192
 
319
193
  setTimeout(() => {
320
194
  QUEUED_PLAYER_ENTITIES.forEach(playerEntity => {
321
- playerEntity.setPosition(GAME_START_POSITION);
195
+ playerEntity.setPosition(GAME_CONFIG.POSITIONS.START);
322
196
  GAME_PLAYER_ENTITIES.add(playerEntity);
323
197
 
324
198
  world.sceneUIManager.getAllEntityAttachedSceneUIs(playerEntity).forEach(sceneUi => {
325
- sceneUi.unload(); // unload the queued ui
199
+ sceneUi.unload();
326
200
  });
327
201
  });
328
202
 
329
203
  QUEUED_PLAYER_ENTITIES.clear();
330
204
  uiUpdate({ queueCount: 0 });
331
205
  startGame(world);
332
- }, GAME_START_DELAY_SECONDS * 1000);
206
+ }, GAME_CONFIG.START_DELAY * 1000);
333
207
  }
334
208
 
335
209
  function startGame(world: World) {
@@ -343,32 +217,27 @@ function startGame(world: World) {
343
217
  if (!gameStartTime) return clearTimeout(gameInterval);
344
218
 
345
219
  const elapsedTime = Date.now() - gameStartTime;
346
- const level = Math.floor(elapsedTime / (GAME_LEVEL_DURATION_SECONDS * 1000)) + 1;
220
+ const level = Math.floor(elapsedTime / (GAME_CONFIG.LEVEL_DURATION * 1000)) + 1;
347
221
 
348
- // Calculate difficulty parameters based on level
349
- const speedModifier = 1 + (level * 0.15); // 15% faster per level
350
- const spawnInterval = Math.max(2000, 10000 - (level * 1000)); // Decrease from 10s by 1s per level, min 2s
222
+ const speedModifier = 1 + (level * 0.15);
223
+ const spawnInterval = Math.max(2000, 10000 - (level * 1000));
351
224
 
352
- // Generate wall from random direction
353
225
  const directions = Object.values(GameWallDirection);
354
226
  const randomDirection = directions[Math.floor(Math.random() * directions.length)];
355
227
 
356
228
  generateWall(world, randomDirection, speedModifier);
357
229
 
358
- // Dynamically adjust interval
359
230
  clearTimeout(gameInterval);
360
231
  gameInterval = setTimeout(gameLoop, spawnInterval);
361
232
 
362
- // Update level
363
233
  if (level > gameLevel) {
364
234
  gameLevel = level;
365
235
  uiUpdate({ gameLevel });
366
236
  }
367
237
  }
368
238
 
369
- gameLoop(); // invoke to start
239
+ gameLoop();
370
240
 
371
- // Update UI
372
241
  uiUpdate({
373
242
  gameState: 'inProgress',
374
243
  gameStartTime,
@@ -383,7 +252,7 @@ function endGame() {
383
252
 
384
253
  const winner = Array.from(GAME_PLAYER_ENTITIES)[0];
385
254
  if (winner) {
386
- winner.setPosition(PLAYER_SPAWN_POSITION);
255
+ winner.setPosition(GAME_CONFIG.POSITIONS.PLAYER_SPAWN);
387
256
  winner.world!.chatManager.sendBroadcastMessage(`${winner.player.username} wins!`, '00FF00');
388
257
  }
389
258
 
@@ -398,17 +267,13 @@ function endGame() {
398
267
  function killPlayer(playerEntity: PlayerEntity) {
399
268
  if (!GAME_PLAYER_ENTITIES.has(playerEntity)) return;
400
269
 
401
- // return to spawn position
402
- playerEntity.setPosition(PLAYER_SPAWN_POSITION);
403
-
404
- // remove from game player list
270
+ playerEntity.setPosition(GAME_CONFIG.POSITIONS.PLAYER_SPAWN);
405
271
  GAME_PLAYER_ENTITIES.delete(playerEntity);
406
272
 
407
- if (GAME_PLAYER_ENTITIES.size < 1) {
273
+ if (GAME_PLAYER_ENTITIES.size <= 1) {
408
274
  endGame();
409
275
  }
410
276
 
411
- // update remaining players for the ui
412
277
  uiUpdate({
413
278
  playersRemaining: Array.from(GAME_PLAYER_ENTITIES).map(playerEntity => playerEntity.player.username),
414
279
  });
@@ -416,11 +281,7 @@ function killPlayer(playerEntity: PlayerEntity) {
416
281
 
417
282
  function removePlayerFromQueue(playerEntity: PlayerEntity) {
418
283
  if (!QUEUED_PLAYER_ENTITIES.has(playerEntity)) return;
419
-
420
- // remove from queue
421
284
  QUEUED_PLAYER_ENTITIES.delete(playerEntity);
422
-
423
- // update ui
424
285
  uiUpdate({ queueCount: QUEUED_PLAYER_ENTITIES.size });
425
286
  }
426
287
 
@@ -428,69 +289,73 @@ function generateWall(world: World, direction: GameWallDirection, speedModifier:
428
289
  const selectedShape = GAME_WALL_SHAPES[Math.floor(Math.random() * GAME_WALL_SHAPES.length)];
429
290
  const isNorthSouth = direction === GameWallDirection.NORTH || direction === GameWallDirection.SOUTH;
430
291
 
431
- // Iterate through the shape array to create wall segments
432
292
  for (let y = 0; y < selectedShape.length; y++) {
433
293
  for (let x = 0; x < selectedShape[y].length; x++) {
434
- if (selectedShape[y][x] === 1 || selectedShape[y][x] === 2 || selectedShape[y][x] === 3) {
435
- // Calculate position offset from center
436
- const xOffset = (x - selectedShape[y].length / 2) * 1 + 0.5; // 1 block width
437
- const yOffset = (selectedShape.length - y - 1) * 1 + 0.5; // 1 block height
438
-
439
- const wallSegment = new Entity({
440
- blockTextureUri: selectedShape[y][x] === 2 ? 'textures/dirt.png' :
441
- selectedShape[y][x] === 3 ? 'textures/sand.png' :
442
- 'textures/oak_leaves.png',
443
- blockHalfExtents: {
444
- x: isNorthSouth ? 0.5 : 0.5,
445
- y: 0.5,
446
- z: isNorthSouth ? 0.5 : 0.5
447
- },
448
- rigidBodyOptions: {
449
- type: RigidBodyType.KINEMATIC_VELOCITY,
450
- }
451
- });
452
-
453
- const spawnPosition = { ...GAME_WALL_SPAWN_POSITIONS[direction] };
454
-
455
- // Adjust position based on direction
456
- if (isNorthSouth) {
457
- spawnPosition.x += xOffset;
458
- spawnPosition.y += yOffset;
459
- if (selectedShape[y][x] === 2) {
460
- spawnPosition.z += direction === GameWallDirection.NORTH ? 1 : -1;
461
- } else if (selectedShape[y][x] === 3) {
462
- spawnPosition.z += direction === GameWallDirection.NORTH ? 2 : -2;
463
- }
464
- } else {
465
- spawnPosition.z += xOffset;
466
- spawnPosition.y += yOffset;
467
- if (selectedShape[y][x] === 2) {
468
- spawnPosition.x += direction === GameWallDirection.WEST ? 1 : -1;
469
- } else if (selectedShape[y][x] === 3) {
470
- spawnPosition.x += direction === GameWallDirection.WEST ? 2 : -2;
471
- }
294
+ if (selectedShape[y][x] === 0) continue;
295
+
296
+ const xOffset = (x - selectedShape[y].length / 2) * 1 + 0.5;
297
+ const yOffset = (selectedShape.length - y - 1) * 1 + 0.5;
298
+
299
+ const wallSegment = new Entity({
300
+ blockTextureUri: selectedShape[y][x] === 2 ? 'textures/dirt.png' :
301
+ selectedShape[y][x] === 3 ? 'textures/sand.png' :
302
+ 'textures/oak_leaves.png',
303
+ blockHalfExtents: {
304
+ x: isNorthSouth ? 0.5 : 0.5,
305
+ y: 0.5,
306
+ z: isNorthSouth ? 0.5 : 0.5
307
+ },
308
+ rigidBodyOptions: {
309
+ type: RigidBodyType.KINEMATIC_VELOCITY,
472
310
  }
311
+ });
312
+
313
+ const spawnPosition = { ...GAME_CONFIG.POSITIONS.WALLS[direction] };
314
+
315
+ if (isNorthSouth) {
316
+ spawnPosition.x += xOffset;
317
+ spawnPosition.y += yOffset;
318
+ if (selectedShape[y][x] === 2) {
319
+ spawnPosition.z += direction === GameWallDirection.NORTH ? 1 : -1;
320
+ } else if (selectedShape[y][x] === 3) {
321
+ spawnPosition.z += direction === GameWallDirection.NORTH ? 2 : -2;
322
+ }
323
+ } else {
324
+ spawnPosition.z += xOffset;
325
+ spawnPosition.y += yOffset;
326
+ if (selectedShape[y][x] === 2) {
327
+ spawnPosition.x += direction === GameWallDirection.WEST ? 1 : -1;
328
+ } else if (selectedShape[y][x] === 3) {
329
+ spawnPosition.x += direction === GameWallDirection.WEST ? 2 : -2;
330
+ }
331
+ }
473
332
 
474
- wallSegment.spawn(world, spawnPosition);
475
-
476
- // Set velocity based on direction
477
- wallSegment.setLinearVelocity({
478
- x: GAME_WALL_VELOCITY_DIRECTIONS[direction].x * speedModifier,
479
- y: GAME_WALL_VELOCITY_DIRECTIONS[direction].y * speedModifier,
480
- z: GAME_WALL_VELOCITY_DIRECTIONS[direction].z * speedModifier,
481
- });
482
-
483
- wallSegment.onTick = () => {
484
- if (!wallSegment.isSpawned) return;
485
- const position = wallSegment.position;
486
-
487
- if (Math.abs(position.x - spawnPosition.x) > GAME_WALL_DESPAWN_DISTANCE || Math.abs(position.z - spawnPosition.z) > GAME_WALL_DESPAWN_DISTANCE) {
488
- wallSegment.setLinearVelocity({ x: 0, y: -32, z: 0 }); // drop it out of the world to trigger decollision
489
-
490
- setTimeout(() => {
333
+ wallSegment.spawn(world, spawnPosition);
334
+
335
+ wallSegment.setCollisionGroupsForSolidColliders({
336
+ belongsTo: [GAME_CONFIG.WALL_COLLISION_GROUP],
337
+ collidesWith: [CollisionGroup.PLAYER, CollisionGroup.ENTITY_SENSOR],
338
+ });
339
+
340
+ wallSegment.setLinearVelocity({
341
+ x: GAME_CONFIG.WALL_VELOCITIES[direction].x * speedModifier,
342
+ y: GAME_CONFIG.WALL_VELOCITIES[direction].y * speedModifier,
343
+ z: GAME_CONFIG.WALL_VELOCITIES[direction].z * speedModifier,
344
+ });
345
+
346
+ wallSegment.onTick = () => {
347
+ if (!wallSegment.isSpawned) return;
348
+ const position = wallSegment.position;
349
+
350
+ if (Math.abs(position.x - spawnPosition.x) > GAME_CONFIG.WALL_DESPAWN_DISTANCE ||
351
+ Math.abs(position.z - spawnPosition.z) > GAME_CONFIG.WALL_DESPAWN_DISTANCE) {
352
+ wallSegment.setLinearVelocity({ x: 0, y: -32, z: 0 });
353
+
354
+ setTimeout(() => {
355
+ if (wallSegment.isSpawned) {
491
356
  wallSegment.despawn();
492
- }, 1000);
493
- }
357
+ }
358
+ }, 1000);
494
359
  }
495
360
  }
496
361
  }
@@ -499,7 +364,6 @@ function generateWall(world: World, direction: GameWallDirection, speedModifier:
499
364
 
500
365
  function uiUpdate(data: object) {
501
366
  gameUiState = { ...gameUiState, ...data };
502
-
503
367
  GameServer.instance.playerManager.getConnectedPlayers().forEach(player => {
504
368
  player.ui.sendData(data);
505
369
  });
@@ -10,7 +10,7 @@
10
10
  "author": "",
11
11
  "license": "ISC",
12
12
  "dependencies": {
13
- "@hytopia.com/assets": "^0.1.6",
14
- "hytopia": "latest"
13
+ "hytopia": "latest",
14
+ "@hytopia.com/assets": "latest"
15
15
  }
16
16
  }
@@ -0,0 +1,145 @@
1
+ const GAME_WALL_SHAPES = [
2
+ // lol shape
3
+ [
4
+ [0, 1, 0, 1, 1, 1, 0, 1],
5
+ [0, 1, 0, 1, 0, 1, 0, 1],
6
+ [0, 1, 0, 1, 1, 1, 0, 1],
7
+ ],
8
+ // stairs
9
+ [
10
+ [0, 0, 0, 0, 0, 0, 0, 1],
11
+ [0, 0, 0, 0, 0, 1, 1, 1],
12
+ [0, 0, 0, 1, 1, 1, 1, 1],
13
+ [0, 1, 1, 1, 1, 1, 1, 1],
14
+ [1, 1, 1, 1, 1, 1, 1, 1],
15
+ ],
16
+ // horizontal bar
17
+ [
18
+ [1, 1, 1, 1, 1, 1, 1, 1],
19
+ [1, 1, 1, 1, 1, 1, 1, 1],
20
+ ],
21
+ // vertical bars
22
+ [
23
+ [1, 0, 1, 0, 1, 0, 1, 0],
24
+ [1, 0, 1, 0, 1, 0, 1, 0],
25
+ [1, 0, 1, 0, 1, 0, 1, 0],
26
+ [1, 0, 1, 0, 1, 0, 1, 0],
27
+ ],
28
+ // lattice
29
+ [
30
+ [1, 0, 1, 0, 1, 0, 1, 0],
31
+ [0, 1, 0, 1, 0, 1, 0, 1],
32
+ [1, 0, 1, 0, 1, 0, 1, 0],
33
+ [0, 1, 0, 1, 0, 1, 0, 1],
34
+ [1, 0, 1, 0, 1, 0, 1, 0],
35
+ [0, 0, 0, 1, 0, 1, 0, 0],
36
+ [1, 0, 1, 0, 1, 0, 1, 0],
37
+ ],
38
+ // platforming hole
39
+ [
40
+ [1, 1, 1, 0, 0, 1, 1, 1],
41
+ [1, 1, 1, 0, 0, 1, 1, 1],
42
+ [1, 1, 1, 2, 2, 1, 1, 1],
43
+ [1, 1, 2, 1, 1, 2, 1, 1],
44
+ [1, 2, 1, 1, 1, 1, 2, 1],
45
+ [2, 1, 1, 1, 1, 1, 1, 2],
46
+ ],
47
+ // 3d stairs
48
+ [
49
+ [1, 1, 1, 1, 1, 1, 1, 1],
50
+ [2, 1, 2, 1, 1, 2, 1, 2],
51
+ [2, 3, 2, 3, 3, 2, 3, 2],
52
+ [3, 3, 3, 3, 3, 3, 3, 3],
53
+ ],
54
+ // 3d lattice
55
+ [
56
+ [0, 2, 0, 2, 0, 2, 0, 2],
57
+ [1, 0, 1, 0, 1, 0, 1, 0],
58
+ [0, 2, 0, 2, 0, 2, 0, 2],
59
+ [1, 0, 2, 0, 2, 0, 1, 0],
60
+ [1, 0, 1, 0, 1, 0, 1, 0],
61
+ [0, 2, 0, 2, 0, 2, 0, 2],
62
+ [1, 0, 3, 0, 3, 0, 1, 0],
63
+ ],
64
+ // full wall
65
+ [
66
+ [1, 1, 1, 1, 1, 1, 1, 1],
67
+ [1, 1, 1, 1, 1, 1, 1, 1],
68
+ [1, 1, 1, 1, 1, 1, 1, 1],
69
+ [1, 1, 1, 1, 1, 1, 1, 1],
70
+ ],
71
+ // zig zag challenge
72
+ [
73
+ [1, 1, 1, 0, 0, 0, 0, 0],
74
+ [0, 0, 1, 1, 1, 0, 0, 0],
75
+ [0, 0, 0, 0, 1, 1, 1, 0],
76
+ [0, 0, 0, 0, 0, 0, 1, 1],
77
+ ],
78
+ // triple jump
79
+ [
80
+ [0, 0, 1, 0, 0, 1, 0, 0],
81
+ [0, 0, 1, 0, 0, 1, 0, 0],
82
+ [1, 1, 1, 0, 0, 1, 1, 1],
83
+ ],
84
+ // layered tunnel
85
+ [
86
+ [1, 1, 1, 1, 1, 1, 1, 1],
87
+ [1, 0, 0, 2, 2, 0, 0, 1],
88
+ [1, 0, 0, 3, 3, 0, 0, 1],
89
+ [1, 1, 1, 1, 1, 1, 1, 1],
90
+ ],
91
+ // diagonal dash
92
+ [
93
+ [1, 1, 0, 0, 0, 0, 0, 0],
94
+ [0, 1, 1, 0, 0, 0, 0, 0],
95
+ [0, 0, 1, 1, 0, 0, 0, 0],
96
+ [0, 0, 0, 1, 1, 0, 0, 0],
97
+ [0, 0, 0, 0, 1, 1, 0, 0],
98
+ ],
99
+ // wave rider
100
+ [
101
+ [1, 0, 0, 0, 0, 0, 0, 1],
102
+ [1, 1, 0, 0, 0, 0, 1, 1],
103
+ [0, 1, 1, 0, 0, 1, 1, 0],
104
+ [0, 0, 1, 1, 1, 1, 0, 0],
105
+ ],
106
+ // forward momentum
107
+ [
108
+ [1, 1, 2, 2, 3, 3, 3, 3],
109
+ [1, 1, 2, 2, 3, 3, 3, 3],
110
+ [1, 1, 2, 2, 3, 3, 3, 3],
111
+ ],
112
+ // spiral staircase
113
+ [
114
+ [1, 1, 1, 1, 2, 2, 3, 3],
115
+ [3, 3, 1, 1, 1, 2, 2, 2],
116
+ [2, 3, 3, 1, 1, 1, 1, 2],
117
+ [2, 2, 2, 3, 3, 1, 1, 1],
118
+ ],
119
+ // hopscotch
120
+ [
121
+ [0, 1, 0, 1, 0, 1, 0, 1],
122
+ [0, 2, 0, 2, 0, 2, 0, 2],
123
+ [1, 0, 1, 0, 1, 0, 1, 0],
124
+ [2, 0, 2, 0, 2, 0, 2, 0],
125
+ ],
126
+ // snake path
127
+ [
128
+ [1, 1, 1, 1, 0, 0, 0, 0],
129
+ [0, 0, 0, 1, 0, 0, 0, 0],
130
+ [0, 0, 0, 2, 0, 0, 0, 0],
131
+ [0, 0, 0, 3, 3, 3, 3, 3],
132
+ ],
133
+ // diamond gate
134
+ [
135
+ [1, 1, 1, 0, 0, 1, 1, 1],
136
+ [1, 1, 0, 0, 0, 0, 1, 1],
137
+ [1, 0, 2, 0, 0, 2, 0, 1],
138
+ [0, 0, 0, 3, 3, 0, 0, 0],
139
+ [1, 0, 2, 0, 0, 2, 0, 1],
140
+ [1, 1, 0, 0, 0, 0, 1, 1],
141
+ [1, 1, 1, 0, 0, 1, 1, 1],
142
+ ],
143
+ ];
144
+
145
+ export default GAME_WALL_SHAPES;