hytopia 0.1.98 → 0.2.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 (68) hide show
  1. package/docs/server.entity.height.md +13 -0
  2. package/docs/server.entity.md +22 -1
  3. package/docs/server.entity.opacity.md +1 -1
  4. package/docs/server.modelregistry.getheight.md +55 -0
  5. package/docs/server.modelregistry.md +14 -0
  6. package/docs/server.player.md +21 -0
  7. package/docs/server.player.profilepictureurl.md +13 -0
  8. package/examples/pathfinding/index.ts +4 -4
  9. package/examples/payload-game/index.ts +12 -10
  10. package/examples/zombies-fps/README.md +5 -0
  11. package/examples/zombies-fps/assets/audio/music/bg.mp3 +0 -0
  12. package/examples/zombies-fps/assets/audio/sfx/pistol-reload.mp3 +0 -0
  13. package/examples/zombies-fps/assets/audio/sfx/pistol-shoot.mp3 +0 -0
  14. package/examples/zombies-fps/assets/audio/sfx/player-hurt.mp3 +0 -0
  15. package/examples/zombies-fps/assets/audio/sfx/purchase.mp3 +0 -0
  16. package/examples/zombies-fps/assets/audio/sfx/rifle-reload.mp3 +0 -0
  17. package/examples/zombies-fps/assets/audio/sfx/rifle-shoot.mp3 +0 -0
  18. package/examples/zombies-fps/assets/audio/sfx/ripper-idle.mp3 +0 -0
  19. package/examples/zombies-fps/assets/audio/sfx/roulette.mp3 +0 -0
  20. package/examples/zombies-fps/assets/audio/sfx/shotgun-reload.mp3 +0 -0
  21. package/examples/zombies-fps/assets/audio/sfx/shotgun-shoot.mp3 +0 -0
  22. package/examples/zombies-fps/assets/audio/sfx/wave-start.mp3 +0 -0
  23. package/examples/zombies-fps/assets/audio/sfx/zombie-idle.mp3 +0 -0
  24. package/examples/zombies-fps/assets/icons/ak-47.png +0 -0
  25. package/examples/zombies-fps/assets/icons/ar-15.png +0 -0
  26. package/examples/zombies-fps/assets/icons/auto-pistol.png +0 -0
  27. package/examples/zombies-fps/assets/icons/auto-shotgun.png +0 -0
  28. package/examples/zombies-fps/assets/icons/heart.png +0 -0
  29. package/examples/zombies-fps/assets/icons/pistol.png +0 -0
  30. package/examples/zombies-fps/assets/icons/shotgun.png +0 -0
  31. package/examples/zombies-fps/assets/models/environment/bombbox.gltf +1 -0
  32. package/examples/zombies-fps/assets/models/environment/bullet-hole.gltf +1 -0
  33. package/examples/zombies-fps/assets/models/environment/healthkit.gltf +1 -0
  34. package/examples/zombies-fps/assets/models/environment/muzzle-flash.gltf +1 -0
  35. package/examples/zombies-fps/assets/models/items/ak-47.glb +0 -0
  36. package/examples/zombies-fps/assets/models/items/ar-15.glb +0 -0
  37. package/examples/zombies-fps/assets/models/items/auto-pistol.glb +0 -0
  38. package/examples/zombies-fps/assets/models/items/auto-shotgun.glb +0 -0
  39. package/examples/zombies-fps/assets/models/items/shotgun.glb +0 -0
  40. package/examples/zombies-fps/assets/models/npcs/ripper-boss.gltf +1 -0
  41. package/examples/zombies-fps/assets/models/players/soldier-player.gltf +1 -1
  42. package/examples/zombies-fps/assets/models/projectiles/bullet-trace.gltf +1 -0
  43. package/examples/zombies-fps/assets/ui/index.html +620 -27
  44. package/examples/zombies-fps/classes/EnemyEntity.ts +183 -4
  45. package/examples/zombies-fps/classes/GameManager.ts +165 -0
  46. package/examples/zombies-fps/classes/GamePlayerEntity.ts +263 -14
  47. package/examples/zombies-fps/classes/GunEntity.ts +225 -13
  48. package/examples/zombies-fps/classes/InteractableEntity.ts +9 -0
  49. package/examples/zombies-fps/classes/PurchaseBarrierEntity.ts +70 -17
  50. package/examples/zombies-fps/classes/WeaponCrateEntity.ts +173 -0
  51. package/examples/zombies-fps/classes/enemies/RipperEntity.ts +67 -0
  52. package/examples/zombies-fps/classes/enemies/ZombieEntity.ts +30 -0
  53. package/examples/zombies-fps/classes/guns/AK47Entity.ts +43 -0
  54. package/examples/zombies-fps/classes/guns/AR15Entity.ts +32 -0
  55. package/examples/zombies-fps/classes/guns/AutoPistolEntity.ts +36 -0
  56. package/examples/zombies-fps/classes/guns/AutoShotgunEntity.ts +37 -0
  57. package/examples/zombies-fps/classes/guns/PistolEntity.ts +23 -15
  58. package/examples/zombies-fps/classes/guns/ShotgunEntity.ts +92 -0
  59. package/examples/zombies-fps/gameConfig.ts +125 -21
  60. package/examples/zombies-fps/index.ts +14 -31
  61. package/package.json +1 -1
  62. package/server.api.json +126 -18
  63. package/server.d.ts +17 -5
  64. package/server.js +98 -90
  65. package/tsdoc-metadata.json +1 -1
  66. package/examples/zombies-fps/assets/audio/sfx/pistol-shoot-1.mp3 +0 -0
  67. package/examples/zombies-fps/assets/audio/sfx/pistol-shoot-2.mp3 +0 -0
  68. package/examples/zombies-fps/classes/guns/BulletEntity.ts +0 -0
