package pneumaticCraft.common.ai; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import net.minecraft.entity.ai.EntityAIBase; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.profiler.Profiler; import net.minecraft.world.ChunkPosition; import net.minecraftforge.common.MinecraftForge; import pneumaticCraft.api.drone.SpecialVariableRetrievalEvent; import pneumaticCraft.common.config.Config; import pneumaticCraft.common.progwidgets.IJumpBackWidget; import pneumaticCraft.common.progwidgets.IProgWidget; import pneumaticCraft.common.progwidgets.IVariableWidget; import pneumaticCraft.common.progwidgets.ProgWidgetStart; import pneumaticCraft.common.remote.GlobalVariableManager; /** * This class is derived from Minecraft's {link EntityAITasks} class. As the original class would need quite a few * accesstransformers or reflection calls to do what I want, I've copied most of that class in here. */ public class DroneAIManager{ /** A list of EntityAITaskEntrys in EntityAITasks. */ public List<EntityAITaskEntry> taskEntries = new ArrayList<EntityAITaskEntry>(); /** A list of EntityAITaskEntrys that are currently being executed. */ private final List<EntityAITaskEntry> executingTaskEntries = new ArrayList<EntityAITaskEntry>(); /** Instance of Profiler. */ private final Profiler theProfiler; private int tickCount; public static final int TICK_RATE = 3; private final IDroneBase drone; private List<IProgWidget> progWidgets; private IProgWidget curActiveWidget; private EntityAIBase curWidgetAI; private EntityAIBase curWidgetTargetAI; private boolean stopWhenEndReached; private boolean wasAIOveridden; private String currentLabel = "Main";//Holds the name of the last label that was jumped to. private Map<String, ChunkPosition> coordinateVariables = new HashMap<String, ChunkPosition>(); private Map<String, ItemStack> itemVariables = new HashMap<String, ItemStack>(); private final Stack<IProgWidget> jumpBackWidgets = new Stack<IProgWidget>();//Used to jump back to a for each widget. private static final int MAX_JUMP_STACK_SIZE = 100; public DroneAIManager(IDroneBase drone){ theProfiler = drone.getWorld().theProfiler; this.drone = drone; setWidgets(drone.getProgWidgets()); } public DroneAIManager(IDroneBase drone, List<IProgWidget> progWidgets){ theProfiler = drone.getWorld().theProfiler; this.drone = drone; stopWhenEndReached = true; setWidgets(progWidgets); } public void dontStopWhenEndReached(){ stopWhenEndReached = false; } public void setWidgets(List<IProgWidget> progWidgets){ this.progWidgets = progWidgets; for(IProgWidget widget : progWidgets) { if(widget instanceof IVariableWidget) { ((IVariableWidget)widget).setAIManager(this); } } gotoFirstWidget(); } public void connectVariables(DroneAIManager subAI){ subAI.coordinateVariables = coordinateVariables; subAI.itemVariables = itemVariables; } public boolean isIdling(){ return curWidgetAI == null; } public EntityAIBase getCurrentAI(){ return curWidgetAI; } public IDroneBase getDrone(){ return drone; } public void writeToNBT(NBTTagCompound tag){ NBTTagList tagList = new NBTTagList(); for(Map.Entry<String, ChunkPosition> entry : coordinateVariables.entrySet()) { NBTTagCompound t = new NBTTagCompound(); t.setString("key", entry.getKey()); t.setInteger("x", entry.getValue().chunkPosX); t.setInteger("y", entry.getValue().chunkPosY); t.setInteger("z", entry.getValue().chunkPosZ); tagList.appendTag(t); } tag.setTag("coords", tagList); GlobalVariableManager.getInstance().writeItemVars(tag, itemVariables); } public void readFromNBT(NBTTagCompound tag){ coordinateVariables.clear(); NBTTagList tagList = tag.getTagList("coords", 10); for(int i = 0; i < tagList.tagCount(); i++) { NBTTagCompound t = tagList.getCompoundTagAt(i); coordinateVariables.put(t.getString("key"), new ChunkPosition(t.getInteger("x"), t.getInteger("y"), t.getInteger("z"))); } GlobalVariableManager.readItemVars(tag, itemVariables); } public ChunkPosition getCoordinate(String varName){ ChunkPosition pos; if(varName.startsWith("$")) { SpecialVariableRetrievalEvent.CoordinateVariable.Drone event = new SpecialVariableRetrievalEvent.CoordinateVariable.Drone(drone, varName.substring(1)); MinecraftForge.EVENT_BUS.post(event); pos = event.coordinate; } else if(varName.startsWith("#")) { pos = GlobalVariableManager.getInstance().getPos(varName.substring(1)); } else { pos = coordinateVariables.get(varName); } return pos != null ? pos : new ChunkPosition(0, 0, 0); } public void setCoordinate(String varName, ChunkPosition coord){ if(varName.startsWith("#")) { GlobalVariableManager.getInstance().set(varName.substring(1), coord); } else if(!varName.startsWith("$")) coordinateVariables.put(varName, coord); } public ItemStack getStack(String varName){ ItemStack item; if(varName.startsWith("$")) { SpecialVariableRetrievalEvent.ItemVariable.Drone event = new SpecialVariableRetrievalEvent.ItemVariable.Drone(drone, varName.substring(1)); MinecraftForge.EVENT_BUS.post(event); item = event.item; } else if(varName.startsWith("#")) { item = GlobalVariableManager.getInstance().getItem(varName.substring(1)); } else { item = itemVariables.get(varName); } return item; } public void setItem(String varName, ItemStack item){ if(varName.startsWith("#")) { GlobalVariableManager.getInstance().set(varName.substring(1), item); } else if(!varName.startsWith("$")) itemVariables.put(varName, item); } private void updateWidgetFlow(){ boolean isExecuting = false; for(EntityAITaskEntry entry : executingTaskEntries) { if(curWidgetAI == entry.action) { isExecuting = true; break; } } if(!isExecuting && curActiveWidget != null && (curWidgetTargetAI == null || !curWidgetTargetAI.shouldExecute())) { IProgWidget widget = curActiveWidget.getOutputWidget(drone, progWidgets); if(widget != null) { if(curActiveWidget.getOutputWidget() != widget) { if(addJumpBackWidget(curActiveWidget)) return; } setActiveWidget(widget); } else { if(stopWhenEndReached) { setActiveWidget(null); } else { gotoFirstWidget(); } } } if(curActiveWidget == null && !stopWhenEndReached) { gotoFirstWidget(); } } private void gotoFirstWidget(){ setLabel("Main"); if(!jumpBackWidgets.isEmpty()) { setActiveWidget(jumpBackWidgets.pop()); } else { for(IProgWidget widget : progWidgets) { if(widget instanceof ProgWidgetStart) { setActiveWidget(widget); return; } } } } private void setActiveWidget(IProgWidget widget){ EntityAIBase targetAI = null; EntityAIBase ai = null; if(widget != null) { boolean first = widget instanceof ProgWidgetStart; targetAI = widget.getWidgetTargetAI(drone, widget); ai = widget.getWidgetAI(drone, widget); Set<IProgWidget> visitedWidgets = new HashSet<IProgWidget>();//Prevent endless loops while(!visitedWidgets.contains(widget) && targetAI == null && ai == null) { visitedWidgets.add(widget); IProgWidget oldWidget = widget; widget = widget.getOutputWidget(drone, progWidgets); if(widget == null) { if(first) { return; } else { if(stopWhenEndReached) { setActiveWidget(null); } else { gotoFirstWidget(); } return; } } else if(oldWidget.getOutputWidget() != widget) { if(addJumpBackWidget(oldWidget)) return; } targetAI = widget.getWidgetTargetAI(drone, widget); ai = widget.getWidgetAI(drone, widget); } drone.setActiveProgram(widget); } else { setLabel("Stopped"); } curActiveWidget = widget; if(curWidgetAI != null) removeTask(curWidgetAI); if(curWidgetTargetAI != null) drone.getTargetAI().removeTask(curWidgetTargetAI); if(ai != null) addTask(2, ai); if(targetAI != null) drone.getTargetAI().addTask(2, targetAI); curWidgetAI = ai; curWidgetTargetAI = targetAI; } private boolean addJumpBackWidget(IProgWidget widget){ if(widget instanceof IJumpBackWidget) { if(jumpBackWidgets.size() >= MAX_JUMP_STACK_SIZE) { drone.overload(); jumpBackWidgets.clear(); setActiveWidget(null); return true; } else { jumpBackWidgets.push(widget); } } return false; } public List<EntityAITaskEntry> getRunningTasks(){ return taskEntries; } public EntityAIBase getTargetAI(){ return curWidgetTargetAI; } /** * * * *START EntityAITasks code * * * */ public void addTask(int par1, EntityAIBase par2EntityAIBase){ taskEntries.add(new EntityAITaskEntry(par1, par2EntityAIBase)); } /** * removes the indicated task from the entity's AI tasks. */ public void removeTask(EntityAIBase par1EntityAIBase){ Iterator iterator = taskEntries.iterator(); while(iterator.hasNext()) { EntityAITaskEntry entityaitaskentry = (EntityAITaskEntry)iterator.next(); EntityAIBase entityaibase1 = entityaitaskentry.action; if(entityaibase1 == par1EntityAIBase) { if(executingTaskEntries.contains(entityaitaskentry)) { entityaibase1.resetTask(); executingTaskEntries.remove(entityaitaskentry); } iterator.remove(); } } } public void onUpdateTasks(){ if(Config.stopDroneAI) return; if(!drone.isAIOverriden()) { if(wasAIOveridden && curWidgetTargetAI != null) drone.getTargetAI().addTask(2, curWidgetTargetAI); wasAIOveridden = false; ArrayList<EntityAITaskEntry> arraylist = new ArrayList<EntityAITaskEntry>(); Iterator<EntityAITaskEntry> iterator; EntityAITaskEntry entityaitaskentry; if(tickCount++ % TICK_RATE == 0) { iterator = taskEntries.iterator(); while(iterator.hasNext()) { entityaitaskentry = iterator.next(); boolean flag = executingTaskEntries.contains(entityaitaskentry); if(flag) { if(canUse(entityaitaskentry) && canContinue(entityaitaskentry)) { continue; } entityaitaskentry.action.resetTask(); executingTaskEntries.remove(entityaitaskentry); } if(canUse(entityaitaskentry) && entityaitaskentry.action.shouldExecute()) { arraylist.add(entityaitaskentry); executingTaskEntries.add(entityaitaskentry); } } updateWidgetFlow(); } else { iterator = executingTaskEntries.iterator(); while(iterator.hasNext()) { entityaitaskentry = iterator.next(); if(!entityaitaskentry.action.continueExecuting()) { entityaitaskentry.action.resetTask(); iterator.remove(); } } } theProfiler.startSection("goalStart"); iterator = arraylist.iterator(); while(iterator.hasNext()) { entityaitaskentry = iterator.next(); theProfiler.startSection(entityaitaskentry.action.getClass().getSimpleName()); entityaitaskentry.action.startExecuting(); theProfiler.endSection(); } theProfiler.endSection(); theProfiler.startSection("goalTick"); iterator = executingTaskEntries.iterator(); while(iterator.hasNext()) { entityaitaskentry = iterator.next(); entityaitaskentry.action.updateTask(); } theProfiler.endSection(); } else {//drone charging ai is running if(!wasAIOveridden && curWidgetTargetAI != null) { drone.getTargetAI().removeTask(curWidgetTargetAI); } wasAIOveridden = true; for(EntityAITaskEntry ai : executingTaskEntries) { ai.action.resetTask(); } executingTaskEntries.clear(); drone.setDugBlock(0, 0, 0); } } /** * Determine if a specific AI Task should continue being executed. */ private boolean canContinue(EntityAITaskEntry par1EntityAITaskEntry){ theProfiler.startSection("canContinue"); boolean flag = par1EntityAITaskEntry.action.continueExecuting(); theProfiler.endSection(); return flag; } /** * Determine if a specific AI Task can be executed, which means that all running higher (= lower int value) priority * tasks are compatible with it or all lower priority tasks can be interrupted. */ private boolean canUse(EntityAITaskEntry par1EntityAITaskEntry){ theProfiler.startSection("canUse"); Iterator iterator = taskEntries.iterator(); while(iterator.hasNext()) { EntityAITaskEntry entityaitaskentry1 = (EntityAITaskEntry)iterator.next(); if(entityaitaskentry1 != par1EntityAITaskEntry) { if(par1EntityAITaskEntry.priority >= entityaitaskentry1.priority) { if(executingTaskEntries.contains(entityaitaskentry1) && !areTasksCompatible(par1EntityAITaskEntry, entityaitaskentry1)) { theProfiler.endSection(); return false; } } else if(executingTaskEntries.contains(entityaitaskentry1) && !entityaitaskentry1.action.isInterruptible()) { theProfiler.endSection(); return false; } } } theProfiler.endSection(); return true; } /** * Returns whether two EntityAITaskEntries can be executed concurrently */ private boolean areTasksCompatible(EntityAITaskEntry par1EntityAITaskEntry, EntityAITaskEntry par2EntityAITaskEntry){ return (par1EntityAITaskEntry.action.getMutexBits() & par2EntityAITaskEntry.action.getMutexBits()) == 0; } public void setLabel(String label){ currentLabel = label; drone.updateLabel(); } public String getLabel(){ if(curWidgetAI instanceof DroneAIExternalProgram) { return ((DroneAIExternalProgram)curWidgetAI).getRunningAI().getLabel() + " --> " + currentLabel; } else { return currentLabel; } } public class EntityAITaskEntry{ /** * The EntityAIBase object. */ public EntityAIBase action; /** * Priority of the EntityAIBase */ public int priority; public EntityAITaskEntry(int par2, EntityAIBase par3EntityAIBase){ priority = par2; action = par3EntityAIBase; } } }