h1z1-server 0.36.2-4 → 0.36.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.
Files changed (38) hide show
  1. package/data/2016/dataSources/AllowedFileHashes.json +1 -1
  2. package/package.json +12 -12
  3. package/src/packets/ClientProtocol/ClientProtocol_1080/dto.ts +1 -1
  4. package/src/packets/ClientProtocol/ClientProtocol_1080/shared.ts +2 -1
  5. package/src/servers/LoginServer/loginserver.test.ts +1 -0
  6. package/src/servers/LoginServer/loginserver.ts +7 -5
  7. package/src/servers/LoginZoneConnection/shared/lzconnectionclient.ts +4 -2
  8. package/src/servers/SoeServer/soeserver.ts +4 -2
  9. package/src/servers/ZoneServer2016/classes/accountinventory.ts +25 -0
  10. package/src/servers/ZoneServer2016/data/loadouts.ts +6 -0
  11. package/src/servers/ZoneServer2016/data/lootspawns.ts +13 -150
  12. package/src/servers/ZoneServer2016/entities/basefullcharacter.ts +7 -12
  13. package/src/servers/ZoneServer2016/entities/character.ts +0 -3
  14. package/src/servers/ZoneServer2016/entities/destroyable.ts +3 -2
  15. package/src/servers/ZoneServer2016/entities/lootableprop.ts +10 -10
  16. package/src/servers/ZoneServer2016/handlers/commands/commands.ts +9 -91
  17. package/src/servers/ZoneServer2016/managers/chatmanager.ts +16 -13
  18. package/src/servers/ZoneServer2016/managers/craftmanager.ts +4 -39
  19. package/src/servers/ZoneServer2016/managers/decaymanager.test.ts +1 -0
  20. package/src/servers/ZoneServer2016/managers/weathermanager.ts +1 -1
  21. package/src/servers/ZoneServer2016/managers/worlddatamanager.test.ts +1 -0
  22. package/src/servers/ZoneServer2016/managers/worlddatamanager.ts +111 -8
  23. package/src/servers/ZoneServer2016/managers/worlddatamanagerthread.ts +9 -0
  24. package/src/servers/ZoneServer2016/managers/worldobjectmanager.ts +1 -0
  25. package/src/servers/ZoneServer2016/models/enums.ts +0 -2
  26. package/src/servers/ZoneServer2016/zonepackethandlers.ts +36 -72
  27. package/src/servers/ZoneServer2016/zoneserver.ts +113 -89
  28. package/src/types/savedata.ts +0 -2
  29. package/src/types/zone2016packets.ts +4 -2
  30. package/src/types/zoneserver.ts +1 -6
  31. package/src/utils/utils.ts +0 -20
  32. package/config.yaml +0 -27
  33. package/src/servers/ZoneServer2016/managers/accountinventorymanager.test.ts +0 -202
  34. package/src/servers/ZoneServer2016/managers/accountinventorymanager.ts +0 -158
  35. package/src/servers/ZoneServer2016/managers/playtimemanager.test.ts +0 -73
  36. package/src/servers/ZoneServer2016/managers/playtimemanager.ts +0 -42
  37. package/src/servers/ZoneServer2016/managers/rewardmanager.test.ts +0 -35
  38. package/src/servers/ZoneServer2016/managers/rewardmanager.ts +0 -95
@@ -1024,6 +1024,6 @@
1024
1024
  },
1025
1025
  {
1026
1026
  "file_name": "Assets_256.pack",
1027
- "crc32_hash": "c8b93131"
1027
+ "crc32_hash": "0f9d31cf"
1028
1028
  }
1029
1029
  ]
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "h1z1-server",
3
- "version": "0.36.2-4",
3
+ "version": "0.36.2",
4
4
  "description": "Library for emulating h1z1 servers",
5
5
  "author": "Quentin Gruber <quentingruber@gmail.com> (http://github.com/quentingruber)",
6
6
  "license": "GPL-3.0-only",
7
7
  "main": "h1z1-server.js",
8
8
  "engines": {
9
- "node": ">=0.17.0 <23"
9
+ "node": ">=0.17.0 <21"
10
10
  },