@@ -0,0 +1,43 @@
1
+ import { Quaternion } from 'hytopia';
2
+ import PistolEntity from './PistolEntity';
3
+ import type { GunEntityOptions } from '../GunEntity';
4
+ import type { PlayerEntity, QuaternionLike, Vector3Like } from 'hytopia';
5
+
6
+ // fire behavior is very similar to a pistol, so we inherit from it.
7
+ export default class AK47Entity extends PistolEntity {
8
+ public constructor(options: Partial<GunEntityOptions> = {}) {
9
+ super({
10
+ ammo: options.ammo ?? 30,
11
+ damage: options.damage ?? 3,
12
+ fireRate: options.fireRate ?? 10,
13
+ iconImageUri: options.iconImageUri ?? 'icons/ak-47.png',
14
+ idleAnimation: options.idleAnimation ?? 'idle_gun_both',
15
+ name: options.name ?? 'AK-47',
16
+ maxAmmo: options.maxAmmo ?? 30,
17
+ modelUri: options.modelUri ?? 'models/items/ak-47.glb',
18
+ reloadAudioUri: options.reloadAudioUri ?? 'audio/sfx/rifle-reload.mp3',
19
+ reloadTimeMs: options.reloadTimeMs ?? 1500,
20
+ shootAnimation: options.shootAnimation ?? 'shoot_gun_both',
21
+ shootAudioUri: options.shootAudioUri ?? 'audio/sfx/rifle-shoot.mp3',
22
+ ...options,
23
+ });
24
+ }
25
+
26
+ public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
27
+ return {
28
+ position: { x: 0, y: 0.01, z: -1.25 },
29
+ rotation: Quaternion.fromEuler(0, 90, 0),
30
+ };
31
+ }
32
+
33
+ public override shoot() {
34
+ if (!this.parent) {
35
+ return;
36
+ }
37
+
38
+ super.shoot();
39
+
40
+ const parentPlayerEntity = this.parent as PlayerEntity;
41
+ parentPlayerEntity.player.input.ml = true; // prevent cancel for auto fire.
42
+ }
43
+ }
@@ -0,0 +1,32 @@
1
+ import { Quaternion } from 'hytopia';
2
+ import PistolEntity from './PistolEntity';
3
+ import type { GunEntityOptions } from '../GunEntity';
4
+ import type { PlayerEntity, QuaternionLike, Vector3Like } from 'hytopia';
5
+
6
+ // fire behavior is very similar to a pistol, so we inherit from it.
7
+ export default class AR15Entity extends PistolEntity {
8
+ public constructor(options: Partial<GunEntityOptions> = {}) {
9
+ super({
10
+ ammo: options.ammo ?? 30,
11
+ damage: options.damage ?? 4,
12
+ fireRate: options.fireRate ?? 15,
13
+ iconImageUri: options.iconImageUri ?? 'icons/ar-15.png',
14
+ idleAnimation: options.idleAnimation ?? 'idle_gun_both',
15
+ name: options.name ?? 'AR-15',
16
+ maxAmmo: options.maxAmmo ?? 30,
17
+ modelUri: options.modelUri ?? 'models/items/ar-15.glb',
18
+ reloadAudioUri: options.reloadAudioUri ?? 'audio/sfx/rifle-reload.mp3',
19
+ reloadTimeMs: options.reloadTimeMs ?? 1500,
20
+ shootAnimation: options.shootAnimation ?? 'shoot_gun_both',
21
+ shootAudioUri: options.shootAudioUri ?? 'audio/sfx/rifle-shoot.mp3',
22
+ ...options,
23
+ });
24
+ }
25
+
26
+ public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
27
+ return {
28
+ position: { x: 0.01, y: 0.03, z: -1.42 },
29
+ rotation: Quaternion.fromEuler(0, 90, 0),
30
+ };
31
+ }
32
+ }
@@ -0,0 +1,36 @@
1
+ import { Quaternion } from 'hytopia';
2
+ import PistolEntity from './PistolEntity';
3
+ import type { GunEntityOptions } from '../GunEntity';
4
+ import type { PlayerEntity, QuaternionLike, Vector3Like } from 'hytopia';
5
+
6
+ export default class AutoPistolEntity extends PistolEntity {
7
+ public constructor(options: Partial<GunEntityOptions> = {}) {
8
+ super({
9
+ ammo: options.ammo ?? 20,
10
+ fireRate: options.fireRate ?? 7,
11
+ iconImageUri: options.iconImageUri ?? 'icons/auto-pistol.png',
12
+ name: options.name ?? 'Auto Pistol',
13
+ maxAmmo: options.maxAmmo ?? 20,
14
+ modelUri: options.modelUri ?? 'models/items/auto-pistol.glb',
15
+ ...options,
16
+ });
17
+ }
18
+
19
+ public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
20
+ return {
21
+ position: { x: 0.01, y: 0.1, z: -0.35 },
22
+ rotation: Quaternion.fromEuler(0, 90, 0),
23
+ };
24
+ }
25
+
26
+ public override shoot() {
27
+ if (!this.parent) {
28
+ return;
29
+ }
30
+
31
+ super.shoot();
32
+
33
+ const parentPlayerEntity = this.parent as PlayerEntity;
34
+ parentPlayerEntity.player.input.ml = true; // prevent cancel for auto fire.
35
+ }
36
+ }
@@ -0,0 +1,37 @@
1
+ import { Quaternion } from 'hytopia';
2
+ import ShotgunEntity from './ShotgunEntity';
3
+ import type { GunEntityOptions } from '../GunEntity';
4
+ import type { PlayerEntity, QuaternionLike, Vector3Like } from 'hytopia';
5
+
6
+ export default class AutoShotgunEntity extends ShotgunEntity {
7
+ public constructor(options: Partial<GunEntityOptions> = {}) {
8
+ super({
9
+ ammo: options.ammo ?? 6,
10
+ fireRate: options.fireRate ?? 2,
11
+ iconImageUri: options.iconImageUri ?? 'icons/auto-shotgun.png',
12
+ name: options.name ?? 'Auto Shotgun',
13
+ maxAmmo: options.maxAmmo ?? 4,
14
+ modelUri: options.modelUri ?? 'models/items/auto-shotgun.glb',
15
+ reloadTimeMs: options.reloadTimeMs ?? 2500,
16
+ ...options,
17
+ });
18
+ }
19
+
20
+ public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
21
+ return {
22
+ position: { x: 0.015, y: 0, z: -1 },
23
+ rotation: Quaternion.fromEuler(0, 90, 0),
24
+ };
25
+ }
26
+
27
+ public override shoot() {
28
+ if (!this.parent) {
29
+ return;
30
+ }
31
+
32
+ super.shoot();
33
+
34
+ const parentPlayerEntity = this.parent as PlayerEntity;
35
+ parentPlayerEntity.player.input.ml = true; // prevent cancel for auto fire.
36
+ }
37
+ }
@@ -1,23 +1,28 @@
1
- import {
2
- Audio,
3
- PlayerEntity,
4
- } from 'hytopia';
1
+ import { Quaternion } from 'hytopia';
2
+ import type { PlayerEntity, Vector3Like, QuaternionLike } from 'hytopia';
5
3
 
