package jas.spawner.modern.command; import jas.common.JustAnotherSpawner; import jas.common.global.BiomeBlacklist; import jas.spawner.modern.MVELProfile; import jas.spawner.modern.spawner.CountInfo; import jas.spawner.modern.spawner.EntityCounter; import jas.spawner.modern.spawner.Tags; import jas.spawner.modern.spawner.CountInfo.ChunkStat; import jas.spawner.modern.spawner.creature.entry.BiomeSpawnListRegistry; import jas.spawner.modern.spawner.creature.entry.SpawnListEntry; import jas.spawner.modern.spawner.creature.handler.LivingHandler; import jas.spawner.modern.spawner.creature.handler.LivingHandlerRegistry; import jas.spawner.modern.spawner.creature.handler.parsing.ParsingHelper; import jas.spawner.modern.spawner.creature.type.CreatureType; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import net.minecraft.command.ICommandSender; import net.minecraft.command.WrongUsageException; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.IEntityLivingData; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.MathHelper; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.ChunkPosition; import net.minecraft.world.World; import net.minecraftforge.event.ForgeEventFactory; import cpw.mods.fml.common.eventhandler.Event.Result; public class CommandEffectiveSpawnList extends CommandJasBase { private BiomeBlacklist biomeBlacklist; public CommandEffectiveSpawnList(BiomeBlacklist biomeBlacklist) { this.biomeBlacklist = biomeBlacklist; } public String getCommandName() { return "effectivespawnlist"; } /** * Return the required permission level for this command. */ public int getRequiredPermissionLevel() { return 2; } @Override public String getCommandUsage(ICommandSender commandSender) { return "commands.effectivespawnlist.usage"; } @Override public void process(ICommandSender commandSender, String[] stringArgs) { // /jas effectivespawnlist [0]<Player=CommanderSender> [1]<CreatureType=*> [2]<ChunkDistance=8> [3]<cycles=100> // /jas effectivespawnlist [0]<CreatureType> [1]<ChunkDistance> [2]<cycles> // /jas effectivespawnlist [0]<CreatureType> [1]<ChunkDistance> // /jas effectivespawnlist [0]<CreatureType> EntityPlayer targetPlayer = null; String desiredCreatureType = "*"; int chunkDistace = 8; int cycles = 3; switch (stringArgs.length) { case 4: targetPlayer = getPlayer(commandSender, stringArgs[0]); desiredCreatureType = stringArgs[1]; cycles = ParsingHelper.parseFilteredInteger(stringArgs[2], cycles, "Cycles"); chunkDistace = ParsingHelper.parseFilteredInteger(stringArgs[3], chunkDistace, "Chunk Distance"); break; // Fall through below is intentional case 3: chunkDistace = ParsingHelper.parseFilteredInteger(stringArgs[2], chunkDistace, "Chunk Distance"); case 2: cycles = ParsingHelper.parseFilteredInteger(stringArgs[1], cycles, "Cycles"); case 1: desiredCreatureType = stringArgs[0]; case 0: targetPlayer = getPlayer(commandSender, commandSender.getCommandSenderName()); break; default: throw new WrongUsageException("commands.jascanspawnhere.usage", new Object[0]); } HashMap<ChunkCoordIntPair, ChunkStat> eligibleChunksForSpawning = determineChunksForSpawnering(targetPlayer, JustAnotherSpawner.globalSettings().chunkSpawnDistance); Iterator<CreatureType> typeIterator = MVELProfile.worldSettings().creatureTypeRegistry() .getCreatureTypes(); StringBuilder resultMessage = new StringBuilder(); resultMessage.append("SpawnList after ").append("\u00A7b").append(cycles).append(" cycles").append("\u00A7r"); while (typeIterator.hasNext()) { CountInfo reportCount = new CountInfo(eligibleChunksForSpawning, new EntityCounter(), new EntityCounter()); CreatureType creatureType = typeIterator.next(); if (!desiredCreatureType.equals("*") && !desiredCreatureType.equals(creatureType.typeID)) { continue; } CountInfo countInfo = new CountInfo(eligibleChunksForSpawning, new EntityCounter(), new EntityCounter()); LivingHandlerRegistry livingHandlerRegistry = MVELProfile.worldSettings().livingHandlerRegistry(); BiomeSpawnListRegistry biomeSpawnListRegistry = MVELProfile.worldSettings().biomeSpawnListRegistry(); attemptSpawnCreaturesInChunks(cycles, targetPlayer.worldObj, MVELProfile.worldSettings() .livingHandlerRegistry(), MVELProfile.worldSettings().biomeSpawnListRegistry(), creatureType, biomeBlacklist, countInfo, reportCount); float totalCount = 0; for (String key : reportCount.getGlobalEntityClassCountKeysSet()) { int count = reportCount.getGlobalEntityClassCount(key); totalCount += count; } resultMessage.append(". \u00A7b").append(creatureType.typeID).append("\u00A7r"); resultMessage.append(" spawned ").append("\u00A79").append((int) totalCount).append("\u00A7r").append(" {"); for (String key : reportCount.getGlobalEntityClassCountKeysSet()) { float count = reportCount.getGlobalEntityClassCount(key); resultMessage.append(" \u00A7a").append(key).append("\u00A7r").append(": "); resultMessage.append("\u00A79").append((int) count).append("\u00A7r"); resultMessage.append("[").append("\u00A79").append((int) (count / totalCount * 100)).append("\u00A7r") .append("%]"); } resultMessage.append("}"); } commandSender.addChatMessage(new ChatComponentText(resultMessage.toString())); } private final HashMap<ChunkCoordIntPair, ChunkStat> determineChunksForSpawnering(EntityPlayer entityplayer, int chunkDistance) { HashMap<ChunkCoordIntPair, ChunkStat> eligibleChunksForSpawning = new HashMap<ChunkCoordIntPair, ChunkStat>(); int posX = MathHelper.floor_double(entityplayer.posX / 16.0D); int posZ = MathHelper.floor_double(entityplayer.posZ / 16.0D); for (int xOffset = -chunkDistance; xOffset <= chunkDistance; ++xOffset) { for (int zOffset = -chunkDistance; zOffset <= chunkDistance; ++zOffset) { boolean flag3 = xOffset == -chunkDistance || xOffset == chunkDistance || zOffset == -chunkDistance || zOffset == chunkDistance; ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(xOffset + posX, zOffset + posZ); ChunkStat chunkStat = new ChunkStat(flag3); eligibleChunksForSpawning.put(chunkcoordintpair, chunkStat); } } return eligibleChunksForSpawning; } /** * Simulates spawning as if Entity Count was always 0. {@link determineChunksForSpawnering} needs to be run to * populate eligibleChunksForSpawning with spawnable chunks. Entities / other players already in the World may * interfere with spawning as they occupy positions in the world * * @param creatureType CreatureType spawnList that is being Spawned */ private void attemptSpawnCreaturesInChunks(int maxCycles, World worldServer, LivingHandlerRegistry livingHandlerRegistry, BiomeSpawnListRegistry biomeSpawnListRegistry, CreatureType creatureType, BiomeBlacklist blacklist, CountInfo countInfo, CountInfo reportCount) { ChunkCoordinates serverOriginPoint = worldServer.getSpawnPoint(); List<ChunkCoordIntPair> eligibleChunksForSpawning = new ArrayList<ChunkCoordIntPair>( countInfo.eligibleChunkLocations()); for (int cycle = 0; cycle < maxCycles; cycle++) { Collections.shuffle(eligibleChunksForSpawning); for (ChunkCoordIntPair chunkCoord : eligibleChunksForSpawning) { ChunkStat chunkStat = countInfo.getChunkStat(chunkCoord); if (chunkStat.isEdge) { continue; } // TODO: CreatureType.passiveSpawnAttempts countInfo.resetEntitiesSpawnedThisLoop(); final int entityTypeCap = creatureType.maxNumberOfCreature * eligibleChunksForSpawning.size() / 256; for (int numLocAttempts = 0; numLocAttempts < creatureType.iterationsPerChunk; ++numLocAttempts) { IEntityLivingData entitylivingdata = null; ChunkPosition startSpawningPoint = creatureType.getRandomSpawningPointInChunk(worldServer, chunkCoord.chunkXPos, chunkCoord.chunkZPos); SpawnListEntry spawnlistentry = null; Class<? extends EntityLiving> livingToSpawn = null; LivingHandler handler = null; countInfo.resetEntitiesPackCount(); // CreatureType if (!creatureType.canSpawnHere(worldServer, countInfo, startSpawningPoint)) { continue; } for (int numEntAttempts = 0; numEntAttempts < creatureType.iterationsPerPack; ++numEntAttempts) { // Randomized on Each Attempt, but horizontally to allow a 'Pack' to spawn near each other final int horVar = 6; final int verVar = 1; ChunkPosition spawningPoint = new ChunkPosition(startSpawningPoint.chunkPosX + worldServer.rand.nextInt(horVar) - worldServer.rand.nextInt(horVar), startSpawningPoint.chunkPosY + worldServer.rand.nextInt(verVar) - worldServer.rand.nextInt(verVar), startSpawningPoint.chunkPosZ + worldServer.rand.nextInt(horVar) - worldServer.rand.nextInt(horVar)); // Biome BlackList if (blacklist.isBlacklisted(worldServer.getBiomeGenForCoords(spawningPoint.chunkPosX, spawningPoint.chunkPosY))) { continue; } if (spawnlistentry == null) { spawnlistentry = biomeSpawnListRegistry.getSpawnListEntryToSpawn(worldServer, creatureType, startSpawningPoint.chunkPosX, startSpawningPoint.chunkPosY, startSpawningPoint.chunkPosZ); if (spawnlistentry == null) { break; } Tags tags = new Tags(worldServer, countInfo, startSpawningPoint.chunkPosX, startSpawningPoint.chunkPosY, startSpawningPoint.chunkPosZ); livingToSpawn = livingHandlerRegistry.getRandomEntity(spawnlistentry.livingGroupID, worldServer.rand, tags); if (livingToSpawn == null) { break; } handler = livingHandlerRegistry.getLivingHandler(spawnlistentry.livingGroupID); } // LivingCap and PackSize { int globalEntityClassCount = countInfo.getGlobalEntityClassCount(livingToSpawn); int livingCap = handler.getLivingCap(); if (livingCap > 0 && globalEntityClassCount >= livingCap) { spawnlistentry = null; continue; } } /* Spawn is Centered Version of blockSpawn such that entity is not placed in Corner */ float spawnX = spawningPoint.chunkPosX + 0.5F; float spawnY = spawningPoint.chunkPosY; float spawnZ = spawningPoint.chunkPosZ + 0.5F; EntityLiving entityliving; try { entityliving = livingToSpawn.getConstructor(new Class[] { World.class }).newInstance( new Object[] { worldServer }); } catch (Exception exception) { exception.printStackTrace(); return; } entityliving.setLocationAndAngles(spawnX, spawnY, spawnZ, worldServer.rand.nextFloat() * 360.0F, 0.0F); if (spawnlistentry.getLivingHandler().getCanSpawnHere(entityliving, spawnlistentry, countInfo)) { reportCount.countSpawn(entityliving, creatureType.typeID); if (countInfo.getEntitiesSpawnedThisLoop() >= spawnlistentry.packSize) { continue; } } } } } } } /** * Adds the strings available in this command to the given list of tab completion options. */ @Override public List<String> getTabCompletions(ICommandSender commandSender, String[] stringArgs) { stringArgs = correctedParseArgs(stringArgs, false); List<String> tabCompletions = new ArrayList<String>(); if (stringArgs.length == 1) { addPlayerUsernames(tabCompletions); addEntityTypes(tabCompletions); } else if (stringArgs.length == 2) { // Two valid inputs are NUMBERS {i.e. ChunkDistance} or CREATURETYPE try { Integer.parseInt(stringArgs[1]); } catch (Exception e) { addEntityTypes(tabCompletions); } } if (!tabCompletions.isEmpty()) { return getStringsMatchingLastWord(stringArgs, tabCompletions); } else { return tabCompletions; } } }