11
11
  "bin": {
12
12
  "h1z1-server-demo": "scripts/h1z1-server-demo.js",
@@ -14,27 +14,27 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@types/js-yaml": "4.0.9",
17
- "@types/node": "20.14.5",
17
+ "@types/node": "20.11.19",
18
18
  "@types/ws": "8.5.10",
19
- "debug": "4.3.5",
19
+ "debug": "4.3.4",
20
20
  "h1emu-core": "1.2.8",
21
- "h1z1-dataschema": "1.7.2",
21
+ "h1z1-dataschema": "1.7.1",
22
22
  "js-yaml": "4.1.0",
23
- "mongodb": "6.7.0",
23
+ "mongodb": "6.3.0",
24
24
  "threads": "1.7.0",
25
- "typescript": "5.4.5",
26
- "ws": "8.17.1"
25
+ "typescript": "5.3.3",
26
+ "ws": "8.16.0"
27
27
  },
28
28
  "directories": {
29
29
  "src": "./src"
30
30
  },
31
31
  "devDependencies": {
32
- "@typescript-eslint/eslint-plugin": "^7.13.1",
33
- "@typescript-eslint/parser": "^7.13.1",
32
+ "@typescript-eslint/eslint-plugin": "^7.0.2",
33
+ "@typescript-eslint/parser": "^7.0.2",
34
34
  "eslint": "^8.56.0",
35
- "prettier": "^3.3.2",
35
+ "prettier": "^3.2.5",
36
36
  "ts-node": "^10.9.2",
37
- "typedoc": "^0.25.13"
37
+ "typedoc": "^0.25.8"
38
38
  },
39
39
  "scripts": {
40
40
  "gen-packets-types": "npx ts-node ./scripts/genPacketsNames.ts",
@@ -55,7 +55,7 @@ export const dtoPackets: PacketStructures = [
55
55
  { name: "unknownDword1", type: "uint32", defaultValue: 0 },
56
56
  { name: "unknownBool1", type: "boolean", defaultValue: true },
57
57
  { name: "unknownBool2", type: "boolean", defaultValue: true },
58
- { name: "invisibility", type: "boolean", defaultValue: true }
58
+ { name: "unknownBool3", type: "boolean", defaultValue: true }
59
59
  ]
60
60
  },