6
4
  import GunEntity from '../GunEntity';
7
5
  import type { GunEntityOptions } from '../GunEntity';
8
6
 
9
7
  export default class PistolEntity extends GunEntity {
10
- public constructor(options: Partial<GunEntityOptions>) {
8
+ public constructor(options: Partial<GunEntityOptions> = {}) {
11
9
  super({
12
10
  ammo: options.ammo ?? 10,
13
11
  damage: options.damage ?? 3,
14
- fireRate: options.fireRate ?? 5,
12
+ fireRate: options.fireRate ?? 9,
15
13
  hand: options.hand ?? 'right',
14
+ iconImageUri: options.iconImageUri ?? 'icons/pistol.png',
15
+ idleAnimation: options.idleAnimation ?? 'idle_gun_right',
16
+ name: options.name ?? 'Pistol',
16
17
  maxAmmo: options.maxAmmo ?? 10,
17
- modelUri: 'models/items/pistol.glb',
18
- modelScale: 1.3,
18
+ modelUri: options.modelUri ?? 'models/items/pistol.glb',
19
+ modelScale: options.modelScale ?? 1.3,
19
20
  parent: options.parent,
20
- reloadTime: options.reloadTime ?? 1,
21
+ range: options.range ?? 50,
22
+ reloadAudioUri: options.reloadAudioUri ?? 'audio/sfx/pistol-reload.mp3',
23
+ reloadTimeMs: options.reloadTimeMs ?? 1250,
24
+ shootAnimation: options.shootAnimation ?? 'shoot_gun_right',
25
+ shootAudioUri: options.shootAudioUri ?? 'audio/sfx/pistol-shoot.mp3',
21
26
  });
22
27
  }
23
28
 
@@ -32,18 +37,21 @@ export default class PistolEntity extends GunEntity {
32
37
  return;
33
38
  }
34
39
 
40
+ // shoot the bullet
41
+ super.shoot();
42
+
35
43
  // cancel the input, pistols require click to shoot
36
44
  parentPlayerEntity.player.input.ml = false;
37
45
 
38
46
  // play shoot animation
39
47
  parentPlayerEntity.startModelOneshotAnimations([ 'shoot_gun_right' ]);
48
+ }
40
49
 
41
- (new Audio({
42
- position: parentPlayerEntity.position,
43
- referenceDistance: 20,
44
- uri: 'audio/sfx/pistol-shoot-1.mp3',
45
- volume: 1,
46
- })).play(parentPlayerEntity.world, true);
50
+ public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
51
+ return {
52
+ position: { x: 0.03, y: 0.1, z: -0.5 },
53
+ rotation: Quaternion.fromEuler(0, 90, 0),
54
+ };
47
55
  }
