diff --git a/src/main/java/noppes/npcs/EventHooks.java b/src/main/java/noppes/npcs/EventHooks.java index e677c9fb6..b013578cb 100644 --- a/src/main/java/noppes/npcs/EventHooks.java +++ b/src/main/java/noppes/npcs/EventHooks.java @@ -15,6 +15,7 @@ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.inventory.Container; import net.minecraft.item.ItemStack; import net.minecraft.util.DamageSource; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.event.entity.EntityEvent; @@ -22,6 +23,7 @@ import net.minecraftforge.event.world.WorldEvent; import noppes.npcs.controllers.*; import noppes.npcs.controllers.data.*; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.scripted.ScriptBlockPos; import noppes.npcs.scripted.event.*; import noppes.npcs.scripted.event.PlayerEvent.ChatEvent; import noppes.npcs.scripted.event.PlayerEvent.ContainerOpen; @@ -35,6 +37,7 @@ import noppes.npcs.scripted.interfaces.*; import noppes.npcs.scripted.interfaces.entity.IEntity; import noppes.npcs.scripted.interfaces.entity.IPlayer; import noppes.npcs.scripted.interfaces.gui.ICustomGui; +import noppes.npcs.scripted.interfaces.handler.data.INaturalSpawn; import noppes.npcs.scripted.interfaces.item.IItemCustom; import noppes.npcs.scripted.interfaces.item.IItemStack; import noppes.npcs.scripted.NpcAPI; @@ -502,6 +505,17 @@ public class EventHooks { } } + public static boolean onCNPCNaturalSpawn(INaturalSpawn naturalSpawn, BlockPos blockPos, boolean animalSpawnPassed, boolean monsterSpawnPassed, boolean liquidSpawnPassed, boolean airSpawnPassed) { + ForgeDataScript handler = ScriptController.Instance.forgeScripts; + if (handler.isEnabled()) { + IPos attemptPosition = new ScriptBlockPos(blockPos); + CustomNPCsEvent.CNPCNaturalSpawnEvent event = new CustomNPCsEvent.CNPCNaturalSpawnEvent(naturalSpawn, attemptPosition, animalSpawnPassed, monsterSpawnPassed, liquidSpawnPassed, airSpawnPassed); + handler.callScript("onCNPCNaturalSpawn", event); + return NpcAPI.EVENT_BUS.post(event); + } + return true; + } + public static void onCustomGuiButton(IPlayer player, ICustomGui gui, int buttonId) { CustomGuiEvent.ButtonEvent event = new CustomGuiEvent.ButtonEvent(player, gui, buttonId); CustomGuiController.onButton(event); diff --git a/src/main/java/noppes/npcs/NPCSpawning.java b/src/main/java/noppes/npcs/NPCSpawning.java index 56b12adf7..a1c2fa982 100644 --- a/src/main/java/noppes/npcs/NPCSpawning.java +++ b/src/main/java/noppes/npcs/NPCSpawning.java @@ -17,11 +17,10 @@ import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EnumCreatureType; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.util.MathHelper; -import net.minecraft.world.ChunkCoordIntPair; -import net.minecraft.world.ChunkPosition; -import net.minecraft.world.World; -import net.minecraft.world.WorldServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.*; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.event.ForgeEventFactory; @@ -32,7 +31,12 @@ import noppes.npcs.entity.EntityNPCInterface; public class NPCSpawning { private static Set eligibleChunksForSpawning = Sets.newHashSet(); - + + private static boolean animalSpawn; + private static boolean monsterSpawn; + private static boolean airSpawn; + private static boolean liquidSpawn; + public static void findChunksForSpawning(WorldServer world){ if(SpawnController.instance.data.isEmpty() || world.getWorldInfo().getWorldTotalTime() % 400L != 0L) return; @@ -126,6 +130,11 @@ public class NPCSpawning { for (int k2 = 0; k2 < 4; ++k2){ int l2 = world.getTopSolidOrLiquidBlock(j1, k1); + if (l2 > data.spawnHeightMax || l2 < data.spawnHeightMin) { + continue; + } else if (data.airSpawning && l2 < data.spawnHeightMax) { + l2 = l2 + (int) ((data.spawnHeightMax - l2) * Math.random()); + } if (!canCreatureTypeSpawnAtLocation(data, world, j1, l2, k1)){ j1 += rand.nextInt(5) - rand.nextInt(5); @@ -156,6 +165,9 @@ public class NPCSpawning { npc.stats.spawnCycle = 3; npc.ai.returnToStart = false; npc.ai.startPos = new int[]{x,y, z}; + npc.updateAI = true; + npc.updateClient = true; + npc.getNavigator().clearPathEntity(); } entity.setLocationAndAngles(x + 0.5, y, z + 0.5, world.rand.nextFloat() * 360.0F, 0.0F); } @@ -167,6 +179,10 @@ public class NPCSpawning { Result canSpawn = ForgeEventFactory.canEntitySpawn(entityliving, world, x + 0.5f, y, z + 0.5f); if (canSpawn == Result.DENY || (canSpawn == Result.DEFAULT && !entityliving.getCanSpawnHere())) return false; + + if (!EventHooks.onCNPCNaturalSpawn(data, new BlockPos(x,y,z), NPCSpawning.animalSpawn, NPCSpawning.monsterSpawn, NPCSpawning.liquidSpawn, NPCSpawning.airSpawn)) { + return false; + } world.spawnEntityInWorld(entityliving); @@ -174,17 +190,23 @@ public class NPCSpawning { } public static boolean canCreatureTypeSpawnAtLocation(SpawnData data, World world, int x, int y, int z){ - if (data.liquid){ - return world.getBlock(x, y, z).getMaterial().isLiquid() && world.getBlock(x, y - 1, z).getMaterial().isLiquid() && !world.getBlock(x, y + 1, z).isNormalCube(); - } - else if (!World.doesBlockHaveSolidTopSurface(world, x, y - 1, z)){ - return false; - } - else{ - Block block = world.getBlock(x, y - 1, z); - boolean spawnBlock = block.canCreatureSpawn(EnumCreatureType.creature, world, x, y - 1, z); - return spawnBlock && !world.getBlock(x, y, z).isNormalCube() && !world.getBlock(x, y, z).getMaterial().isLiquid() && !world.getBlock(x, y + 1, z).isNormalCube(); - } + Block block = world.getBlock(x, y - 1, z); + boolean hasSolidSurface = !World.doesBlockHaveSolidTopSurface(world, x, y - 1, z); + + boolean spawnBlockCreature = block.canCreatureSpawn(EnumCreatureType.creature, world, x, y - 1, z); + boolean spawnBlockMonster = block.canCreatureSpawn(EnumCreatureType.monster, world, x, y - 1, z); + + boolean animalSpawn = data.animalSpawning && hasSolidSurface && spawnBlockCreature && !world.getBlock(x, y, z).isNormalCube() && !world.getBlock(x, y, z).getMaterial().isLiquid() && !world.getBlock(x, y + 1, z).isNormalCube(); + boolean monsterSpawn = data.monsterSpawning && hasSolidSurface && spawnBlockMonster && !world.getBlock(x, y, z).isNormalCube() && !world.getBlock(x, y, z).getMaterial().isLiquid() && !world.getBlock(x, y + 1, z).isNormalCube(); + boolean liquidSpawn = data.liquidSpawning && world.getBlock(x, y, z).getMaterial().isLiquid() && world.getBlock(x, y - 1, z).getMaterial().isLiquid() && !world.getBlock(x, y + 1, z).isNormalCube(); + boolean caveSpawn = data.airSpawning && world.getBlock(x, y - 1, z) == Blocks.air && world.getBlock(x, y, z) == Blocks.air && world.getBlock(x, y + 1, z) == Blocks.air; + + NPCSpawning.animalSpawn = animalSpawn; + NPCSpawning.monsterSpawn = monsterSpawn; + NPCSpawning.liquidSpawn = liquidSpawn; + NPCSpawning.airSpawn = caveSpawn; + + return animalSpawn || monsterSpawn || caveSpawn || liquidSpawn; } } diff --git a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java index 5f7e0cce9..fcbce0410 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java @@ -178,8 +178,6 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb hookList.add("usingItem"); hookList.add("stopItem"); hookList.add("finishItem"); - //hookList.add(""); - //hookList.add(""); } else if(handler instanceof ForgeDataScript) { hooks.setSize(238, 198); @@ -215,6 +213,8 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb } } } + + hookList.add("onCNPCNaturalSpawn"); } hooks.setUnsortedList(hookList); addScroll(hooks); diff --git a/src/main/java/noppes/npcs/controllers/SpawnController.java b/src/main/java/noppes/npcs/controllers/SpawnController.java index 4fa327469..fbc3b99a8 100644 --- a/src/main/java/noppes/npcs/controllers/SpawnController.java +++ b/src/main/java/noppes/npcs/controllers/SpawnController.java @@ -192,7 +192,7 @@ public class SpawnController implements INaturalSpawnsHandler { return map; } - public void saveAllData() { + public void save() { for (SpawnData spawn : this.data) { if (spawn.id < 0) { spawn.id = getUnusedId(); @@ -216,4 +216,8 @@ public class SpawnController implements INaturalSpawnsHandler { public INaturalSpawn[] getSpawns(String biome) { return getSpawnList(biome).toArray(new INaturalSpawn[]{}); } + + public INaturalSpawn createSpawn() { + return new SpawnData(); + } } diff --git a/src/main/java/noppes/npcs/controllers/data/SpawnData.java b/src/main/java/noppes/npcs/controllers/data/SpawnData.java index 848428a6f..f377b83db 100644 --- a/src/main/java/noppes/npcs/controllers/data/SpawnData.java +++ b/src/main/java/noppes/npcs/controllers/data/SpawnData.java @@ -13,7 +13,6 @@ import noppes.npcs.controllers.ServerCloneController; import noppes.npcs.scripted.CustomNPCsException; import noppes.npcs.scripted.NpcAPI; import noppes.npcs.scripted.interfaces.IWorld; -import noppes.npcs.scripted.interfaces.entity.ICustomNpc; import noppes.npcs.scripted.interfaces.entity.IEntity; import noppes.npcs.scripted.interfaces.handler.data.INaturalSpawn; @@ -22,9 +21,15 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn { public int id = -1; public String name = ""; public NBTTagCompound compound1 = new NBTTagCompound(); - public boolean liquid = false; - public boolean air = false; - + + public boolean animalSpawning = true; + public boolean monsterSpawning = false; + public boolean liquidSpawning = false; + public boolean airSpawning = false; + + public int spawnHeightMin; + public int spawnHeightMax; + public SpawnData() { super(10); } @@ -39,8 +44,17 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn { biomes = NBTTags.getStringList(compound.getTagList("SpawnBiomes", 10)); compound1 = compound.getCompoundTag("SpawnCompound1"); - liquid = compound.getBoolean("Liquid"); - air = compound.getBoolean("Air"); + animalSpawning = compound.getBoolean("AnimalSpawning"); + monsterSpawning = compound.getBoolean("MonsterSpawning"); + liquidSpawning = compound.getBoolean("LiquidSpawning"); + airSpawning = compound.getBoolean("CaveSpawning"); + + spawnHeightMin = compound.getInteger("HeightMin"); + if (compound.hasKey("HeightMax")) { + spawnHeightMax = 100; + } else { + spawnHeightMax = compound.getInteger("HeightMax"); + } } public NBTTagCompound writeNBT(NBTTagCompound compound) { @@ -51,8 +65,13 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn { compound.setTag("SpawnBiomes", NBTTags.nbtStringList(biomes)); compound.setTag("SpawnCompound1", compound1); - compound.setBoolean("Liquid", liquid); - compound.setBoolean("Air", air); + compound.setBoolean("AnimalSpawning", animalSpawning); + compound.setBoolean("MonsterSpawning", monsterSpawning); + compound.setBoolean("LiquidSpawning", liquidSpawning); + compound.setBoolean("CaveSpawning", airSpawning); + + compound.setInteger("HeightMin", spawnHeightMin); + compound.setInteger("HeightMax", spawnHeightMax); return compound; } @@ -68,8 +87,8 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn { public void setEntity(IEntity entity) { NBTTagCompound compound = new NBTTagCompound(); - if (!entity.getMCEntity().isEntityAlive()) { - throw new CustomNPCsException("Cannot save dead entities"); + if (!entity.getMCEntity().writeToNBTOptional(compound)) { + throw new CustomNPCsException("Entity could not be written to NBT"); } else { ServerCloneController.Instance.cleanTags(compound1); compound1 = compound; @@ -98,12 +117,52 @@ public class SpawnData extends WeightedRandom.Item implements INaturalSpawn { return this.itemWeight; } + public void setMinHeight(int height) { + this.spawnHeightMin = height; + } + + public int getMinHeight() { + return this.spawnHeightMin; + } + + public void setMaxHeight(int height) { + this.spawnHeightMax = height; + } + + public int getMaxHeight() { + return this.spawnHeightMax; + } + + public void spawnsLikeAnimal(boolean spawns) { + this.animalSpawning = spawns; + } + + public boolean spawnsLikeAnimal() { + return animalSpawning; + } + + public void spawnsLikeMonster(boolean spawns) { + this.monsterSpawning = spawns; + } + + public boolean spawnsLikeMonster() { + return monsterSpawning; + } + public void spawnsInLiquid(boolean spawns) { - this.liquid = spawns; + this.liquidSpawning = spawns; } public boolean spawnsInLiquid() { - return this.liquid; + return this.liquidSpawning; + } + + public void spawnsInAir(boolean spawns) { + this.airSpawning = spawns; + } + + public boolean spawnsInAir() { + return this.airSpawning; } public String[] getBiomes() { diff --git a/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java b/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java index ec0b120f1..744d94755 100644 --- a/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java @@ -5,8 +5,12 @@ package noppes.npcs.scripted.event; +import cpw.mods.fml.common.eventhandler.Cancelable; import cpw.mods.fml.common.eventhandler.Event; import noppes.npcs.scripted.interfaces.AbstractNpcAPI; +import noppes.npcs.scripted.interfaces.IPos; +import noppes.npcs.scripted.interfaces.entity.IPlayer; +import noppes.npcs.scripted.interfaces.handler.data.INaturalSpawn; public class CustomNPCsEvent extends Event{ public final AbstractNpcAPI API = AbstractNpcAPI.Instance(); @@ -19,4 +23,24 @@ public class CustomNPCsEvent extends Event{ } public boolean isCancelled(){return this.isCanceled();} + + @Cancelable + public static class CNPCNaturalSpawnEvent extends CustomNPCsEvent { + public final INaturalSpawn naturalSpawn; + public final IPos attemptPosition; + public final boolean animalSpawnPassed; + public final boolean monsterSpawnPassed; + public final boolean liquidSpawnPassed; + public final boolean airSpawnPassed; + + public CNPCNaturalSpawnEvent(INaturalSpawn naturalSpawn, IPos attemptPosition, boolean animalSpawnPassed, boolean monsterSpawnPassed, boolean liquidSpawnPassed, boolean airSpawnPassed) { + this.naturalSpawn = naturalSpawn; + this.attemptPosition = attemptPosition; + + this.animalSpawnPassed = animalSpawnPassed; + this.monsterSpawnPassed = monsterSpawnPassed; + this.liquidSpawnPassed = liquidSpawnPassed; + this.airSpawnPassed = airSpawnPassed; + } + } } diff --git a/src/main/java/noppes/npcs/scripted/interfaces/handler/INaturalSpawnsHandler.java b/src/main/java/noppes/npcs/scripted/interfaces/handler/INaturalSpawnsHandler.java index 6b55bca21..711ba0f98 100644 --- a/src/main/java/noppes/npcs/scripted/interfaces/handler/INaturalSpawnsHandler.java +++ b/src/main/java/noppes/npcs/scripted/interfaces/handler/INaturalSpawnsHandler.java @@ -4,9 +4,11 @@ import noppes.npcs.scripted.interfaces.handler.data.INaturalSpawn; public interface INaturalSpawnsHandler { - void saveAllData(); + void save(); INaturalSpawn[] getSpawns(); INaturalSpawn[] getSpawns(String biome); + + INaturalSpawn createSpawn(); } diff --git a/src/main/java/noppes/npcs/scripted/interfaces/handler/data/INaturalSpawn.java b/src/main/java/noppes/npcs/scripted/interfaces/handler/data/INaturalSpawn.java index e74ca5d65..f770f79dc 100644 --- a/src/main/java/noppes/npcs/scripted/interfaces/handler/data/INaturalSpawn.java +++ b/src/main/java/noppes/npcs/scripted/interfaces/handler/data/INaturalSpawn.java @@ -17,10 +17,30 @@ public interface INaturalSpawn { int getWeight(); + void setMinHeight(int height); + + int getMinHeight(); + + void setMaxHeight(int height); + + int getMaxHeight(); + + void spawnsLikeAnimal(boolean spawns); + + boolean spawnsLikeAnimal(); + + void spawnsLikeMonster(boolean spawns); + + boolean spawnsLikeMonster(); + void spawnsInLiquid(boolean spawns); boolean spawnsInLiquid(); + void spawnsInAir(boolean spawns); + + boolean spawnsInAir(); + String[] getBiomes(); void setBiomes(String[] biomes);