61
61
  {
@@ -2857,7 +2857,8 @@ export function pack2ByteLengthString(string: string) {
2857
2857
 
2858
2858
  export const accountItemSchema: PacketFields = [
2859
2859
  { name: "itemId", type: "uint64string", defaultValue: "0" },
2860
- { name: "itemDefinitionId", type: "uint64", defaultValue: 0 },
2860
+ { name: "itemDefinitionId", type: "uint32", defaultValue: 0 },
2861
+ { name: "unknownDword2", type: "uint32", defaultValue: 0 }, // 44 for itemDefId 1871, 37 for itemDefId 37, ... (No idea where it comes from)
2861
2862
  { name: "itemCount", type: "uint32", defaultValue: 0 },
2862
2863
  { name: "itemGuid", type: "uint64string", defaultValue: "0" },
2863
2864
  { name: "unknownDword4", type: "uint32", defaultValue: 0 }
@@ -19,6 +19,7 @@ test("LoginServer-mongo", { timeout: 5000, skip: !isMongoTests }, async (t) => {
19
19
  await t.test("start", async () => {
20
20
  await loginServer.start();
21
21
  });
22
+ // TODO: start should stop awaiting only when everything is really done
22
23
  await scheduler.yield();
23
24
  await t.test("stop", async () => {
24
25
  await loginServer.stop();
@@ -40,7 +40,7 @@ import { Worker } from "node:worker_threads";
40
40
  import { FileHash, httpServerMessage } from "types/shared";
41
41
  import { LoginProtocol2016 } from "../../protocols/loginprotocol2016";
42
42
  import { crc_length_options } from "../../types/soeserver";
43
- import { DB_NAME, DEFAULT_CRYPTO_KEY, MAX_UINT32 } from "../../utils/constants";
43
+ import { DB_NAME, DEFAULT_CRYPTO_KEY } from "../../utils/constants";
44
44
  import {
45
45
  LoginReply,
46
46
  CharacterSelectInfoReply,
@@ -1112,10 +1112,12 @@ export class LoginServer extends EventEmitter {
1112
1112
  ];
1113
1113
  const [address, port] = zoneConnectionString.split(":");
1114
1114
 
1115
- const LZClient = new LZConnectionClient({ address, port: Number(port) });
1116
- // Hack since the loginserver doesn't have a serverId
1117
- LZClient.serverId = MAX_UINT32;
1118
- return LZClient;
1115
+ return {
1116
+ address: address,
1117
+ port: port,
1118
+ clientId: zoneConnectionString,
1119
+ serverId: 1 // TODO: that's a hack
1120
+ } as any;
1119
1121
  }
1120
1122
 
1121
1123
  async askZone(
@@ -11,6 +11,8 @@
11
11
  // Based on https://github.com/psemu/soe-network
12
12
  // ======================================================================
13
13
 
14
+ import { RemoteInfo } from "node:dgram";
15
+
14
16
  export class LZConnectionClient {
15
17
  sessionId: number = 0;
16
18
  address: string;
@@ -19,8 +21,8 @@ export class LZConnectionClient {
19
21
  clientId: string;
20
22
  lastPing: number = 0;
21
23
 
22
- constructor(remote: { address?: string; port: number }) {
23
- this.address = remote.address ?? "";
24
+ constructor(remote: RemoteInfo) {
25
+ this.address = remote.address;
24
26
  this.port = remote.port;
25
27
  this.clientId = `${remote.address}:${remote.port}`;
26
28
  }
@@ -71,13 +71,15 @@ export class SOEServer extends EventEmitter {
71
71
  this.avgEventLoopLag =
72
72
  this.eventLoopLagValues.reduce((a, b) => a + b, 0) /
73
73
  this.eventLoopLagValues.length;
74
+ performance.clearMarks();
75
+ performance.clearMeasures();
76
+ performance.mark("A");
74
77
  });
75
78
  obs.observe({ entryTypes: ["measure"], buffered: true });
76
79
  performance.mark("A");
77
80
  setInterval(() => {
78
81
  performance.mark("B");
79
82
  performance.measure("A to B", "A", "B");
80
- performance.mark("A");
81
83
  }, intervalTime);
82
84
  } catch (e) {
83
85
  console.log("PerformanceObserver not available");
@@ -410,7 +412,7 @@ export class SOEServer extends EventEmitter {
410
412
  );
411
413
 
412
414
  // client.outputStream.on(SOEOutputChannels.Raw, (data: Buffer) => {
413
- // unused in h1z1
415
+ // TODO:
414
416
  // });
415
417
  } else {
416
418
  client = this._clients.get(clientId) as SOEClient;
@@ -0,0 +1,25 @@
1
+ // ======================================================================
2
+ //
3
+ // GNU GENERAL PUBLIC LICENSE
4
+ // Version 3, 29 June 2007
5
+ // copyright (C) 2020 - 2021 Quentin Gruber
6
+ // copyright (C) 2021 - 2024 H1emu community
7
+ //
8
+ // https://github.com/QuentinGruber/h1z1-server
9
+ // https://www.npmjs.com/package/h1z1-server
10
+ //
11
+ // Based on https://github.com/psemu/soe-network
12
+ // ======================================================================
13
+
14
+ import { EntityDictionary } from "types/zoneserver";
15
+ import { BaseItem } from "./baseItem";
16
+
17
+ export class AccountInventory {
18
+ loginSessionId: string;
19
+ items: EntityDictionary<BaseItem>;
20
+
21
+ constructor(loginSessionId: string, items: EntityDictionary<BaseItem>) {
22
+ this.loginSessionId = loginSessionId;
23
+ this.items = items;
24
+ }
25
+ }
@@ -195,3 +195,9 @@ export const lootableContainerDefaultLoadouts = {
195
195
  repair_box: [{ item: Items.CONTAINER_REPAIR_BOX }],
196
196
  stash: [{ item: Items.CONTAINER_STASH }]
197
197
  };
198
+
199
+ export const accountInventoryDefaultRewards = [
200
+ { item: Items.REWARD_CRATE_WEARABLES, count: 3 },
201
+ { item: Items.REWARD_CRATE_ALPHA_LAUNCH, count: 3 },
202
+ { item: Items.MYSTERY_BAG_1, count: 2 }
203
+ ];
@@ -220,7 +220,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
220
220
  ]
221
221
  },
222
222
  "ItemSpawner_Weapon_Bat01.adr": {
223
- spawnChance: 10,
223
+ spawnChance: 20,
224
224
  items: [
225
225
  {
226
226
  item: Items.WEAPON_BAT_WOOD,
@@ -311,7 +311,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
311
311
  ]
312
312
  },
313
313
  "ItemSpawner_Weapon_Bat02.adr": {
314
- spawnChance: 10,
314
+ spawnChance: 20,
315
315
  items: [
316
316
  {
317
317
  item: Items.WEAPON_BAT_ALUM,
@@ -482,19 +482,6 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
482
482
  }
483
483
  ]
484
484
  },
485
- "ItemSpawner_Clothes_Helmet_Tactical_ChipsScratches.adr": {
486
- spawnChance: 10,
487
- items: [
488
- {
489
- item: Items.DEFAULT_TACTICAL_HELMET,
490
- weight: 100,
491
- spawnCount: {
492
- min: 1,
493
- max: 1
494
- }
495
- }
496
- ]
497
- },
498
485
  "ItemSpawner_Clothes_BaseballCap.adr": {
499
486
  spawnChance: 20,
500
487
  items: [
@@ -685,7 +672,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
685
672
  }
686
673
  },
687
674
  {
688
- item: Items.WATER_DIRTY,
675
+ item: Items.WATER_PURE,
689
676
  weight: 15,
690
677
  spawnCount: {
691
678
  min: 1,
@@ -782,7 +769,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
782
769
  },
783
770
  {
784
771
  item: Items.WEAPON_BAT_WOOD,
785
- weight: 10,
772
+ weight: 15,
786
773
  spawnCount: {
787
774
  min: 1,
788
775
  max: 1
@@ -790,7 +777,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
790
777
  },
791
778
  {
792
779
  item: Items.WEAPON_BAT_ALUM,
793
- weight: 10,
780
+ weight: 15,
794
781
  spawnCount: {
795
782
  min: 1,
796
783
  max: 1
@@ -1070,7 +1057,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
1070
1057
  }
1071
1058
  },
1072
1059
  {
1073
- item: Items.WATER_DIRTY,
1060
+ item: Items.WATER_PURE,
1074
1061
  weight: 10,
1075
1062
  spawnCount: {
1076
1063
  min: 1,
@@ -1212,30 +1199,6 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
1212
1199
  min: 1,
1213
1200
  max: 1
1214
1201
  }
1215
- },
1216
- {
1217
- item: Items.GROUND_COFFEE,
1218
- weight: 15,
1219
- spawnCount: {
1220
- min: 1,
1221
- max: 1
1222
- }
1223
- },
1224
- {
1225
- item: Items.SUGAR,
1226
- weight: 10,
1227
- spawnCount: {
1228
- min: 1,
1229
- max: 1
1230
- }
1231
- },
1232
- {
1233
- item: Items.CANNED_FOOD26,
1234
- weight: 7,
1235
- spawnCount: {
1236
- min: 1,
1237
- max: 1
1238
- }
1239
1202
  }
1240
1203
  ]
1241
1204
  },
@@ -1342,15 +1305,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
1342
1305
  }
1343
1306
  },
1344
1307
  {
1345
- item: Items.SHIRT_SCRUBS_GRAY,
1346
- weight: 100,
1347
- spawnCount: {
1348
- min: 1,
1349
- max: 1
1350
- }
1351
- },
1352
- {
1353
- item: Items.PANTS_SCRUBS_GRAY,
1308
+ item: Items.SHIRT_DEFAULT,
1354
1309
  weight: 100,
1355
1310
  spawnCount: {
1356
1311
  min: 1,
@@ -1358,7 +1313,7 @@ export const lootTables: { [lootSpawner: string]: LootSpawner } = {
1358
1313
  }
1359
1314
  },
1360
1315
  {
1361
- item: Items.CAP_SCRUBS_GRAY,
1316
+ item: Items.PANTS_DEFAULT,
1362
1317
  weight: 100,
1363
1318
  spawnCount: {
1364
1319
  min: 1,
@@ -2117,24 +2072,16 @@ export const containerLootSpawners: {
2117
2072
  min: 2,
2118
2073
  max: 10
2119
2074
  }
2120
- },
2121
- {
2122
- item: Items.AMMO_12GA,
2123
- weight: 20,
2124
- spawnCount: {
2125
- min: 1,
2126
- max: 6
2127
- }
2128
2075
  }
2129
2076
  ]
2130
2077
  },
2131
2078
  Locker: {
2132
- spawnChance: 60,
2079
+ spawnChance: 100,
2133
2080
  maxItems: 1,
2134
2081
  items: [
2135
2082
  {
2136
2083
  item: Items.BACKPACK_MILITARY_TAN,
2137
- weight: 25,
2084
+ weight: 35,
2138
2085
  spawnCount: {
2139
2086
  min: 1,
2140
2087
  max: 1
@@ -2150,15 +2097,7 @@ export const containerLootSpawners: {
2150
2097
  },
2151
2098
  {
2152
2099
  item: Items.KEVLAR_DEFAULT,
2153
- weight: 25,
2154
- spawnCount: {
2155
- min: 1,
2156
- max: 1
2157
- }
2158
- },
2159
- {
2160
- item: Items.DEFAULT_TACTICAL_HELMET,
2161
- weight: 25,
2100
+ weight: 35,
2162
2101
  spawnCount: {
2163
2102
  min: 1,
2164
2103
  max: 1
@@ -3488,82 +3427,6 @@ export const containerLootSpawners: {
3488
3427
  }
3489
3428
  ]
3490
3429
  },
3491
- Washer: {
3492
- spawnChance: 10,
3493
- maxItems: 2,
3494
- items: [
3495
- {
3496
- item: Items.SHIRT_DEFAULT,
3497
- weight: 30,
3498
- spawnCount: {
3499
- min: 1,
3500
- max: 1
3501
- }
3502
- },
3503
- {
3504
- item: Items.PANTS_DEFAULT,
3505
- weight: 20,
3506
- spawnCount: {
3507
- min: 1,
3508
- max: 1
3509
- }
3510
- },
3511
- {
3512
- item: Items.BASIC_HOODIE,
3513
- weight: 10,
3514
- spawnCount: {
3515
- min: 1,
3516
- max: 1
3517
- }
3518
- },
3519
- {
3520
- item: Items.CLOTH,
3521
- weight: 5,
3522
- spawnCount: {
3523
- min: 1,
3524
- max: 1
3525
- }
3526
- }
3527
- ]
3528
- },
3529
- Dryer: {
3530
- spawnChance: 10,
3531
- maxItems: 2,
3532
- items: [
3533
- {
3534
- item: Items.SHIRT_DEFAULT,
3535
- weight: 30,
3536
- spawnCount: {
3537
- min: 1,
3538
- max: 1
3539
- }
3540
- },
3541
- {
3542
- item: Items.PANTS_DEFAULT,
3543
- weight: 20,
3544
- spawnCount: {
3545
- min: 1,
3546
- max: 1
3547
- }
3548
- },
3549
- {
3550
- item: Items.BASIC_HOODIE,
3551
- weight: 10,
3552
- spawnCount: {
3553
- min: 1,
3554
- max: 1
3555
- }
3556
- },
3557
- {
3558
- item: Items.CLOTH,
3559
- weight: 5,
3560
- spawnCount: {
3561
- min: 1,
3562
- max: 1
3563
- }
3564
- }
3565
- ]
3566
- },
3567
3430
  // used for crate props
3568
3431
  Crate_buffed: {
3569
3432
  spawnChance: 60,
@@ -3594,7 +3457,7 @@ export const containerLootSpawners: {
3594
3457
  }
3595
3458
  },
3596
3459
  {
3597
- item: Items.WATER_DIRTY,
3460
+ item: Items.WATER_PURE,
3598
3461
  weight: 10,
3599
3462
  spawnCount: {
3600
3463
  min: 1,
@@ -3616,7 +3479,7 @@ export const containerLootSpawners: {
3616
3479
  }
3617
3480
  },
3618
3481
  {
3619
- item: Items.WATER_DIRTY,
3482
+ item: Items.WATER_PURE,
3620
3483
  weight: 10,
3621
3484
  spawnCount: {
3622
3485
  min: 1,
@@ -444,19 +444,15 @@ export abstract class BaseFullCharacter extends BaseLightweightCharacter {
444
444
  }
445
445
  }
446
446
 
447
- async lootAccountItem(
447
+ lootAccountItem(
448
448
  server: ZoneServer2016,
449
449
  client: ZoneClient2016,
450
- item?: BaseItem,
450
+ item: BaseItem,
451
451
  sendUpdate: boolean = false
452
452
  ) {
453
- if (!item) {
454
- return;
455
- }
456
- await server.accountInventoriesManager.addAccountItem(
457
- client.loginSessionId,
458
- item
459
- );
453
+ const items = server._accountInventories[client.loginSessionId]?.items;
454
+ if (!items) return;
455
+ items[item.itemGuid] = item;
460
456
  server.sendData(client, "Items.AddEscrowAccountItem", {
461
457
  itemData: {
462
458
  itemId: item.itemGuid,
@@ -560,9 +556,7 @@ export abstract class BaseFullCharacter extends BaseLightweightCharacter {
560
556
  if (
561
557
  ammo &&
562
558
  loadoutItem.weapon.ammoCount > 0 &&
563
- loadoutItem.weapon.itemDefinitionId != Items.WEAPON_REMOVER &&
564
- server.getItemDefinition(loadoutItem.itemDefinitionId)?.ITEM_CLASS !=
565
- ItemClasses.THROWABLES
559
+ loadoutItem.weapon.itemDefinitionId != Items.WEAPON_REMOVER
566
560
  ) {
567
561
  this.lootContainerItem(server, ammo, ammo.stackCount, true);
568
562
  }
@@ -957,6 +951,7 @@ export abstract class BaseFullCharacter extends BaseLightweightCharacter {
957
951
  let slot = loadoutSlotItemClass?.SLOT;
958
952
  if (!slot) return 0;
959
953
  switch (itemDef?.ITEM_CLASS) {
954
+ // TODO: Prevent equipping throwables until fixed
960
955
  case ItemClasses.THROWABLES:
961
956
  case ItemClasses.WEAPONS_LONG:
962
957
  case ItemClasses.WEAPONS_PISTOL:
@@ -101,9 +101,6 @@ export class Character2016 extends BaseFullCharacter {
101
101
  /** Used to update the status of the players resources */
102
102
  resourcesUpdater?: any;
103
103
  factionId = 2;
104
- isInInventory: boolean = false;
105
- playTime: number = 0;
106
- lastDropPlaytime: number = 0;
107
104
  set godMode(state: boolean) {
108
105
  this.characterStates.invincibility = state;
109
106
  }
@@ -17,6 +17,7 @@ import { eul2quat } from "../../../utils/utils";
17
17
  import { Effects, ModelIds } from "../models/enums";
18
18
  import { AddLightweightNpc, AddSimpleNpc } from "types/zone2016packets";
19
19
  import { BaseSimpleNpc } from "./basesimplenpc";
20
+ import { MAX_UINT32 } from "../../../utils/constants";
20
21
 
21
22
  function getDestroyedModels(actorModelId: ModelIds): number[] {
22
23
  switch (actorModelId) {
@@ -47,8 +48,8 @@ function getMaxHealth(actorModelId: ModelIds): number {
47
48
  case ModelIds.FENCES_WOOD_PLANKS_GREY_1X1:
48
49
  case ModelIds.FENCES_WOOD_PLANKS_GREY_POSTS_1X2:
49
50
  case ModelIds.FENCES_WOOD_PLANKS_GREY_GAP_1X1:
50
- // temp hack so it can't be destroyed ( or you really wanted to )
51
- // return MAX_UINT32;
51
+ // temp hack so it can't be destroyed ( or you really wanted to )
52
+ return MAX_UINT32;
52
53
  default:
53
54
  return 5000;
54
55
  }
@@ -118,16 +118,6 @@ function getContainerAndTime(entity: LootableProp) {
118
118
  entity.searchTime = 500;
119
119
  entity.lootSpawner = "Fridge";
120
120
  break;
121
- case ModelIds.WASHER:
122
- entity.containerId = Items.CONTAINER_WASHING_MACHINE;
123
- entity.searchTime = 500;
124
- entity.lootSpawner = "Washer";
125
- break;
126
- case ModelIds.DRYER:
127
- entity.containerId = Items.CONTAINER_DRYER;
128
- entity.searchTime = 500;
129
- entity.lootSpawner = "Dryer";
130
- break;
131
121
  case ModelIds.OTTOMAN_01:
132
122
  case ModelIds.OTTOMAN_02:
133
123
  entity.containerId = Items.CONTAINER_OTTOMAN;
@@ -191,6 +181,16 @@ function getContainerAndTime(entity: LootableProp) {
191
181
  entity.searchTime = 500;
192
182
  entity.lootSpawner = "Dresser";
193
183
  break;
184
+ case ModelIds.WASHER:
185
+ entity.containerId = Items.CONTAINER_WASHING_MACHINE;
186
+ entity.searchTime = 500;
187
+ entity.lootSpawner = "Washer_Dryer";
188
+ break;
189
+ case ModelIds.DRYER:
190
+ entity.containerId = Items.CONTAINER_DRYER;
191
+ entity.searchTime = 500;
192
+ entity.lootSpawner = "Washer_Dryer";
193
+ break;
194
194
  case ModelIds.FURNACE:
195
195
  entity.containerId = Items.CONTAINER_FURNACE;
196
196
  entity.searchTime = 0;