48
56
  }
49
57
 
@@ -0,0 +1,92 @@
1
+ import { Quaternion } from 'hytopia';
2
+ import type { PlayerEntity, QuaternionLike, Vector3Like } from 'hytopia';
3
+
4
+ import GunEntity from '../GunEntity';
5
+ import type { GunEntityOptions } from '../GunEntity';
6
+
7
+ export default class ShotgunEntity extends GunEntity {
8
+ public constructor(options: Partial<GunEntityOptions> = {}) {
9
+ super({
10
+ ammo: options.ammo ?? 3,
11
+ damage: options.damage ?? 4, // damage per pellet
12
+ fireRate: options.fireRate ?? 1.3,
13
+ hand: options.hand ?? 'right',
14
+ iconImageUri: options.iconImageUri ?? 'icons/shotgun.png',
15
+ idleAnimation: options.idleAnimation ?? 'idle_gun_both',
16
+ name: options.name ?? 'Shotgun',
17
+ maxAmmo: options.maxAmmo ?? 3,
18
+ modelUri: options.modelUri ?? 'models/items/shotgun.glb',
19
+ modelScale: options.modelScale ?? 1.2,
20
+ parent: options.parent,
21
+ range: options.range ?? 8,
22
+ reloadAudioUri: options.reloadAudioUri ?? 'audio/sfx/shotgun-reload.mp3',
23
+ reloadTimeMs: options.reloadTimeMs ?? 1000,
24
+ shootAudioUri: options.shootAudioUri ?? 'audio/sfx/shotgun-shoot.mp3',
25
+ shootAnimation: options.shootAnimation ?? 'shoot_gun_both',
26
+ });
27
+ }
28
+
29
+ public override shoot() {
30
+ if (!this.parent || !this.processShoot()) {
31
+ return;
32
+ }
33
+
34
+ const parentPlayerEntity = this.parent as PlayerEntity;
35
+
36
+ if (!parentPlayerEntity.world) {
37
+ return;
38
+ }
39
+
40
+ // shoot the bullet
41
+ super.shoot();
42
+
43
+ // cancel the input, pistols require click to shoot
44
+ parentPlayerEntity.player.input.ml = false;
45
+ }
46
+
47
+ public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
48
+ return {
49
+ position: { x: 0.03, y: 0.1, z: -1.5 },
50
+ rotation: Quaternion.fromEuler(0, 90, 0),
51
+ };
52
+ }
53
+
54
+ public override shootRaycast(origin: Vector3Like, direction: Vector3Like, length: number) {
55
+ // Create spread pattern for shotgun pellets using angles relative to direction
56
+ const spreadAngles = [
57
+ { x: 0, y: 0 }, // Center
58
+ { x: 0.035, y: 0.035 }, // Upper right
59
+ { x: -0.035, y: 0.035 }, // Upper left
60
+ { x: 0.05, y: 0 }, // Right
61
+ { x: -0.05, y: 0 }, // Left
62
+ { x: 0.035, y: -0.035 }, // Lower right
63
+ { x: -0.035, y: -0.035 } // Lower left
64
+ ];
65
+
66
+ // Fire each pellet with spread applied to original direction
67
+ for (const angle of spreadAngles) {
68
+ // Calculate spread direction relative to original direction
69
+ const spreadDirection = {
70
+ x: direction.x + (direction.z * angle.x), // Add horizontal spread
71
+ y: direction.y + angle.y, // Add vertical spread
72
+ z: direction.z - (direction.x * angle.x) // Maintain direction magnitude
73
+ };
74
+
75
+ // Normalize the spread direction to maintain consistent range
76
+ const magnitude = Math.sqrt(
77
+ spreadDirection.x * spreadDirection.x +
78
+ spreadDirection.y * spreadDirection.y +
79
+ spreadDirection.z * spreadDirection.z
80
+ );
81
+
82
+ const normalizedDirection = {
83
+ x: spreadDirection.x / magnitude,
84
+ y: spreadDirection.y / magnitude,
85
+ z: spreadDirection.z / magnitude
86
+ };
87
+
88
+ super.shootRaycast(origin, normalizedDirection, length);
89
+ }
90
+ }
91
+ }
92
+
@@ -1,4 +1,7 @@
1
- import { Quaternion } from 'hytopia';
1
+ import { CollisionGroup, Quaternion } from 'hytopia';
2
+ import type { Vector3Like } from 'hytopia';
3
+
4
+ export const INVISIBLE_WALL_COLLISION_GROUP = CollisionGroup.GROUP_1;
2
5
 
