h1z1-server 0.47.2-9 → 0.47.2
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/package.json +1 -1
- package/src/packets/ClientProtocol/ClientProtocol_1080/shared.ts +10 -9
- package/src/servers/LoginZoneConnection/shared/baselzconnection.ts +3 -1
- package/src/servers/SoeServer/soeserver.ts +6 -2
- package/src/servers/ZoneServer2016/entities/basefullcharacter.ts +4 -0
- package/src/servers/ZoneServer2016/entities/constructionchildentity.ts +4 -0
- package/src/servers/ZoneServer2016/entities/itemobject.ts +134 -1
- package/src/servers/ZoneServer2016/managers/constructionmanager.ts +5 -1
- package/src/servers/ZoneServer2016/managers/craftmanager.ts +8 -0
- package/src/servers/ZoneServer2016/zoneserver.ts +28 -2
- package/src/utils/utils.ts +103 -0
package/package.json
CHANGED
|
@@ -1188,10 +1188,10 @@ export function packVehicleReferenceData(obj: any) {
|
|
|
1188
1188
|
}
|
|
1189
1189
|
|
|
1190
1190
|
export function generateOldInteractionComponent() {
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
];
|
|
1194
|
-
return Buffer.from(raw);
|
|
1191
|
+
const floatBuf = Buffer.allocUnsafe(4);
|
|
1192
|
+
floatBuf.writeFloatLE(3.0, 0);
|
|
1193
|
+
const raw = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
|
1194
|
+
return Buffer.concat([floatBuf, Buffer.from(raw)]);
|
|
1195
1195
|
}
|
|
1196
1196
|
export function generateOldNpcComponent() {
|
|
1197
1197
|
const old = [
|
|
@@ -1214,18 +1214,19 @@ export function generateOldNpcComponent() {
|
|
|
1214
1214
|
];
|
|
1215
1215
|
return Buffer.from([...old, ...raw]);
|
|
1216
1216
|
}
|
|
1217
|
-
export function generateWorldItemRepData() {
|
|
1217
|
+
export function generateWorldItemRepData(nameId: number = 0) {
|
|
1218
|
+
const data = Buffer.allocUnsafe(16);
|
|
1219
|
+
data.writeUintLE(nameId, 12, 4);
|
|
1218
1220
|
const raw = [
|
|
1219
1221
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
1220
1222
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
1221
1223
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
1222
1224
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
1223
1225
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
1224
|
-
0x00,
|
|
1225
|
-
|
|
1226
|
-
// /\ Set isWorldItem byte
|
|
1226
|
+
0x00, 0x01
|
|
1227
|
+
// /\ Set isWorldItem byte
|
|
1227
1228
|
];
|
|
1228
|
-
return Buffer.from(raw);
|
|
1229
|
+
return Buffer.concat([data, Buffer.from(raw)]);
|
|
1229
1230
|
}
|
|
1230
1231
|
export function packComponentNameString(name: string) {
|
|
1231
1232
|
const stringBuffer = Buffer.from(name, "ascii");
|
|
@@ -19,6 +19,7 @@ import dgram, { RemoteInfo } from "node:dgram";
|
|
|
19
19
|
const debug = require("debug")("LZConnection");
|
|
20
20
|
|
|
21
21
|
export abstract class BaseLZConnection extends EventEmitter {
|
|
22
|
+
_serverAddress: string;
|
|
22
23
|
_serverPort?: number;
|
|
23
24
|
_protocol: LZConnectionProtocol;
|
|
24
25
|
_udpLength: number = 512;
|
|
@@ -29,6 +30,7 @@ export abstract class BaseLZConnection extends EventEmitter {
|
|
|
29
30
|
_pingTimer!: NodeJS.Timeout;
|
|
30
31
|
protected constructor(serverPort?: number) {
|
|
31
32
|
super();
|
|
33
|
+
this._serverAddress = process.env.SERVER_BIND_ADDRESS || "0.0.0.0";
|
|
32
34
|
this._serverPort = serverPort;
|
|
33
35
|
this._protocol = new LZConnectionProtocol();
|
|
34
36
|
this._connection = dgram.createSocket("udp4");
|
|
@@ -72,7 +74,7 @@ export abstract class BaseLZConnection extends EventEmitter {
|
|
|
72
74
|
);
|
|
73
75
|
|
|
74
76
|
return await new Promise((resolve) => {
|
|
75
|
-
this._connection.bind(this._serverPort,
|
|
77
|
+
this._connection.bind(this._serverPort, this._serverAddress, () => {
|
|
76
78
|
resolve();
|
|
77
79
|
});
|
|
78
80
|
});
|
|
@@ -25,6 +25,8 @@ import { PacketsQueue } from "./PacketsQueue";
|
|
|
25
25
|
const debug = require("debug")("SOEServer");
|
|
26
26
|
|
|
27
27
|
export class SOEServer extends EventEmitter {
|
|
28
|
+
_serverAddress: string;
|
|
29
|
+
_serverAddressV6: string;
|
|
28
30
|
_serverPort: number;
|
|
29
31
|
_cryptoKey: Uint8Array;
|
|
30
32
|
_protocol!: Soeprotocol;
|
|
@@ -50,6 +52,8 @@ export class SOEServer extends EventEmitter {
|
|
|
50
52
|
const oneMb = 1024 * 1024;
|
|
51
53
|
Buffer.poolSize = oneMb;
|
|
52
54
|
this._serverPort = serverPort;
|
|
55
|
+
this._serverAddress = process.env.SERVER_BIND_ADDRESS || "0.0.0.0";
|
|
56
|
+
this._serverAddressV6 = process.env.SERVER_BIND_ADDRESS_V6 || "::";
|
|
53
57
|
this._cryptoKey = cryptoKey;
|
|
54
58
|
this._maxMultiBufferSize = this._udpLength - 4 - this._crcLength;
|
|
55
59
|
this._connection = dgram.createSocket({
|
|
@@ -463,9 +467,9 @@ export class SOEServer extends EventEmitter {
|
|
|
463
467
|
this._connectionv6.on("message", (data, remote) => {
|
|
464
468
|
this.onMessage(data, remote);
|
|
465
469
|
});
|
|
466
|
-
this._connection.bind(this._serverPort);
|
|
470
|
+
this._connection.bind(this._serverPort, this._serverAddress);
|
|
467
471
|
if (!process.env.DISABLE_IPV6) {
|
|
468
|
-
this._connectionv6.bind(this._serverPort);
|
|
472
|
+
this._connectionv6.bind(this._serverPort, this._serverAddressV6);
|
|
469
473
|
}
|
|
470
474
|
}
|
|
471
475
|
|
|
@@ -119,6 +119,10 @@ export abstract class BaseFullCharacter extends BaseLightweightCharacter {
|
|
|
119
119
|
gender: number;
|
|
120
120
|
hoodState: string = "Up";
|
|
121
121
|
|
|
122
|
+
// Updates from constructionPermissionsManager, avoids checking all buildings every inventory access.
|
|
123
|
+
/** The guid of the building the character is inside of */
|
|
124
|
+
insideBuilding: string = "";
|
|
125
|
+
|
|
122
126
|
/** The default items that will spawn on and with the BaseFullCharacter */
|
|
123
127
|
defaultLoadout: LoadoutKit = [];
|
|
124
128
|
constructor(
|
|
@@ -287,6 +287,10 @@ export class ConstructionChildEntity extends BaseLightweightCharacter {
|
|
|
287
287
|
const p = position[1] + 2.4;
|
|
288
288
|
this.boundsOn = getCubeBounds(position, 5, 5, angle, p, p + 1.8);
|
|
289
289
|
|
|
290
|
+
break;
|
|
291
|
+
case Items.METAL_DOORWAY:
|
|
292
|
+
case Items.METAL_WALL:
|
|
293
|
+
this.fixedPosition = movePoint(position, -(rotation[1] + 1.575), 2.5);
|
|
290
294
|
break;
|
|
291
295
|
}
|
|
292
296
|
|
|
@@ -17,7 +17,12 @@ import { ZoneServer2016 } from "../zoneserver";
|
|
|
17
17
|
import { BaseItem } from "../classes/baseItem";
|
|
18
18
|
import { BaseLightweightCharacter } from "./baselightweightcharacter";
|
|
19
19
|
import { ZoneClient2016 } from "../classes/zoneclient";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
checkLineThroughDoorway,
|
|
22
|
+
randomIntFromInterval,
|
|
23
|
+
rotateAroundPivot,
|
|
24
|
+
wallInterceptsLine
|
|
25
|
+
} from "../../../utils/utils";
|
|
21
26
|
import {
|
|
22
27
|
AddLightweightNpc,
|
|
23
28
|
ClientUpdateProximateItems
|
|
@@ -25,6 +30,9 @@ import {
|
|
|
25
30
|
import { LoadoutContainer } from "../classes/loadoutcontainer";
|
|
26
31
|
import { BaseFullCharacter } from "./basefullcharacter";
|
|
27
32
|
import { LOADOUT_CONTAINER_GUID } from "../../../utils/constants";
|
|
33
|
+
import { ConstructionDoor } from "./constructiondoor";
|
|
34
|
+
import { ConstructionParentEntity } from "./constructionparententity";
|
|
35
|
+
import { ConstructionChildEntity } from "./constructionchildentity";
|
|
28
36
|
|
|
29
37
|
// TODO find a better way to handle items not inside containers
|
|
30
38
|
function transferItemObject(
|
|
@@ -129,6 +137,7 @@ export class ItemObject extends BaseLightweightCharacter {
|
|
|
129
137
|
npcRenderDistance = 25;
|
|
130
138
|
spawnerId = 0;
|
|
131
139
|
item: BaseItem;
|
|
140
|
+
insideBuilding: string = "";
|
|
132
141
|
isWorldItem: boolean = false;
|
|
133
142
|
creationTime: number = 0;
|
|
134
143
|
triggerExplosionShots = Math.floor(Math.random() * 3) + 2; // random number 2-4 neccesary shots
|
|
@@ -224,6 +233,130 @@ export class ItemObject extends BaseLightweightCharacter {
|
|
|
224
233
|
}
|
|
225
234
|
}
|
|
226
235
|
|
|
236
|
+
checkBuildingObstruct(
|
|
237
|
+
server: ZoneServer2016,
|
|
238
|
+
character: Float32Array,
|
|
239
|
+
foundation: ConstructionParentEntity | undefined
|
|
240
|
+
): boolean {
|
|
241
|
+
const charPos = new Float32Array(character);
|
|
242
|
+
|
|
243
|
+
const itemPos = this.state.position;
|
|
244
|
+
charPos[1] += 1.8;
|
|
245
|
+
|
|
246
|
+
const charInFoundation: boolean =
|
|
247
|
+
foundation instanceof ConstructionParentEntity;
|
|
248
|
+
|
|
249
|
+
if (!charInFoundation) {
|
|
250
|
+
foundation =
|
|
251
|
+
server._constructionFoundations[this.insideBuilding] ??
|
|
252
|
+
server._constructionSimple[this.insideBuilding]?.getParent(server);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!foundation) return false;
|
|
256
|
+
|
|
257
|
+
if (
|
|
258
|
+
foundation.itemDefinitionId == Items.SHACK ||
|
|
259
|
+
foundation.itemDefinitionId == Items.SHACK_BASIC || // TODO
|
|
260
|
+
foundation.itemDefinitionId == Items.SHACK_SMALL
|
|
261
|
+
) {
|
|
262
|
+
if (!foundation.isSecured) return false;
|
|
263
|
+
|
|
264
|
+
if (charInFoundation && foundation.isInside(itemPos)) return false;
|
|
265
|
+
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (
|
|
270
|
+
foundation.itemDefinitionId != Items.FOUNDATION_EXPANSION &&
|
|
271
|
+
foundation.itemDefinitionId != Items.FOUNDATION &&
|
|
272
|
+
foundation.itemDefinitionId != Items.GROUND_TAMPER
|
|
273
|
+
) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (foundation.itemDefinitionId == Items.FOUNDATION_EXPANSION)
|
|
278
|
+
foundation = foundation.getParentFoundation(server);
|
|
279
|
+
|
|
280
|
+
if (!foundation) return false;
|
|
281
|
+
|
|
282
|
+
const allShelters = {
|
|
283
|
+
...foundation.occupiedShelterSlots,
|
|
284
|
+
...Object.assign(
|
|
285
|
+
{},
|
|
286
|
+
...Object.values(foundation.occupiedExpansionSlots).map(
|
|
287
|
+
(exp) => exp.occupiedShelterSlots
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const allWalls = {
|
|
293
|
+
...foundation.occupiedWallSlots,
|
|
294
|
+
...Object.assign(
|
|
295
|
+
{},
|
|
296
|
+
...Object.values(foundation.occupiedExpansionSlots).map(
|
|
297
|
+
(exp) => exp.occupiedWallSlots
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
for (const w in allWalls) {
|
|
303
|
+
const wall = allWalls[w];
|
|
304
|
+
const wallStart = new Float32Array<ArrayBufferLike>(wall.state.position);
|
|
305
|
+
let wallEnd = new Float32Array(wall.fixedPosition);
|
|
306
|
+
|
|
307
|
+
wallEnd[0] = 2 * wallEnd[0] - wallStart[0]; // doorEnd is currently the midpoint
|
|
308
|
+
wallEnd[2] = 2 * wallEnd[2] - wallStart[2]; // extend it to the end
|
|
309
|
+
wallEnd[1] += 3.5;
|
|
310
|
+
|
|
311
|
+
if (wall instanceof ConstructionDoor && wall.isOpen)
|
|
312
|
+
wallEnd = rotateAroundPivot(wallStart, wallEnd, -Math.PI / 2);
|
|
313
|
+
|
|
314
|
+
if (wallInterceptsLine(charPos, itemPos, wallStart, wallEnd)) {
|
|
315
|
+
if (
|
|
316
|
+
wall.itemDefinitionId == Items.METAL_DOORWAY &&
|
|
317
|
+
checkLineThroughDoorway(charPos, itemPos, wall)
|
|
318
|
+
)
|
|
319
|
+
continue;
|
|
320
|
+
else return true;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
for (const s in allShelters) {
|
|
325
|
+
const shelter: ConstructionChildEntity = allShelters[s];
|
|
326
|
+
|
|
327
|
+
if (!shelter.cubebounds) continue;
|
|
328
|
+
const walls: [Float32Array, Float32Array][] = [
|
|
329
|
+
[
|
|
330
|
+
new Float32Array(shelter.cubebounds[0]),
|
|
331
|
+
new Float32Array(shelter.cubebounds[5])
|
|
332
|
+
],
|
|
333
|
+
|
|
334
|
+
[
|
|
335
|
+
new Float32Array(shelter.cubebounds[1]),
|
|
336
|
+
new Float32Array(shelter.cubebounds[6])
|
|
337
|
+
],
|
|
338
|
+
|
|
339
|
+
[
|
|
340
|
+
new Float32Array(shelter.cubebounds[2]),
|
|
341
|
+
new Float32Array(shelter.cubebounds[7])
|
|
342
|
+
],
|
|
343
|
+
|
|
344
|
+
[
|
|
345
|
+
new Float32Array(shelter.cubebounds[3]),
|
|
346
|
+
new Float32Array(shelter.cubebounds[4])
|
|
347
|
+
]
|
|
348
|
+
];
|
|
349
|
+
|
|
350
|
+
for (const wall of walls) {
|
|
351
|
+
if (wallInterceptsLine(charPos, itemPos, ...wall)) {
|
|
352
|
+
return !checkLineThroughDoorway(charPos, itemPos, shelter);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
227
360
|
OnInteractionString(server: ZoneServer2016, client: ZoneClient2016): void {
|
|
228
361
|
server.sendData(client, "Command.InteractionString", {
|
|
229
362
|
guid: this.characterId,
|
|
@@ -2418,9 +2418,11 @@ export class ConstructionManager {
|
|
|
2418
2418
|
client: Client
|
|
2419
2419
|
) {
|
|
2420
2420
|
let hide = false;
|
|
2421
|
-
|
|
2421
|
+
client.character.insideBuilding = "";
|
|
2422
2422
|
for (const object of client.spawnedEntities) {
|
|
2423
2423
|
if (object instanceof ConstructionParentEntity) {
|
|
2424
|
+
if (object.isInside(client.character.state.position))
|
|
2425
|
+
client.character.insideBuilding = object.characterId;
|
|
2424
2426
|
if (this.checkFoundationPermission(server, client, object)) {
|
|
2425
2427
|
hide = true;
|
|
2426
2428
|
continue;
|
|
@@ -2428,6 +2430,8 @@ export class ConstructionManager {
|
|
|
2428
2430
|
}
|
|
2429
2431
|
|
|
2430
2432
|
if (object instanceof ConstructionChildEntity) {
|
|
2433
|
+
if (object.isInside(client.character.state.position))
|
|
2434
|
+
client.character.insideBuilding = object.characterId;
|
|
2431
2435
|
if (
|
|
2432
2436
|
this.checkConstructionChildEntityPermission(server, client, object)
|
|
2433
2437
|
) {
|
|
@@ -21,6 +21,7 @@ import { BaseItem } from "../classes/baseItem";
|
|
|
21
21
|
import { BaseLootableEntity } from "../entities/baselootableentity";
|
|
22
22
|
import { ChallengeType } from "./challengemanager";
|
|
23
23
|
import { ItemObject } from "../entities/itemobject";
|
|
24
|
+
import { ClientUpdateProximateItems } from "types/zone2016packets";
|
|
24
25
|
const debug = require("debug")("ZoneServer");
|
|
25
26
|
|
|
26
27
|
interface CraftComponentDSEntry {
|
|
@@ -179,6 +180,13 @@ export class CraftManager {
|
|
|
179
180
|
const entity = server.getEntity(item.ownerCharacterId);
|
|
180
181
|
if (entity instanceof ItemObject) {
|
|
181
182
|
entity.item.stackCount -= count;
|
|
183
|
+
const client = server.getClientByCharId(itemDS.character.characterId);
|
|
184
|
+
if (!client) return true;
|
|
185
|
+
server.sendData<ClientUpdateProximateItems>(
|
|
186
|
+
client,
|
|
187
|
+
"ClientUpdate.ProximateItems",
|
|
188
|
+
server.getProximityItems(client)
|
|
189
|
+
);
|
|
182
190
|
return true;
|
|
183
191
|
}
|
|
184
192
|
|
|
@@ -261,6 +261,7 @@ import { ChallengeManager, ChallengeType } from "./managers/challengemanager";
|
|
|
261
261
|
import { RandomEventsManager } from "./managers/randomeventsmanager";
|
|
262
262
|
import { AiManager } from "./managers/aimanager";
|
|
263
263
|
import { AirdropManager } from "./managers/airdropmanager";
|
|
264
|
+
import { generateWorldItemRepData } from "../../packets/ClientProtocol/ClientProtocol_1080/shared";
|
|
264
265
|
//import { TaskManager } from "./managers/tasksmanager";
|
|
265
266
|
|
|
266
267
|
const spawnLocations2 = require("../../../data/2016/zoneData/Z1_gridSpawns.json"),
|
|
@@ -1297,17 +1298,37 @@ export class ZoneServer2016 extends EventEmitter {
|
|
|
1297
1298
|
|
|
1298
1299
|
getProximityItems(client: Client): ClientUpdateProximateItems {
|
|
1299
1300
|
const proximityItems: ClientUpdateProximateItems = { items: [] };
|
|
1301
|
+
let foundation: ConstructionParentEntity | undefined =
|
|
1302
|
+
this._constructionFoundations[client.character.insideBuilding] ??
|
|
1303
|
+
this._constructionSimple[
|
|
1304
|
+
client.character.insideBuilding
|
|
1305
|
+
]?.getParentFoundation(this);
|
|
1300
1306
|
|
|
1301
1307
|
for (const object of client.spawnedEntities) {
|
|
1302
1308
|
if (object instanceof ItemObject) {
|
|
1309
|
+
let yDistance = 1;
|
|
1310
|
+
if (
|
|
1311
|
+
client.character.state.position[1] <
|
|
1312
|
+
object.state.position[1] - 0.5
|
|
1313
|
+
) {
|
|
1314
|
+
yDistance = 1.8;
|
|
1315
|
+
}
|
|
1303
1316
|
if (
|
|
1304
1317
|
isPosInRadiusWithY(
|
|
1305
1318
|
this.proximityItemsDistance,
|
|
1306
1319
|
client.character.state.position,
|
|
1307
1320
|
object.state.position,
|
|
1308
|
-
|
|
1321
|
+
yDistance
|
|
1309
1322
|
)
|
|
1310
1323
|
) {
|
|
1324
|
+
if (
|
|
1325
|
+
object.checkBuildingObstruct(
|
|
1326
|
+
this,
|
|
1327
|
+
client.character.state.position,
|
|
1328
|
+
foundation
|
|
1329
|
+
)
|
|
1330
|
+
)
|
|
1331
|
+
continue;
|
|
1311
1332
|
const proximityItem = {
|
|
1312
1333
|
itemDefinitionId: object.item.itemDefinitionId,
|
|
1313
1334
|
associatedCharacterGuid: object.characterId,
|
|
@@ -4100,7 +4121,11 @@ export class ZoneServer2016 extends EventEmitter {
|
|
|
4100
4121
|
transientId: entity.transientId,
|
|
4101
4122
|
stringSize: "ClientNpcComponent".length,
|
|
4102
4123
|
componentName: "ClientNpcComponent",
|
|
4103
|
-
properties: [
|
|
4124
|
+
properties: [
|
|
4125
|
+
{
|
|
4126
|
+
bufferData: generateWorldItemRepData(entity.nameId)
|
|
4127
|
+
}
|
|
4128
|
+
]
|
|
4104
4129
|
}
|
|
4105
4130
|
);
|
|
4106
4131
|
}
|
|
@@ -6712,6 +6737,7 @@ export class ZoneServer2016 extends EventEmitter {
|
|
|
6712
6737
|
);
|
|
6713
6738
|
|
|
6714
6739
|
if (!obj) return;
|
|
6740
|
+
obj.insideBuilding = character.insideBuilding;
|
|
6715
6741
|
this.executeFuncForAllReadyClientsInRange((c) => {
|
|
6716
6742
|
c.spawnedEntities.add(obj);
|
|
6717
6743
|
this.addLightweightNpc(c, obj);
|
package/src/utils/utils.ts
CHANGED
|
@@ -33,6 +33,7 @@ import { Resolver } from "node:dns";
|
|
|
33
33
|
import { ZoneClient2016 } from "servers/ZoneServer2016/classes/zoneclient";
|
|
34
34
|
import * as crypto from "crypto";
|
|
35
35
|
import { ZoneClient } from "servers/ZoneServer2015/classes/zoneclient";
|
|
36
|
+
import { ConstructionDoor } from "../servers/ZoneServer2016/entities/constructiondoor";
|
|
36
37
|
|
|
37
38
|
const startTime = Date.now();
|
|
38
39
|
|
|
@@ -319,6 +320,108 @@ export function getAngle(position1: Float32Array, position2: Float32Array) {
|
|
|
319
320
|
return angle;
|
|
320
321
|
}
|
|
321
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Rotates point around pivot by angle
|
|
325
|
+
*
|
|
326
|
+
* @param pivot - The pivot to rotate around
|
|
327
|
+
* @param point - The point that will be rotated
|
|
328
|
+
* @param angle - The angle to rotate
|
|
329
|
+
* @returns - A new point at the new position
|
|
330
|
+
*/
|
|
331
|
+
export function rotateAroundPivot(
|
|
332
|
+
pivot: Float32Array,
|
|
333
|
+
point: Float32Array,
|
|
334
|
+
angle: number
|
|
335
|
+
): Float32Array {
|
|
336
|
+
// Calculate the vector from pivot
|
|
337
|
+
const vX = point[0] - pivot[0];
|
|
338
|
+
const vY = point[1] - pivot[1];
|
|
339
|
+
const vZ = point[2] - pivot[2];
|
|
340
|
+
|
|
341
|
+
const cos = Math.cos(angle);
|
|
342
|
+
const sin = Math.sin(angle);
|
|
343
|
+
|
|
344
|
+
// Do matrix rotation and then add back the pivot
|
|
345
|
+
return new Float32Array([
|
|
346
|
+
pivot[0] + vX * cos - vZ * sin,
|
|
347
|
+
pivot[1] + vY,
|
|
348
|
+
pivot[2] + vX * sin + vZ * cos
|
|
349
|
+
]);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Checks if a line goes through a open or missing door slot.
|
|
354
|
+
*
|
|
355
|
+
* @param a1 - The first point of the line
|
|
356
|
+
* @param a2 - The second point of the line
|
|
357
|
+
* @param doorway - The construction entity that has a door.
|
|
358
|
+
* @returns - Boolean if the line passes through a open door or a door slot with no door.
|
|
359
|
+
*/
|
|
360
|
+
export function checkLineThroughDoorway(
|
|
361
|
+
a1: Float32Array,
|
|
362
|
+
a2: Float32Array,
|
|
363
|
+
doorway: ConstructionChildEntity
|
|
364
|
+
): boolean {
|
|
365
|
+
const door = doorway.occupiedWallSlots[1];
|
|
366
|
+
if (door && door instanceof ConstructionDoor && !door.isOpen) return false;
|
|
367
|
+
let doorEnd: Float32Array;
|
|
368
|
+
let doorStart: Float32Array;
|
|
369
|
+
|
|
370
|
+
if (door instanceof ConstructionDoor && door.isOpen) {
|
|
371
|
+
doorEnd = new Float32Array(door.fixedPosition);
|
|
372
|
+
doorStart = new Float32Array(door.state.position);
|
|
373
|
+
} else {
|
|
374
|
+
// No door, calculate the door slot
|
|
375
|
+
const rotation = doorway.getSlotRotation(1, doorway.wallSlots);
|
|
376
|
+
doorStart = new Float32Array(
|
|
377
|
+
doorway.getSlotPosition(1, doorway.wallSlots)!
|
|
378
|
+
);
|
|
379
|
+
doorEnd = movePoint(doorStart!, -(rotation![1] + 1.575), 0.625);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
doorEnd[0] = 2 * doorEnd[0] - doorStart[0]; // doorEnd is currently the midpoint
|
|
383
|
+
doorEnd[2] = 2 * doorEnd[2] - doorStart[2]; // extend it to the end
|
|
384
|
+
doorEnd[1] += 2;
|
|
385
|
+
doorStart![1] -= 0.5;
|
|
386
|
+
return wallInterceptsLine(a1, a2, doorStart!, doorEnd);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Check if the wall defined by b1, b2 is between the line a1, a2
|
|
391
|
+
*
|
|
392
|
+
* @param a1 - The first point of the line
|
|
393
|
+
* @param a2 - The second point of the line
|
|
394
|
+
* @param b1 - The bottom left corner of the wall
|
|
395
|
+
* @param b2 - The top right corner of the wall
|
|
396
|
+
* @returns - A boolean if the line is intercepted by the wall
|
|
397
|
+
*/
|
|
398
|
+
export function wallInterceptsLine(
|
|
399
|
+
a1: Float32Array,
|
|
400
|
+
a2: Float32Array,
|
|
401
|
+
b1: Float32Array,
|
|
402
|
+
b2: Float32Array
|
|
403
|
+
): boolean {
|
|
404
|
+
b1[1] -= 0.5; // extend the plane down a bit
|
|
405
|
+
|
|
406
|
+
const orient = (p: Float32Array, q: Float32Array, r: Float32Array) =>
|
|
407
|
+
(q[0] - p[0]) * (r[2] - p[2]) - (q[2] - p[2]) * (r[0] - p[0]);
|
|
408
|
+
|
|
409
|
+
const o1 = orient(a1, a2, b1);
|
|
410
|
+
const o2 = orient(a1, a2, b2);
|
|
411
|
+
const o3 = orient(b1, b2, a1);
|
|
412
|
+
const o4 = orient(b1, b2, a2);
|
|
413
|
+
|
|
414
|
+
const intersects2D = o1 * o2 < 0 && o3 * o4 < 0;
|
|
415
|
+
if (!intersects2D) return false;
|
|
416
|
+
|
|
417
|
+
const lineMinY = Math.min(a1[1], a2[1]);
|
|
418
|
+
const lineMaxY = Math.max(a1[1], a2[1]);
|
|
419
|
+
const wallMinY = Math.min(b1[1], b2[1]);
|
|
420
|
+
const wallMaxY = Math.max(b1[1], b2[1]);
|
|
421
|
+
|
|
422
|
+
return lineMaxY >= wallMinY && lineMinY <= wallMaxY;
|
|
423
|
+
}
|
|
424
|
+
|
|
322
425
|
/**
|
|
323
426
|
* Shuts down the zone server with a countdown timer and performs necessary cleanup operations.
|
|
324
427
|
*
|