package com.hypixel.hytale.builtin.deployables.config;
import com.hypixel.hytale.builtin.deployables.DeployablesUtils;
import com.hypixel.hytale.builtin.deployables.component.DeployableComponent;
import com.hypixel.hytale.builtin.deployables.component.DeployableProjectileComponent;
import com.hypixel.hytale.builtin.deployables.component.DeployableProjectileShooterComponent;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.map.MapCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.protocol.Opacity;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.knockback.KnockbackComponent;
import com.hypixel.hytale.server.core.modules.debug.DebugUtils;
import com.hypixel.hytale.server.core.modules.entity.DespawnComponent;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.combat.Knockback;
import com.hypixel.hytale.server.core.modules.projectile.config.ProjectileConfig;
import com.hypixel.hytale.server.core.modules.projectile.config.StandardPhysicsProvider;
import com.hypixel.hytale.server.core.modules.time.TimeResource;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.TargetUtil;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nonnull;
public class DeployableTurretConfig extends DeployableConfig {
@Nonnull
public static final BuilderCodec<DeployableTurretConfig> CODEC;
protected float trackableRadius;
protected float detectionRadius;
protected float rotationSpeed;
protected float projectileDamage;
protected boolean preferOwnerTarget;
protected int ammo;
protected ProjectileConfig projectileConfig;
protected float deployDelay;
protected float shotInterval;
protected int burstCount;
protected float burstCooldown;
protected boolean canShootOwner;
protected Knockback projectileKnockback;
protected Vector3d targetOffset = new Vector3d(0.0, 0.0, 0.0);
protected boolean doLineOfSightTest = true;
protected String projectileHitWorldSoundEventId;
protected String projectileHitLocalSoundEventId;
protected int projectileHitLocalSoundEventIndex = 0;
protected int projectileHitWorldSoundEventIndex = 0;
protected boolean respectTeams = true;
protected Map<String, Vector3d> projectileSpawnOffsets = new Object2ObjectOpenHashMap<String, Vector3d>();
public DeployableTurretConfig() {
}
protected void processConfig() {
if (this.projectileHitWorldSoundEventId != null) {
this.projectileHitWorldSoundEventIndex = this.projectileHitLocalSoundEventIndex = SoundEvent.getAssetMap().getIndex(this.projectileHitWorldSoundEventId);
}
if (this.projectileHitLocalSoundEventId != null) {
this.projectileHitLocalSoundEventIndex = SoundEvent.getAssetMap().getIndex(this.projectileHitLocalSoundEventId);
}
}
public void tick(@Nonnull DeployableComponent deployableComponent, float dt, int index, @Nonnull ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
Ref<EntityStore> entityRef = archetypeChunk.getReferenceTo(index);
switch (deployableComponent.getFlag(DeployableComponent.DeployableFlag.STATE)) {
case 0 -> this.tickInitState(entityRef, deployableComponent, store, commandBuffer);
case 1 -> this.tickStartDeployState(entityRef, deployableComponent, store);
case 2 -> this.tickAwaitDeployState(entityRef, deployableComponent, store);
case 3 -> this.tickAttackState(entityRef, deployableComponent, dt, store, commandBuffer);
}
}
private void tickInitState(@Nonnull Ref<EntityStore> entityRef, @Nonnull DeployableComponent component, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
component.setFlag(DeployableComponent.DeployableFlag.STATE, 1);
commandBuffer.addComponent(entityRef, DeployableProjectileShooterComponent.getComponentType());
playAnimation(store, entityRef, this, "Deploy");
}
private void tickStartDeployState(@Nonnull Ref<EntityStore> ref, @Nonnull DeployableComponent component, @Nonnull Store<EntityStore> store) {
component.setFlag(DeployableComponent.DeployableFlag.STATE, 2);
playAnimation(store, ref, this, "Deploy");
}
private void tickAwaitDeployState(@Nonnull Ref<EntityStore> ref, @Nonnull DeployableComponent component, @Nonnull Store<EntityStore> store) {
Instant now = ((TimeResource)store.getResource(TimeResource.getResourceType())).getNow();
Instant readyTime = component.getSpawnInstant().plus((long)this.deployDelay, ChronoUnit.SECONDS);
if (now.isAfter(readyTime)) {
component.setFlag(DeployableComponent.DeployableFlag.STATE, 3);
playAnimation(store, ref, this, "Loop");
}
}
private void tickAttackState(@Nonnull Ref<EntityStore> ref, @Nonnull DeployableComponent component, float dt, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
component.setTimeSinceLastAttack(component.getTimeSinceLastAttack() + dt);
World world = ((EntityStore)commandBuffer.getExternalData()).getWorld();
DeployableProjectileShooterComponent shooterComponent = (DeployableProjectileShooterComponent)store.getComponent(ref, DeployableProjectileShooterComponent.getComponentType());
Vector3d spawnPos = Vector3d.ZERO.clone();
if (this.projectileSpawnOffsets != null) {
spawnPos.add((Vector3d)this.projectileSpawnOffsets.get(component.getSpawnFace()));
}
if (shooterComponent == null) {
world.execute(() -> {
if (ref.isValid()) {
DespawnComponent despawn = (DespawnComponent)store.ensureAndGetComponent(ref, DespawnComponent.getComponentType());
WorldTimeResource timeManager = (WorldTimeResource)commandBuffer.getResource(WorldTimeResource.getResourceType());
despawn.setDespawn(timeManager.getGameTime());
}
});
} else {
TransformComponent transformComponent = (TransformComponent)store.getComponent(ref, TransformComponent.getComponentType());
assert transformComponent != null;
HeadRotation headRotationComponent = (HeadRotation)store.getComponent(ref, HeadRotation.getComponentType());
assert headRotationComponent != null;
Vector3d pos = Vector3d.add(spawnPos, transformComponent.getPosition());
this.updateProjectiles(store, commandBuffer, shooterComponent);
boolean hasTarget = false;
Ref<EntityStore> target = shooterComponent.getActiveTarget();
if (target != null && target.isValid()) {
TransformComponent targetTransformComponent = (TransformComponent)store.getComponent(target, TransformComponent.getComponentType());
assert targetTransformComponent != null;
Vector3d targetPos = this.calculatedTargetPosition(targetTransformComponent.getPosition());
Vector3d direction = Vector3d.directionTo(pos, targetPos);
if (targetPos.distanceTo(pos) <= (double)this.trackableRadius && this.testLineOfSight(pos, targetPos, direction, commandBuffer)) {
hasTarget = true;
}
}
if (!hasTarget) {
Ref<EntityStore> closestTarget = null;
Vector3d closestTargetPos = Vector3d.MAX;
for(Ref<EntityStore> potentialTargetRef : TargetUtil.getAllEntitiesInSphere(pos, (double)this.detectionRadius, commandBuffer)) {
if (potentialTargetRef != null && potentialTargetRef.isValid()) {
TransformComponent targetTransformComponent = (TransformComponent)store.getComponent(potentialTargetRef, TransformComponent.getComponentType());
assert targetTransformComponent != null;
Vector3d targetPosition = this.calculatedTargetPosition(targetTransformComponent.getPosition());
Vector3d direction = Vector3d.directionTo(pos, targetPosition);
if (this.testLineOfSight(pos, targetPosition, direction, commandBuffer) && this.isValidTarget(ref, store, potentialTargetRef) && pos.distanceTo(targetPosition) < pos.distanceTo(closestTargetPos)) {
closestTargetPos = targetPosition;
closestTarget = potentialTargetRef;
}
}
}
if (closestTarget != null) {
shooterComponent.setActiveTarget(closestTarget);
target = closestTarget;
hasTarget = true;
}
}
Vector3d targetPos = Vector3d.ZERO;
Vector3f targetLookRotation = Vector3f.ZERO;
Vector3f lookRotation = Vector3f.ZERO;
if (hasTarget) {
TransformComponent targetTransformComponent = (TransformComponent)store.getComponent(target, TransformComponent.getComponentType());
assert targetTransformComponent != null;
targetPos = this.calculatedTargetPosition(targetTransformComponent.getPosition().clone());
Vector3d relativeTargetOffset = new Vector3d(pos.x - targetPos.x, pos.y - targetPos.y, pos.z - targetPos.z);
targetLookRotation = Vector3f.lookAt(relativeTargetOffset.negate());
lookRotation = Vector3f.lerpAngle(headRotationComponent.getRotation(), targetLookRotation, this.rotationSpeed * dt);
}
headRotationComponent.setRotation(lookRotation);
int shotsFired = component.getFlag(DeployableComponent.DeployableFlag.BURST_SHOTS);
float timeSinceLastAttack = component.getTimeSinceLastAttack();
boolean canFire = false;
if (shotsFired < this.burstCount && timeSinceLastAttack >= this.shotInterval) {
component.setFlag(DeployableComponent.DeployableFlag.BURST_SHOTS, shotsFired + 1);
canFire = true;
} else if (shotsFired >= this.burstCount && timeSinceLastAttack >= this.burstCooldown) {
component.setFlag(DeployableComponent.DeployableFlag.BURST_SHOTS, 1);
canFire = true;
}
if (canFire && hasTarget) {
Vector3d fwdDirection = (new Vector3d()).assign((double)lookRotation.getYaw(), (double)lookRotation.getPitch());
Vector3d rootPos = transformComponent.getPosition();
Vector3d projectileSpawnPos = Vector3d.ZERO.clone();
if (this.projectileSpawnOffsets != null) {
projectileSpawnPos = ((Vector3d)this.projectileSpawnOffsets.get(component.getSpawnFace())).clone();
}
projectileSpawnPos.add(fwdDirection.clone().normalize());
projectileSpawnPos.add(rootPos);
UUID uuid = ((UUIDComponent)store.getComponent(ref, UUIDComponent.getComponentType())).getUuid();
shooterComponent.spawnProjectile(ref, commandBuffer, this.projectileConfig, uuid, projectileSpawnPos, fwdDirection.clone());
playAnimation(store, ref, this, "Shoot");
component.setTimeSinceLastAttack(0.0F);
}
}
}
private Vector3d calculatedTargetPosition(@Nonnull Vector3d original) {
return Vector3d.add(original.clone(), this.targetOffset);
}
private boolean isValidTarget(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> targetRef) {
if (targetRef.equals(ref)) {
return false;
} else {
DeployableComponent deployableComponent = (DeployableComponent)store.getComponent(ref, DeployableComponent.getComponentType());
if (deployableComponent == null) {
return true;
} else {
return this.canShootOwner || !targetRef.equals(deployableComponent.getOwner());
}
}
}
private boolean testLineOfSight(@Nonnull Vector3d attackerPos, @Nonnull Vector3d targetPos, @Nonnull Vector3d direction, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
if (!this.doLineOfSightTest) {
return true;
} else {
com.hypixel.hytale.protocol.Vector3f spawnOffset = this.projectileConfig.getSpawnOffset();
Vector3d testFromPos = attackerPos.clone().add((double)spawnOffset.x, (double)(spawnOffset.y + this.generatedModel.getEyeHeight()), (double)spawnOffset.z);
double distance = testFromPos.distanceTo(targetPos);
World world = ((EntityStore)commandBuffer.getExternalData()).getWorld();
Vector3f whiteColor = new Vector3f(1.0F, 1.0F, 1.0F);
if (this.getDebugVisuals()) {
Vector3d increment = direction.scale(distance);
for(int i = 0; i < 10; ++i) {
Vector3d pos = testFromPos.clone();
pos.addScaled(increment, (double)((float)i / 10.0F));
DebugUtils.addSphere(world, pos, whiteColor, 0.10000000149011612, 0.5F);
}
}
Vector3i blockPosition = TargetUtil.getTargetBlock(world, (id, fluid_id) -> {
if (id == 0) {
return false;
} else {
BlockType blockType = (BlockType)BlockType.getAssetMap().getAsset(id);
BlockMaterial material = blockType.getMaterial();
if (material == BlockMaterial.Empty) {
return false;
} else {
return blockType.getOpacity() != Opacity.Transparent;
}
}
}, attackerPos.x, attackerPos.y, attackerPos.z, direction.x, direction.y, direction.z, distance);
if (blockPosition == null) {
return true;
} else {
double entityDistance = attackerPos.distanceSquaredTo(targetPos);
double blockDistance = attackerPos.distanceSquaredTo((double)blockPosition.x + 0.5, (double)blockPosition.y + 0.5, (double)blockPosition.z + 0.5);
return entityDistance < blockDistance;
}
}
}
private void updateProjectiles(@Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer, @Nonnull DeployableProjectileShooterComponent shooterComponent) {
List<Ref<EntityStore>> projectiles = shooterComponent.getProjectiles();
List<Ref<EntityStore>> projectilesForRemoval = shooterComponent.getProjectilesForRemoval();
projectiles.removeAll(Collections.singleton((Object)null));
for(Ref<EntityStore> projectile : projectiles) {
this.updateProjectile(projectile, shooterComponent, store, commandBuffer);
}
for(Ref<EntityStore> projectile : projectilesForRemoval) {
if (projectile.isValid()) {
commandBuffer.removeEntity(projectile, RemoveReason.REMOVE);
}
projectiles.remove(projectile);
}
projectilesForRemoval.clear();
}
private void updateProjectile(@Nonnull Ref<EntityStore> projectileRef, @Nonnull DeployableProjectileShooterComponent shooterComponent, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
if (!projectileRef.isValid()) {
shooterComponent.getProjectilesForRemoval().add(projectileRef);
} else {
TransformComponent projTransformComponent = (TransformComponent)store.getComponent(projectileRef, TransformComponent.getComponentType());
assert projTransformComponent != null;
Vector3d projPos = projTransformComponent.getPosition();
AtomicReference<Boolean> hit = new AtomicReference(Boolean.FALSE);
DeployableProjectileComponent dProjComponent = (DeployableProjectileComponent)store.getComponent(projectileRef, DeployableProjectileComponent.getComponentType());
assert dProjComponent != null;
Vector3d prevPos = dProjComponent.getPreviousTickPosition();
Vector3d increment = new Vector3d((projPos.x - prevPos.x) * 0.10000000149011612, (projPos.y - prevPos.y) * 0.10000000149011612, (projPos.z - prevPos.z) * 0.10000000149011612);
for(int j = 0; j < 10; ++j) {
if (!(Boolean)hit.get()) {
Vector3d scanPos = dProjComponent.getPreviousTickPosition().clone();
scanPos.x += increment.x * (double)j;
scanPos.y += increment.y * (double)j;
scanPos.z += increment.z * (double)j;
if (this.getDebugVisuals()) {
DebugUtils.addSphere(((EntityStore)store.getExternalData()).getWorld(), scanPos, new Vector3f(1.0F, 1.0F, 1.0F), 0.10000000149011612, 5.0F);
}
for(Ref<EntityStore> targetEntityRef : TargetUtil.getAllEntitiesInSphere(scanPos, 0.1, store)) {
if ((Boolean)hit.get()) {
return;
}
this.projectileHit(targetEntityRef, projectileRef, shooterComponent, store, commandBuffer);
hit.set(Boolean.TRUE);
}
}
}
dProjComponent.setPreviousTickPosition(projPos);
if (!(Boolean)hit.get()) {
StandardPhysicsProvider physics = (StandardPhysicsProvider)store.getComponent(projectileRef, StandardPhysicsProvider.getComponentType());
if (physics != null && physics.getState() != StandardPhysicsProvider.STATE.ACTIVE) {
shooterComponent.getProjectilesForRemoval().add(projectileRef);
}
}
}
}
private void projectileHit(@Nonnull Ref<EntityStore> ref, @Nonnull Ref<EntityStore> projectileRef, @Nonnull DeployableProjectileShooterComponent shooterComponent, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
Damage damageEntry = new Damage(new Damage.EntitySource(ref), DamageCause.PHYSICAL, this.projectileDamage);
DamageSystems.executeDamage(ref, commandBuffer, damageEntry);
TransformComponent projectileTransformComponent = (TransformComponent)store.getComponent(projectileRef, TransformComponent.getComponentType());
assert projectileTransformComponent != null;
Vector3d projectilePosition = projectileTransformComponent.getPosition().clone();
if (this.projectileKnockback != null) {
float projectileRotationYaw = projectileTransformComponent.getRotation().getYaw();
((EntityStore)store.getExternalData()).getWorld().execute(() -> {
if (ref.isValid()) {
this.applyKnockback(ref, projectilePosition, projectileRotationYaw, store);
}
});
}
DeployablesUtils.playSoundEventsAtEntity(ref, commandBuffer, this.projectileHitLocalSoundEventIndex, this.projectileHitWorldSoundEventIndex, projectilePosition);
shooterComponent.getProjectilesForRemoval().add(projectileRef);
}
private void applyKnockback(@Nonnull Ref<EntityStore> targetRef, @Nonnull Vector3d attackerPos, float attackerYaw, @Nonnull Store<EntityStore> store) {
KnockbackComponent knockbackComponent = (KnockbackComponent)store.ensureAndGetComponent(targetRef, KnockbackComponent.getComponentType());
TransformComponent transformComponent = (TransformComponent)store.getComponent(targetRef, TransformComponent.getComponentType());
assert transformComponent != null;
knockbackComponent.setVelocity(this.projectileKnockback.calculateVector(attackerPos, attackerYaw, transformComponent.getPosition()));
knockbackComponent.setVelocityType(this.projectileKnockback.getVelocityType());
knockbackComponent.setVelocityConfig(this.projectileKnockback.getVelocityConfig());
knockbackComponent.setDuration(this.projectileKnockback.getDuration());
}
public String toString() {
return "DeployableTurretConfig{}" + super.toString();
}
static {
CODEC = ((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)BuilderCodec.builder(DeployableTurretConfig.class, DeployableTurretConfig::new, DeployableConfig.BASE_CODEC).appendInherited(new KeyedCodec("TrackableRadius", Codec.FLOAT), (o, i) -> o.trackableRadius = i, (o) -> o.trackableRadius, (o, p) -> o.trackableRadius = p.trackableRadius).documentation("The radius in which a targeted entity can be tracked").add()).appendInherited(new KeyedCodec("DetectionRadius", Codec.FLOAT), (o, i) -> o.detectionRadius = i, (o) -> o.detectionRadius, (o, p) -> o.detectionRadius = p.detectionRadius).documentation("The radius in which an entity can be targeted").add()).appendInherited(new KeyedCodec("RotationSpeed", Codec.FLOAT), (o, i) -> o.rotationSpeed = i, (o) -> o.rotationSpeed, (o, p) -> o.rotationSpeed = p.rotationSpeed).documentation("The speed at which the turret can rotate to hit it's target").add()).appendInherited(new KeyedCodec("PreferOwnerTarget", Codec.BOOLEAN), (o, i) -> o.preferOwnerTarget = i, (o) -> o.preferOwnerTarget, (o, p) -> o.preferOwnerTarget = p.preferOwnerTarget).documentation("If true, will prefer targeting entities that the owner is attacking").add()).appendInherited(new KeyedCodec("Ammo", Codec.INTEGER), (o, i) -> o.ammo = i, (o) -> o.ammo, (o, p) -> o.ammo = p.ammo).documentation("The total ammo the turret has, each projectile will consume one").add()).appendInherited(new KeyedCodec("DeployDelay", Codec.FLOAT), (o, i) -> o.deployDelay = i, (o) -> o.deployDelay, (o, p) -> o.deployDelay = p.deployDelay).documentation("The delay in seconds until the deployable is ready to begin targeting logic").add()).appendInherited(new KeyedCodec("ProjectileConfig", ProjectileConfig.CODEC), (o, i) -> o.projectileConfig = i, (o) -> o.projectileConfig, (o, p) -> o.projectileConfig = p.projectileConfig).addValidator(Validators.nonNull()).add()).appendInherited(new KeyedCodec("ShotInterval", Codec.FLOAT), (o, i) -> o.shotInterval = i, (o) -> o.shotInterval, (o, p) -> o.shotInterval = p.shotInterval).add()).appendInherited(new KeyedCodec("BurstCount", Codec.INTEGER), (o, i) -> o.burstCount = i, (o) -> o.burstCount, (o, p) -> o.burstCount = p.burstCount).add()).appendInherited(new KeyedCodec("BurstCooldown", Codec.FLOAT), (o, i) -> o.burstCooldown = i, (o) -> o.burstCooldown, (o, p) -> o.burstCooldown = p.burstCooldown).add()).appendInherited(new KeyedCodec("ProjectileDamage", Codec.FLOAT), (o, i) -> o.projectileDamage = i, (o) -> o.projectileDamage, (o, p) -> o.projectileDamage = p.projectileDamage).add()).appendInherited(new KeyedCodec("CanShootOwner", Codec.BOOLEAN), (o, i) -> o.canShootOwner = i, (o) -> o.canShootOwner, (o, p) -> o.canShootOwner = p.canShootOwner).add()).appendInherited(new KeyedCodec("Knockback", Knockback.CODEC), (i, s) -> i.projectileKnockback = s, (i) -> i.projectileKnockback, (i, parent) -> i.projectileKnockback = parent.projectileKnockback).add()).appendInherited(new KeyedCodec("TargetOffset", Vector3d.CODEC), (i, s) -> i.targetOffset = s, (i) -> i.targetOffset, (i, parent) -> i.targetOffset = parent.targetOffset).add()).appendInherited(new KeyedCodec("DoLineOfSightTest", Codec.BOOLEAN), (o, i) -> o.doLineOfSightTest = i, (o) -> o.doLineOfSightTest, (o, p) -> o.doLineOfSightTest = p.doLineOfSightTest).add()).appendInherited(new KeyedCodec("ProjectileHitWorldSoundEventId", Codec.STRING), (o, i) -> o.projectileHitWorldSoundEventId = i, (o) -> o.projectileHitWorldSoundEventId, (o, p) -> o.projectileHitWorldSoundEventId = p.projectileHitWorldSoundEventId).documentation("The positioned sound event played to surrounding players when the projectile hits a player").add()).appendInherited(new KeyedCodec("ProjectileHitLocalSoundEventId", Codec.STRING), (o, i) -> o.projectileHitLocalSoundEventId = i, (o) -> o.projectileHitLocalSoundEventId, (o, p) -> o.projectileHitLocalSoundEventId = p.projectileHitLocalSoundEventId).documentation("The positioned sound event played to a player hit by the projectile").add()).appendInherited(new KeyedCodec("RespectTeams", Codec.BOOLEAN), (o, i) -> o.respectTeams = i, (o) -> o.respectTeams, (o, p) -> o.respectTeams = p.respectTeams).add()).appendInherited(new KeyedCodec("ProjectileSpawnOffsets", new MapCodec(Vector3d.CODEC, Object2ObjectOpenHashMap::new, true)), (o, i) -> o.projectileSpawnOffsets = i, (o) -> o.projectileSpawnOffsets, (o, p) -> o.projectileSpawnOffsets = p.projectileSpawnOffsets).add()).afterDecode(DeployableTurretConfig::processConfig)).build();
}
}