hytale-server/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableFromRaycastI...

173 lines
7.7 KiB
Java

package com.hypixel.hytale.builtin.deployables.interaction;
import com.hypixel.hytale.builtin.deployables.DeployablesUtils;
import com.hypixel.hytale.builtin.deployables.config.DeployableConfig;
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.Object2FloatMapCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.protocol.Direction;
import com.hypixel.hytale.protocol.Interaction;
import com.hypixel.hytale.protocol.InteractionState;
import com.hypixel.hytale.protocol.InteractionSyncData;
import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.protocol.Position;
import com.hypixel.hytale.protocol.WaitForDataFrom;
import com.hypixel.hytale.server.core.entity.InteractionContext;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap;
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatValue;
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule;
import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType;
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap.Entry;
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import javax.annotation.Nonnull;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
public class SpawnDeployableFromRaycastInteraction extends SimpleInstantInteraction {
@Nonnull
public static final BuilderCodec<SpawnDeployableFromRaycastInteraction> CODEC = BuilderCodec.builder(
SpawnDeployableFromRaycastInteraction.class, SpawnDeployableFromRaycastInteraction::new, SimpleInstantInteraction.CODEC
)
.append(new KeyedCodec<>("Config", DeployableConfig.CODEC), (i, s) -> i.config = s, i -> i.config)
.addValidator(Validators.nonNull())
.add()
.<Object2FloatMap<String>>append(
new KeyedCodec<>("PreviewStatConditions", new Object2FloatMapCodec<>(Codec.STRING, Object2FloatOpenHashMap::new)),
(changeStatInteraction, stringObject2DoubleMap) -> changeStatInteraction.unknownEntityStats = stringObject2DoubleMap,
changeStatInteraction -> changeStatInteraction.unknownEntityStats
)
.addValidator(EntityStatType.VALIDATOR_CACHE.getMapKeyValidator())
.documentation("Modifiers to apply to EntityStats.")
.add()
.<Float>appendInherited(
new KeyedCodec<>("MaxPlacementDistance", Codec.FLOAT),
(o, i) -> o.maxPlacementDistance = i,
o -> o.maxPlacementDistance,
(i, o) -> i.maxPlacementDistance = o.maxPlacementDistance
)
.documentation("The max distance at which the player can deploy the deployable.")
.add()
.afterDecode(SpawnDeployableFromRaycastInteraction::processConfig)
.build();
protected Object2FloatMap<String> unknownEntityStats;
protected Int2FloatMap entityStats;
protected float maxPlacementDistance;
private DeployableConfig config;
private void processConfig() {
if (this.unknownEntityStats != null) {
this.entityStats = EntityStatsModule.resolveEntityStats(this.unknownEntityStats);
}
}
private static boolean isSurface(@Nonnull Vector3f normal) {
return normal.x == 0.0F && normal.y - 1.0F < 0.01 && normal.z == 0.0F;
}
@Override
public boolean needsRemoteSync() {
return true;
}
@NonNullDecl
@Override
public WaitForDataFrom getWaitForDataFrom() {
return WaitForDataFrom.Client;
}
@Override
protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) {
Ref<EntityStore> entityRef = context.getOwningEntity();
Store<EntityStore> store = entityRef.getStore();
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
assert commandBuffer != null;
InteractionSyncData clientState = context.getClientState();
assert clientState != null;
if (!this.canAfford(context.getEntity(), commandBuffer)) {
context.getState().state = InteractionState.Failed;
} else {
Position raycastHit = clientState.raycastHit;
if (raycastHit == null) {
TransformComponent transformComponent = store.getComponent(entityRef, TransformComponent.getComponentType());
assert transformComponent != null;
Vector3d position = transformComponent.getPosition();
raycastHit = new Position((float)position.x, (float)position.y, (float)position.z);
}
com.hypixel.hytale.protocol.Vector3f raycastNormal = clientState.raycastNormal;
float correctedRaycastDistance = clientState.raycastDistance;
com.hypixel.hytale.protocol.Vector3f spawnPosition = new com.hypixel.hytale.protocol.Vector3f(
(float)raycastHit.x, (float)raycastHit.y, (float)raycastHit.z
);
Vector3f norm = new Vector3f(raycastNormal.x, raycastNormal.y, raycastNormal.z);
if (correctedRaycastDistance > 0.0F
&& correctedRaycastDistance <= this.maxPlacementDistance
&& (this.config.getAllowPlaceOnWalls() || isSurface(norm))) {
Direction attackerRot = clientState.attackerRot;
Vector3f rot = new Vector3f(0.0F, attackerRot.yaw, 0.0F);
DeployablesUtils.spawnDeployable(
commandBuffer, store, this.config, entityRef, new Vector3f(spawnPosition.x, spawnPosition.y, spawnPosition.z), rot, "UP"
);
}
}
}
protected boolean canAfford(@Nonnull Ref<EntityStore> entityRef, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
if (this.entityStats != null && !this.entityStats.isEmpty()) {
EntityStatMap entityStatMapComponent = componentAccessor.getComponent(entityRef, EntityStatMap.getComponentType());
if (entityStatMapComponent == null) {
return false;
} else {
ObjectIterator var4 = this.entityStats.int2FloatEntrySet().iterator();
while (var4.hasNext()) {
Entry cost = (Entry)var4.next();
EntityStatValue stat = entityStatMapComponent.get(cost.getIntKey());
if (stat == null || stat.get() < cost.getFloatValue()) {
return false;
}
}
return true;
}
} else {
return true;
}
}
@NonNullDecl
@Override
protected Interaction generatePacket() {
return new com.hypixel.hytale.protocol.SpawnDeployableFromRaycastInteraction();
}
@Override
protected void configurePacket(Interaction packet) {
super.configurePacket(packet);
com.hypixel.hytale.protocol.SpawnDeployableFromRaycastInteraction p = (com.hypixel.hytale.protocol.SpawnDeployableFromRaycastInteraction)packet;
p.deployableConfig = this.config.toPacket();
p.maxDistance = this.maxPlacementDistance;
p.costs = this.entityStats;
}
}