package com.hypixel.hytale.builtin.adventure.farming.states;
import com.hypixel.hytale.builtin.adventure.farming.FarmingPlugin;
import com.hypixel.hytale.builtin.adventure.farming.FarmingUtil;
import com.hypixel.hytale.builtin.adventure.farming.component.CoopResidentComponent;
import com.hypixel.hytale.builtin.adventure.farming.config.FarmingCoopAsset;
import com.hypixel.hytale.builtin.tagset.TagSetPlugin;
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
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.array.ArrayCodec;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.function.consumer.TriConsumer;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.range.IntRange;
import com.hypixel.hytale.math.util.MathUtil;
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.server.core.asset.type.item.config.ItemDrop;
import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList;
import com.hypixel.hytale.server.core.asset.type.item.config.container.ItemDropContainer;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.reference.PersistentRef;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.EmptyItemContainer;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
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.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.npc.metadata.CapturedNPCMetadata;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
import com.hypixel.hytale.server.spawning.SpawnTestResult;
import com.hypixel.hytale.server.spawning.SpawningContext;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nullable;
public class CoopBlock implements Component<ChunkStore> {
public static final String STATE_PRODUCE = "Produce_Ready";
public static final BuilderCodec<CoopBlock> CODEC;
protected String coopAssetId;
protected List<CoopResident> residents = new ArrayList();
protected ItemContainer itemContainer;
public static ComponentType<ChunkStore, CoopBlock> getComponentType() {
return FarmingPlugin.get().getCoopBlockStateComponentType();
}
public CoopBlock() {
this.itemContainer = EmptyItemContainer.INSTANCE;
ArrayList<ItemStack> remainder = new ArrayList();
this.itemContainer = ItemContainer.ensureContainerCapacity(this.itemContainer, (short)5, SimpleItemContainer::new, remainder);
}
@Nullable
public FarmingCoopAsset getCoopAsset() {
return (FarmingCoopAsset)FarmingCoopAsset.getAssetMap().getAsset(this.coopAssetId);
}
public CoopBlock(String farmingCoopId, List<CoopResident> residents, ItemContainer itemContainer) {
this.itemContainer = EmptyItemContainer.INSTANCE;
this.coopAssetId = farmingCoopId;
this.residents.addAll(residents);
this.itemContainer = itemContainer.clone();
List<ItemStack> remainder = new ObjectArrayList<ItemStack>();
this.itemContainer = ItemContainer.ensureContainerCapacity(this.itemContainer, (short)5, SimpleItemContainer::new, remainder);
}
public boolean tryPutResident(CapturedNPCMetadata metadata, WorldTimeResource worldTimeResource) {
FarmingCoopAsset coopAsset = this.getCoopAsset();
if (coopAsset == null) {
return false;
} else if (this.residents.size() >= coopAsset.getMaxResidents()) {
return false;
} else if (!this.getCoopAcceptsNPCGroup(metadata.getRoleIndex())) {
return false;
} else {
this.residents.add(new CoopResident(metadata, (PersistentRef)null, worldTimeResource.getGameTime()));
return true;
}
}
public boolean tryPutWildResidentFromWild(Store<EntityStore> store, Ref<EntityStore> entityRef, WorldTimeResource worldTimeResource, Vector3i coopLocation) {
FarmingCoopAsset coopAsset = this.getCoopAsset();
if (coopAsset == null) {
return false;
} else {
NPCEntity npcComponent = (NPCEntity)store.getComponent(entityRef, NPCEntity.getComponentType());
if (npcComponent == null) {
return false;
} else {
CoopResidentComponent coopResidentComponent = (CoopResidentComponent)store.getComponent(entityRef, CoopResidentComponent.getComponentType());
if (coopResidentComponent != null) {
return false;
} else if (!this.getCoopAcceptsNPCGroup(npcComponent.getRoleIndex())) {
return false;
} else if (this.residents.size() >= coopAsset.getMaxResidents()) {
return false;
} else {
coopResidentComponent = (CoopResidentComponent)store.ensureAndGetComponent(entityRef, CoopResidentComponent.getComponentType());
coopResidentComponent.setCoopLocation(coopLocation);
UUIDComponent uuidComponent = (UUIDComponent)store.getComponent(entityRef, UUIDComponent.getComponentType());
if (uuidComponent == null) {
return false;
} else {
PersistentRef persistentRef = new PersistentRef();
persistentRef.setEntity(entityRef, uuidComponent.getUuid());
CapturedNPCMetadata metadata = FarmingUtil.generateCapturedNPCMetadata(store, entityRef, npcComponent.getRoleIndex());
CoopResident residentRecord = new CoopResident(metadata, persistentRef, worldTimeResource.getGameTime());
residentRecord.deployedToWorld = true;
this.residents.add(residentRecord);
return true;
}
}
}
}
}
public boolean getCoopAcceptsNPCGroup(int npcRoleIndex) {
TagSetPlugin.TagSetLookup tagSetPlugin = TagSetPlugin.get(NPCGroup.class);
FarmingCoopAsset coopAsset = this.getCoopAsset();
if (coopAsset == null) {
return false;
} else {
int[] acceptedNpcGroupIndexes = coopAsset.getAcceptedNpcGroupIndexes();
if (acceptedNpcGroupIndexes == null) {
return true;
} else {
for(int group : acceptedNpcGroupIndexes) {
if (tagSetPlugin.tagInSet(group, npcRoleIndex)) {
return true;
}
}
return false;
}
}
}
public void generateProduceToInventory(WorldTimeResource worldTimeResource) {
Instant currentTime = worldTimeResource.getGameTime();
FarmingCoopAsset coopAsset = this.getCoopAsset();
if (coopAsset != null) {
Map<String, String> produceDropsMap = coopAsset.getProduceDrops();
if (!produceDropsMap.isEmpty()) {
ThreadLocalRandom random = ThreadLocalRandom.current();
List<ItemStack> generatedItemDrops = new ArrayList();
for(CoopResident resident : this.residents) {
Instant lastProduced = resident.getLastProduced();
if (lastProduced == null) {
resident.setLastProduced(currentTime);
} else {
CapturedNPCMetadata residentMeta = resident.getMetadata();
int npcRoleIndex = residentMeta.getRoleIndex();
String npcName = NPCPlugin.get().getName(npcRoleIndex);
String npcDropListName = (String)produceDropsMap.get(npcName);
if (npcDropListName != null) {
ItemDropList dropListAsset = (ItemDropList)ItemDropList.getAssetMap().getAsset(npcDropListName);
if (dropListAsset != null) {
Duration harvestDiff = Duration.between(lastProduced, currentTime);
long hoursSinceLastHarvest = harvestDiff.toHours();
int produceCount = MathUtil.ceil((double)((float)hoursSinceLastHarvest / (float)WorldTimeResource.HOURS_PER_DAY));
List<ItemDrop> configuredItemDrops = new ArrayList();
for(int i = 0; i < produceCount; ++i) {
ItemDropContainer var10000 = dropListAsset.getContainer();
Objects.requireNonNull(random);
var10000.populateDrops(configuredItemDrops, random::nextDouble, npcDropListName);
for(ItemDrop drop : configuredItemDrops) {
if (drop != null && drop.getItemId() != null) {
int amount = drop.getRandomQuantity(random);
if (amount > 0) {
generatedItemDrops.add(new ItemStack(drop.getItemId(), amount, drop.getMetadata()));
}
} else {
((HytaleLogger.Api)HytaleLogger.forEnclosingClass().atWarning()).log("Tried to create ItemDrop for non-existent item in drop list id '%s'", npcDropListName);
}
}
configuredItemDrops.clear();
}
resident.setLastProduced(currentTime);
}
}
}
}
this.itemContainer.addItemStacks(generatedItemDrops);
}
}
}
public void gatherProduceFromInventory(ItemContainer playerInventory) {
for(ItemStack item : this.itemContainer.removeAllItemStacks()) {
playerInventory.addItemStack(item);
}
}
public void ensureSpawnResidentsInWorld(World world, Store<EntityStore> store, Vector3d coopLocation, Vector3d spawnOffset) {
NPCPlugin npcModule = NPCPlugin.get();
FarmingCoopAsset coopAsset = this.getCoopAsset();
if (coopAsset != null) {
float radiansPerSpawn = 6.2831855F / (float)coopAsset.getMaxResidents();
Vector3d spawnOffsetIteration = spawnOffset;
SpawningContext spawningContext = new SpawningContext();
for(CoopResident resident : this.residents) {
CapturedNPCMetadata residentMeta = resident.getMetadata();
int npcRoleIndex = residentMeta.getRoleIndex();
boolean residentDeployed = resident.getDeployedToWorld();
PersistentRef residentEntityId = resident.getPersistentRef();
if (!residentDeployed && residentEntityId == null) {
Vector3d residentSpawnLocation = (new Vector3d()).assign(coopLocation).add(spawnOffsetIteration);
Builder<Role> roleBuilder = NPCPlugin.get().tryGetCachedValidRole(npcRoleIndex);
if (roleBuilder != null) {
spawningContext.setSpawnable((ISpawnableWithModel)roleBuilder);
if (spawningContext.set(world, residentSpawnLocation.x, residentSpawnLocation.y, residentSpawnLocation.z) && spawningContext.canSpawn() == SpawnTestResult.TEST_OK) {
Pair<Ref<EntityStore>, NPCEntity> npcPair = npcModule.spawnEntity(store, npcRoleIndex, spawningContext.newPosition(), Vector3f.ZERO, (Model)null, (TriConsumer)null);
if (npcPair == null) {
resident.setPersistentRef((PersistentRef)null);
resident.setDeployedToWorld(false);
} else {
Ref<EntityStore> npcRef = npcPair.first();
NPCEntity npcComponent = npcPair.second();
npcComponent.getLeashPoint().assign(coopLocation);
if (npcRef != null && npcRef.isValid()) {
UUIDComponent uuidComponent = (UUIDComponent)store.getComponent(npcRef, UUIDComponent.getComponentType());
if (uuidComponent == null) {
resident.setPersistentRef((PersistentRef)null);
resident.setDeployedToWorld(false);
} else {
CoopResidentComponent coopResidentComponent = new CoopResidentComponent();
coopResidentComponent.setCoopLocation(coopLocation.toVector3i());
store.addComponent(npcRef, CoopResidentComponent.getComponentType(), coopResidentComponent);
PersistentRef persistentRef = new PersistentRef();
persistentRef.setEntity(npcRef, uuidComponent.getUuid());
resident.setPersistentRef(persistentRef);
resident.setDeployedToWorld(true);
spawnOffsetIteration = spawnOffsetIteration.rotateY(radiansPerSpawn);
}
} else {
resident.setPersistentRef((PersistentRef)null);
resident.setDeployedToWorld(false);
}
}
}
}
}
}
}
}
public void ensureNoResidentsInWorld(Store<EntityStore> store) {
ArrayList<CoopResident> residentsToRemove = new ArrayList();
for(CoopResident resident : this.residents) {
boolean deployed = resident.getDeployedToWorld();
PersistentRef entityUuid = resident.getPersistentRef();
if (deployed || entityUuid != null) {
Ref<EntityStore> entityRef = entityUuid.getEntity(store);
if (entityRef == null) {
residentsToRemove.add(resident);
} else {
CoopResidentComponent coopResidentComponent = (CoopResidentComponent)store.getComponent(entityRef, CoopResidentComponent.getComponentType());
if (coopResidentComponent == null) {
residentsToRemove.add(resident);
} else {
DeathComponent deathComponent = (DeathComponent)store.getComponent(entityRef, DeathComponent.getComponentType());
if (deathComponent != null) {
residentsToRemove.add(resident);
} else {
coopResidentComponent.setMarkedForDespawn(true);
resident.setPersistentRef((PersistentRef)null);
resident.setDeployedToWorld(false);
}
}
}
}
}
for(CoopResident resident : residentsToRemove) {
this.residents.remove(resident);
}
}
public boolean shouldResidentsBeInCoop(WorldTimeResource worldTimeResource) {
FarmingCoopAsset coopAsset = this.getCoopAsset();
if (coopAsset == null) {
return true;
} else {
IntRange roamTimeRange = coopAsset.getResidentRoamTime();
if (roamTimeRange == null) {
return true;
} else {
int gameHour = worldTimeResource.getCurrentHour();
return !roamTimeRange.includes(gameHour);
}
}
}
@Nullable
public Instant getNextScheduledTick(WorldTimeResource worldTimeResource) {
Instant gameTime = worldTimeResource.getGameTime();
LocalDateTime gameDateTime = worldTimeResource.getGameDateTime();
int gameHour = worldTimeResource.getCurrentHour();
int minutes = gameDateTime.getMinute();
FarmingCoopAsset coopAsset = this.getCoopAsset();
if (coopAsset == null) {
return null;
} else {
IntRange roamTimeRange = coopAsset.getResidentRoamTime();
if (roamTimeRange == null) {
return null;
} else {
int nextScheduledHour = 0;
int minTime = roamTimeRange.getInclusiveMin();
int maxTime = roamTimeRange.getInclusiveMax();
if (coopAsset.getResidentRoamTime().includes(gameHour)) {
nextScheduledHour = coopAsset.getResidentRoamTime().getInclusiveMax() + 1 - gameHour;
} else if (gameHour > maxTime) {
nextScheduledHour = WorldTimeResource.HOURS_PER_DAY - gameHour + minTime;
} else {
nextScheduledHour = minTime - gameHour;
}
return gameTime.plus((long)nextScheduledHour * 60L - (long)minutes, ChronoUnit.MINUTES);
}
}
}
public void handleResidentDespawn(UUID entityUuid) {
CoopResident removedResident = null;
for(CoopResident resident : this.residents) {
if (resident.persistentRef != null && resident.persistentRef.getUuid() == entityUuid) {
removedResident = resident;
break;
}
}
if (removedResident != null) {
this.residents.remove(removedResident);
}
}
public void handleBlockBroken(World world, WorldTimeResource worldTimeResource, Store<EntityStore> store, int blockX, int blockY, int blockZ) {
Vector3i location = new Vector3i(blockX, blockY, blockZ);
world.execute(() -> this.ensureSpawnResidentsInWorld(world, store, location.toVector3d(), (new Vector3d()).assign(Vector3d.FORWARD)));
this.generateProduceToInventory(worldTimeResource);
Vector3d dropPosition = new Vector3d((double)((float)blockX + 0.5F), (double)blockY, (double)((float)blockZ + 0.5F));
Holder<EntityStore>[] itemEntityHolders = ItemComponent.generateItemDrops(store, this.itemContainer.removeAllItemStacks(), dropPosition, Vector3f.ZERO);
if (itemEntityHolders.length > 0) {
world.execute(() -> store.addEntities(itemEntityHolders, AddReason.SPAWN));
}
world.execute(() -> {
for(CoopResident resident : this.residents) {
PersistentRef persistentRef = resident.getPersistentRef();
if (persistentRef != null) {
Ref<EntityStore> ref = persistentRef.getEntity(store);
if (ref == null) {
return;
}
store.tryRemoveComponent(ref, CoopResidentComponent.getComponentType());
}
}
});
}
public boolean hasProduce() {
return !this.itemContainer.isEmpty();
}
public Component<ChunkStore> clone() {
return new CoopBlock(this.coopAssetId, this.residents, this.itemContainer);
}
static {
CODEC = ((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)BuilderCodec.builder(CoopBlock.class, CoopBlock::new).append(new KeyedCodec("FarmingCoopId", Codec.STRING, true), (coop, s) -> coop.coopAssetId = s, (coop) -> coop.coopAssetId).add()).append(new KeyedCodec("Residents", new ArrayCodec(CoopBlock.CoopResident.CODEC, (x$0) -> new CoopResident[x$0])), (coop, residents) -> coop.residents = new ArrayList(Arrays.asList(residents)), (coop) -> (CoopResident[])coop.residents.toArray((x$0) -> new CoopResident[x$0])).add()).append(new KeyedCodec("Storage", ItemContainer.CODEC), (coop, storage) -> coop.itemContainer = storage, (coop) -> coop.itemContainer).add()).build();
}
public static class CoopResident {
public static final BuilderCodec<CoopResident> CODEC;
protected CapturedNPCMetadata metadata;
@Nullable
protected PersistentRef persistentRef;
protected boolean deployedToWorld;
protected Instant lastProduced;
public CoopResident() {
}
public CoopResident(CapturedNPCMetadata metadata, PersistentRef persistentRef, Instant lastProduced) {
this.metadata = metadata;
this.persistentRef = persistentRef;
this.lastProduced = lastProduced;
}
public CapturedNPCMetadata getMetadata() {
return this.metadata;
}
@Nullable
public PersistentRef getPersistentRef() {
return this.persistentRef;
}
public void setPersistentRef(@Nullable PersistentRef persistentRef) {
this.persistentRef = persistentRef;
}
public boolean getDeployedToWorld() {
return this.deployedToWorld;
}
public void setDeployedToWorld(boolean deployedToWorld) {
this.deployedToWorld = deployedToWorld;
}
public Instant getLastProduced() {
return this.lastProduced;
}
public void setLastProduced(Instant lastProduced) {
this.lastProduced = lastProduced;
}
static {
CODEC = ((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)BuilderCodec.builder(CoopResident.class, CoopResident::new).append(new KeyedCodec("Metadata", CapturedNPCMetadata.CODEC), (coop, meta) -> coop.metadata = meta, (coop) -> coop.metadata).add()).append(new KeyedCodec("PersistentRef", PersistentRef.CODEC), (coop, persistentRef) -> coop.persistentRef = persistentRef, (coop) -> coop.persistentRef).add()).append(new KeyedCodec("DeployedToWorld", Codec.BOOLEAN), (coop, deployedToWorld) -> coop.deployedToWorld = deployedToWorld, (coop) -> coop.deployedToWorld).add()).append(new KeyedCodec("LastHarvested", Codec.INSTANT), (coop, instant) -> coop.lastProduced = instant, (coop) -> coop.lastProduced).add()).build();
}
}
}