diff --git a/src/servers/ZoneServer2016/data/lootspawns.ts b/src/servers/ZoneServer2016/data/lootspawns.ts index 2c2283d36b..f3024daf5b 100644 --- a/src/servers/ZoneServer2016/data/lootspawns.ts +++ b/src/servers/ZoneServer2016/data/lootspawns.ts @@ -3344,14 +3344,6 @@ export const containerLootSpawners: { max: 1 } }, - { - item: Items.WOOD_PLANK, - weight: 20, - spawnCount: { - min: 1, - max: 1 - } - }, { item: Items.CLOTH, weight: 15, @@ -3382,14 +3374,6 @@ export const containerLootSpawners: { spawnChance: 50, maxItems: 1, items: [ - { - item: Items.WOOD_PLANK, - weight: 50, - spawnCount: { - min: 1, - max: 1 - } - }, { item: Items.CANNED_FOOD01, weight: 10, diff --git a/src/servers/ZoneServer2016/entities/crate.ts b/src/servers/ZoneServer2016/entities/crate.ts index 0c4497ea3c..a42ef51a5b 100644 --- a/src/servers/ZoneServer2016/entities/crate.ts +++ b/src/servers/ZoneServer2016/entities/crate.ts @@ -17,7 +17,7 @@ import { randomIntFromInterval, isPosInRadius } from "../../../utils/utils"; import { containerLootSpawners } from "../data/lootspawns"; import { getRandomItem } from "../managers/worldobjectmanager"; import { BaseSimpleNpc } from "./basesimplenpc"; -import { Effects, ModelIds } from "../models/enums"; +import { Effects, Items, ModelIds } from "../models/enums"; import { CharacterRemovePlayer } from "../../../types/zone2016packets"; export function getActorModelId(actorModel: string): number { @@ -137,9 +137,45 @@ export class Crate extends BaseSimpleNpc { this.pGetSimpleProxyHealth() ); if (this.health > 0) return; + + this.generateWoodPlanks(server, damageInfo); this.destroy(server); } + generateWoodPlanks(server: ZoneServer2016, damageInfo: DamageInfo) { + const client = server.getClientByCharId(damageInfo.entity); + if (!client) return; + + const weapon = client.character.getEquippedWeapon(); + if (!weapon) return; + + // 20% chance to spawn wood planks, 60% if it's a crowbar + const woodPlanksChance = Math.floor(Math.random() * 100) + 1; + const spawnChanceWoodPlank = + weapon.itemDefinitionId == Items.WEAPON_CROWBAR ? 60 : 20; + + if (woodPlanksChance < spawnChanceWoodPlank) { + const woodPlankItem = server.worldObjectManager.createLootEntity( + server, + server.generateItem(Items.WOOD_PLANK), + new Float32Array([ + this.state.position[0], + this.actorModelId == ModelIds.CRATE_BOX_2 + ? this.state.position[1] + 0.1 + : this.state.position[1], + this.state.position[2], + 1 + ]), + new Float32Array([0, 0, 0, 0]) + ); + if (!woodPlankItem) return; + server.executeFuncForAllReadyClientsInRange((c) => { + c.spawnedEntities.add(woodPlankItem); + server.addLightweightNpc(c, woodPlankItem); + }, woodPlankItem); + } + } + destroy(server: ZoneServer2016): boolean { this.spawnLoot(server); diff --git a/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts b/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts index a57c14c6c7..14217ba29e 100644 --- a/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts +++ b/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts @@ -62,6 +62,7 @@ export class LootableConstructionEntity extends BaseLootableEntity { /** Used by DecayManager, determines if the entity will be damaged the next decay tick */ isDecayProtected: boolean = false; + isProp: boolean = false; constructor( characterId: string, transientId: number, @@ -72,7 +73,8 @@ export class LootableConstructionEntity extends BaseLootableEntity { scale: Float32Array, itemDefinitionId: number, parentObjectCharacterId: string, - subEntityType: string + subEntityType: string, + isProp = false ) { super(characterId, transientId, actorModelId, position, rotation, server); this.parentObjectCharacterId = parentObjectCharacterId || ""; @@ -80,6 +82,7 @@ export class LootableConstructionEntity extends BaseLootableEntity { const itemDefinition = server.getItemDefinition(itemDefinitionId); if (itemDefinition) this.nameId = itemDefinition.NAME_ID; this.profileId = 999; /// mark as construction + this.isProp = isProp; this.maxHealth = getMaxHealth(this.itemDefinitionId); this.health = this.maxHealth; @@ -279,55 +282,74 @@ export class LootableConstructionEntity extends BaseLootableEntity { } } - async OnMeleeHit(server: ZoneServer2016, damageInfo: DamageInfo) { - if ( - this.itemDefinitionId == Items.BEE_BOX && - damageInfo.weapon != Items.WEAPON_HAMMER_DEMOLITION - ) { - const client = server.getClientByCharId(damageInfo.entity); - const dictionary = server.getEntityDictionary(this.characterId); + async handleBeeboxSwarm(server: ZoneServer2016, damageInfo: DamageInfo) { + const client = server.getClientByCharId(damageInfo.entity); + const dictionary = server.getEntityDictionary(this.characterId); + if (!client || !dictionary) return; - if (!client) return; - if (!dictionary) return; + server.sendDataToAllWithSpawnedEntity( + dictionary, + this.characterId, + "Character.PlayWorldCompositeEffect", + { + characterId: this.characterId, + effectId: Effects.PFX_Bee_Swarm_Attack, + position: client.character.state.position, + effectTime: 5 + } + ); - server.sendDataToAllWithSpawnedEntity( - dictionary, - this.characterId, - "Character.PlayWorldCompositeEffect", - { - characterId: this.characterId, - effectId: Effects.PFX_Bee_Swarm_Attack, - position: client.character.state.position, - effectTime: 5 - } - ); + for (let i = 0; i < 12; i++) { + const dmgInfo: DamageInfo = { + entity: "", + damage: 25 + }; + client.character.damage(server, dmgInfo); + await scheduler.wait(500); + } - for (let i = 0; i < 3; i++) { - const dmgInfo: DamageInfo = { - entity: "", - damage: 100 - }; - client.character.damage(server, dmgInfo); - await scheduler.wait(500); - } + let hudIndicator: HudIndicator | undefined = undefined; + hudIndicator = server._hudIndicators[ResourceIndicators.BEES]; - let hudIndicator: HudIndicator | undefined = undefined; - hudIndicator = server._hudIndicators[ResourceIndicators.BEES]; + if (!hudIndicator) return; - if (!hudIndicator) return; + if (client.character.hudIndicators[hudIndicator.typeName]) { + client.character.hudIndicators[hudIndicator.typeName].expirationTime += + 5000; + } else { + client.character.hudIndicators[hudIndicator.typeName] = { + typeName: hudIndicator.typeName, + expirationTime: Date.now() + 5000 + }; + server.sendHudIndicators(client); + } + } - if (client.character.hudIndicators[hudIndicator.typeName]) { - client.character.hudIndicators[hudIndicator.typeName].expirationTime += - 5000; - } else { - client.character.hudIndicators[hudIndicator.typeName] = { - typeName: hudIndicator.typeName, - expirationTime: Date.now() + 5000 - }; - server.sendHudIndicators(client); - } + OnMeleeHit(server: ZoneServer2016, damageInfo: DamageInfo) { + if (this.isProp) return; + if ( + this.itemDefinitionId == Items.BEE_BOX && + damageInfo.weapon != Items.WEAPON_HAMMER_DEMOLITION + ) { + this.handleBeeboxSwarm(server, damageInfo); } server.constructionManager.OnMeleeHit(server, damageInfo, this); } + + OnProjectileHit(server: ZoneServer2016, damageInfo: DamageInfo) { + if (this.isProp) return; + let freePlaceDmgMultiplier = 1; + + const dictionary = server.getEntityDictionary(this.characterId); + if ( + dictionary == server._worldLootableConstruction || + (server._worldSimpleConstruction && !this.parentObjectCharacterId) + ) { + freePlaceDmgMultiplier = 2; + } + // 26 308 shots for freeplaced objects, 13 for parented objects + const damage = damageInfo.damage * (3 * freePlaceDmgMultiplier); + this.damage(server, { ...damageInfo, damage }); + } } diff --git a/src/servers/ZoneServer2016/managers/constructionmanager.ts b/src/servers/ZoneServer2016/managers/constructionmanager.ts index 0117409bbd..00181a7051 100644 --- a/src/servers/ZoneServer2016/managers/constructionmanager.ts +++ b/src/servers/ZoneServer2016/managers/constructionmanager.ts @@ -1488,7 +1488,8 @@ export class ConstructionManager { position: Float32Array, rotation: Float32Array, scale: Float32Array, - parentObjectCharacterId?: string + parentObjectCharacterId?: string, + isProp: boolean = false ): boolean { const characterId = server.generateGuid(), transientId = server.getTransientId(characterId); @@ -1502,7 +1503,8 @@ export class ConstructionManager { scale, itemDefinitionId, parentObjectCharacterId || "", - "SmeltingEntity" + "SmeltingEntity", + isProp ); const parent = obj.getParent(server); @@ -2531,6 +2533,37 @@ export class ConstructionManager { ); } + crowbarConstructionEntity( + server: ZoneServer2016, + client: Client, + entity: ConstructionEntity, + weaponItem: LoadoutItem + ) { + switch (entity.itemDefinitionId) { + case Items.CAMPFIRE: + case Items.DEW_COLLECTOR: + case Items.BEE_BOX: + case Items.ANIMAL_TRAP: + return; + } + let worldFreeplaceMultiplier = 1; + const dictionary = server.getEntityDictionary(entity.characterId); + + if ( + dictionary == server._worldSimpleConstruction || + (server._worldLootableConstruction && !entity.parentObjectCharacterId) + ) { + worldFreeplaceMultiplier = 2; + } + + // 8 melee hits for entities with parents, 4 for freeplace world entities + entity.damage(server, { + entity: "", + damage: entity.maxHealth / (8 / worldFreeplaceMultiplier) + }); + server.damageItem(client, weaponItem, 50); + } + fullyRepairFoundation( server: ZoneServer2016, entity: ConstructionParentEntity @@ -2649,6 +2682,9 @@ export class ConstructionManager { case Items.WEAPON_HAMMER: this.hammerConstructionEntity(server, client, construction, weapon); return; + case Items.WEAPON_CROWBAR: + this.crowbarConstructionEntity(server, client, construction, weapon); + return; } if (this.isConstructionInSecuredArea(server, construction)) { diff --git a/src/servers/ZoneServer2016/managers/decaymanager.ts b/src/servers/ZoneServer2016/managers/decaymanager.ts index 3c8671d975..46888d4533 100644 --- a/src/servers/ZoneServer2016/managers/decaymanager.ts +++ b/src/servers/ZoneServer2016/managers/decaymanager.ts @@ -169,7 +169,8 @@ export class DecayManager { entity: | LootableConstructionEntity | ConstructionDoor - | ConstructionChildEntity + | ConstructionChildEntity, + freeplaceDecayMultiplier: number = 1 ) { if (entity.isDecayProtected) { entity.isDecayProtected = false; @@ -178,7 +179,8 @@ export class DecayManager { entity.damage(server, { entity: "Server.DecayManager", - damage: entity.maxHealth / this.ticksToFullDecay + damage: + entity.maxHealth / (this.ticksToFullDecay / freeplaceDecayMultiplier) }); } @@ -248,10 +250,18 @@ export class DecayManager { } for (const a in server._worldLootableConstruction) { - this.decayDamage(server, server._worldLootableConstruction[a]); + this.decayDamage( + server, + server._worldLootableConstruction[a], + this.worldFreeplaceDecayMultiplier + ); } for (const a in server._worldSimpleConstruction) { - this.decayDamage(server, server._worldSimpleConstruction[a]); + this.decayDamage( + server, + server._worldSimpleConstruction[a], + this.worldFreeplaceDecayMultiplier + ); } for (const a in server._constructionSimple) { const simple = server._constructionSimple[a]; diff --git a/src/servers/ZoneServer2016/managers/worldobjectmanager.ts b/src/servers/ZoneServer2016/managers/worldobjectmanager.ts index e622a145d7..4b85a5d096 100644 --- a/src/servers/ZoneServer2016/managers/worldobjectmanager.ts +++ b/src/servers/ZoneServer2016/managers/worldobjectmanager.ts @@ -605,7 +605,8 @@ export class WorldObjectManager { new Float32Array(propInstance.position), new Float32Array(fixEulerOrder(propInstance.rotation)), new Float32Array(propInstance.scale), - server._serverGuid + server._serverGuid, + true ); return; } diff --git a/src/servers/ZoneServer2016/models/enums.ts b/src/servers/ZoneServer2016/models/enums.ts index f8a01386d2..cfc3943abe 100644 --- a/src/servers/ZoneServer2016/models/enums.ts +++ b/src/servers/ZoneServer2016/models/enums.ts @@ -160,7 +160,6 @@ export enum ModelIds { BUISNESS_DOORS_GLASS_BROKEN_2 = 9899, BUISNESS_DOORS_GLASS_BROKEN_3 = 9900, DESK = 9038, - SNARE = 1974, LOCKERS_LOCKER_POLICE = 9254, LOCKERS_LOCKER_WEAPONS = 9255, OFFROADER = 7225, @@ -204,6 +203,10 @@ export enum ModelIds { WHEAT_CROPSTATE_1 = 9191, WEHAT_CROPSTATE_2 = 9190, WHEAT_CROPSTATE_3 = 9189, + BARBED_WIRE = 28, + BARBED_WIRE_POSTS = 43, + SNARE = 1974, + BATTERY = 70, SURVIVOR_MALE_HEAD_01 = 9240, SURVIVAL_FEMALE_HEAD_01 = 9474, ZOMBIE_FEMALE_HEAD = 9510,