();
+ }
+
+ @Override
+ public void cleanup() {
+ MinecraftForge.EVENT_BUS.unregister(this);
+ }
+
+ @SubscribeEvent
+ public void onItemCraft(PlayerEvent.ItemCraftedEvent event) {
+ if (callCraft)
+ checkForMatch(event.crafting);
+
+ callCraft = !callCraft;
+ }
+
+ /**
+ * Checks whether the ItemStack matches a variant stored in the item list. If
+ * so, returns true, else returns false.
+ *
+ * @param is The item stack
+ * @return If the stack is allowed in the item matchers and has color or
+ * variants enabled, returns true, else false.
+ */
+ private boolean getVariant(ItemStack is) {
+ for (ItemQuitMatcher matcher : matchers) {
+ if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) {
+ if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0)
+ return true;
+ if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private int getCraftedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ if (variant)
+ return (craftedItems.get(is.getUnlocalizedName()) == null) ? 0 : craftedItems.get(is.getUnlocalizedName());
+ else
+ return (craftedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0
+ : craftedItems.get(is.getItem().getUnlocalizedName());
+ }
+
+ private void addCraftedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0
+ : craftedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ craftedItems.put(is.getUnlocalizedName(), prev + is.getCount());
+ else
+ craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount());
+ }
+
+ private void checkForMatch(ItemStack is) {
+ int savedCrafted = getCraftedItemCount(is);
+ if (is != null) {
+ for (ItemQuitMatcher matcher : this.matchers) {
+ if (matcher.matches(is)) {
+ if (savedCrafted != 0) {
+ if (is.getCount() + savedCrafted >= matcher.matchSpec.getAmount()) {
+ this.quitCode = matcher.description();
+ this.wantToQuit = true;
+ }
+ } else if (is.getCount() >= matcher.matchSpec.getAmount()) {
+ this.quitCode = matcher.description();
+ this.wantToQuit = true;
+ }
+ }
+ }
+
+ addCraftedItemCount(is);
+ }
+ }
+}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromPossessingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromPossessingItemImplementation.java
new file mode 100644
index 000000000..717164a33
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromPossessingItemImplementation.java
@@ -0,0 +1,192 @@
+package com.microsoft.Malmo.MissionHandlers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.microsoft.Malmo.Schemas.AgentQuitFromPossessingItem;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.entity.item.ItemTossEvent;
+import net.minecraftforge.event.entity.player.EntityItemPickupEvent;
+import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
+import net.minecraftforge.event.world.BlockEvent.PlaceEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit;
+import com.microsoft.Malmo.MissionHandlers.RewardForCollectingItemImplementation.GainItemEvent;
+import com.microsoft.Malmo.MissionHandlers.RewardForDiscardingItemImplementation.LoseItemEvent;
+import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithDescription;
+import com.microsoft.Malmo.Schemas.MissionInit;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Quits the mission when the agent has possessed the right amount of items. The count on the item collection is non-absolute.
+ *
+ * In order to quit the mission, the agent must have the requisite items in its inventory all at one time.
+ */
+public class AgentQuitFromPossessingItemImplementation extends HandlerBase implements IWantToQuit {
+
+ private AgentQuitFromPossessingItem params;
+ private HashMap collectedItems;
+ private List matchers;
+ private String quitCode = "";
+ private boolean wantToQuit = false;
+
+ public static class ItemQuitMatcher extends RewardForItemBase.ItemMatcher {
+ String description;
+
+ ItemQuitMatcher(BlockOrItemSpecWithDescription spec) {
+ super(spec);
+ this.description = spec.getDescription();
+ }
+
+ String description() {
+ return this.description;
+ }
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof AgentQuitFromPossessingItem))
+ return false;
+
+ this.params = (AgentQuitFromPossessingItem) params;
+ this.matchers = new ArrayList();
+ for (BlockOrItemSpecWithDescription bs : this.params.getItem())
+ this.matchers.add(new ItemQuitMatcher(bs));
+ return true;
+ }
+
+ @Override
+ public boolean doIWantToQuit(MissionInit missionInit) {
+ return this.wantToQuit;
+ }
+
+ @Override
+ public String getOutcome() {
+ return this.quitCode;
+ }
+
+ @Override
+ public void prepare(MissionInit missionInit) {
+ MinecraftForge.EVENT_BUS.register(this);
+ collectedItems = new HashMap();
+ }
+
+ @Override
+ public void cleanup() {
+ MinecraftForge.EVENT_BUS.unregister(this);
+ }
+
+ @SubscribeEvent
+ public void onGainItem(GainItemEvent event) {
+ checkForMatch(event.stack);
+ }
+
+ @SubscribeEvent
+ public void onPickupItem(EntityItemPickupEvent event) {
+ if (event.getItem() != null)
+ checkForMatch(event.getItem().getEntityItem());
+ }
+
+ @SubscribeEvent
+ public void onLoseItem(LoseItemEvent event) {
+ if (event.stack != null)
+ removeCollectedItemCount(event.stack);
+ }
+
+ @SubscribeEvent
+ public void onDropItem(ItemTossEvent event) {
+ removeCollectedItemCount(event.getEntityItem().getEntityItem());
+ }
+
+ @SubscribeEvent
+ public void onDestroyItem(PlayerDestroyItemEvent event) {
+ removeCollectedItemCount(event.getOriginal());
+ }
+
+ @SubscribeEvent
+ public void onBlockPlace(PlaceEvent event) {
+ if (!event.isCanceled() && event.getPlacedBlock() != null) {
+ ItemStack stack = new ItemStack(event.getPlacedBlock().getBlock());
+ removeCollectedItemCount(stack);
+ }
+ }
+
+ /**
+ * Checks whether the ItemStack matches a variant stored in the item list. If
+ * so, returns true, else returns false.
+ *
+ * @param is The item stack
+ * @return If the stack is allowed in the item matchers and has color or
+ * variants enabled, returns true, else false.
+ */
+ private boolean getVariant(ItemStack is) {
+ for (ItemQuitMatcher matcher : matchers) {
+ if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) {
+ if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0)
+ return true;
+ if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void addCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0
+ : collectedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ collectedItems.put(is.getUnlocalizedName(), prev + is.getCount());
+ else
+ collectedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount());
+
+ }
+
+ private void removeCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0
+ : collectedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ collectedItems.put(is.getUnlocalizedName(), prev - is.getCount());
+ else
+ collectedItems.put(is.getItem().getUnlocalizedName(), prev - is.getCount());
+ }
+
+ private int getCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ if (variant)
+ return (collectedItems.get(is.getUnlocalizedName()) == null) ? 0 : collectedItems.get(is.getUnlocalizedName());
+ else
+ return (collectedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0
+ : collectedItems.get(is.getItem().getUnlocalizedName());
+ }
+
+ private void checkForMatch(ItemStack is) {
+ int savedCollected = getCollectedItemCount(is);
+ if (is != null) {
+ for (ItemQuitMatcher matcher : this.matchers) {
+ if (matcher.matches(is)) {
+ if (savedCollected != 0) {
+ if (is.getCount() + savedCollected >= matcher.matchSpec.getAmount()) {
+ this.quitCode = matcher.description();
+ this.wantToQuit = true;
+ }
+ } else if (is.getCount() >= matcher.matchSpec.getAmount()) {
+ this.quitCode = matcher.description();
+ this.wantToQuit = true;
+ }
+ }
+ }
+
+ addCollectedItemCount(is);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromSmeltingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromSmeltingItemImplementation.java
new file mode 100644
index 000000000..269317a92
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromSmeltingItemImplementation.java
@@ -0,0 +1,150 @@
+package com.microsoft.Malmo.MissionHandlers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.PlayerEvent;
+
+import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit;
+import com.microsoft.Malmo.MissionHandlers.RewardForItemBase.ItemMatcher;
+import com.microsoft.Malmo.Schemas.AgentQuitFromSmeltingItem;
+import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithDescription;
+import com.microsoft.Malmo.Schemas.MissionInit;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Gives agents rewards when items are smelted. Handles variants and colors.
+ */
+public class AgentQuitFromSmeltingItemImplementation extends HandlerBase implements IWantToQuit {
+
+ private AgentQuitFromSmeltingItem params;
+ private HashMap smeltedItems;
+ private List matchers;
+ private String quitCode = "";
+ private boolean wantToQuit = false;
+ private int callSmelt = 0;
+
+ public static class ItemQuitMatcher extends RewardForItemBase.ItemMatcher {
+ String description;
+
+ ItemQuitMatcher(BlockOrItemSpecWithDescription spec) {
+ super(spec);
+ this.description = spec.getDescription();
+ }
+
+ String description() {
+ return this.description;
+ }
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof AgentQuitFromSmeltingItem))
+ return false;
+
+ this.params = (AgentQuitFromSmeltingItem) params;
+ this.matchers = new ArrayList();
+ for (BlockOrItemSpecWithDescription bs : this.params.getItem())
+ this.matchers.add(new ItemQuitMatcher(bs));
+ return true;
+ }
+
+ @Override
+ public boolean doIWantToQuit(MissionInit missionInit) {
+ return this.wantToQuit;
+ }
+
+ @Override
+ public String getOutcome() {
+ return this.quitCode;
+ }
+
+ @Override
+ public void prepare(MissionInit missionInit) {
+ MinecraftForge.EVENT_BUS.register(this);
+ smeltedItems = new HashMap();
+ }
+
+ @Override
+ public void cleanup() {
+ MinecraftForge.EVENT_BUS.unregister(this);
+ }
+
+ @SubscribeEvent
+ public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) {
+ if (callSmelt % 4 == 0)
+ checkForMatch(event.smelting);
+
+ callSmelt = (callSmelt + 1) % 4;
+ }
+
+ /**
+ * Checks whether the ItemStack matches a variant stored in the item list. If
+ * so, returns true, else returns false.
+ *
+ * @param is The item stack
+ * @return If the stack is allowed in the item matchers and has color or
+ * variants enabled, returns true, else false.
+ */
+ private boolean getVariant(ItemStack is) {
+ for (ItemMatcher matcher : matchers) {
+ if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) {
+ if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0)
+ return true;
+ if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private int getSmeltedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ if (variant)
+ return (smeltedItems.get(is.getUnlocalizedName()) == null) ? 0 : smeltedItems.get(is.getUnlocalizedName());
+ else
+ return (smeltedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0
+ : smeltedItems.get(is.getItem().getUnlocalizedName());
+
+ }
+
+ private void addSmeltedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ if (variant) {
+ int prev = (smeltedItems.get(is.getUnlocalizedName()) == null ? 0
+ : smeltedItems.get(is.getUnlocalizedName()));
+ smeltedItems.put(is.getUnlocalizedName(), prev + is.getCount());
+ } else {
+ int prev = (smeltedItems.get(is.getItem().getUnlocalizedName()) == null ? 0
+ : smeltedItems.get(is.getItem().getUnlocalizedName()));
+ smeltedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount());
+ }
+ }
+
+ private void checkForMatch(ItemStack is) {
+ int savedSmelted = getSmeltedItemCount(is);
+ for (ItemQuitMatcher matcher : this.matchers) {
+ if (matcher.matches(is)) {
+ if (savedSmelted != 0) {
+ if (is.getCount() + savedSmelted >= matcher.matchSpec.getAmount()) {
+ this.quitCode = matcher.description();
+ this.wantToQuit = true;
+ }
+ } else if (is.getCount() >= matcher.matchSpec.getAmount()) {
+ this.quitCode = matcher.description();
+ this.wantToQuit = true;
+ }
+ }
+ }
+
+ addSmeltedItemCount(is);
+ }
+}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromTouchingBlockTypeImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromTouchingBlockTypeImplementation.java
index 73b2dab14..c42e15cc8 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromTouchingBlockTypeImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AgentQuitFromTouchingBlockTypeImplementation.java
@@ -32,7 +32,6 @@
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit;
-import com.microsoft.Malmo.MissionHandlers.RewardForCollectingItemImplementation.GainItemEvent;
import com.microsoft.Malmo.Schemas.AgentQuitFromTouchingBlockType;
import com.microsoft.Malmo.Schemas.BlockSpec;
import com.microsoft.Malmo.Schemas.BlockSpecWithDescription;
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbyCraftCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbyCraftCommandsImplementation.java
new file mode 100644
index 000000000..a7a08d951
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbyCraftCommandsImplementation.java
@@ -0,0 +1,201 @@
+// --------------------------------------------------------------------------------------------------
+// Copyright (c) 2016 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// --------------------------------------------------------------------------------------------------
+
+package com.microsoft.Malmo.MissionHandlers;
+
+import io.netty.buffer.ByteBuf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.block.BlockWorkbench;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.crafting.IRecipe;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.entity.living.LivingEvent;
+import net.minecraftforge.event.world.BlockEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.network.ByteBufUtils;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
+import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
+
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.Schemas.NearbyCraftCommand;
+import com.microsoft.Malmo.Schemas.NearbyCraftCommands;
+import com.microsoft.Malmo.Utils.CraftingHelper;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Extends the functionality of the SimpleCraftCommands by requiring a crafting table close-by. Only handles crafting, no smelting.
+ */
+public class NearbyCraftCommandsImplementation extends CommandBase {
+ private boolean isOverriding;
+ private static ArrayList craftingTables;
+
+ @SubscribeEvent
+ public void onJump(LivingEvent.LivingJumpEvent event) {
+ if (event.getEntity() instanceof EntityPlayerSP) {
+ System.out.println("Jump! Attempting to craft sticks.");
+ onExecute("craftNearby", "stick", null);
+ }
+ }
+
+ public static class CraftNearbyMessage implements IMessage {
+ String parameters;
+
+ public CraftNearbyMessage() {
+ }
+
+ public CraftNearbyMessage(String parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public void fromBytes(ByteBuf buf) {
+ this.parameters = ByteBufUtils.readUTF8String(buf);
+ }
+
+ @Override
+ public void toBytes(ByteBuf buf) {
+ ByteBufUtils.writeUTF8String(buf, this.parameters);
+ }
+ }
+
+ @SubscribeEvent
+ public void onBlockPlace(BlockEvent.PlaceEvent event) {
+ if (!event.isCanceled() && event.getPlacedBlock().getBlock() instanceof BlockWorkbench)
+ craftingTables.add(event.getPos());
+ }
+
+ @SubscribeEvent
+ public void onBlockDestroy(BlockEvent.BreakEvent event) {
+ if (!event.isCanceled() && event.getState().getBlock() instanceof BlockWorkbench)
+ for (int i = craftingTables.size() - 1; i >= 0; i--)
+ if (craftingTables.get(i).equals(event.getPos()))
+ craftingTables.remove(i);
+ }
+
+ public static class CraftNearbyMessageHandler implements IMessageHandler {
+ @Override
+ public IMessage onMessage(CraftNearbyMessage message, MessageContext ctx) {
+ EntityPlayerMP player = ctx.getServerHandler().playerEntity;
+ Vec3d headPos = new Vec3d(player.posX, player.posY + 1.6, player.posZ);
+
+ // Location checking
+ boolean closeTable = false;
+ for (BlockPos furnace : craftingTables) {
+ Vec3d blockVec = new Vec3d(furnace.getX() + 0.5, furnace.getY() + 0.5, furnace.getZ() + 0.5);
+
+ if (headPos.squareDistanceTo(blockVec) <= 25.0) {
+ // Within a reasonable FOV?
+ // Lots of trig, let's go
+ double fov = Minecraft.getMinecraft().gameSettings.fovSetting;
+ double height = Minecraft.getMinecraft().displayHeight;
+ double width = Minecraft.getMinecraft().displayWidth;
+ Vec3d lookVec = player.getLookVec();
+ Vec3d toBlock = blockVec.subtract(headPos);
+
+ // Projection of block onto player look vector - if greater than 0, then in front of us
+ double scalarProjection = lookVec.dotProduct(toBlock) / lookVec.lengthVector();
+ if (scalarProjection > 0) {
+ Vec3d yUnit = new Vec3d(0, 1.0, 0);
+ Vec3d lookCross = lookVec.crossProduct(yUnit);
+ Vec3d blockProjectedOntoCross = lookCross.scale(lookCross.dotProduct(toBlock) / lookCross.lengthVector());
+ Vec3d blockProjectedOntoPlayerPlane = toBlock.subtract(blockProjectedOntoCross);
+ double xyDot = lookVec.dotProduct(blockProjectedOntoPlayerPlane);
+ double pitchTheta = Math.acos(xyDot / (lookVec.lengthVector() * blockProjectedOntoPlayerPlane.lengthVector()));
+
+ Vec3d playerY = lookCross.crossProduct(lookVec);
+ Vec3d blockProjectedOntoPlayerY = playerY.scale(playerY.dotProduct(toBlock) / playerY.lengthVector());
+ Vec3d blockProjectedOntoYawPlane = toBlock.subtract(blockProjectedOntoPlayerY);
+ double xzDot = lookVec.dotProduct(blockProjectedOntoYawPlane);
+ double yawTheta = Math.acos(xzDot / (lookVec.lengthVector() * blockProjectedOntoYawPlane.lengthVector()));
+
+ if (Math.abs(Math.toDegrees(yawTheta)) <= Math.min(1, width / height) * (fov / 2.0) && Math.abs(Math.toDegrees(pitchTheta)) <= Math.min(1, height / width) * (fov / 2.0))
+ closeTable = true;
+ }
+ }
+ }
+
+ if (closeTable) {
+ System.out.println("We are close enough to the table");
+ // We are close enough, try crafting recipes
+ List matching_recipes = CraftingHelper.getRecipesForRequestedOutput(message.parameters);
+ for (IRecipe recipe : matching_recipes)
+ if (CraftingHelper.attemptCrafting(player, recipe)) {
+ System.out.println("Crafted " + recipe.getRecipeOutput().toString());
+ return null;
+ }
+ } else
+ System.out.println("We are not close enough to a table");
+
+ System.out.println("Either didn't have the requisite materials or weren't close enough to a table");
+ return null;
+ }
+ }
+
+ @Override
+ protected boolean onExecute(String verb, String parameter, MissionInit missionInit) {
+ if (verb.equalsIgnoreCase(NearbyCraftCommand.CRAFT_NEARBY.value())) {
+ MalmoMod.network.sendToServer(new CraftNearbyMessage(parameter));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof NearbyCraftCommands))
+ return false;
+
+ craftingTables = new ArrayList();
+
+ NearbyCraftCommands cParams = (NearbyCraftCommands) params;
+ setUpAllowAndDenyLists(cParams.getModifierList());
+ return true;
+ }
+
+ @Override
+ public void install(MissionInit missionInit) {
+ CraftingHelper.reset();
+ MinecraftForge.EVENT_BUS.register(this);
+ }
+
+ @Override
+ public void deinstall(MissionInit missionInit) {
+ MinecraftForge.EVENT_BUS.unregister(this);
+ }
+
+ @Override
+ public boolean isOverriding() {
+ return this.isOverriding;
+ }
+
+ @Override
+ public void setOverriding(boolean b) {
+ this.isOverriding = b;
+ }
+}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbySmeltCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbySmeltCommandsImplementation.java
new file mode 100644
index 000000000..cff9f6a2a
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/NearbySmeltCommandsImplementation.java
@@ -0,0 +1,181 @@
+// --------------------------------------------------------------------------------------------------
+// Copyright (c) 2016 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// --------------------------------------------------------------------------------------------------
+
+package com.microsoft.Malmo.MissionHandlers;
+
+import io.netty.buffer.ByteBuf;
+
+import java.util.ArrayList;
+
+import net.minecraft.block.BlockFurnace;
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import net.minecraftforge.event.world.BlockEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.network.ByteBufUtils;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
+import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
+
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.Schemas.NearbySmeltCommand;
+import com.microsoft.Malmo.Schemas.NearbySmeltCommands;
+import com.microsoft.Malmo.Utils.CraftingHelper;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Extends the functionality of the SimpleCraftCommands by requiring a furnace close-by. Only handles smelting, no crafting.
+ */
+public class NearbySmeltCommandsImplementation extends CommandBase {
+ private boolean isOverriding;
+ private static ArrayList furnaces;
+
+ public static class SmeltNearbyMessage implements IMessage {
+ String parameters;
+
+ public SmeltNearbyMessage() {
+ }
+
+ public SmeltNearbyMessage(String parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public void fromBytes(ByteBuf buf) {
+ this.parameters = ByteBufUtils.readUTF8String(buf);
+ }
+
+ @Override
+ public void toBytes(ByteBuf buf) {
+ ByteBufUtils.writeUTF8String(buf, this.parameters);
+ }
+ }
+
+ @SubscribeEvent
+ public void onBlockPlace(BlockEvent.PlaceEvent event) {
+ if (!event.isCanceled() && event.getPlacedBlock().getBlock() instanceof BlockFurnace)
+ furnaces.add(event.getPos());
+ }
+
+ @SubscribeEvent
+ public void onBlockDestroy(BlockEvent.BreakEvent event) {
+ if (!event.isCanceled() && event.getState().getBlock() instanceof BlockFurnace)
+ for (int i = furnaces.size() - 1; i >= 0; i--)
+ if (furnaces.get(i).equals(event.getPos()))
+ furnaces.remove(i);
+ }
+
+ public static class SmeltNearbyMessageHandler implements IMessageHandler {
+ @Override
+ public IMessage onMessage(SmeltNearbyMessage message, MessageContext ctx) {
+ EntityPlayerMP player = ctx.getServerHandler().playerEntity;
+ Vec3d headPos = new Vec3d(player.posX, player.posY + 1.6, player.posZ);
+
+ // Location checking
+ boolean closeFurnace = false;
+ for (BlockPos furnace : furnaces) {
+ Vec3d blockVec = new Vec3d(furnace.getX() + 0.5, furnace.getY() + 0.5, furnace.getZ() + 0.5);
+
+ if (headPos.squareDistanceTo(blockVec) <= 25.0) {
+ // Within a reasonable FOV?
+ // Lots of trig, let's go
+ double fov = Minecraft.getMinecraft().gameSettings.fovSetting;
+ double height = Minecraft.getMinecraft().displayHeight;
+ double width = Minecraft.getMinecraft().displayWidth;
+ Vec3d lookVec = player.getLookVec();
+ Vec3d toBlock = blockVec.subtract(headPos);
+
+ // Projection of block onto player look vector - if greater than 0, then in front of us
+ double scalarProjection = lookVec.dotProduct(toBlock) / lookVec.lengthVector();
+ if (scalarProjection > 0) {
+ Vec3d yUnit = new Vec3d(0, 1.0, 0);
+ Vec3d lookCross = lookVec.crossProduct(yUnit);
+ Vec3d blockProjectedOntoCross = lookCross.scale(lookCross.dotProduct(toBlock) / lookCross.lengthVector());
+ Vec3d blockProjectedOntoPlayerPlane = toBlock.subtract(blockProjectedOntoCross);
+ double xyDot = lookVec.dotProduct(blockProjectedOntoPlayerPlane);
+ double pitchTheta = Math.acos(xyDot / (lookVec.lengthVector() * blockProjectedOntoPlayerPlane.lengthVector()));
+
+ Vec3d playerY = lookCross.crossProduct(lookVec);
+ Vec3d blockProjectedOntoPlayerY = playerY.scale(playerY.dotProduct(toBlock) / playerY.lengthVector());
+ Vec3d blockProjectedOntoYawPlane = toBlock.subtract(blockProjectedOntoPlayerY);
+ double xzDot = lookVec.dotProduct(blockProjectedOntoYawPlane);
+ double yawTheta = Math.acos(xzDot / (lookVec.lengthVector() * blockProjectedOntoYawPlane.lengthVector()));
+
+ if (Math.abs(Math.toDegrees(yawTheta)) <= Math.min(1, width / height) * (fov / 2.0) && Math.abs(Math.toDegrees(pitchTheta)) <= Math.min(1, height / width) * (fov / 2.0))
+ closeFurnace = true;
+ }
+ }
+ }
+
+ if (closeFurnace) {
+ ItemStack input = CraftingHelper.getSmeltingRecipeForRequestedOutput(message.parameters);
+ if (input != null)
+ if (CraftingHelper.attemptSmelting(player, input))
+ return null;
+ }
+
+ return null;
+ }
+ }
+
+ @Override
+ protected boolean onExecute(String verb, String parameter, MissionInit missionInit) {
+ if (verb.equalsIgnoreCase(NearbySmeltCommand.SMELT_NEARBY.value())) {
+ MalmoMod.network.sendToServer(new SmeltNearbyMessage(parameter));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ furnaces = new ArrayList();
+
+ if (!(params instanceof NearbySmeltCommands))
+ return false;
+
+ NearbySmeltCommands cParams = (NearbySmeltCommands) params;
+ setUpAllowAndDenyLists(cParams.getModifierList());
+ return true;
+ }
+
+ @Override
+ public void install(MissionInit missionInit) {
+ CraftingHelper.reset();
+ }
+
+ @Override
+ public void deinstall(MissionInit missionInit) {
+ }
+
+ @Override
+ public boolean isOverriding() {
+ return this.isOverriding;
+ }
+
+ @Override
+ public void setOverriding(boolean b) {
+ this.isOverriding = b;
+ }
+}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/PlaceCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/PlaceCommandsImplementation.java
new file mode 100644
index 000000000..aa23a8ce4
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/PlaceCommandsImplementation.java
@@ -0,0 +1,170 @@
+// --------------------------------------------------------------------------------------------------
+// Copyright (c) 2016 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// --------------------------------------------------------------------------------------------------
+
+package com.microsoft.Malmo.MissionHandlers;
+
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.Schemas.*;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+
+import com.microsoft.Malmo.MissionHandlerInterfaces.ICommandHandler;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.RayTraceResult;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Place commands allow agents to place blocks in the world without having to worry about inventory management.
+ */
+public class PlaceCommandsImplementation extends CommandBase implements ICommandHandler {
+ private boolean isOverriding;
+
+ @Override
+ protected boolean onExecute(String verb, String parameter, MissionInit missionInit) {
+ EntityPlayerSP player = Minecraft.getMinecraft().player;
+ if (player == null)
+ return false;
+
+ if (!verb.equalsIgnoreCase("place"))
+ return false;
+
+ Item item = Item.getByNameOrId(parameter);
+ Block block = Block.getBlockFromItem(item);
+ if (item == null || item.getRegistryName() == null || block.getRegistryName() == null)
+ return false;
+
+ InventoryPlayer inv = player.inventory;
+ boolean blockInInventory = false;
+ ItemStack stackInInventory = null;
+ int stackIndex = -1;
+ for (int i = 0; !blockInInventory && i < inv.getSizeInventory(); i++) {
+ Item stack = inv.getStackInSlot(i).getItem();
+ if (stack.getRegistryName() != null && stack.getRegistryName().equals(item.getRegistryName())) {
+ stackInInventory = inv.getStackInSlot(i);
+ stackIndex = i;
+ blockInInventory = true;
+ }
+ }
+
+ // We don't have that item in our inventories
+ if (!blockInInventory)
+ return false;
+
+ RayTraceResult mop = Minecraft.getMinecraft().objectMouseOver;
+ if (mop.typeOfHit == RayTraceResult.Type.BLOCK) {
+ BlockPos pos = mop.getBlockPos().add(mop.sideHit.getDirectionVec());
+ // Can we place this block here?
+ AxisAlignedBB axisalignedbb = block.getDefaultState().getCollisionBoundingBox(player.world, pos);
+ if (axisalignedbb == null || player.world.checkNoEntityCollision(axisalignedbb.offset(pos), null)) {
+ MalmoMod.network.sendToServer(new DiscreteMovementCommandsImplementation.UseActionMessage(mop.getBlockPos(), new ItemStack(block), mop.sideHit, false, mop.hitVec));
+ if (stackInInventory.getCount() == 1)
+ inv.setInventorySlotContents(stackIndex, new ItemStack(Block.getBlockById(0)));
+ else
+ stackInInventory.setCount(stackInInventory.getCount() - 1);
+ }
+ }
+
+ /*
+
+ InventoryPlayer inv = player.inventory;
+ Block b = Block.getBlockFromName(parameter);
+ if (b == null || b.getRegistryName() == null)
+ return false;
+
+ boolean foundBlock = false;
+ for (int i = 0; i < 41 && !foundBlock; i++) {
+ ItemStack s = inv.getStackInSlot(i).copy();
+ if (b.getRegistryName().equals(s.getItem().getRegistryName())) {
+ int selectedHotBarItem = inv.currentItem;
+// ItemStack selected = inv.getStackInSlot(selectedHotBarItem).copy();
+ inv.setInventorySlotContents(selectedHotBarItem, s);
+
+ RayTraceResult mop = Minecraft.getMinecraft().objectMouseOver;
+ if (mop.typeOfHit == RayTraceResult.Type.BLOCK) {
+ BlockPos pos = mop.getBlockPos().add(mop.sideHit.getDirectionVec());
+ // Can we place this block here?
+ AxisAlignedBB axisalignedbb = b.getDefaultState().getCollisionBoundingBox(player.world, pos);
+ if (axisalignedbb == null) {
+ PlayerInteractEvent event = new PlayerInteractEvent.RightClickBlock(player, EnumHand.MAIN_HAND, mop.getBlockPos(), mop.sideHit, mop.hitVec);
+ MinecraftForge.EVENT_BUS.post(event);
+ if (!event.isCanceled()) {
+ BlockPos newPos = mop.getBlockPos().add(mop.sideHit.getDirectionVec());
+ Block newB = Block.getBlockFromItem(inv.getCurrentItem().getItem());
+ IBlockState blockType = newB.getStateFromMeta(inv.getCurrentItem().getMetadata());
+ if (player.world.setBlockState(newPos, blockType)) {
+ BlockSnapshot snapshot = new BlockSnapshot(player.world, newPos, blockType);
+ BlockEvent.PlaceEvent placeEvent = new BlockEvent.PlaceEvent(snapshot, player.world.getBlockState(mop.getBlockPos()), player, EnumHand.MAIN_HAND);
+ MinecraftForge.EVENT_BUS.post(placeEvent);
+ // We set the block, so remove it from the inventory.
+ if (!player.isCreative()) {
+ if (player.inventory.getCurrentItem().getCount() > 1)
+ player.inventory.getCurrentItem().setCount(player.inventory.getCurrentItem().getCount() - 1);
+ else
+ player.inventory.mainInventory.get(player.inventory.currentItem).setCount(0);
+ }
+ }
+ }
+ }
+ }
+ foundBlock = true;
+
+ inv.setInventorySlotContents(i, inv.getCurrentItem());
+// inv.setInventorySlotContents(selectedHotBarItem, selected);
+ }
+ }
+ */
+
+ return true;
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof PlaceCommands))
+ return false;
+
+ PlaceCommands pParams = (PlaceCommands) params;
+ setUpAllowAndDenyLists(pParams.getModifierList());
+ return true;
+ }
+
+ @Override
+ public void install(MissionInit missionInit) {
+ }
+
+ @Override
+ public void deinstall(MissionInit missionInit) {
+ }
+
+ @Override
+ public boolean isOverriding() {
+ return this.isOverriding;
+ }
+
+ @Override
+ public void setOverriding(boolean b) {
+ this.isOverriding = b;
+ }
+}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java
index 6907e56d2..ef775b916 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCollectingItemImplementation.java
@@ -1,31 +1,19 @@
-// --------------------------------------------------------------------------------------------------
-// Copyright (c) 2016 Microsoft Corporation
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-// associated documentation files (the "Software"), to deal in the Software without restriction,
-// including without limitation the rights to use, copy, modify, merge, publish, distribute,
-// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
-// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-// --------------------------------------------------------------------------------------------------
-
package com.microsoft.Malmo.MissionHandlers;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Map;
-import javax.xml.bind.DatatypeConverter;
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener;
+import com.microsoft.Malmo.MalmoMod.MalmoMessageType;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer;
+import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.Schemas.RewardForCollectingItem;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.MinecraftForge;
@@ -34,95 +22,171 @@
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.ByteBufUtils;
-import com.microsoft.Malmo.MalmoMod;
-import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener;
-import com.microsoft.Malmo.MalmoMod.MalmoMessageType;
-import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer;
-import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward;
-import com.microsoft.Malmo.Schemas.MissionInit;
-import com.microsoft.Malmo.Schemas.RewardForCollectingItem;
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Sends a reward when the agent collected the specified item with
+ * specified amounts. Counter is absolute.
+ */
+public class RewardForCollectingItemImplementation extends RewardForItemBase
+ implements IRewardProducer, IMalmoMessageListener {
-public class RewardForCollectingItemImplementation extends RewardForItemBase implements IRewardProducer, IMalmoMessageListener
-{
private RewardForCollectingItem params;
+ private ArrayList matchers;
+ private HashMap craftedItems;
- @Override
- public void onMessage(MalmoMessageType messageType, Map data)
- {
- String bufstring = data.get("message");
- ByteBuf buf = Unpooled.copiedBuffer(DatatypeConverter.parseBase64Binary(bufstring));
- ItemStack itemStack = ByteBufUtils.readItemStack(buf);
- if (itemStack != null && itemStack.getItem() != null)
- {
- accumulateReward(this.params.getDimension(), itemStack);
+ @SubscribeEvent
+ public void onPickupItem(EntityItemPickupEvent event) {
+ if (event.getItem() != null) {
+ checkForMatch(event.getItem().getEntityItem());
+ if (event.getEntityPlayer() instanceof EntityPlayerMP)
+ sendItemStackToClient((EntityPlayerMP) event.getEntityPlayer(), MalmoMessageType.SERVER_COLLECTITEM, event.getItem().getEntityItem());
}
- else
- {
- System.out.println("Error - couldn't understand the itemstack we received.");
+ }
+
+ @SubscribeEvent
+ public void onGainItem(GainItemEvent event) {
+ if (event.stack != null) {
+ accumulateReward(this.params.getDimension(), event.stack);
}
}
-
- public static class GainItemEvent extends Event
- {
- public final ItemStack stack;
- public GainItemEvent(ItemStack stack)
- {
- this.stack = stack;
+ /**
+ * Checks whether the ItemStack matches a variant stored in the item list. If
+ * so, returns true, else returns false.
+ *
+ * @param is The item stack
+ * @return If the stack is allowed in the item matchers and has color or
+ * variants enabled, returns true, else false.
+ */
+ private boolean getVariant(ItemStack is) {
+ for (ItemMatcher matcher : matchers) {
+ if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) {
+ if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0)
+ return true;
+ if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0)
+ return true;
+ }
}
+
+ return false;
}
- @Override
- public boolean parseParameters(Object params) {
- if (params == null || !(params instanceof RewardForCollectingItem))
- return false;
+ private int getCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
- // Build up a map of rewards per item:
- this.params = (RewardForCollectingItem) params;
- for (BlockOrItemSpecWithReward is : this.params.getItem())
- addItemSpecToRewardStructure(is);
+ if (variant)
+ return (craftedItems.get(is.getUnlocalizedName()) == null) ? 0 : craftedItems.get(is.getUnlocalizedName());
+ else
+ return (craftedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0
+ : craftedItems.get(is.getItem().getUnlocalizedName());
+ }
- return true;
+ private void addCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0
+ : craftedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ craftedItems.put(is.getUnlocalizedName(), prev + is.getCount());
+ else
+ craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount());
}
- @SubscribeEvent
- public void onGainItem(GainItemEvent event)
- {
- if (event.stack != null)
- {
- accumulateReward(this.params.getDimension(), event.stack);
+ private void checkForMatch(ItemStack is) {
+ int savedCollected = getCollectedItemCount(is);
+ if (is != null) {
+ for (ItemMatcher matcher : this.matchers) {
+ if (matcher.matches(is)) {
+ if (!params.isSparse()) {
+ if (savedCollected != 0 && savedCollected < matcher.matchSpec.getAmount()) {
+ for (int i = savedCollected; i < matcher.matchSpec.getAmount()
+ && i - savedCollected < is.getCount(); i++) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+
+ } else if (savedCollected != 0 && savedCollected >= matcher.matchSpec.getAmount()) {
+ // Do nothing
+ } else {
+ for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ } else {
+ if (savedCollected < matcher.matchSpec.getAmount()
+ && savedCollected + is.getCount() >= matcher.matchSpec.getAmount()) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ }
+ }
+
+ addCollectedItemCount(is);
}
}
- @SubscribeEvent
- public void onPickupItem(EntityItemPickupEvent event)
- {
- if (event.getItem() != null && event.getEntityPlayer() instanceof EntityPlayerMP )
- {
- // This event is received on the server side, so we need to pass it to the client.
- sendItemStackToClient((EntityPlayerMP)event.getEntityPlayer(), MalmoMessageType.SERVER_COLLECTITEM, event.getItem().getEntityItem());
- }
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof RewardForCollectingItem))
+ return false;
+
+ matchers = new ArrayList();
+
+ this.params = (RewardForCollectingItem) params;
+ for (BlockOrItemSpecWithReward spec : this.params.getItem())
+ this.matchers.add(new ItemMatcher(spec));
+
+ return true;
}
@Override
- public void prepare(MissionInit missionInit)
- {
+ public void prepare(MissionInit missionInit) {
super.prepare(missionInit);
MinecraftForge.EVENT_BUS.register(this);
MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
+ craftedItems = new HashMap();
}
@Override
- public void getReward(MissionInit missionInit, MultidimensionalReward reward)
- {
+ public void getReward(MissionInit missionInit, MultidimensionalReward reward) {
super.getReward(missionInit, reward);
}
@Override
- public void cleanup()
- {
+ public void cleanup() {
super.cleanup();
MinecraftForge.EVENT_BUS.unregister(this);
MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
}
-}
+
+ @Override
+ public void onMessage(MalmoMessageType messageType, Map data) {
+ String buffString = data.get("message");
+ ByteBuf buf = Unpooled.copiedBuffer(DatatypeConverter.parseBase64Binary(buffString));
+ ItemStack itemStack = ByteBufUtils.readItemStack(buf);
+ if (itemStack != null) {
+ accumulateReward(this.params.getDimension(), itemStack);
+ } else {
+ System.out.println("Error - couldn't understand the itemstack we received.");
+ }
+ }
+
+ public static class GainItemEvent extends Event {
+ public final ItemStack stack;
+
+ public GainItemEvent(ItemStack stack) {
+ this.stack = stack;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCraftingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCraftingItemImplementation.java
new file mode 100644
index 000000000..0b6ed8030
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForCraftingItemImplementation.java
@@ -0,0 +1,162 @@
+package com.microsoft.Malmo.MissionHandlers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener;
+import com.microsoft.Malmo.MalmoMod.MalmoMessageType;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer;
+import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.Schemas.RewardForCraftingItem;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.PlayerEvent;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Sends a reward when the agent crafts the specified item with
+ * specified amounts.
+ */
+public class RewardForCraftingItemImplementation extends RewardForItemBase
+ implements IRewardProducer, IMalmoMessageListener {
+
+ private RewardForCraftingItem params;
+ private ArrayList matchers;
+ private HashMap craftedItems;
+ private boolean callCraft = true;
+
+ @SubscribeEvent
+ public void onItemCraft(PlayerEvent.ItemCraftedEvent event) {
+ if (callCraft)
+ checkForMatch(event.crafting);
+
+ callCraft = !callCraft;
+ }
+
+ /**
+ * Checks whether the ItemStack matches a variant stored in the item list. If
+ * so, returns true, else returns false.
+ *
+ * @param is The item stack
+ * @return If the stack is allowed in the item matchers and has color or
+ * variants enabled, returns true, else false.
+ */
+ private boolean getVariant(ItemStack is) {
+ for (ItemMatcher matcher : matchers) {
+ if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) {
+ if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0)
+ return true;
+ if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private int getCraftedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ if (variant)
+ return (craftedItems.get(is.getUnlocalizedName()) == null) ? 0 : craftedItems.get(is.getUnlocalizedName());
+ else
+ return (craftedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0
+ : craftedItems.get(is.getItem().getUnlocalizedName());
+ }
+
+ private void addCraftedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0
+ : craftedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ craftedItems.put(is.getUnlocalizedName(), prev + is.getCount());
+ else
+ craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount());
+ }
+
+ private void checkForMatch(ItemStack is) {
+ int savedCrafted = getCraftedItemCount(is);
+ if (is != null) {
+ for (ItemMatcher matcher : this.matchers) {
+ if (matcher.matches(is)) {
+ if (!params.isSparse()) {
+ if (savedCrafted != 0 && savedCrafted < matcher.matchSpec.getAmount()) {
+ for (int i = savedCrafted; i < matcher.matchSpec.getAmount()
+ && i - savedCrafted < is.getCount(); i++) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+
+ } else if (savedCrafted != 0 && savedCrafted >= matcher.matchSpec.getAmount()) {
+ // Do nothing
+ } else {
+ for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ } else {
+ if (savedCrafted < matcher.matchSpec.getAmount()
+ && savedCrafted + is.getCount() >= matcher.matchSpec.getAmount()) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ }
+ }
+
+ addCraftedItemCount(is);
+ }
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof RewardForCraftingItem))
+ return false;
+
+ matchers = new ArrayList();
+
+ this.params = (RewardForCraftingItem) params;
+ for (BlockOrItemSpecWithReward spec : this.params.getItem())
+ this.matchers.add(new ItemMatcher(spec));
+
+ return true;
+ }
+
+ @Override
+ public void prepare(MissionInit missionInit) {
+ super.prepare(missionInit);
+ MinecraftForge.EVENT_BUS.register(this);
+ MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
+ craftedItems = new HashMap();
+ }
+
+ @Override
+ public void getReward(MissionInit missionInit, MultidimensionalReward reward) {
+ super.getReward(missionInit, reward);
+ }
+
+ @Override
+ public void cleanup() {
+ super.cleanup();
+ MinecraftForge.EVENT_BUS.unregister(this);
+ MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
+ }
+
+ @Override
+ public void onMessage(MalmoMessageType messageType, Map data) {
+ }
+}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java
index d0ee7ec3e..9f9e584db 100644
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForDistanceTraveledToCompassTargetImplementation.java
@@ -1,5 +1,7 @@
package com.microsoft.Malmo.MissionHandlers;
+import java.lang.*;
+
import com.microsoft.Malmo.Schemas.MissionInit;
import com.microsoft.Malmo.Schemas.RewardForDistanceTraveledToCompassTarget;
@@ -7,6 +9,7 @@
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
public class RewardForDistanceTraveledToCompassTargetImplementation extends RewardBase
{
@@ -14,6 +17,7 @@ public class RewardForDistanceTraveledToCompassTargetImplementation extends Rewa
double previousDistance;
float totalReward;
boolean positionInitialized;
+ BlockPos prevSpawn;
@Override
public boolean parseParameters(Object params)
@@ -25,9 +29,9 @@ public boolean parseParameters(Object params)
this.params = (RewardForDistanceTraveledToCompassTarget)params;
EntityPlayerSP player = Minecraft.getMinecraft().player;
- BlockPos spawn = player.world.getSpawnPoint();
+ prevSpawn = player.world.getSpawnPoint();
BlockPos playerLoc = player.getPosition();
- this.previousDistance = playerLoc.getDistance(spawn.getX(), spawn.getY(), spawn.getZ());
+ this.previousDistance = playerLoc.getDistance(prevSpawn.getX(), prevSpawn.getY(), prevSpawn.getZ());
this.totalReward = 0;
this.positionInitialized = false;
@@ -42,10 +46,11 @@ public void getReward(MissionInit missionInit, MultidimensionalReward reward)
EntityPlayerSP player = Minecraft.getMinecraft().player;
BlockPos spawn = player.world.getSpawnPoint();
- BlockPos playerLoc = player.getPosition();
+ Vec3d playerLoc = player.getPositionVector();
+ Vec3d spawnPos = new Vec3d(spawn.getX(), spawn.getY(), spawn.getZ());
- double currentDistance = playerLoc.getDistance(spawn.getX(), spawn.getY(), spawn.getZ());
- float delta = -1 * (float)(currentDistance - previousDistance);
+ double currentDistance = playerLoc.distanceTo(spawnPos);
+ float delta = (float)(this.previousDistance - currentDistance);
switch (this.params.getDensity()) {
case MISSION_END:
@@ -65,12 +70,14 @@ public void getReward(MissionInit missionInit, MultidimensionalReward reward)
}
// Avoid sending large rewards as the result of an initial teleport event
- if(!this.positionInitialized && (delta < -0.0001 || 0.0001 < delta)){
- this.positionInitialized = true;
+ if (this.prevSpawn.getX() != spawn.getX() ||
+ this.prevSpawn.getY() != spawn.getY() ||
+ this.prevSpawn.getZ() != spawn.getZ()) {
this.totalReward = 0;
}
- this.previousDistance = playerLoc.getDistance(spawn.getX(), spawn.getY(), spawn.getZ());
+ this.previousDistance = currentDistance;
+ this.prevSpawn = spawn;
super.getReward(missionInit, reward);
if (sendReward)
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForPossessingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForPossessingItemImplementation.java
new file mode 100644
index 000000000..33473a80b
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForPossessingItemImplementation.java
@@ -0,0 +1,232 @@
+package com.microsoft.Malmo.MissionHandlers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener;
+import com.microsoft.Malmo.MalmoMod.MalmoMessageType;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer;
+import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.MissionHandlers.RewardForDiscardingItemImplementation.LoseItemEvent;
+import com.microsoft.Malmo.Schemas.RewardForPossessingItem;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.entity.item.ItemTossEvent;
+import net.minecraftforge.event.entity.player.EntityItemPickupEvent;
+import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
+import net.minecraftforge.event.world.BlockEvent.PlaceEvent;
+import net.minecraftforge.fml.common.eventhandler.Event;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.network.ByteBufUtils;
+
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Sends a reward when the agent possesses the specified item with
+ * specified amounts. The counter is relative, meaning it goes down if items are placed, lost, or destroyed.
+ */
+public class RewardForPossessingItemImplementation extends RewardForItemBase
+ implements IRewardProducer, IMalmoMessageListener {
+
+ private RewardForPossessingItem params;
+ private ArrayList matchers;
+ private HashMap collectedItems;
+
+ @SubscribeEvent
+ public void onPickupItem(EntityItemPickupEvent event) {
+ if (event.getItem() != null) {
+ checkForMatch(event.getItem().getEntityItem());
+ if (event.getEntityPlayer() instanceof EntityPlayerMP)
+ sendItemStackToClient((EntityPlayerMP) event.getEntityPlayer(), MalmoMessageType.SERVER_COLLECTITEM, event.getItem().getEntityItem());
+ }
+ }
+
+ @SubscribeEvent
+ public void onGainItem(GainItemEvent event) {
+ if (event.stack != null) {
+ accumulateReward(this.params.getDimension(), event.stack);
+ }
+ }
+
+ @SubscribeEvent
+ public void onLoseItem(LoseItemEvent event) {
+ if (event.stack != null)
+ removeCollectedItemCount(event.stack);
+ }
+
+ @SubscribeEvent
+ public void onDropItem(ItemTossEvent event) {
+ removeCollectedItemCount(event.getEntityItem().getEntityItem());
+ }
+
+ @SubscribeEvent
+ public void onDestroyItem(PlayerDestroyItemEvent event) {
+ removeCollectedItemCount(event.getOriginal());
+ }
+
+ @SubscribeEvent
+ public void onBlockPlace(PlaceEvent event) {
+ if (!event.isCanceled() && event.getPlacedBlock() != null) {
+ ItemStack stack = new ItemStack(event.getPlacedBlock().getBlock());
+ removeCollectedItemCount(stack);
+ }
+ }
+
+ /**
+ * Checks whether the ItemStack matches a variant stored in the item list. If
+ * so, returns true, else returns false.
+ *
+ * @param is The item stack
+ * @return If the stack is allowed in the item matchers and has color or
+ * variants enabled, returns true, else false.
+ */
+ private boolean getVariant(ItemStack is) {
+ for (ItemMatcher matcher : matchers) {
+ if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) {
+ if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0)
+ return true;
+ if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private int getCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ if (variant)
+ return (collectedItems.get(is.getUnlocalizedName()) == null) ? 0 : collectedItems.get(is.getUnlocalizedName());
+ else
+ return (collectedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0
+ : collectedItems.get(is.getItem().getUnlocalizedName());
+ }
+
+ private void addCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0
+ : collectedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ collectedItems.put(is.getUnlocalizedName(), prev + is.getCount());
+ else
+ collectedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount());
+ }
+
+ private void removeCollectedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (collectedItems.get(is.getUnlocalizedName()) == null ? 0
+ : collectedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ collectedItems.put(is.getUnlocalizedName(), prev - is.getCount());
+ else
+ collectedItems.put(is.getItem().getUnlocalizedName(), prev - is.getCount());
+ }
+
+ private void checkForMatch(ItemStack is) {
+ int savedCollected = getCollectedItemCount(is);
+ if (is != null) {
+ for (ItemMatcher matcher : this.matchers) {
+ if (matcher.matches(is)) {
+ if (!params.isSparse()) {
+ if (savedCollected != 0 && savedCollected < matcher.matchSpec.getAmount()) {
+ for (int i = savedCollected; i < matcher.matchSpec.getAmount()
+ && i - savedCollected < is.getCount(); i++) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+
+ } else if (savedCollected != 0 && savedCollected >= matcher.matchSpec.getAmount()) {
+ // Do nothing
+ } else {
+ for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ } else {
+ if (savedCollected < matcher.matchSpec.getAmount()
+ && savedCollected + is.getCount() >= matcher.matchSpec.getAmount()) {
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ }
+ }
+
+ addCollectedItemCount(is);
+ }
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof RewardForPossessingItem))
+ return false;
+
+ matchers = new ArrayList();
+
+ this.params = (RewardForPossessingItem) params;
+ for (BlockOrItemSpecWithReward spec : this.params.getItem())
+ this.matchers.add(new ItemMatcher(spec));
+
+ return true;
+ }
+
+ @Override
+ public void prepare(MissionInit missionInit) {
+ super.prepare(missionInit);
+ MinecraftForge.EVENT_BUS.register(this);
+ MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
+ collectedItems = new HashMap();
+ }
+
+ @Override
+ public void getReward(MissionInit missionInit, MultidimensionalReward reward) {
+ super.getReward(missionInit, reward);
+ }
+
+ @Override
+ public void cleanup() {
+ super.cleanup();
+ MinecraftForge.EVENT_BUS.unregister(this);
+ MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
+ }
+
+ @Override
+ public void onMessage(MalmoMessageType messageType, Map data) {
+ String buffString = data.get("message");
+ ByteBuf buf = Unpooled.copiedBuffer(DatatypeConverter.parseBase64Binary(buffString));
+ ItemStack itemStack = ByteBufUtils.readItemStack(buf);
+ if (itemStack != null) {
+ accumulateReward(this.params.getDimension(), itemStack);
+ } else {
+ System.out.println("Error - couldn't understand the itemstack we received.");
+ }
+ }
+
+ public static class GainItemEvent extends Event {
+ public final ItemStack stack;
+
+ public GainItemEvent(ItemStack stack) {
+ this.stack = stack;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForSmeltingItemImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForSmeltingItemImplementation.java
new file mode 100644
index 000000000..9f3ccc761
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/RewardForSmeltingItemImplementation.java
@@ -0,0 +1,164 @@
+package com.microsoft.Malmo.MissionHandlers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener;
+import com.microsoft.Malmo.MalmoMod.MalmoMessageType;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IRewardProducer;
+import com.microsoft.Malmo.Schemas.BlockOrItemSpecWithReward;
+import com.microsoft.Malmo.Schemas.MissionInit;
+
+import com.microsoft.Malmo.Schemas.RewardForSmeltingItem;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.PlayerEvent;
+
+/**
+ * @author Cayden Codel, Carnegie Mellon University
+ *
+ * Sends a reward when the agent smelts the specified item with
+ * specified amounts.
+ */
+public class RewardForSmeltingItemImplementation extends RewardForItemBase
+ implements IRewardProducer, IMalmoMessageListener {
+
+ private RewardForSmeltingItem params;
+ private ArrayList matchers;
+ private HashMap craftedItems;
+ private int callSmelt = 0;
+
+ @SubscribeEvent
+ public void onItemSmelt(PlayerEvent.ItemSmeltedEvent event) {
+ if (callSmelt % 4 == 0)
+ checkForMatch(event.smelting);
+
+ callSmelt = (callSmelt + 1) % 4;
+ }
+
+ /**
+ * Checks whether the ItemStack matches a variant stored in the item list. If
+ * so, returns true, else returns false.
+ *
+ * @param is The item stack
+ * @return If the stack is allowed in the item matchers and has color or
+ * variants enabled, returns true, else false.
+ */
+ private boolean getVariant(ItemStack is) {
+ for (ItemMatcher matcher : matchers) {
+ if (matcher.allowedItemTypes.contains(is.getItem().getUnlocalizedName())) {
+ if (matcher.matchSpec.getColour() != null && matcher.matchSpec.getColour().size() > 0)
+ return true;
+ if (matcher.matchSpec.getVariant() != null && matcher.matchSpec.getVariant().size() > 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private int getSmeltedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ if (variant)
+ return (craftedItems.get(is.getUnlocalizedName()) == null) ? 0 : craftedItems.get(is.getUnlocalizedName());
+ else
+ return (craftedItems.get(is.getItem().getUnlocalizedName()) == null) ? 0
+ : craftedItems.get(is.getItem().getUnlocalizedName());
+ }
+
+ private void addSmeltedItemCount(ItemStack is) {
+ boolean variant = getVariant(is);
+
+ int prev = (craftedItems.get(is.getUnlocalizedName()) == null ? 0
+ : craftedItems.get(is.getUnlocalizedName()));
+ if (variant)
+ craftedItems.put(is.getUnlocalizedName(), prev + is.getCount());
+ else
+ craftedItems.put(is.getItem().getUnlocalizedName(), prev + is.getCount());
+ }
+
+ private void checkForMatch(ItemStack is) {
+ System.out.println("[REWARD] Checking stack " + is.getUnlocalizedName() + " " + is.getCount());
+ int savedSmelted = getSmeltedItemCount(is);
+ System.out.println("[REWARD] Previous saved amount is " + savedSmelted);
+ for (ItemMatcher matcher : this.matchers) {
+ if (matcher.matches(is)) {
+ if (!params.isSparse()) {
+ if (savedSmelted != 0 && savedSmelted < matcher.matchSpec.getAmount()) {
+ for (int i = savedSmelted; i < matcher.matchSpec.getAmount()
+ && i - savedSmelted < is.getCount(); i++) {
+ System.out.println("Giving reward");
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ } else if (savedSmelted != 0 && savedSmelted >= matcher.matchSpec.getAmount()) {
+ // Do nothing
+ } else {
+ for (int i = 0; i < is.getCount() && i < matcher.matchSpec.getAmount(); i++) {
+ System.out.println("Giving reward");
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ } else {
+ if (savedSmelted < matcher.matchSpec.getAmount()
+ && savedSmelted + is.getCount() >= matcher.matchSpec.getAmount()) {
+ System.out.println("Giving reward");
+ this.adjustAndDistributeReward(
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getReward().floatValue(),
+ params.getDimension(),
+ ((BlockOrItemSpecWithReward) matcher.matchSpec).getDistribution());
+ }
+ }
+ }
+ }
+
+ addSmeltedItemCount(is);
+ }
+
+ @Override
+ public boolean parseParameters(Object params) {
+ if (!(params instanceof RewardForSmeltingItem))
+ return false;
+
+ matchers = new ArrayList();
+
+ this.params = (RewardForSmeltingItem) params;
+ for (BlockOrItemSpecWithReward spec : this.params.getItem())
+ this.matchers.add(new ItemMatcher(spec));
+
+ return true;
+ }
+
+ @Override
+ public void prepare(MissionInit missionInit) {
+ super.prepare(missionInit);
+ MinecraftForge.EVENT_BUS.register(this);
+ MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
+ craftedItems = new HashMap();
+ }
+
+ @Override
+ public void getReward(MissionInit missionInit, MultidimensionalReward reward) {
+ super.getReward(missionInit, reward);
+ }
+
+ @Override
+ public void cleanup() {
+ super.cleanup();
+ MinecraftForge.EVENT_BUS.unregister(this);
+ MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_COLLECTITEM);
+ }
+
+ @Override
+ public void onMessage(MalmoMessageType messageType, Map data) {
+ }
+}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java
index d550377a4..90d943f5b 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/SimpleCraftCommandsImplementation.java
@@ -26,6 +26,8 @@
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.gameevent.PlayerEvent;
import net.minecraftforge.fml.common.network.ByteBufUtils;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
@@ -104,7 +106,7 @@ protected boolean onExecute(String verb, String parameter, MissionInit missionIn
@Override
public boolean parseParameters(Object params)
{
- if (params == null || !(params instanceof SimpleCraftCommands))
+ if (!(params instanceof SimpleCraftCommands))
return false;
SimpleCraftCommands cparams = (SimpleCraftCommands)params;
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java b/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java
index c8129fdaa..3f22ecf5a 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java
@@ -41,6 +41,7 @@
import net.minecraft.block.properties.PropertyDirection;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
@@ -202,6 +203,32 @@ public static boolean playerHasIngredients(EntityPlayerMP player, List
+ * The ingredients list MUST be amalgamated such that no two ItemStacks contain the same type of item.
+ * @param player
+ * @param ingredients an amalgamated list of ingredients
+ * @return true if the player's inventory contains sufficient quantities of all the required items.
+ */
+ public static boolean playerHasIngredients(EntityPlayerSP player, List ingredients)
+ {
+ NonNullList main = player.inventory.mainInventory;
+ NonNullList arm = player.inventory.armorInventory;
+
+ for (ItemStack isIngredient : ingredients)
+ {
+ int target = isIngredient.getCount();
+ for (int i = 0; i < main.size() + arm.size() && target > 0; i++)
+ {
+ ItemStack isPlayer = (i >= main.size()) ? arm.get(i - main.size()) : main.get(i);
+ if (isPlayer != null && isIngredient != null && itemStackIngredientsMatch(isPlayer, isIngredient))
+ target -= isPlayer.getCount();
+ }
+ if (target > 0)
+ return false; // Don't have enough of this.
+ }
+ return true;
+ }
+
/** Compare two ItemStacks and see if their items match - take wildcards into account, don't take stacksize into account.
* @param A ItemStack A
* @param B ItemStack B
@@ -338,7 +365,7 @@ public static List getRecipesForRequestedOutput(String output)
if (obj instanceof IRecipe)
{
ItemStack is = ((IRecipe)obj).getRecipeOutput();
- if (is == null)
+ if (is == null || target == null)
continue;
if (ItemStack.areItemsEqual(is, target))
{
diff --git a/Minecraft/src/main/resources/schemas.index b/Minecraft/src/main/resources/schemas.index
index 70b4a99d8..55dcedb71 100644
--- a/Minecraft/src/main/resources/schemas.index
+++ b/Minecraft/src/main/resources/schemas.index
@@ -1,5 +1,5 @@
-Mission.xsd
-MissionEnded.xsd
MissionHandlers.xsd
MissionInit.xsd
+MissionEnded.xsd
Types.xsd
+Mission.xsd
diff --git a/Schemas/Mission.xsd b/Schemas/Mission.xsd
old mode 100755
new mode 100644
index 12261c8e0..0dd229ae3
--- a/Schemas/Mission.xsd
+++ b/Schemas/Mission.xsd
@@ -353,9 +353,12 @@
+
+
+
@@ -368,6 +371,9 @@
+
+
+
@@ -378,6 +384,9 @@
+
+
+
@@ -398,6 +407,8 @@
+
+
diff --git a/Schemas/Mission.xsd.in b/Schemas/Mission.xsd.in
index c04f7b6f0..056b460ca 100644
--- a/Schemas/Mission.xsd.in
+++ b/Schemas/Mission.xsd.in
@@ -353,9 +353,12 @@
+
+
+
@@ -368,6 +371,9 @@
+
+
+
@@ -378,6 +384,9 @@
+
+
+
@@ -398,6 +407,8 @@
+
+
diff --git a/Schemas/MissionEnded.xsd b/Schemas/MissionEnded.xsd
old mode 100755
new mode 100644
diff --git a/Schemas/MissionHandlers.xsd b/Schemas/MissionHandlers.xsd
old mode 100755
new mode 100644
index 14593c411..4a91670d0
--- a/Schemas/MissionHandlers.xsd
+++ b/Schemas/MissionHandlers.xsd
@@ -9,7 +9,7 @@
version="0.37">
-
+
@@ -139,28 +139,28 @@
-
+
- Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders.
-
- If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example:
-
- {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}}
+ Generates a survival world with the specified biome.
-
+
- The path to the saved world folder.
+ The biome type for the world. Each chunk will be loaded with the biome specified.
+
+ If left blank, the world will be a normal survival world.
+
+ Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs
- Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename).
+ Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed).
Force reloading is slow, but will guarantee that no world changes will carry over between missions.
@@ -176,28 +176,28 @@
-
+
- Generates a survival world with the specified biome.
+ Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders.
+
+ If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example:
+
+ {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}}
-
+
- The biome type for the world. Each chunk will be loaded with the biome specified.
-
- If left blank, the world will be a normal survival world.
-
- Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs
+ The path to the saved world folder.
- Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed).
+ Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename).
Force reloading is slow, but will guarantee that no world changes will carry over between missions.
@@ -1257,9 +1257,9 @@
"{{{strafe -1}}}" - start moving left at 100% of the normal walking speed (-ve = left, +ve = right).
- "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
+ "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
- "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
+ "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
"{{{jump 1}}}" - start jumping (1 = start, 0 = stop).
@@ -1267,7 +1267,7 @@
"{{{attack 1}}}" - start attacking (1 = start, 0 = stop). The 'attack' command is for destroying blocks and attacking mobs.
- "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too.
+ "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too.
@@ -1401,10 +1401,10 @@
To select a hotbar slot:
- "{{{hotbar.1 1}}}"
- "{{{hotbar.1 0}}}"
+ "{{{hotbar.1 1}}}"
+ "{{{hotbar.1 0}}}"
- Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands
+ Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands
- e.g. if the agent does 'use' while holding a block item it will place the block into the world.
If the agent is currently pointed at a container item - eg a chest, shulker box, dispenser etc - then the swap and combine commands
@@ -1415,7 +1415,7 @@
"{{{swapInventoryItems 3 Chest:0}}}"
- Note that this is the same as writing
+ Note that this is the same as writing
"{{{swapInventoryItems Inventory:3 Chest:0}}}"
@@ -1445,21 +1445,64 @@
A command for simple crafting:
- Will look up all recipes that produce the requested object, and attempt each one in turn until one is successful or all have failed. This ignores all issues like requiring a crafting table / brewing stand etc, and the shape of the recipe (which items go in which slots on the crafting table). It will simply check to see whether the player has the necessary raw ingredients, and, if so, will remove them from the player's inventory and add the result of the recipe.
+ "{{{craft carpet PINK}}}"
- For basic objects, use the ItemTypes or BlockTypes found in Types.xsd. Eg:
+ etc.
+
+
+
+
+
+
- "{{{craft diamond_pickaxe}}}" will remove three diamonds and two sticks from the player's inventory, and add a diamond pickaxe.
+
+
+
+ A command for simple nearby crafting:
- For more control over colours, types etc, add a Variation or Colour. Eg:
+ "{{{craftNearby carpet PINK}}}"
- "{{{craft carpet PINK}}}"
+ etc.
+
+
+
+
+
+
+
+
+
+
+ A command for simple smelting:
+
+ "{{{smeltNearby carpet PINK}}}"
etc.
-
+
+
+
+
+
+
+
+ A command for placing blocks in the agent's inventory:
+
+ Will look in the agent's inventory. If the block exists, will try to place the block in the world.
+
+ For basic objects, use BlockTypes found in Types.xsd. Eg:
+
+ "{{{place diamond_block}}}" will attempt to place a diamond block from the agent's inventory.
+
+ for more control over colours, types, etc, add a Variation or Colour. Eg:
+
+ "{{{place carpet PINK}}}"
+
+
+
+
@@ -1525,7 +1568,7 @@
-
+
@@ -1670,6 +1713,7 @@
+
@@ -1685,6 +1729,87 @@
+
+
+
+ When present, the Mod will accept simple commands that implement a basic form of crafting. Success of the craft command depends on the presence of a nearby crafting table previously placed by the agent and within reach and in field of view.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ When present, the Mod will accept simple commands that implement a basic form of smelting. Success of the smelt command depends on the presence of a nearby furnace previously placed by the agent and within reach and in field of view. Fails when a command corresponds to an item not able to be smelted. Each command takes fuel as if the agent had placed the items in a furnace.
+
+ Eg:
+
+ If called on 16 iron ore, requires 2 coal, 2 charcoal, 11 planks, etc.
+
+ If called twice separately on 8 iron ore, requires 1 coal and then 1 coal, etc.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ When present, the Mod will accept commands that allow for the placement of blocks. If the specified block is contained in the agent's inventory, then the agent will attempt to place the block in the world.
+
+ If a non-block is specified, the command fails.
+
+ If a block is specified, the block will swap to the hotbar, be placed, and then swapped back, so no changes to the inventory are made besides losing a block.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2152,12 +2277,14 @@
+
+
@@ -2249,16 +2376,78 @@
Sends a reward when the agent collects a specific item.
+
+ If Sparse is set to true, will only give full reward on collecting entire amount.
+
+ Otherwise, will give the reward amount notated for each item collected up to the amount noted.
+
+
+
+
+ Sends a reward when the agent collects a specific item.
+
+ If Sparse is set to true, will only give full reward on possessing the entire amount.
+
+ Otherwise, will give the rewards amount notated for each item crafted up to the amount noted.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sends a reward when the agent crafts a specific item.
+
+ If Sparse is set to true, will only give full reward on crafting of entire amount.
+
+ Otherwise, will give the reward amount notated for each item crafted up to the amount noted.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sends a reward when the agent smelts a specific item.
+
+ If Sparse is set to true, will only give full reward on crafting of entire amount.
+
+ Otherwise, will give the reward amount notated for each item crafted up to the amount noted.
+
+
+
+
+
+
+
+
+
+
+
@@ -2551,6 +2740,45 @@
+
+
+
+ When this is included the agent's mission will end when they craft a specified item.
+
+
+
+
+
+
+
+
+
+
+
+
+ When this is included the agent's mission will end when they smelt a specified item.
+
+
+
+
+
+
+
+
+
+
+
+
+ When this is included the agent's mission will end when they possess the specified item in their inventory all at once.
+
+
+
+
+
+
+
+
+
diff --git a/Schemas/MissionHandlers.xsd.in b/Schemas/MissionHandlers.xsd.in
index af106e8be..4a91670d0 100644
--- a/Schemas/MissionHandlers.xsd.in
+++ b/Schemas/MissionHandlers.xsd.in
@@ -6,10 +6,10 @@
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
elementFormDefault="qualified"
jaxb:version="2.1"
- version="${MALMO_VERSION_MAJOR}.${MALMO_VERSION_MINOR}">
+ version="0.37">
-
+
@@ -139,28 +139,28 @@
-
+
- Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders.
-
- If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example:
-
- {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}}
+ Generates a survival world with the specified biome.
-
+
- The path to the saved world folder.
+ The biome type for the world. Each chunk will be loaded with the biome specified.
+
+ If left blank, the world will be a normal survival world.
+
+ Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs
- Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename).
+ Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed).
Force reloading is slow, but will guarantee that no world changes will carry over between missions.
@@ -176,28 +176,28 @@
-
+
- Generates a survival world with the specified biome.
+ Loads a saved world from disk. You can find the saved worlds in "{{{Minecraft\run\saves}}}". Use the full path to one of those folders.
+
+ If Minecraft is running on a different machine then copy the folder to a readable network location and update the path accordingly. Example:
+
+ {{{<FileWorldGenerator src="\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena" />}}}
-
+
- The biome type for the world. Each chunk will be loaded with the biome specified.
-
- If left blank, the world will be a normal survival world.
-
- Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs
+ The path to the saved world folder.
- Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested seed).
+ Set this to true to force the world to be reloaded, otherwise the current world will be used (provided it matches the requested source filename).
Force reloading is slow, but will guarantee that no world changes will carry over between missions.
@@ -1257,9 +1257,9 @@
"{{{strafe -1}}}" - start moving left at 100% of the normal walking speed (-ve = left, +ve = right).
- "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
+ "{{{pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve = down). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
- "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
+ "{{{turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve = clockwise/right). The maximum speed is set by {{{turnSpeedDegs}}} in {{{ContinuousMovementCommands}}}.
"{{{jump 1}}}" - start jumping (1 = start, 0 = stop).
@@ -1267,7 +1267,7 @@
"{{{attack 1}}}" - start attacking (1 = start, 0 = stop). The 'attack' command is for destroying blocks and attacking mobs.
- "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too.
+ "{{{use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for other things too.
@@ -1401,10 +1401,10 @@
To select a hotbar slot:
- "{{{hotbar.1 1}}}"
- "{{{hotbar.1 0}}}"
+ "{{{hotbar.1 1}}}"
+ "{{{hotbar.1 0}}}"
- Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands
+ Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use commands
- e.g. if the agent does 'use' while holding a block item it will place the block into the world.
If the agent is currently pointed at a container item - eg a chest, shulker box, dispenser etc - then the swap and combine commands
@@ -1415,7 +1415,7 @@
"{{{swapInventoryItems 3 Chest:0}}}"
- Note that this is the same as writing
+ Note that this is the same as writing
"{{{swapInventoryItems Inventory:3 Chest:0}}}"
@@ -1445,21 +1445,64 @@
A command for simple crafting:
- Will look up all recipes that produce the requested object, and attempt each one in turn until one is successful or all have failed. This ignores all issues like requiring a crafting table / brewing stand etc, and the shape of the recipe (which items go in which slots on the crafting table). It will simply check to see whether the player has the necessary raw ingredients, and, if so, will remove them from the player's inventory and add the result of the recipe.
+ "{{{craft carpet PINK}}}"
- For basic objects, use the ItemTypes or BlockTypes found in Types.xsd. Eg:
+ etc.
+
+
+
+
+
+
- "{{{craft diamond_pickaxe}}}" will remove three diamonds and two sticks from the player's inventory, and add a diamond pickaxe.
+
+
+
+ A command for simple nearby crafting:
- For more control over colours, types etc, add a Variation or Colour. Eg:
+ "{{{craftNearby carpet PINK}}}"
- "{{{craft carpet PINK}}}"
+ etc.
+
+
+
+
+
+
+
+
+
+
+ A command for simple smelting:
+
+ "{{{smeltNearby carpet PINK}}}"
etc.
-
+
+
+
+
+
+
+
+ A command for placing blocks in the agent's inventory:
+
+ Will look in the agent's inventory. If the block exists, will try to place the block in the world.
+
+ For basic objects, use BlockTypes found in Types.xsd. Eg:
+
+ "{{{place diamond_block}}}" will attempt to place a diamond block from the agent's inventory.
+
+ for more control over colours, types, etc, add a Variation or Colour. Eg:
+
+ "{{{place carpet PINK}}}"
+
+
+
+
@@ -1525,7 +1568,7 @@
-
+
@@ -1670,6 +1713,7 @@
+
@@ -1685,6 +1729,87 @@
+
+
+
+ When present, the Mod will accept simple commands that implement a basic form of crafting. Success of the craft command depends on the presence of a nearby crafting table previously placed by the agent and within reach and in field of view.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ When present, the Mod will accept simple commands that implement a basic form of smelting. Success of the smelt command depends on the presence of a nearby furnace previously placed by the agent and within reach and in field of view. Fails when a command corresponds to an item not able to be smelted. Each command takes fuel as if the agent had placed the items in a furnace.
+
+ Eg:
+
+ If called on 16 iron ore, requires 2 coal, 2 charcoal, 11 planks, etc.
+
+ If called twice separately on 8 iron ore, requires 1 coal and then 1 coal, etc.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ When present, the Mod will accept commands that allow for the placement of blocks. If the specified block is contained in the agent's inventory, then the agent will attempt to place the block in the world.
+
+ If a non-block is specified, the command fails.
+
+ If a block is specified, the block will swap to the hotbar, be placed, and then swapped back, so no changes to the inventory are made besides losing a block.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2152,12 +2277,14 @@
+
+
@@ -2249,16 +2376,78 @@
Sends a reward when the agent collects a specific item.
+
+ If Sparse is set to true, will only give full reward on collecting entire amount.
+
+ Otherwise, will give the reward amount notated for each item collected up to the amount noted.
+
+
+
+
+ Sends a reward when the agent collects a specific item.
+
+ If Sparse is set to true, will only give full reward on possessing the entire amount.
+
+ Otherwise, will give the rewards amount notated for each item crafted up to the amount noted.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sends a reward when the agent crafts a specific item.
+
+ If Sparse is set to true, will only give full reward on crafting of entire amount.
+
+ Otherwise, will give the reward amount notated for each item crafted up to the amount noted.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sends a reward when the agent smelts a specific item.
+
+ If Sparse is set to true, will only give full reward on crafting of entire amount.
+
+ Otherwise, will give the reward amount notated for each item crafted up to the amount noted.
+
+
+
+
+
+
+
+
+
+
+
@@ -2551,6 +2740,45 @@
+
+
+
+ When this is included the agent's mission will end when they craft a specified item.
+
+
+
+
+
+
+
+
+
+
+
+
+ When this is included the agent's mission will end when they smelt a specified item.
+
+
+
+
+
+
+
+
+
+
+
+
+ When this is included the agent's mission will end when they possess the specified item in their inventory all at once.
+
+
+
+
+
+
+
+
+
diff --git a/Schemas/MissionInit.xsd b/Schemas/MissionInit.xsd
old mode 100755
new mode 100644
diff --git a/Schemas/Schemas.iml b/Schemas/Schemas.iml
new file mode 100644
index 000000000..8021953ed
--- /dev/null
+++ b/Schemas/Schemas.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Schemas/Types.xsd b/Schemas/Types.xsd
old mode 100755
new mode 100644