hytopia 0.3.6 → 0.3.8
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/boilerplate/assets/map.json +191 -43
- package/docs/server.playercameramode.md +14 -0
- package/examples/hygrounds/README.md +0 -0
- package/examples/hygrounds/assets/audio/sfx/chest-open-1.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/chest-open-2.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/machine-gun-reload.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/machine-gun-shoot.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/medpack-consume.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/mining-drill-drilling.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/pistol-reload.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/pistol-shoot.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/player-hurt.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/potion-consume.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/rifle-reload.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/rifle-shoot.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/rocket-launcher-explosion.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/rocket-launcher-reload.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/rocket-launcher-shoot.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/shield.png +0 -0
- package/examples/hygrounds/assets/audio/sfx/shotgun-reload.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/shotgun-shoot.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/sniper-reload.mp3 +0 -0
- package/examples/hygrounds/assets/audio/sfx/sniper-shoot.mp3 +0 -0
- package/examples/hygrounds/assets/icons/ak-47.png +0 -0
- package/examples/hygrounds/assets/icons/ammo.png +0 -0
- package/examples/hygrounds/assets/icons/auto-shotgun.png +0 -0
- package/examples/hygrounds/assets/icons/auto-sniper.png +0 -0
- package/examples/hygrounds/assets/icons/block.png +0 -0
- package/examples/hygrounds/assets/icons/bolt-action-sniper.png +0 -0
- package/examples/hygrounds/assets/icons/crown-bronze.png +0 -0
- package/examples/hygrounds/assets/icons/crown-gold.png +0 -0
- package/examples/hygrounds/assets/icons/crown-silver.png +0 -0
- package/examples/hygrounds/assets/icons/gravity-potion.png +0 -0
- package/examples/hygrounds/assets/icons/heart.png +0 -0
- package/examples/hygrounds/assets/icons/light-machine-gun.png +0 -0
- package/examples/hygrounds/assets/icons/medpack.png +0 -0
- package/examples/hygrounds/assets/icons/mining-drill.png +0 -0
- package/examples/hygrounds/assets/icons/pickaxe.png +0 -0
- package/examples/hygrounds/assets/icons/pistol.png +0 -0
- package/examples/hygrounds/assets/icons/revolver.png +0 -0
- package/examples/hygrounds/assets/icons/rocket-launcher.png +0 -0
- package/examples/hygrounds/assets/icons/shield-potion.png +0 -0
- package/examples/hygrounds/assets/icons/shield.png +0 -0
- package/examples/hygrounds/assets/icons/shotgun.png +0 -0
- package/examples/hygrounds/assets/icons/submachine-gun.png +0 -0
- package/examples/hygrounds/assets/map.json +31796 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion/explosion-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion/explosion.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion/explosion.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion-2/explosion-2-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion-2/explosion-2.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion-2/explosion-2.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion-3/explosion-3-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion-3/explosion-3.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/explosion-3/explosion-3.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/environment/.optimized/helicopter/helicopter-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/helicopter/helicopter.glb +0 -0
- package/examples/hygrounds/assets/models/environment/.optimized/helicopter/helicopter.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/environment/chest.gltf +1 -0
- package/examples/hygrounds/assets/models/environment/explosion.glb +0 -0
- package/examples/hygrounds/assets/models/environment/muzzle-flash.gltf +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/auto-sniper/auto-sniper-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/auto-sniper/auto-sniper.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/auto-sniper/auto-sniper.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/gravity-potion/gravity-potion-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/gravity-potion/gravity-potion.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/gravity-potion/gravity-potion.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/medkit/medkit-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/medkit/medkit.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/medkit/medkit.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/medpack/medpack-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/medpack/medpack.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/medpack/medpack.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/mining-drill/mining-drill-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/mining-drill/mining-drill.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/mining-drill/mining-drill.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/revolver/revolver-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/revolver/revolver.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/revolver/revolver.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/rocket-missile/rocket-missile-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/rocket-missile/rocket-missile.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/rocket-missile/rocket-missile.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/shield-potion/shield-potion-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/shield-potion/shield-potion.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/shield-potion/shield-potion.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/shield-potion-2/shield-potion-2-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/shield-potion-2/shield-potion-2.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/shield-potion-2/shield-potion-2.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/.optimized/submachine-gun/submachine-gun-named-nodes.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/submachine-gun/submachine-gun.glb +0 -0
- package/examples/hygrounds/assets/models/items/.optimized/submachine-gun/submachine-gun.glb.md5 +1 -0
- package/examples/hygrounds/assets/models/items/ak-47.glb +0 -0
- package/examples/hygrounds/assets/models/items/auto-shotgun.glb +0 -0
- package/examples/hygrounds/assets/models/items/auto-sniper.glb +0 -0
- package/examples/hygrounds/assets/models/items/bolt-action-sniper.glb +0 -0
- package/examples/hygrounds/assets/models/items/gravity-potion.glb +0 -0
- package/examples/hygrounds/assets/models/items/light-machine-gun.glb +0 -0
- package/examples/hygrounds/assets/models/items/medpack.glb +0 -0
- package/examples/hygrounds/assets/models/items/mining-drill.glb +0 -0
- package/examples/hygrounds/assets/models/items/pickaxe.gltf +1 -0
- package/examples/hygrounds/assets/models/items/pistol.glb +0 -0
- package/examples/hygrounds/assets/models/items/revolver.glb +0 -0
- package/examples/hygrounds/assets/models/items/rocket-launcher.glb +0 -0
- package/examples/hygrounds/assets/models/items/rocket-missile.glb +0 -0
- package/examples/hygrounds/assets/models/items/shield-potion.glb +0 -0
- package/examples/hygrounds/assets/models/items/shotgun.glb +0 -0
- package/examples/hygrounds/assets/models/items/submachine-gun.glb +0 -0
- package/examples/hygrounds/assets/models/players/soldier-player.gltf +1 -0
- package/examples/hygrounds/assets/ui/images/scope.png +0 -0
- package/examples/hygrounds/assets/ui/index.html +1122 -0
- package/examples/hygrounds/bun.lock +503 -0
- package/examples/hygrounds/classes/ChestEntity.ts +133 -0
- package/examples/hygrounds/classes/GameManager.ts +422 -0
- package/examples/hygrounds/classes/GamePlayerEntity.ts +595 -0
- package/examples/hygrounds/classes/GunEntity.ts +259 -0
- package/examples/hygrounds/classes/ItemEntity.ts +225 -0
- package/examples/hygrounds/classes/ItemFactory.ts +61 -0
- package/examples/hygrounds/classes/MeleeWeaponEntity.ts +138 -0
- package/examples/hygrounds/classes/TerrainDamageManager.ts +56 -0
- package/examples/hygrounds/classes/items/GravityPotionEntity.ts +48 -0
- package/examples/hygrounds/classes/items/MedPackEntity.ts +43 -0
- package/examples/hygrounds/classes/items/ShieldPotionEntity.ts +43 -0
- package/examples/hygrounds/classes/weapons/AK47Entity.ts +43 -0
- package/examples/hygrounds/classes/weapons/AutoShotgunEntity.ts +80 -0
- package/examples/hygrounds/classes/weapons/AutoSniperEntity.ts +43 -0
- package/examples/hygrounds/classes/weapons/BoltActionSniperEntity.ts +46 -0
- package/examples/hygrounds/classes/weapons/LightMachineGunEntity.ts +43 -0
- package/examples/hygrounds/classes/weapons/MiningDrillEntity.ts +38 -0
- package/examples/hygrounds/classes/weapons/PickaxeEntity.ts +38 -0
- package/examples/hygrounds/classes/weapons/PistolEntity.ts +46 -0
- package/examples/hygrounds/classes/weapons/RevolverEntity.ts +46 -0
- package/examples/hygrounds/classes/weapons/RocketLauncherEntity.ts +195 -0
- package/examples/hygrounds/classes/weapons/ShotgunEntity.ts +84 -0
- package/examples/hygrounds/classes/weapons/SubmachineGunEntity.ts +43 -0
- package/examples/hygrounds/gameConfig.ts +430 -0
- package/examples/hygrounds/index.ts +41 -0
- package/examples/hygrounds/package.json +16 -0
- package/examples/player-persistence/README.md +3 -0
- package/examples/player-persistence/assets/map.json +2623 -0
- package/examples/player-persistence/dev/persistence/player-player-1.json +6 -0
- package/examples/player-persistence/dev/persistence/test.json +3 -0
- package/examples/player-persistence/index.ts +126 -0
- package/examples/player-persistence/package.json +16 -0
- package/package.json +1 -1
- package/server.api.json +21 -0
- package/server.d.ts +2 -1
- package/server.js +113 -113
@@ -0,0 +1,195 @@
|
|
1
|
+
import { Audio, CollisionGroup, Entity, Quaternion, Vector3Like, QuaternionLike, RigidBodyType, EntityEvent, Vector3, Collider } from 'hytopia';
|
2
|
+
import GunEntity from '../GunEntity';
|
3
|
+
import { BEDROCK_BLOCK_ID } from '../../gameConfig';
|
4
|
+
import type { GunEntityOptions } from '../GunEntity';
|
5
|
+
import type GamePlayerEntity from '../GamePlayerEntity';
|
6
|
+
|
7
|
+
const ROCKET_DESTRUCTION_RADIUS = 4;
|
8
|
+
|
9
|
+
const DEFAULT_ROCKET_LAUNCHER_OPTIONS: GunEntityOptions = {
|
10
|
+
ammo: 1,
|
11
|
+
damage: 80,
|
12
|
+
fireRate: 0.8,
|
13
|
+
heldHand: 'right',
|
14
|
+
iconImageUri: 'icons/rocket-launcher.png',
|
15
|
+
idleAnimation: 'idle_gun_right',
|
16
|
+
mlAnimation: 'shoot_gun_right',
|
17
|
+
name: 'Rocket Launcher',
|
18
|
+
maxAmmo: 1,
|
19
|
+
totalAmmo: 5,
|
20
|
+
modelUri: 'models/items/rocket-launcher.glb',
|
21
|
+
modelScale: 1.3,
|
22
|
+
range: 8,
|
23
|
+
reloadAudioUri: 'audio/sfx/rocket-launcher-reload.mp3',
|
24
|
+
reloadTimeMs: 2500,
|
25
|
+
shootAudioUri: 'audio/sfx/rocket-launcher-shoot.mp3',
|
26
|
+
};
|
27
|
+
|
28
|
+
export default class RocketLauncherEntity extends GunEntity {
|
29
|
+
public constructor(options: Partial<GunEntityOptions> = {}) {
|
30
|
+
super({ ...DEFAULT_ROCKET_LAUNCHER_OPTIONS, ...options });
|
31
|
+
}
|
32
|
+
|
33
|
+
public override shoot(): void {
|
34
|
+
if (!this.parent || !this.processShoot()) return;
|
35
|
+
|
36
|
+
super.shoot();
|
37
|
+
|
38
|
+
// Cancel input since rocket launcher requires click-to-shoot
|
39
|
+
(this.parent as GamePlayerEntity).player.input.ml = false;
|
40
|
+
}
|
41
|
+
|
42
|
+
public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
|
43
|
+
return {
|
44
|
+
position: { x: 0.03, y: 0.6, z: -1.5 },
|
45
|
+
rotation: Quaternion.fromEuler(0, 90, 0),
|
46
|
+
};
|
47
|
+
}
|
48
|
+
|
49
|
+
public override equip(): void {
|
50
|
+
super.equip();
|
51
|
+
|
52
|
+
this.setPosition({ x: 0, y: 0.3, z: 0.4 });
|
53
|
+
}
|
54
|
+
|
55
|
+
public override shootRaycast(origin: Vector3Like, direction: Vector3Like, length: number) {
|
56
|
+
// Instead of a raycast, we'll spawn a projectile that on collision with a block or entity explodes
|
57
|
+
// and deals damage and blows up blocks
|
58
|
+
if (!this.parent?.world) {
|
59
|
+
return;
|
60
|
+
}
|
61
|
+
|
62
|
+
const rocketMissileEntity = new Entity({
|
63
|
+
modelUri: 'models/items/rocket-missile.glb',
|
64
|
+
modelScale: 0.75,
|
65
|
+
rigidBodyOptions: {
|
66
|
+
type: RigidBodyType.KINEMATIC_VELOCITY,
|
67
|
+
colliders: [
|
68
|
+
{
|
69
|
+
...Collider.optionsFromModelUri('models/items/rocket-missile.glb', 0.75),
|
70
|
+
collisionGroups: {
|
71
|
+
belongsTo: [ CollisionGroup.ENTITY ],
|
72
|
+
collidesWith: [ CollisionGroup.BLOCK ],
|
73
|
+
}
|
74
|
+
},
|
75
|
+
],
|
76
|
+
linearVelocity: {
|
77
|
+
x: direction.x * 30,
|
78
|
+
y: direction.y * 30,
|
79
|
+
z: direction.z * 30,
|
80
|
+
},
|
81
|
+
}
|
82
|
+
});
|
83
|
+
|
84
|
+
// Create a despawn timer if it doesn't hit
|
85
|
+
setTimeout(() => {
|
86
|
+
if (rocketMissileEntity.isSpawned) {
|
87
|
+
rocketMissileEntity.despawn();
|
88
|
+
}
|
89
|
+
}, 3000);
|
90
|
+
|
91
|
+
// Convert direction vector to quaternion that faces in the direction vector
|
92
|
+
const directionQuat = Quaternion.fromEuler(
|
93
|
+
Math.atan2(-direction.y, Math.sqrt(direction.x * direction.x + direction.z * direction.z)) * 180 / Math.PI,
|
94
|
+
Math.atan2(direction.x, direction.z) * 180 / Math.PI,
|
95
|
+
0
|
96
|
+
);
|
97
|
+
|
98
|
+
rocketMissileEntity.on(EntityEvent.BLOCK_COLLISION, ({ blockType, colliderHandleA, colliderHandleB }) => {
|
99
|
+
if (!this.parent?.world || !rocketMissileEntity.isSpawned || blockType.isLiquid) {
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
|
103
|
+
const { world } = this.parent;
|
104
|
+
const contactManifold = world.simulation.getContactManifolds(colliderHandleA, colliderHandleB)[0];
|
105
|
+
|
106
|
+
if (!contactManifold) {
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
|
110
|
+
const contactPoint = contactManifold.contactPoints[0];
|
111
|
+
const contactCoordinate = {
|
112
|
+
x: Math.floor(contactPoint.x),
|
113
|
+
y: Math.floor(contactPoint.y),
|
114
|
+
z: Math.floor(contactPoint.z)
|
115
|
+
};
|
116
|
+
|
117
|
+
// Deal damage to nearby players
|
118
|
+
this.parent.world.entityManager.getAllPlayerEntities().forEach(playerEntity => {
|
119
|
+
const playerPos = Vector3.fromVector3Like(playerEntity.position);
|
120
|
+
const contactPos = Vector3.fromVector3Like(contactPoint);
|
121
|
+
const distance = playerPos.distance(contactPos);
|
122
|
+
|
123
|
+
if (distance <= ROCKET_DESTRUCTION_RADIUS) {
|
124
|
+
(playerEntity as GamePlayerEntity).takeDamage(this.damage, direction, this.parent as GamePlayerEntity);
|
125
|
+
}
|
126
|
+
});
|
127
|
+
|
128
|
+
// Break blocks
|
129
|
+
for (let dx = -ROCKET_DESTRUCTION_RADIUS; dx <= ROCKET_DESTRUCTION_RADIUS; dx++) {
|
130
|
+
for (let dy = -ROCKET_DESTRUCTION_RADIUS; dy <= ROCKET_DESTRUCTION_RADIUS; dy++) {
|
131
|
+
for (let dz = -ROCKET_DESTRUCTION_RADIUS; dz <= ROCKET_DESTRUCTION_RADIUS; dz++) {
|
132
|
+
// Calculate distance from center of explosion
|
133
|
+
const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
134
|
+
|
135
|
+
// Only destroy blocks within the spherical radius
|
136
|
+
if (distance > ROCKET_DESTRUCTION_RADIUS) {
|
137
|
+
continue;
|
138
|
+
}
|
139
|
+
|
140
|
+
const coordinate = {
|
141
|
+
x: contactCoordinate.x + dx,
|
142
|
+
y: contactCoordinate.y + dy,
|
143
|
+
z: contactCoordinate.z + dz
|
144
|
+
}
|
145
|
+
|
146
|
+
// do not destroy bedrock!
|
147
|
+
if (world.chunkLattice.getBlockId(coordinate) !== BEDROCK_BLOCK_ID) {
|
148
|
+
world.chunkLattice.setBlock(coordinate, 0);
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
// Explosion Visual
|
155
|
+
const explosionEntity = new Entity({
|
156
|
+
modelUri: 'models/environment/explosion.glb',
|
157
|
+
modelScale: 0.2,
|
158
|
+
rigidBodyOptions: { type: RigidBodyType.KINEMATIC_POSITION },
|
159
|
+
});
|
160
|
+
|
161
|
+
const explosionDirectionQuat = Quaternion.fromEuler(
|
162
|
+
Math.atan2(-direction.y, Math.sqrt(direction.x * direction.x + direction.z * direction.z)) * 180 / Math.PI + 90,
|
163
|
+
Math.atan2(direction.x, direction.z) * 180 / Math.PI + 180, // Add 180 degrees to invert direction
|
164
|
+
0
|
165
|
+
);
|
166
|
+
|
167
|
+
explosionEntity.spawn(world, contactPoint, explosionDirectionQuat);
|
168
|
+
explosionEntity.setCollisionGroupsForSolidColliders({
|
169
|
+
belongsTo: [],
|
170
|
+
collidesWith: [],
|
171
|
+
})
|
172
|
+
|
173
|
+
const explosionEffectInterval = setInterval(() => {
|
174
|
+
if (explosionEntity.opacity <= 0) {
|
175
|
+
explosionEntity.despawn();
|
176
|
+
clearInterval(explosionEffectInterval);
|
177
|
+
return;
|
178
|
+
}
|
179
|
+
|
180
|
+
explosionEntity.setOpacity(explosionEntity.opacity - 0.1);
|
181
|
+
}, 100);
|
182
|
+
|
183
|
+
// Explosion Audio
|
184
|
+
(new Audio({
|
185
|
+
uri: 'audio/sfx/rocket-launcher-explosion.mp3',
|
186
|
+
referenceDistance: 20,
|
187
|
+
volume: 0.4,
|
188
|
+
})).play(world);
|
189
|
+
|
190
|
+
rocketMissileEntity.despawn();
|
191
|
+
});
|
192
|
+
|
193
|
+
rocketMissileEntity.spawn(this.parent.world, origin, directionQuat);
|
194
|
+
}
|
195
|
+
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import { Quaternion, Vector3Like, QuaternionLike } from 'hytopia';
|
2
|
+
import GunEntity from '../GunEntity';
|
3
|
+
import type { GunEntityOptions } from '../GunEntity';
|
4
|
+
import type GamePlayerEntity from '../GamePlayerEntity';
|
5
|
+
|
6
|
+
const DEFAULT_SHOTGUN_OPTIONS: GunEntityOptions = {
|
7
|
+
ammo: 4,
|
8
|
+
damage: 13,
|
9
|
+
fireRate: 0.8,
|
10
|
+
heldHand: 'both',
|
11
|
+
iconImageUri: 'icons/shotgun.png',
|
12
|
+
idleAnimation: 'idle_gun_both',
|
13
|
+
mlAnimation: 'shoot_gun_both',
|
14
|
+
name: 'Shotgun',
|
15
|
+
maxAmmo: 4,
|
16
|
+
totalAmmo: 24,
|
17
|
+
modelUri: 'models/items/shotgun.glb',
|
18
|
+
modelScale: 1.2,
|
19
|
+
range: 8,
|
20
|
+
reloadAudioUri: 'audio/sfx/shotgun-reload.mp3',
|
21
|
+
reloadTimeMs: 3000,
|
22
|
+
shootAudioUri: 'audio/sfx/shotgun-shoot.mp3',
|
23
|
+
};
|
24
|
+
|
25
|
+
export default class ShotgunEntity extends GunEntity {
|
26
|
+
public constructor(options: Partial<GunEntityOptions> = {}) {
|
27
|
+
super({ ...DEFAULT_SHOTGUN_OPTIONS, ...options });
|
28
|
+
}
|
29
|
+
|
30
|
+
public override shoot(): void {
|
31
|
+
if (!this.parent || !this.processShoot()) return;
|
32
|
+
|
33
|
+
super.shoot();
|
34
|
+
|
35
|
+
// Cancel input since shotgun requires click-to-shoot
|
36
|
+
(this.parent as GamePlayerEntity).player.input.ml = false;
|
37
|
+
}
|
38
|
+
|
39
|
+
public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
|
40
|
+
return {
|
41
|
+
position: { x: 0.03, y: 0.1, z: -1.5 },
|
42
|
+
rotation: Quaternion.fromEuler(0, 90, 0),
|
43
|
+
};
|
44
|
+
}
|
45
|
+
|
46
|
+
public override shootRaycast(origin: Vector3Like, direction: Vector3Like, length: number) {
|
47
|
+
// Create spread pattern for shotgun pellets using angles relative to direction
|
48
|
+
const spreadAngles = [
|
49
|
+
{ x: 0, y: 0 }, // Center
|
50
|
+
{ x: 0.05, y: 0.05 }, // Upper right
|
51
|
+
{ x: -0.05, y: 0.05 }, // Upper left
|
52
|
+
{ x: 0.07, y: 0 }, // Right
|
53
|
+
{ x: -0.07, y: 0 }, // Left
|
54
|
+
{ x: 0.05, y: -0.05 }, // Lower right
|
55
|
+
{ x: -0.05, y: -0.05 } // Lower left
|
56
|
+
];
|
57
|
+
|
58
|
+
// Fire each pellet with spread applied to original direction
|
59
|
+
for (const angle of spreadAngles) {
|
60
|
+
// Calculate spread direction relative to original direction
|
61
|
+
const spreadDirection = {
|
62
|
+
x: direction.x + (direction.z * angle.x), // Add horizontal spread
|
63
|
+
y: direction.y + angle.y, // Add vertical spread
|
64
|
+
z: direction.z - (direction.x * angle.x) // Maintain direction magnitude
|
65
|
+
};
|
66
|
+
|
67
|
+
// Normalize the spread direction to maintain consistent range
|
68
|
+
const magnitude = Math.sqrt(
|
69
|
+
spreadDirection.x * spreadDirection.x +
|
70
|
+
spreadDirection.y * spreadDirection.y +
|
71
|
+
spreadDirection.z * spreadDirection.z
|
72
|
+
);
|
73
|
+
|
74
|
+
const normalizedDirection = {
|
75
|
+
x: spreadDirection.x / magnitude,
|
76
|
+
y: spreadDirection.y / magnitude,
|
77
|
+
z: spreadDirection.z / magnitude
|
78
|
+
};
|
79
|
+
|
80
|
+
super.shootRaycast(origin, normalizedDirection, length);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { Quaternion, Vector3Like, QuaternionLike } from 'hytopia';
|
2
|
+
import GunEntity from '../GunEntity';
|
3
|
+
import type { GunEntityOptions } from '../GunEntity';
|
4
|
+
|
5
|
+
const DEFAULT_SUBMACHINE_GUN_OPTIONS: GunEntityOptions = {
|
6
|
+
ammo: 60,
|
7
|
+
damage: 6,
|
8
|
+
fireRate: 12,
|
9
|
+
heldHand: 'right',
|
10
|
+
iconImageUri: 'icons/submachine-gun.png',
|
11
|
+
idleAnimation: 'idle_gun_right',
|
12
|
+
mlAnimation: 'shoot_gun_right',
|
13
|
+
name: 'Submachine Gun',
|
14
|
+
maxAmmo: 60,
|
15
|
+
totalAmmo: 240,
|
16
|
+
scopeZoom: 1.35,
|
17
|
+
modelUri: 'models/items/submachine-gun.glb',
|
18
|
+
modelScale: 1.3,
|
19
|
+
range: 35,
|
20
|
+
reloadAudioUri: 'audio/sfx/rifle-reload.mp3',
|
21
|
+
reloadTimeMs: 1500,
|
22
|
+
shootAudioUri: 'audio/sfx/pistol-shoot.mp3',
|
23
|
+
};
|
24
|
+
|
25
|
+
export default class SubmachineGunEntity extends GunEntity {
|
26
|
+
public constructor(options: Partial<GunEntityOptions> = {}) {
|
27
|
+
super({ ...DEFAULT_SUBMACHINE_GUN_OPTIONS, ...options });
|
28
|
+
}
|
29
|
+
|
30
|
+
public override shoot(): void {
|
31
|
+
if (!this.parent || !this.processShoot()) return;
|
32
|
+
|
33
|
+
super.shoot();
|
34
|
+
}
|
35
|
+
|
36
|
+
public override getMuzzleFlashPositionRotation(): { position: Vector3Like, rotation: QuaternionLike } {
|
37
|
+
return {
|
38
|
+
position: { x: 0, y: 0.05, z: -0.95 },
|
39
|
+
rotation: Quaternion.fromEuler(0, 90, 0),
|
40
|
+
};
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|