package joshie.harvest.town.data; import com.google.common.cache.Cache; import joshie.harvest.api.HFApi; import joshie.harvest.api.buildings.Building; import joshie.harvest.api.calendar.CalendarDate; import joshie.harvest.api.calendar.Festival; import joshie.harvest.api.npc.NPC; import joshie.harvest.api.quests.Quest; import joshie.harvest.api.quests.TargetType; import joshie.harvest.buildings.BuildingStage; import joshie.harvest.buildings.HFBuildings; import joshie.harvest.calendar.CalendarAPI; import joshie.harvest.calendar.CalendarHelper; import joshie.harvest.calendar.HFFestivals; import joshie.harvest.core.HFTrackers; import joshie.harvest.core.helpers.EntityHelper; import joshie.harvest.core.helpers.NBTHelper; import joshie.harvest.core.network.PacketHandler; import joshie.harvest.core.util.interfaces.ISyncMaster; import joshie.harvest.gathering.GatheringData; import joshie.harvest.knowledge.letter.LetterDataServer; import joshie.harvest.knowledge.packet.PacketSyncLetters; import joshie.harvest.mining.gen.MineManager; import joshie.harvest.mining.gen.MiningProvider; import joshie.harvest.npcs.HFNPCs; import joshie.harvest.npcs.NPCHelper; import joshie.harvest.npcs.entity.EntityNPCBuilder; import joshie.harvest.npcs.entity.EntityNPCHuman; import joshie.harvest.npcs.entity.EntityNPCMiner; import joshie.harvest.quests.data.QuestDataServer; import joshie.harvest.quests.packet.PacketSharedSync; import joshie.harvest.town.packet.*; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.util.Rotation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.fml.common.FMLCommonHandler; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.*; import java.util.Map.Entry; import static joshie.harvest.mining.HFMining.MINING_ID; public class TownDataServer extends TownData<QuestDataServer, LetterDataServer> implements ISyncMaster { public final GatheringData gathering = new GatheringData(); private Map<ResourceLocation, BlockPos> deadVillagers = new HashMap<>(); private final QuestDataServer quests = new QuestDataServer(this); private final LetterDataServer letters = new LetterDataServer(this); private Festival targetFestival = Festival.NONE; private int targetFestivalDays; private int dimension; public TownDataServer() {} public TownDataServer(int dimension, BlockPos townCentre, CalendarDate date) { this.dimension = dimension; this.townCentre = townCentre; this.uuid = UUID.randomUUID(); this.birthday = date.copy(); } public void removeBuilding(TownBuilding theBuilding) { buildings.remove(theBuilding.building.getResource()); inhabitants.removeAll(theBuilding.building.getInhabitants()); for (NPC npc: theBuilding.building.getInhabitants()) { deadVillagers.remove(npc.getResource()); } HFTrackers.markTownsDirty(); PacketHandler.sendToEveryone(new PacketRemoveBuilding(getID(), theBuilding.building)); } @Override public QuestDataServer getQuests() { return quests; } @Override public LetterDataServer getLetters() { return letters; } @Override public TargetType getTargetType() { return TargetType.TOWN; } @Override public void sync(@Nullable EntityPlayer player, @Nonnull PacketSharedSync packet) { if (player != null) PacketHandler.sendToClient(packet.setUUID(getID()), player); else PacketHandler.sendToEveryone(packet.setUUID(getID())); } private boolean isDead(NPC npc) { return deadVillagers.keySet().contains(npc.getResource()); } public void createOrUpdateBuilder(WorldServer world, BlockPos pos) { Entity builder = world.getEntityFromUuid(getID()); if (!(builder instanceof EntityNPCBuilder) && !isDead(HFNPCs.CARPENTER)) { EntityNPCBuilder creator = new EntityNPCBuilder(world); creator.setPositionAndUpdate(pos.getX(), pos.getY() + 1.5D, pos.getZ()); creator.setUniqueId(getID()); //Marking the builder as having the same data world.spawnEntityInWorld(creator); //Towns owner now spawned } } public void markNPCDead(ResourceLocation name, BlockPos location) { deadVillagers.put(name, location); } public void syncBuildings(World world) { PacketHandler.sendToDimension(world.provider.getDimension(), new PacketSyncBuilding(getID(), buildingQueue)); } public boolean setBuilding(World world, Building building, BlockPos pos, Rotation rotation) { BuildingStage stage = new BuildingStage(building, pos, rotation); if (!buildingQueue.contains(stage)) { buildingQueue.addLast(stage); syncBuildings(world); if (building == HFBuildings.CARPENTER) { townCentre = pos; //Set the town centre to the carpenters position PacketHandler.sendToDimension(world.provider.getDimension(), new PacketSyncCentre(getID(), townCentre)); } HFTrackers.markTownsDirty(); return true; } return false; } public void finishBuilding() { buildingQueue.removeFirst(); //Remove the first building } public void addBuilding(World world, Building building, Rotation rotation, BlockPos pos) { TownBuilding newBuilding = new TownBuilding(building, rotation, pos); buildings.put(building.getResource(), newBuilding); inhabitants.addAll(building.getInhabitants()); //Add all the inhabitants PacketHandler.sendToEveryone(new PacketNewBuilding(uuid, newBuilding)); building.onBuilt(world, pos, rotation); HFTrackers.markTownsDirty(); } private boolean isRepeatable(World world, Quest quest) { if (!quest.isRepeatable()) return false; if (quest.getDaysBetween() == 0) return true; else { CalendarDate date = getQuests().getLastCompletionOfQuest(quest); return date == null || CalendarHelper.getDays(date, HFApi.calendar.getDate(world)) >= quest.getDaysBetween(); } } private void generateNewDailyQuest(World world) { List<Quest> quests = new ArrayList<>(); for (Quest quest: Quest.REGISTRY) { if (isRepeatable(world, quest) || !getQuests().getFinished().contains(quest)) { if (!getQuests().getCurrent().contains(quest)) { if (quest.canStartDailyQuest(this, world, townCentre)) { quests.add(quest); } } } } if (quests.size() > 0) { dailyQuest = quests.get(world.rand.nextInt(quests.size())); dailyQuest.onSelectedAsDailyQuest(this, world, townCentre); } else dailyQuest = null; PacketHandler.sendToDimension(world.provider.getDimension(), new PacketDailyQuest(uuid, dailyQuest)); } public void clearDailyQuest(World world) { dailyQuest = null; //Remove the current daily quest as it has been started in the town PacketHandler.sendToDimension(world.provider.getDimension(), new PacketDailyQuest(uuid, dailyQuest)); } public void startFestival(Festival festival) { this.targetFestival = festival; this.targetFestivalDays = festival.getFestivalLength(); } public void newDay(World world, Cache<BlockPos, Boolean> isFar, CalendarDate yesterday, CalendarDate today) { if (world.isBlockLoaded(getTownCentre())) { shops.newDay(world, uuid); gathering.newDay(world, townCentre, buildings.values(), isFar); generateNewDailyQuest(world); for (Entry<ResourceLocation, BlockPos> entry: deadVillagers.entrySet()) { NPC npc = NPC.REGISTRY.get(entry.getKey()); if (npc == HFNPCs.MINER) { WorldServer server = FMLCommonHandler.instance().getMinecraftServerInstance().worldServerForDimension(MINING_ID); EntityNPCMiner entity = NPCHelper.getEntityForNPC(server, HFNPCs.MINER); int id = HFTrackers.getTowns(entity.worldObj).getMineIDFromCoordinates(getTownCentre()); MiningProvider provider = ((MiningProvider) server.provider); BlockPos pos = MineManager.modifyNPCPosition(server, provider.getSpawnCoordinateForMine(id, 1), entity); entity.setPosition(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5); entity.setHeldItem(EnumHand.MAIN_HAND, new ItemStack(Blocks.TORCH)); entity.setHeldItem(EnumHand.OFF_HAND, new ItemStack(Items.IRON_PICKAXE)); server.spawnEntityInWorld(entity); } else if (npc != HFNPCs.GODDESS) { Entity theEntity = NPCHelper.getNPCIfExists((WorldServer) world, townCentre, npc); if (!(theEntity != null && !theEntity.isDead)) { EntityNPCHuman entity = NPCHelper.getEntityForNPC(world, npc); entity.setPosition(townCentre.getX(), townCentre.getY(), townCentre.getZ()); BlockPos home = NPCHelper.getHomeForEntity(entity); BlockPos pos = home != null ? home : entry.getValue(); int attempts = 0; while (!EntityHelper.isSpawnable(world, pos) && attempts < 64) { pos = pos.add(world.rand.nextInt(16) - 8, world.rand.nextInt(8), world.rand.nextInt(16) - 8); attempts++; } entity.setPositionAndUpdate(pos.getX(), pos.getY(), pos.getZ()); if (npc == HFNPCs.CARPENTER) entity.setUniqueId(getID()); //Keep the Unique ID the same world.spawnEntityInWorld(entity); } } } //Festival updates //Set the festival for today Festival previousFestival = festival; if (targetFestival != Festival.NONE) { festival = targetFestival; festivalDays = targetFestivalDays; targetFestival = Festival.NONE; targetFestivalDays = 0; } if (previousFestival == festival) { //If there is a festival active festivalDays--; //Decrease the amount of days of the festival left if (festivalDays <= 0 && (festival != HFFestivals.NEW_YEARS_EVE || (today.getDay() != 0 && festival == HFFestivals.NEW_YEARS_EVE)) || quests.getFinished().contains(festival.getQuest())) { //If we have no days left then v festival = Festival.NONE; //Cancel the festival } } //If the festival is not the same, call for a change of festivals if (festival != previousFestival) { PacketHandler.sendToDimension(world.provider.getDimension(), new PacketSyncFestival(uuid, festival, festivalDays)); for (TownBuilding building : buildings.values()) { building.building.onFestivalChanged(world, building.pos, building.rotation, previousFestival, festival); } } letters.newDay(today); boolean changed = false; Festival newFestival = CalendarAPI.INSTANCE.getFestivalFromDate(this, today); if (!newFestival.equals(Festival.NONE)) { Festival oldFestival = CalendarAPI.INSTANCE.getFestivalFromDate(this, yesterday); if (!newFestival.equals(oldFestival)) { //Remove the old letter if (oldFestival.getLetter() != null) { changed = true; letters.remove(oldFestival.getLetter()); } //Add the new letter if (newFestival.getLetter() != null) { changed = true; letters.add(newFestival.getLetter()); } } } if (changed) sync(null, new PacketSyncLetters(letters.getLetters())); deadVillagers.clear(); //Reset the dead villagers } } public Rotation getFacingFor(ResourceLocation resource) { TownBuilding building = buildings.get(resource); if (building == null) return null; return building.getFacing(); } //Called to sync the data about this town to the client public void writePacketNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); letters.readFromNBT(nbt); quests.readFromNBT(nbt); gathering.readFromNBT(nbt); dimension = nbt.getInteger("Dimension"); //TODO: Remove in 0.7+ if (nbt.hasKey("DeadVillagers")) { Set<ResourceLocation> dead = NBTHelper.readResourceSet(nbt, "DeadVillagers"); dead.stream().forEach((r) -> deadVillagers.put(r, townCentre)); } else if (nbt.hasKey("DeadNPCs")) { NBTTagList list = nbt.getTagList("DeadNPCs", 10); for (int i = 0; i < list.tagCount(); i++) { NBTTagCompound tag = list.getCompoundTagAt(i); ResourceLocation resource = new ResourceLocation(tag.getString("Resource")); BlockPos pos = BlockPos.fromLong(tag.getLong("Position")); deadVillagers.put(resource, pos); } } //Target festival if (nbt.hasKey("FestivalTarget")) { targetFestival = Festival.REGISTRY.get(new ResourceLocation(nbt.getString("FestivalTarget"))); targetFestivalDays = nbt.getInteger("FestivalTargetDays"); } else festival = Festival.NONE; } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); letters.writeToNBT(nbt); quests.writeToNBT(nbt); gathering.writeToNBT(nbt); nbt.setInteger("Dimension", dimension); NBTTagList list = new NBTTagList(); for (Entry<ResourceLocation, BlockPos> entry: deadVillagers.entrySet()) { NBTTagCompound tag = new NBTTagCompound(); tag.setString("Resource", entry.getKey().toString()); tag.setLong("Position", entry.getValue().toLong()); list.appendTag(tag); } nbt.setTag("DeadNPCs", list); //Target festival if (targetFestival != null) { nbt.setString("FestivalTarget", targetFestival.getResource().toString()); nbt.setInteger("FestivalTargetDays", targetFestivalDays); } return nbt; } }