3
6
  export const INVISIBLE_WALLS = [
4
7
  { // Main entrance (south door)
@@ -22,7 +25,7 @@ export const INVISIBLE_WALLS = [
22
25
  halfExtents: { x: 1, y: 5, z: 0.5 },
23
26
  },
24
27
  { // Parlor (south window)
25
- position: { x: -21, y: 1, z: 16},
28
+ position: { x: -22, y: 1, z: 16},
26
29
  halfExtents: { x: 1, y: 5, z: 0.5 },
27
30
  },
28
31
  { // Parlor (north window)
@@ -53,73 +56,174 @@ export const INVISIBLE_WALLS = [
53
56
 
54
57
  export const PURCHASE_BARRIERS = [
55
58
  {
56
- name: 'Unlock Theater Room (South)',
57
- removalPrice: 100,
59
+ name: 'Theater Room (South)',
60
+ removalPrice: 300,
58
61
  position: { x: 2.5, y: 1.5, z: 15 },
59
62
  rotation: Quaternion.fromEuler(0, 0, 0),
60
63
  width: 5,
64
+ unlockIds: [ 'theater' ],
61
65
  },
62
66
  {
63
- name: 'Unlock Parlor (South)',
64
- removalPrice: 25,
67
+ name: 'Parlor (South)',
68
+ removalPrice: 75,
65
69
  position: { x: -8, y: 1.5, z: 18.5 },
66
70
  rotation: Quaternion.fromEuler(0, 90, 0),
67
71
  width: 3,
72
+ unlockIds: [ 'parlor' ],
68
73
  },
69
74
  {
70
- name: 'Unlock Dining Hall (South)',
71
- removalPrice: 50,
75
+ name: 'Dining Hall (South)',
76
+ removalPrice: 75,
72
77
  position: { x: 13, y: 1.5, z: 18.5 },
73
78
  rotation: Quaternion.fromEuler(0, 90, 0),
74
79
  width: 3,
80
+ unlockIds: [ 'dining' ],
75
81
  },
76
82
  {
77
- name: 'Unlock Theater Room (West)',
78
- removalPrice: 75,
83
+ name: 'Theater Room (West)',
84
+ removalPrice: 250,
79
85
  position: { x: -15, y: 1.5, z: 3 },
80
86
  rotation: Quaternion.fromEuler(0, 90, 0),
81
87
  width: 5,
88
+ unlockIds: [ 'theater', 'parlor' ],
82
89
  },
83
90
  {
84
- name: 'Unlock Theater Room (East)',
85
- removalPrice: 75,
91
+ name: 'Theater Room (East)',
92
+ removalPrice: 250,
86
93
  position: { x: 19, y: 1.5, z: 3 },
87
94
  rotation: Quaternion.fromEuler(0, 90, 0),
88
95
  width: 5,
96
+ unlockIds: [ 'theater', 'dining' ],
89
97
  },
90
98
  {
91
- name: 'Unlock Art Gallery (South)',
92
- removalPrice: 200,
99
+ name: 'Art Gallery (South)',
100
+ removalPrice: 500,
93
101
  position: { x: 26.5, y: 1.5, z: -2 },
94
102
  rotation: Quaternion.fromEuler(0, 0, 0),
95
103
  width: 5,
104
+ unlockIds: [ 'gallery', 'dining' ],
96
105
  },
97
106
  {
98
- name: 'Unlock Kitchen (South)',
99
- removalPrice: 200,
107
+ name: 'Kitchen (South)',
108
+ removalPrice: 500,
100
109
  position: { x: -22, y: 1.5, z: -2 },
101
110
  rotation: Quaternion.fromEuler(0, 0, 0),
102
111
  width: 5,
112
+ unlockIds: [ 'kitchen', 'parlor' ],
103
113
  },
104
114
  {
105
- name: 'Unlock Vault',
106
- removalPrice: 200,
115
+ name: 'Vault',
116
+ removalPrice: 1000,
107
117
  position: { x: 0.5, y: 1.5, z: -26 },
108
118
  rotation: Quaternion.fromEuler(0, 0, 0),
109
119
  width: 3,
120
+ unlockIds: [ 'vault' ],
110
121
  },
111
122
  {
112
- name: 'Unlock Treasure Room (West)',
123
+ name: 'Treasure Room (West)',
113
124
  removalPrice: 75,
114
125
  position: { x: -15, y: 1.5, z: -19 },
115
126
  rotation: Quaternion.fromEuler(0, 90, 0),
116
127
  width: 5,
128
+ unlockIds: [ 'treasure', 'kitchen' ],
117
129
  },
118
130
  {
119
- name: 'Unlock Treasure Room (East)',
131
+ name: 'Treasure Room (East)',
120
132
  removalPrice: 75,
121
133
  position: { x: 20, y: 1.5, z: -19 },
122
134
  rotation: Quaternion.fromEuler(0, 90, 0),
123
135
  width: 5,
136
+ unlockIds: [ 'treasure', 'gallery' ],
137
+ },
138
+ ]
139
+
140
+ export const WEAPON_CRATES = [
141
+ {
142
+ name: 'Rusty Weapon Crate',
143
+ position: { x: -3, y: 1.5, z: 16.5 },
144
+ rotation: Quaternion.fromEuler(0, 0, 0),
145
+ price: 100,
146
+ rollableWeaponIds: [ 'pistol', 'shotgun', 'ar15' ],
147
+ },
148
+ {
149
+ name: 'Rusty Weapon Crate',
150
+ position: { x: 10.5, y: 1.5, z: 16.5 },
151
+ rotation: Quaternion.fromEuler(0, 0, 0),
152
+ price: 100,
153
+ rollableWeaponIds: [ 'pistol', 'shotgun', 'ar15' ],
154
+ },
155
+ {
156
+ name: 'Weapon Crate',
157
+ position: { x: -27.5, y: 1.5, z: 2.5 },
158
+ rotation: Quaternion.fromEuler(0, 90, 0),
159
+ price: 200,
160
+ rollableWeaponIds: [ 'shotgun', 'ar15', 'auto-pistol', ],
124
161
  },
125
- ]
162
+ {
163
+ name: 'Weapon Crate',
164
+ position: { x: 22, y: 1.5, z: 7 },
165
+ rotation: Quaternion.fromEuler(0, -45, 0),
166
+ price: 200,
167
+ rollableWeaponIds: [ 'shotgun', 'ar15', 'auto-pistol', ],
168
+ },
169
+ {
170
+ name: 'Weapon Crate',
171
+ position: { x: -23.5, y: 1.5, z: -24.5 },
172
+ rotation: Quaternion.fromEuler(0, 0, 0),
173
+ price: 200,
174
+ rollableWeaponIds: [ 'shotgun', 'ar15', 'auto-pistol', ],
175
+ },
176
+ {
177
+ name: 'Elite Weapon Crate',
178
+ position: { x: 31, y: 1.5, z: -14.5 },
179
+ rotation: Quaternion.fromEuler(0, 45, 0),
180
+ price: 300,
181
+ rollableWeaponIds: [ 'ak47', 'ar15', 'auto-pistol', 'auto-shotgun', ],
182
+ },
183
+ {
184
+ name: 'Elite Weapon Crate',
185
+ position: { x: 2.5, y: 2.5, z: -4.5 },
186
+ rotation: Quaternion.fromEuler(0, 0, 0),
187
+ price: 300,
188
+ rollableWeaponIds: [ 'ak47', 'ar15', 'auto-pistol', 'auto-shotgun', ],
189
+ },
190
+ {
191
+ name: 'Elite Weapon Crate',
192
+ position: { x: 0.5, y: 1.5, z: -29.5 },
193
+ rotation: Quaternion.fromEuler(0, 0, 0),
194
+ price: 300,
195
+ rollableWeaponIds: [ 'ak47', 'ar15', 'auto-pistol', 'auto-shotgun', ],
196
+ },
197
+ ]
198
+
199
+ export const ENEMY_SPAWN_POINTS: Record<string, Vector3Like[]> = {
200
+ start: [
201
+ { x: -20, y: 3, z: 34 },
202
+ { x: 12, y: 3, z: 36 },
203
+ { x: 26, y: 3, z: 20 },
204
+ { x: 17, y: 3, z: 13.5 },
205
+ ],
206
+ theater: [
207
+ { x: -13.5, y: 3, z: 10 },
208
+ ],
209
+ parlor: [
210
+ { x: -36, y: 3, z: 23 },
211
+ { x: -35, y: 3, z: -5 },
212
+ ],
213
+ dining: [
214
+ { x: 46, y: 3, z: 16 },
215
+ { x: 41, y: 3, z: -5 },
216
+ ],
217
+ gallery: [
218
+ { x: 35, y: 3, z: -39 },
219
+ { x: 12, y: 3, z: -40 },
220
+ ],
221
+ kitchen: [
222
+ { x: -28, y: 3, z: -32 },
223
+ { x: -40, y: 3, z: -5 },
224
+ ],
225
+ treasure: [
226
+ { x: -13, y: 3, z: -27 },
227
+ { x: 0, y: 3, z: -37 },
228
+ ],
229
+ };
@@ -1,46 +1,29 @@
1
- import { startServer, Collider, ColliderShape, CollisionGroup } from 'hytopia';
1
+ import { startServer } from 'hytopia';
2
2
  import GamePlayerEntity from './classes/GamePlayerEntity';
3
- import PurchaseBarrierEntity from './classes/PurchaseBarrierEntity';
4
- import { INVISIBLE_WALLS, PURCHASE_BARRIERS } from './gameConfig';
5
3
  import worldMap from './assets/maps/terrain.json';
6
4
 
5
+ import GameManager from './classes/GameManager';
6
+
7
7
  startServer(world => {
8
8
  // Load map.
9
9
  world.loadMap(worldMap);
10
10
 
11
+ //world.simulation.enableDebugRaycasting(true);
12
+
11
13
  // Setup lighting
12
- world.setAmbientLightIntensity(0.3);
14
+ world.setAmbientLightIntensity(0.0001);
13
15
  world.setAmbientLightColor({ r: 255, g: 192, b: 192 });
14
- world.setDirectionalLightIntensity(0.8);
15
-
16
- // Setup invisible walls that only enemies can pass through
17
- INVISIBLE_WALLS.forEach(wall => {
18
- const wallCollider = new Collider({
19
- shape: ColliderShape.BLOCK,
20
- halfExtents: wall.halfExtents,
21
- relativePosition: wall.position, // since this is not attached to a rigid body, relative position is realtive to the world global coordinate space.
22
- collisionGroups: {
23
- belongsTo: [ CollisionGroup.BLOCK ],
24
- collidesWith: [ CollisionGroup.PLAYER ],
25
- },
26
- });
27
-
28
- wallCollider.addToSimulation(world.simulation);
29
- });
30
-
31
- // Setup purchase barriers
32
- PURCHASE_BARRIERS.forEach(barrier => {
33
- const purchaseBarrier = new PurchaseBarrierEntity({
34
- name: barrier.name,
35
- removalPrice: barrier.removalPrice,
36
- width: barrier.width,
37
- });
38
-
39
- purchaseBarrier.spawn(world, barrier.position, barrier.rotation);
40
- });
16
+ world.setDirectionalLightIntensity(0.0001);
17
+
18
+ // Setup game
19
+ GameManager.instance.setupGame(world);
41
20
 
42
21
  // Spawn a player entity when a player joins the game.
43
22
  world.onPlayerJoin = player => {
23
+ if (GameManager.instance.isStarted) {
24
+ return world.chatManager.sendPlayerMessage(player, 'This round has already started, please wait for the next round. You can fly around as a spectator.', 'FF0000');
25
+ }
26
+
44
27
  const playerEntity = new GamePlayerEntity(player);
45
28
 
46
29
  playerEntity.spawn(world, { x: 2, y: 10, z: 19 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hytopia",
3
- "version": "0.1.98",
3
+ "version": "0.2.0",
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": {