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.
- package/docs/server.entity.height.md +13 -0
- package/docs/server.entity.md +22 -1
- package/docs/server.entity.opacity.md +1 -1
- package/docs/server.modelregistry.getheight.md +55 -0
- package/docs/server.modelregistry.md +14 -0
- package/docs/server.player.md +21 -0
- package/docs/server.player.profilepictureurl.md +13 -0
- package/examples/pathfinding/index.ts +4 -4
- package/examples/payload-game/index.ts +12 -10
- package/examples/zombies-fps/README.md +5 -0
- package/examples/zombies-fps/assets/audio/music/bg.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/pistol-reload.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/pistol-shoot.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/player-hurt.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/purchase.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/rifle-reload.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/rifle-shoot.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/ripper-idle.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/roulette.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/shotgun-reload.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/shotgun-shoot.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/wave-start.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/zombie-idle.mp3 +0 -0
- package/examples/zombies-fps/assets/icons/ak-47.png +0 -0
- package/examples/zombies-fps/assets/icons/ar-15.png +0 -0
- package/examples/zombies-fps/assets/icons/auto-pistol.png +0 -0
- package/examples/zombies-fps/assets/icons/auto-shotgun.png +0 -0
- package/examples/zombies-fps/assets/icons/heart.png +0 -0
- package/examples/zombies-fps/assets/icons/pistol.png +0 -0
- package/examples/zombies-fps/assets/icons/shotgun.png +0 -0
- package/examples/zombies-fps/assets/models/environment/bombbox.gltf +1 -0
- package/examples/zombies-fps/assets/models/environment/bullet-hole.gltf +1 -0
- package/examples/zombies-fps/assets/models/environment/healthkit.gltf +1 -0
- package/examples/zombies-fps/assets/models/environment/muzzle-flash.gltf +1 -0
- package/examples/zombies-fps/assets/models/items/ak-47.glb +0 -0
- package/examples/zombies-fps/assets/models/items/ar-15.glb +0 -0
- package/examples/zombies-fps/assets/models/items/auto-pistol.glb +0 -0
- package/examples/zombies-fps/assets/models/items/auto-shotgun.glb +0 -0
- package/examples/zombies-fps/assets/models/items/shotgun.glb +0 -0
- package/examples/zombies-fps/assets/models/npcs/ripper-boss.gltf +1 -0
- package/examples/zombies-fps/assets/models/players/soldier-player.gltf +1 -1
- package/examples/zombies-fps/assets/models/projectiles/bullet-trace.gltf +1 -0
- package/examples/zombies-fps/assets/ui/index.html +620 -27
- package/examples/zombies-fps/classes/EnemyEntity.ts +183 -4
- package/examples/zombies-fps/classes/GameManager.ts +165 -0
- package/examples/zombies-fps/classes/GamePlayerEntity.ts +263 -14
- package/examples/zombies-fps/classes/GunEntity.ts +225 -13
- package/examples/zombies-fps/classes/InteractableEntity.ts +9 -0
- package/examples/zombies-fps/classes/PurchaseBarrierEntity.ts +70 -17
- package/examples/zombies-fps/classes/WeaponCrateEntity.ts +173 -0
- package/examples/zombies-fps/classes/enemies/RipperEntity.ts +67 -0
- package/examples/zombies-fps/classes/enemies/ZombieEntity.ts +30 -0
- package/examples/zombies-fps/classes/guns/AK47Entity.ts +43 -0
- package/examples/zombies-fps/classes/guns/AR15Entity.ts +32 -0
- package/examples/zombies-fps/classes/guns/AutoPistolEntity.ts +36 -0
- package/examples/zombies-fps/classes/guns/AutoShotgunEntity.ts +37 -0
- package/examples/zombies-fps/classes/guns/PistolEntity.ts +23 -15
- package/examples/zombies-fps/classes/guns/ShotgunEntity.ts +92 -0
- package/examples/zombies-fps/gameConfig.ts +125 -21
- package/examples/zombies-fps/index.ts +14 -31
- package/package.json +1 -1
- package/server.api.json +126 -18
- package/server.d.ts +17 -5
- package/server.js +98 -90
- package/tsdoc-metadata.json +1 -1
- package/examples/zombies-fps/assets/audio/sfx/pistol-shoot-1.mp3 +0 -0
- package/examples/zombies-fps/assets/audio/sfx/pistol-shoot-2.mp3 +0 -0
- 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
|
-
|
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 ??
|
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
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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: -
|
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: '
|
57
|
-
removalPrice:
|
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: '
|
64
|
-
removalPrice:
|
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: '
|
71
|
-
removalPrice:
|
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: '
|
78
|
-
removalPrice:
|
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: '
|
85
|
-
removalPrice:
|
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: '
|
92
|
-
removalPrice:
|
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: '
|
99
|
-
removalPrice:
|
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: '
|
106
|
-
removalPrice:
|
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: '
|
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: '
|
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
|
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.
|
14
|
+
world.setAmbientLightIntensity(0.0001);
|
13
15
|
world.setAmbientLightColor({ r: 255, g: 192, b: 192 });
|
14
|
-
world.setDirectionalLightIntensity(0.
|
15
|
-
|
16
|
-
// Setup
|
17
|
-
|
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 });
|