package mhfc.net.common.quests.world;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import mhfc.net.common.world.AreaTeleportation;
import mhfc.net.common.world.area.IArea;
import mhfc.net.common.world.area.IWorldView;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Vec3;
import net.minecraft.world.IWorldAccess;
import net.minecraft.world.World;
public abstract class SpawnControllerAdapter implements IQuestAreaSpawnController {
public static interface Spawnable extends Function<World, Entity> {}
public static class SpawnInformation {
private Spawnable spawnable;
private double relX, relY, relZ;
public SpawnInformation(Spawnable entity, double relX, double relY, double relZ) {
this.spawnable = entity;
this.relX = relX;
this.relY = relY;
this.relZ = relZ;
}
public Spawnable getEntity() {
return spawnable;
}
public void setEntity(Spawnable entity) {
this.spawnable = entity;
}
public double getRelX() {
return relX;
}
public void setRelX(double relX) {
this.relX = relX;
}
public double getRelY() {
return relY;
}
public void setRelY(double relY) {
this.relY = relY;
}
public double getRelZ() {
return relZ;
}
public void setRelZ(double relZ) {
this.relZ = relZ;
}
}
protected class WorldAccessor implements IWorldAccess {
@Override
public void markBlockForUpdate(int p_147586_1_, int p_147586_2_, int p_147586_3_) {}
@Override
public void markBlockForRenderUpdate(int p_147588_1_, int p_147588_2_, int p_147588_3_) {}
@Override
public void markBlockRangeForRenderUpdate(
int p_147585_1_,
int p_147585_2_,
int p_147585_3_,
int p_147585_4_,
int p_147585_5_,
int p_147585_6_) {}
@Override
public void playSound(
String p_72704_1_,
double p_72704_2_,
double p_72704_4_,
double p_72704_6_,
float p_72704_8_,
float p_72704_9_) {}
@Override
public void playSoundToNearExcept(
EntityPlayer p_85102_1_,
String p_85102_2_,
double p_85102_3_,
double p_85102_5_,
double p_85102_7_,
float p_85102_9_,
float p_85102_10_) {}
@Override
public void spawnParticle(
String p_72708_1_,
double p_72708_2_,
double p_72708_4_,
double p_72708_6_,
double p_72708_8_,
double p_72708_10_,
double p_72708_12_) {}
@Override
public void onEntityCreate(Entity entity) {
Vec3 pos = Vec3.createVectorHelper(entity.posX, entity.posY, entity.posZ);
pos = SpawnControllerAdapter.this.worldView.convertToLocal(pos);
if (!inArea(pos.xCoord, pos.yCoord, pos.zCoord))
return;
SpawnControllerAdapter.this.controlEntity(entity);
}
@Override
public void onEntityDestroy(Entity entity) {
SpawnControllerAdapter.this.releaseEntity(entity);
}
@Override
public void playRecord(String p_72702_1_, int p_72702_2_, int p_72702_3_, int p_72702_4_) {}
@Override
public void broadcastSound(int p_82746_1_, int p_82746_2_, int p_82746_3_, int p_82746_4_, int p_82746_5_) {}
@Override
public void playAuxSFX(
EntityPlayer p_72706_1_,
int p_72706_2_,
int p_72706_3_,
int p_72706_4_,
int p_72706_5_,
int p_72706_6_) {}
@Override
public void destroyBlockPartially(
int p_147587_1_,
int p_147587_2_,
int p_147587_3_,
int p_147587_4_,
int p_147587_5_) {}
@Override
public void onStaticEntitiesChanged() {}
}
protected class TickerEntity extends TileEntity {
public TickerEntity() {
super();
xCoord = 0;
yCoord = 0;
zCoord = 0;
tileEntityInvalid = false;
}
@Override
public void updateEntity() {
SpawnControllerAdapter.this.runSpawnCycle();
}
}
protected Set<Queue<Spawnable>> spawnQueues;
protected Map<Class<? extends Entity>, Integer> aliveCount;
protected Map<Class<? extends Entity>, Integer> spawnMaximum;
protected Set<Entity> managedEntities;
protected TileEntity tickerEntity;
protected IWorldView worldView;
protected IArea areaInstance;
public void setAreaInstance(IArea areaInstance) {
this.areaInstance = areaInstance;
}
public SpawnControllerAdapter(IWorldView worldView) {
Objects.requireNonNull(worldView);
this.worldView = worldView;
this.spawnQueues = new HashSet<>();
this.aliveCount = new HashMap<>();
this.spawnMaximum = new HashMap<>();
this.managedEntities = new HashSet<>();
this.tickerEntity = new TickerEntity();
worldView.addWorldAccess(new WorldAccessor());
worldView.addTileEntity(tickerEntity);
}
@Override
public void defaultSpawnsEnabled(boolean defaultSpawnsEnabled) {
if (defaultSpawnsEnabled)
enqueDefaultSpawns();
}
protected abstract void enqueDefaultSpawns();
protected abstract SpawnInformation constructDefaultSpawnInformation(Spawnable entity);
@Override
public void spawnEntity(String entityID) {
spawnEntity((world) -> EntityList.createEntityByName(entityID, world));
}
@Override
public void spawnEntity(String entityID, double x, double y, double z) {
spawnEntity(new SpawnInformation((world) -> EntityList.createEntityByName(entityID, world), x, y, z));
}
@Override
public void spawnEntity(Spawnable entity) {
spawnEntity(constructDefaultSpawnInformation(entity));
}
public void spawnEntity(Spawnable entity, double x, double y, double z) {
spawnEntity(new SpawnInformation(entity, x, y, z));
}
@Override
public void enqueueSpawns(Queue<Spawnable> qu) {
spawnQueues.add(qu);
}
public void dequeueSpawns(Queue<Spawnable> qu) {
spawnQueues.remove(qu);
}
@Override
public void clearQueues() {
spawnQueues.clear();
}
@Override
public void setGenerationMaximum(String entityID, int maxAmount) {
@SuppressWarnings("unchecked")
Class<? extends Entity> clazz = (Class<? extends Entity>) EntityList.stringToClassMapping.get(entityID);
if (clazz == null)
return;
setGenerationMaximum(clazz, maxAmount);
}
@Override
public <T extends Entity> void setGenerationMaximum(Class<T> entityclass, int maxAmount) {
spawnMaximum.put(entityclass, maxAmount);
}
@Override
public int clearArea() {
List<Entity> allEntities = new ArrayList<>(managedEntities);
return allEntities.stream().filter((e) -> despawnEntity(e)).collect(Collectors.counting()).intValue();
}
public Set<Entity> getControlledEntities() {
return Collections.unmodifiableSet(this.managedEntities);
}
@Override
public int clearAreaOf(String entityClassID) {
@SuppressWarnings("unchecked") // This cast should be safe, EntityList does it itself
Class<? extends Entity> clazz = (Class<? extends Entity>) EntityList.stringToClassMapping.get(entityClassID);
if (clazz == null)
return 0;
List<Entity> allEntities = new ArrayList<>(managedEntities);
return allEntities.stream().filter((e) -> clazz.isInstance(e)).filter((e) -> despawnEntity(e))
.collect(Collectors.counting()).intValue();
}
protected boolean inArea(double posX, double posY, double posZ) {
return worldView.isManaged(posX, posY, posZ);
}
/**
* Directly spawns the given entity at the position. Forces the spawn
*/
protected boolean spawnEntity(SpawnInformation information) {
Entity entity = information.spawnable.apply(worldView.getWorldObject());
AreaTeleportation.assignAreaForEntity(entity, areaInstance);
worldView.spawnEntityAt(entity, information.relX, information.relY, information.relZ);
return controlEntity(entity);
}
/**
* Directly despawns the entity (if it is managed) and removes it from this manager.
*
* @param entity
* @return
*/
protected boolean despawnEntity(Entity entity) {
worldView.removeEntity(entity);
return releaseEntity(entity);
}
/**
* This method should be called by all methods spawning an entity
*/
protected boolean controlEntity(Entity entity) {
if (!managedEntities.add(entity))
return false;
Class<? extends Entity> clazz = entity.getClass();
Integer count = aliveCount.getOrDefault(clazz, new Integer(0));
aliveCount.put(clazz, count.intValue() + 1);
return true;
}
/**
* This method should be called by all methods removing entities from the area. It only handles the internal lists
* and maps, not the actual world.
*/
protected boolean releaseEntity(Entity entity) {
if (!managedEntities.remove(entity))
return false;
Class<? extends Entity> clazz = entity.getClass();
Integer count = aliveCount.get(clazz);
aliveCount.put(clazz, count.intValue() - 1);
return true;
}
@Override
public void runSpawnCycle() {
Iterator<Queue<Spawnable>> it = spawnQueues.iterator();
while (it.hasNext()) {
Queue<Spawnable> spawnQ = it.next();
Spawnable firstEnt = spawnQ.peek();
if (firstEnt == null)
it.remove();
if (spawnIfFreeSlots(firstEnt))
spawnQ.poll();
}
}
private boolean spawnIfFreeSlots(Spawnable firstEnt) {
Entity entity = firstEnt.apply(worldView.getWorldObject());
if (aliveCount.getOrDefault(entity.getClass(), 0) >= spawnMaximum.getOrDefault(entity.getClass(), 0))
return false;
spawnEntity((world) -> entity);
return true;
}
}