package com.hbm.handler.pollution; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map.Entry; import java.util.UUID; import com.hbm.config.MobConfig; import com.hbm.config.RadiationConfig; import com.hbm.entity.mob.EntityGlyphidDigger; import com.hbm.entity.mob.EntityGlyphidScout; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent; import cpw.mods.fml.common.gameevent.TickEvent.Phase; import cpw.mods.fml.relauncher.Side; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EnumCreatureType; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.monster.IMob; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.event.entity.living.LivingSpawnEvent; import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; import net.minecraftforge.event.world.WorldEvent; public class PollutionHandler { public static final String fileName = "hbmpollution.dat"; public static HashMap perWorld = new HashMap(); /** Baserate of soot generation for a furnace-equivalent machine per second */ public static final float SOOT_PER_SECOND = 1F / 25F; /** Baserate of heavy metal generation, balanced around the soot values of combustion engines */ public static final float HEAVY_METAL_PER_SECOND = 1F / 50F; public static Vec3 targetCoords; /////////////////////// /// UTILITY METHODS /// /////////////////////// public static void incrementPollution(World world, int x, int y, int z, PollutionType type, float amount) { if(!RadiationConfig.enablePollution) return; PollutionPerWorld ppw = perWorld.get(world); if(ppw == null) return; ChunkCoordIntPair pos = new ChunkCoordIntPair(x >> 6, z >> 6); PollutionData data = ppw.pollution.get(pos); if(data == null) { data = new PollutionData(); ppw.pollution.put(pos, data); } data.pollution[type.ordinal()] = MathHelper.clamp_float((float) (data.pollution[type.ordinal()] + amount * MobConfig.pollutionMult), 0F, 10_000F); } public static void decrementPollution(World world, int x, int y, int z, PollutionType type, float amount) { incrementPollution(world, x, y, z, type, -amount); } public static void setPollution(World world, int x, int y, int z, PollutionType type, float amount) { if(!RadiationConfig.enablePollution) return; PollutionPerWorld ppw = perWorld.get(world); if(ppw == null) return; ChunkCoordIntPair pos = new ChunkCoordIntPair(x >> 6, z >> 6); PollutionData data = ppw.pollution.get(pos); if(data == null) { data = new PollutionData(); ppw.pollution.put(pos, data); } data.pollution[type.ordinal()] = amount; } public static float getPollution(World world, int x, int y, int z, PollutionType type) { if(!RadiationConfig.enablePollution) return 0; PollutionPerWorld ppw = perWorld.get(world); if(ppw == null) return 0F; ChunkCoordIntPair pos = new ChunkCoordIntPair(x >> 6, z >> 6); PollutionData data = ppw.pollution.get(pos); if(data == null) return 0F; return data.pollution[type.ordinal()]; } public static PollutionData getPollutionData(World world, int x, int y, int z) { if(!RadiationConfig.enablePollution) return null; PollutionPerWorld ppw = perWorld.get(world); if(ppw == null) return null; ChunkCoordIntPair pos = new ChunkCoordIntPair(x >> 6, z >> 6); PollutionData data = ppw.pollution.get(pos); return data; } ////////////////////// /// EVENT HANDLING /// ////////////////////// @SubscribeEvent public void onWorldLoad(WorldEvent.Load event) { if(!event.world.isRemote && RadiationConfig.enablePollution) { WorldServer world = (WorldServer) event.world; String dirPath = getDataDir(world); try { File pollutionFile = new File(dirPath, fileName); if(pollutionFile != null) { if(pollutionFile.exists()) { FileInputStream io = new FileInputStream(pollutionFile); NBTTagCompound data = CompressedStreamTools.readCompressed(io); io.close(); perWorld.put(event.world, new PollutionPerWorld(data)); } else { perWorld.put(event.world, new PollutionPerWorld()); } } } catch(Exception ex) { ex.printStackTrace(); } } } @SubscribeEvent public void onWorldUnload(WorldEvent.Unload event) { if(!event.world.isRemote) perWorld.remove(event.world); } @SubscribeEvent public void onWorldSave(WorldEvent.Save event) { if(!event.world.isRemote) { WorldServer world = (WorldServer) event.world; String dirPath = getDataDir(world); File pollutionFile = new File(dirPath, fileName); try { if(!pollutionFile.getParentFile().exists()) pollutionFile.getParentFile().mkdirs(); if(!pollutionFile.exists()) pollutionFile.createNewFile(); PollutionPerWorld ppw = perWorld.get(world); if(ppw != null) { NBTTagCompound data = ppw.writeToNBT(); CompressedStreamTools.writeCompressed(data, new FileOutputStream(pollutionFile)); } } catch(Exception ex) { System.out.println("Failed to write " + pollutionFile.getAbsolutePath()); ex.printStackTrace(); } } } public String getDataDir(WorldServer world) { String dir = world.getSaveHandler().getWorldDirectory().getAbsolutePath(); // Crucible and probably Thermos provide dimId by themselves String dimId = File.separator + "DIM" + world.provider.dimensionId; if(world.provider.dimensionId != 0 && !dir.endsWith(dimId)) { dir += dimId; } dir += File.separator + "data"; return dir; } ////////////////////////// /// SYSTEM UPDATE LOOP /// ////////////////////////// int eggTimer = 0; @SubscribeEvent public void updateSystem(TickEvent.ServerTickEvent event) { if(event.side == Side.SERVER && event.phase == Phase.END) { eggTimer++; if(eggTimer < 60) return; eggTimer = 0; for(Entry entry : perWorld.entrySet()) { HashMap newPollution = new HashMap(); for(Entry chunk : entry.getValue().pollution.entrySet()) { int x = chunk.getKey().chunkXPos; int z = chunk.getKey().chunkZPos; PollutionData data = chunk.getValue(); float[] pollutionForNeightbors = new float[PollutionType.values().length]; int S = PollutionType.SOOT.ordinal(); int H = PollutionType.HEAVYMETAL.ordinal(); int P = PollutionType.POISON.ordinal(); /* CALCULATION */ if(data.pollution[S] > 15) { pollutionForNeightbors[S] = (float) (data.pollution[S] * 0.05F); data.pollution[S] *= 0.8F; } else { data.pollution[S] *= 0.99F; } data.pollution[H] *= 0.9995F; if(data.pollution[P] > 10) { pollutionForNeightbors[P] = data.pollution[P] * 0.025F; data.pollution[P] *= 0.9F; } else { data.pollution[P] *= 0.995F; } /* SPREADING */ //apply new data to self PollutionData newData = newPollution.get(chunk.getKey()); if(newData == null) newData = new PollutionData(); boolean shouldPut = false; for(int i = 0; i < newData.pollution.length; i++) { newData.pollution[i] += data.pollution[i]; if(newData.pollution[i] > 0) shouldPut = true; } if(shouldPut) newPollution.put(chunk.getKey(), newData); //apply neighbor data to neighboring chunks int[][] offsets = new int[][] {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; for(int[] offset : offsets) { ChunkCoordIntPair offPos = new ChunkCoordIntPair(x + offset[0], z + offset[1]); PollutionData offsetData = newPollution.get(offPos); if(offsetData == null) offsetData = new PollutionData(); shouldPut = false; for(int i = 0; i < offsetData.pollution.length; i++) { offsetData.pollution[i] += pollutionForNeightbors[i]; if(offsetData.pollution[i] > 0) shouldPut = true; } if(shouldPut) newPollution.put(offPos, offsetData); } } entry.getValue().pollution.clear(); entry.getValue().pollution.putAll(newPollution); } } } ////////////////////// /// DATA STRUCTURE /// ////////////////////// public static class PollutionPerWorld { public HashMap pollution = new HashMap(); public PollutionPerWorld() { } public PollutionPerWorld(NBTTagCompound data) { NBTTagList list = data.getTagList("entries", 10); for(int i = 0; i < list.tagCount(); i++) { NBTTagCompound nbt = list.getCompoundTagAt(i); int chunkX = nbt.getInteger("chunkX"); int chunkZ = nbt.getInteger("chunkZ"); pollution.put(new ChunkCoordIntPair(chunkX, chunkZ), PollutionData.fromNBT(nbt)); } } public NBTTagCompound writeToNBT() { NBTTagCompound data = new NBTTagCompound(); NBTTagList list = new NBTTagList(); for(Entry entry : pollution.entrySet()) { NBTTagCompound nbt = new NBTTagCompound(); nbt.setInteger("chunkX", entry.getKey().chunkXPos); nbt.setInteger("chunkZ", entry.getKey().chunkZPos); entry.getValue().toNBT(nbt); list.appendTag(nbt); } data.setTag("entries", list); return data; } } public static class PollutionData { public float[] pollution = new float[PollutionType.values().length]; public static PollutionData fromNBT(NBTTagCompound nbt) { PollutionData data = new PollutionData(); for(int i = 0; i < PollutionType.values().length; i++) { data.pollution[i] = nbt.getFloat(PollutionType.values()[i].name().toLowerCase(Locale.US)); } return data; } public void toNBT(NBTTagCompound nbt) { for(int i = 0; i < PollutionType.values().length; i++) { nbt.setFloat(PollutionType.values()[i].name().toLowerCase(Locale.US), pollution[i]); } } } public static enum PollutionType { SOOT, POISON, HEAVYMETAL, FALLOUT; } /////////////////// /// MOB EFFECTS /// /////////////////// public static final UUID maxHealth = UUID.fromString("25462f6c-2cb2-4ca8-9b47-3a011cc61207"); public static final UUID attackDamage = UUID.fromString("8f442d7c-d03f-49f6-a040-249ae742eed9"); @SubscribeEvent public void decorateMob(LivingSpawnEvent event) { if(!RadiationConfig.enablePollution) return; World world = event.world; if(world.isRemote) return; EntityLivingBase living = event.entityLiving; PollutionData data = getPollutionData(world, (int) Math.floor(event.x), (int) Math.floor(event.y), (int) Math.floor(event.z)); if(data == null) return; if(living instanceof IMob) { if(data.pollution[PollutionType.SOOT.ordinal()] > RadiationConfig.buffMobThreshold) { if(living.getEntityAttribute(SharedMonsterAttributes.maxHealth) != null && living.getEntityAttribute(SharedMonsterAttributes.maxHealth).getModifier(maxHealth) == null) living.getEntityAttribute(SharedMonsterAttributes.maxHealth).applyModifier(new AttributeModifier(maxHealth, "Soot Anger Health Increase", 1D, 1)); if(living.getEntityAttribute(SharedMonsterAttributes.attackDamage) != null && living.getEntityAttribute(SharedMonsterAttributes.attackDamage).getModifier(attackDamage) == null) living.getEntityAttribute(SharedMonsterAttributes.attackDamage).applyModifier(new AttributeModifier(attackDamage, "Soot Anger Damage Increase", 1.5D, 1)); living.heal(living.getMaxHealth()); } } } ///RAMPANT MODE STUFFS/// @SubscribeEvent public void rampantTargetSetter(PlayerSleepInBedEvent event){ if (MobConfig.rampantGlyphidGuidance) targetCoords = Vec3.createVectorHelper(event.x, event.y, event.z); } @SubscribeEvent public void rampantScoutPopulator(WorldEvent.PotentialSpawns event){ //yell at me if this vertical formatting hurts your brain if(MobConfig.rampantNaturalScoutSpawn && !event.world.isRemote && event.world.provider.dimensionId == 0 && event.type == EnumCreatureType.monster && event.world.canBlockSeeTheSky(event.x, event.y, event.z) && !event.isCanceled()) { if (event.world.rand.nextInt(MobConfig.rampantScoutSpawnChance) == 0) { float soot = PollutionHandler.getPollution(event.world, event.x, event.y, event.z, PollutionType.SOOT); if (soot >= MobConfig.rampantScoutSpawnThresh) { EntityGlyphidScout scout = new EntityGlyphidScout(event.world); if(scout.isValidLightLevel()) { //escort for the scout, which can also deal with obstacles EntityGlyphidDigger digger = new EntityGlyphidDigger(event.world); scout.setLocationAndAngles(event.x, event.y, event.z, event.world.rand.nextFloat() * 360.0F, 0.0F); digger.setLocationAndAngles(event.x, event.y, event.z, event.world.rand.nextFloat() * 360.0F, 0.0F); event.world.spawnEntityInWorld(scout); event.world.spawnEntityInWorld(digger); } } } } } }