From 0b6ff77515ed176b02f77d4e444eb9e98bd5ef22 Mon Sep 17 00:00:00 2001 From: SpaceToad Date: Thu, 7 Aug 2014 09:40:17 +0200 Subject: [PATCH] made progress for crafter robot, #1973 --- api/buildcraft/api/robots/AIRobot.java | 4 + .../core/robots/AIRobotCraftFurnace.java | 38 +++ .../core/robots/AIRobotCraftGeneric.java | 32 +++ .../core/robots/AIRobotCraftWorkbench.java | 230 ++++++++++++++++ .../core/robots/AIRobotGotoStationToLoad.java | 8 +- .../robots/AIRobotGotoStationToUnload.java | 8 +- .../core/robots/boards/BoardRobotBuilder.java | 2 +- .../core/robots/boards/BoardRobotCarrier.java | 4 +- .../core/robots/boards/BoardRobotCrafter.java | 247 ++++-------------- 9 files changed, 369 insertions(+), 204 deletions(-) create mode 100755 common/buildcraft/core/robots/AIRobotCraftFurnace.java create mode 100755 common/buildcraft/core/robots/AIRobotCraftGeneric.java create mode 100755 common/buildcraft/core/robots/AIRobotCraftWorkbench.java diff --git a/api/buildcraft/api/robots/AIRobot.java b/api/buildcraft/api/robots/AIRobot.java index 41d7bb0e..1a45f639 100755 --- a/api/buildcraft/api/robots/AIRobot.java +++ b/api/buildcraft/api/robots/AIRobot.java @@ -55,6 +55,10 @@ public class AIRobot { } + public boolean success() { + return true; + } + public double getEnergyCost() { return 0.1; } diff --git a/common/buildcraft/core/robots/AIRobotCraftFurnace.java b/common/buildcraft/core/robots/AIRobotCraftFurnace.java new file mode 100755 index 00000000..f8bbacb2 --- /dev/null +++ b/common/buildcraft/core/robots/AIRobotCraftFurnace.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team + * http://www.mod-buildcraft.com + * + * BuildCraft is distributed under the terms of the Minecraft Mod Public + * License 1.0, or MMPL. Please check the contents of the license located in + * http://www.mod-buildcraft.com/MMPL-1.0.txt + */ +package buildcraft.core.robots; + +import java.util.ArrayList; + +import buildcraft.api.robots.EntityRobotBase; +import buildcraft.core.inventory.filters.ArrayStackFilter; + +public class AIRobotCraftFurnace extends AIRobotCraftGeneric { + + public AIRobotCraftFurnace(EntityRobotBase iRobot) { + super(iRobot); + } + + @Override + protected ArrayList tryCraft(boolean doRemove) { + // TODO Auto-generated method stub + return null; + } + + // How to operate furnaces + // [1] identify a furnace + // [2] verify that proper item is in. If empty, and slot out empty or + // contains order get proper item, otherwise skip + // [3] bring proper item and put in + // [4] as soon as output contains expected item, get it and place it + // somewhere + + // How to operate assembly tables + +} diff --git a/common/buildcraft/core/robots/AIRobotCraftGeneric.java b/common/buildcraft/core/robots/AIRobotCraftGeneric.java new file mode 100755 index 00000000..fe6910fe --- /dev/null +++ b/common/buildcraft/core/robots/AIRobotCraftGeneric.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team + * http://www.mod-buildcraft.com + * + * BuildCraft is distributed under the terms of the Minecraft Mod Public + * License 1.0, or MMPL. Please check the contents of the license located in + * http://www.mod-buildcraft.com/MMPL-1.0.txt + */ +package buildcraft.core.robots; + +import java.util.ArrayList; + +import buildcraft.api.robots.AIRobot; +import buildcraft.api.robots.EntityRobotBase; +import buildcraft.core.inventory.filters.ArrayStackFilter; + +public abstract class AIRobotCraftGeneric extends AIRobot { + + protected boolean crafted = false; + private ArrayList requirements = new ArrayList(); + + public AIRobotCraftGeneric(EntityRobotBase iRobot) { + super(iRobot); + } + + protected abstract ArrayList tryCraft(boolean doRemove); + + @Override + public boolean success() { + return crafted; + } +} diff --git a/common/buildcraft/core/robots/AIRobotCraftWorkbench.java b/common/buildcraft/core/robots/AIRobotCraftWorkbench.java new file mode 100755 index 00000000..a71cfb40 --- /dev/null +++ b/common/buildcraft/core/robots/AIRobotCraftWorkbench.java @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team + * http://www.mod-buildcraft.com + * + * BuildCraft is distributed under the terms of the Minecraft Mod Public + * License 1.0, or MMPL. Please check the contents of the license located in + * http://www.mod-buildcraft.com/MMPL-1.0.txt + */ +package buildcraft.core.robots; + +import java.util.ArrayList; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockWorkbench; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; + +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +import buildcraft.api.core.IInvSlot; +import buildcraft.api.robots.AIRobot; +import buildcraft.api.robots.EntityRobotBase; +import buildcraft.core.inventory.ITransactor; +import buildcraft.core.inventory.InventoryCopy; +import buildcraft.core.inventory.InventoryIterator; +import buildcraft.core.inventory.Transactor; +import buildcraft.core.inventory.filters.ArrayStackFilter; +import buildcraft.core.inventory.filters.IStackFilter; + +public class AIRobotCraftWorkbench extends AIRobotCraftGeneric { + + private IRecipe recipe; + private int craftingTimer = 0; + private ArrayList requirements; + private ItemStack output; + + public AIRobotCraftWorkbench(EntityRobotBase iRobot, IRecipe iRecipe) { + super(iRobot); + + recipe = iRecipe; + } + + @Override + public void start () { + requirements = tryCraft(false); + + if (requirements == null) { + terminate (); + return; + } + } + + @Override + public void update() { + + // [1] look for a crafting order + // -- if none, clear temporary item blacklist and sleep + // [2] look and fetch items needed to craft (problem with 9 slots inv?) + // -- allow upgrades!! And to show contents + // -- if can't be done, add item to temporary blacklist, drop inv either + // -- in a inventory accepting items or drop in the world, then look for + // -- another order + // [3] look and goto a station next to a workbench, craft + // -- if not, sleep + + if (craftingTimer > 0) { + craftingTimer--; + + if (craftingTimer == 0) { + craft(); + terminate(); + } + } else if (requirements.size() > 0) { + startDelegateAI(new AIRobotGotoStationToLoad(robot, new ReqStackFilter(), robot.getZoneToWork())); + } else { + startDelegateAI(new AIRobotSearchAndGotoStation(robot, new StationWorkbenchFilter(), robot.getZoneToWork())); + } + } + + @Override + public void delegateAIEnded(AIRobot ai) { + if (ai instanceof AIRobotGotoStationToLoad) { + if (ai.success()) { + startDelegateAI(new AIRobotLoad(robot, new ReqStackFilter(), 1)); + } else { + terminate(); + } + } else if (ai instanceof AIRobotLoad) { + requirements = tryCraft(false); + } else if (ai instanceof AIRobotSearchAndGotoStation) { + if (new StationWorkbenchFilter().matches((DockingStation) robot.getDockingStation())) { + craftingTimer = 40; + } else { + terminate(); + } + } + } + + @Override + protected ArrayList tryCraft(boolean doRemove) { + Object[] items = new Object[0]; + + if (recipe instanceof ShapedRecipes) { + items = ((ShapedRecipes) recipe).recipeItems; + } else if (recipe instanceof ShapelessRecipes) { + items = ((ShapelessRecipes) recipe).recipeItems.toArray(); + } else if (recipe instanceof ShapedOreRecipe) { + items = ((ShapedOreRecipe) recipe).getInput(); + } else if (recipe instanceof ShapelessOreRecipe) { + items = ((ShapelessOreRecipe) recipe).getInput().toArray(); + } + + ArrayList result = new ArrayList(); + + IInventory inv = robot; + + if (!doRemove) { + inv = new InventoryCopy(robot); + } + + InventoryCrafting invCraft = new InventoryCrafting(new Container() { + + @Override + public boolean canInteractWith(EntityPlayer player) { + // TODO Auto-generated method stub + return false; + } + }, 3, 3); + + for (int i = 0; i < items.length; ++i) { + Object tmp = items [i]; + + if (tmp == null) { + continue; + } + + int qty = 0; + ArrayStackFilter filter; + + if (tmp instanceof ItemStack) { + ItemStack stack = (ItemStack) tmp; + qty = stack.stackSize; + filter = new ArrayStackFilter(stack); + } else { + ArrayList stacks = (ArrayList) tmp; + qty = stacks.get(0).stackSize; + filter = new ArrayStackFilter(stacks.toArray(new ItemStack[stacks.size()])); + } + + for (IInvSlot s : InventoryIterator.getIterable(inv)) { + if (filter.matches(s.getStackInSlot())) { + ItemStack removed = s.decreaseStackInSlot(qty); + + qty = qty - removed.stackSize; + + if (invCraft.getStackInSlot(i) != null) { + invCraft.getStackInSlot(i).stackSize += qty; + } else { + invCraft.setInventorySlotContents(i, removed); + } + + invCraft.setInventorySlotContents(i, removed); + + if (removed.stackSize == 0) { + break; + } + } + } + + if (qty > 0) { + result.add(filter); + } + } + + if (result.size() == 0 && doRemove) { + output = recipe.getCraftingResult(invCraft); + } + + return result; + } + + private void craft() { + if (tryCraft(true).size() == 0 && output != null) { + crafted = true; + + ITransactor transactor = Transactor.getTransactorFor(robot); + + transactor.add(output, ForgeDirection.UNKNOWN, true); + } + } + + private class ReqStackFilter implements IStackFilter { + @Override + public boolean matches(ItemStack stack) { + for (ArrayStackFilter s : requirements) { + if (s.matches(stack)) { + return true; + } + } + + return false; + } + } + + private class StationWorkbenchFilter implements IStationFilter { + + @Override + public boolean matches(DockingStation station) { + for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { + Block nearbyBlock = robot.worldObj.getBlock(station.x() + dir.offsetX, station.y() + + dir.offsetY, station.z() + + dir.offsetZ); + + if (nearbyBlock instanceof BlockWorkbench) { + return true; + } + } + + return false; + } + } +} diff --git a/common/buildcraft/core/robots/AIRobotGotoStationToLoad.java b/common/buildcraft/core/robots/AIRobotGotoStationToLoad.java index 342f0b53..a39b0f01 100755 --- a/common/buildcraft/core/robots/AIRobotGotoStationToLoad.java +++ b/common/buildcraft/core/robots/AIRobotGotoStationToLoad.java @@ -27,8 +27,7 @@ import buildcraft.transport.gates.ActionSlot; public class AIRobotGotoStationToLoad extends AIRobot { - public boolean found = false; - + private boolean found = false; private IStackFilter filter; private IZone zone; @@ -57,6 +56,11 @@ public class AIRobotGotoStationToLoad extends AIRobot { } } + @Override + public boolean success() { + return found; + } + private class StationFilter implements IStationFilter { @Override diff --git a/common/buildcraft/core/robots/AIRobotGotoStationToUnload.java b/common/buildcraft/core/robots/AIRobotGotoStationToUnload.java index f9963c1b..8db9730c 100755 --- a/common/buildcraft/core/robots/AIRobotGotoStationToUnload.java +++ b/common/buildcraft/core/robots/AIRobotGotoStationToUnload.java @@ -22,8 +22,7 @@ import buildcraft.transport.gates.ActionSlot; public class AIRobotGotoStationToUnload extends AIRobot { - public boolean found = false; - + private boolean found = false; private IZone zone; public AIRobotGotoStationToUnload(EntityRobotBase iRobot) { @@ -51,6 +50,11 @@ public class AIRobotGotoStationToUnload extends AIRobot { } } + @Override + public boolean success() { + return found; + } + private class StationInventory implements IStationFilter { @Override public boolean matches(DockingStation station) { diff --git a/common/buildcraft/core/robots/boards/BoardRobotBuilder.java b/common/buildcraft/core/robots/boards/BoardRobotBuilder.java index bd6925c4..c257d0a8 100755 --- a/common/buildcraft/core/robots/boards/BoardRobotBuilder.java +++ b/common/buildcraft/core/robots/boards/BoardRobotBuilder.java @@ -123,7 +123,7 @@ public class BoardRobotBuilder extends RedstoneBoardRobot { @Override public void delegateAIEnded(AIRobot ai) { if (ai instanceof AIRobotGotoStationToLoad) { - if (((AIRobotGotoStationToLoad) ai).found) { + if (ai.success()) { startDelegateAI(new AIRobotLoad(robot, new ArrayStackFilter(requirementsToLookFor.getFirst()), requirementsToLookFor.getFirst().stackSize)); } else { diff --git a/common/buildcraft/core/robots/boards/BoardRobotCarrier.java b/common/buildcraft/core/robots/boards/BoardRobotCarrier.java index 8890335e..e9c55161 100755 --- a/common/buildcraft/core/robots/boards/BoardRobotCarrier.java +++ b/common/buildcraft/core/robots/boards/BoardRobotCarrier.java @@ -46,7 +46,7 @@ public class BoardRobotCarrier extends RedstoneBoardRobot { @Override public void delegateAIEnded(AIRobot ai) { if (ai instanceof AIRobotGotoStationToLoad) { - if (((AIRobotGotoStationToLoad) ai).found) { + if (ai.success()) { loadFound = true; startDelegateAI(new AIRobotLoad(robot, ActionRobotFilter.getGateFilter(robot .getLinkedStation()))); @@ -60,7 +60,7 @@ public class BoardRobotCarrier extends RedstoneBoardRobot { } } } else if (ai instanceof AIRobotGotoStationToUnload) { - if (((AIRobotGotoStationToUnload) ai).found) { + if (ai.success()) { unloadFound = true; startDelegateAI(new AIRobotUnload(robot)); } else { diff --git a/common/buildcraft/core/robots/boards/BoardRobotCrafter.java b/common/buildcraft/core/robots/boards/BoardRobotCrafter.java index 2bf021e6..5a855961 100755 --- a/common/buildcraft/core/robots/boards/BoardRobotCrafter.java +++ b/common/buildcraft/core/robots/boards/BoardRobotCrafter.java @@ -11,42 +11,30 @@ package buildcraft.core.robots.boards; import java.util.ArrayList; import java.util.HashSet; -import net.minecraft.block.Block; -import net.minecraft.block.BlockWorkbench; -import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.ShapedRecipes; import net.minecraft.item.crafting.ShapelessRecipes; -import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.oredict.ShapedOreRecipe; import net.minecraftforge.oredict.ShapelessOreRecipe; import buildcraft.api.boards.RedstoneBoardRobot; import buildcraft.api.boards.RedstoneBoardRobotNBT; -import buildcraft.api.core.IInvSlot; import buildcraft.api.gates.ActionParameterItemStack; import buildcraft.api.gates.IActionParameter; import buildcraft.api.robots.AIRobot; import buildcraft.api.robots.EntityRobotBase; import buildcraft.api.robots.IDockingStation; -import buildcraft.core.inventory.ITransactor; -import buildcraft.core.inventory.InventoryCopy; -import buildcraft.core.inventory.InventoryIterator; import buildcraft.core.inventory.StackHelper; -import buildcraft.core.inventory.Transactor; -import buildcraft.core.inventory.filters.ArrayStackFilter; -import buildcraft.core.inventory.filters.IStackFilter; -import buildcraft.core.robots.AIRobotGotoStationToLoad; +import buildcraft.core.robots.AIRobotCraftGeneric; +import buildcraft.core.robots.AIRobotCraftWorkbench; +import buildcraft.core.robots.AIRobotGotoSleep; import buildcraft.core.robots.AIRobotGotoStationToUnload; -import buildcraft.core.robots.AIRobotLoad; -import buildcraft.core.robots.AIRobotSearchAndGotoStation; import buildcraft.core.robots.AIRobotSleep; import buildcraft.core.robots.AIRobotUnload; import buildcraft.core.robots.DockingStation; -import buildcraft.core.robots.IStationFilter; import buildcraft.silicon.statements.ActionRobotCraft; import buildcraft.transport.gates.ActionIterator; import buildcraft.transport.gates.ActionSlot; @@ -56,9 +44,6 @@ public class BoardRobotCrafter extends RedstoneBoardRobot { private ItemStack order; private ArrayList craftingBlacklist = new ArrayList(); private HashSet reservedStations = new HashSet(); - private ArrayList requirements = new ArrayList(); - private IRecipe recipe; - private int craftingTimer = 0; public BoardRobotCrafter(EntityRobotBase iRobot) { super(iRobot); @@ -71,146 +56,68 @@ public class BoardRobotCrafter extends RedstoneBoardRobot { @Override public void update() { + if (robot.containsItems()) { + // Always makes sure that when starting a craft, the inventory is + // clean. - // [1] look for a crafting order - // -- if none, clear temporary item blacklist and sleep - // [2] look and fetch items needed to craft (problem with 9 slots inv?) - // -- if can't be done, add item to temporary blacklist, drop inv either - // -- in a inventory accepting items or drop in the world, then look for - // -- another order - // [3] look and goto a station next to a workbench, craft - // -- if not, sleep - // [4] drop the crafting item where possible - // -- if not, sleep - - if (craftingTimer > 0) { - craftingTimer--; - - if (craftingTimer == 0) { - craft(); - startDelegateAI(new AIRobotGotoStationToUnload(robot, robot.getZoneToWork())); - } - } else if (order == null) { - order = getCraftingOrder(); - - if (order == null) { - craftingBlacklist.clear(); - startDelegateAI(new AIRobotSleep(robot)); - return; - } - - recipe = lookForRecipe(order); - - if (recipe == null) { - craftingBlacklist.add(order); - order = null; - return; - } - - requirements = tryCraft(false); - - if (requirements == null) { - craftingBlacklist.add(order); - order = null; - return; - } - } else if (requirements.size() > 0) { - startDelegateAI(new AIRobotGotoStationToLoad(robot, new ReqStackFilter(), robot.getZoneToWork())); - } else { - startDelegateAI(new AIRobotSearchAndGotoStation(robot, new StationWorkbenchFilter(), robot.getZoneToWork())); + // TODO: We should call load or drop, in order to clean items even + // if no destination is to be found + startDelegateAI(new AIRobotGotoStationToUnload(robot, robot.getZoneToWork())); + return; } + + order = getCraftingOrder(); + + if (order == null) { + craftingBlacklist.clear(); + startDelegateAI(new AIRobotSleep(robot)); + return; + } + + IRecipe recipe = lookForWorkbenchRecipe(order); + + if (recipe != null) { + startDelegateAI(new AIRobotCraftWorkbench(robot, recipe)); + return; + } + + /* + * if (hasFurnaceRecipe(order)) { startDelegateAI(new + * AIRobotCraftFurnace(robot)); } + * + * recipe = lookForAssemblyTableRecipe(order); + * + * if (recipe != null) { startDelegateAI(new + * AIRobotCraftAssemblyTable(robot)); } + * + * recipe = lookForIntegrationTableRecipe(order); + * + * if (recipe != null) { startDelegateAI(new + * AIRobotCraftIntegrationTable(robot)); } + */ + + craftingBlacklist.add(order); } @Override public void delegateAIEnded(AIRobot ai) { - if (ai instanceof AIRobotGotoStationToLoad) { - if (((AIRobotGotoStationToLoad) ai).found) { - startDelegateAI(new AIRobotLoad(robot, new ReqStackFilter(), 1)); - } else { + if (ai instanceof AIRobotCraftGeneric) { + if (!ai.success()) { craftingBlacklist.add(order); - order = null; - requirements.clear(); - - // drop items in inventory. - startDelegateAI(new AIRobotGotoStationToUnload(robot, robot.getZoneToWork())); + } else { + // The extra crafted items may make some crafting possible + craftingBlacklist.clear(); } - } else if (ai instanceof AIRobotLoad) { - // Check requirements v.s. contents - - requirements = tryCraft(false); } else if (ai instanceof AIRobotGotoStationToUnload) { - if (((AIRobotGotoStationToUnload) ai).found) { + if (ai.success()) { startDelegateAI(new AIRobotUnload(robot)); - } - } else if (ai instanceof AIRobotSearchAndGotoStation) { - if (new StationWorkbenchFilter().matches((DockingStation) robot.getDockingStation())) { - craftingTimer = 40; } else { - startDelegateAI(new AIRobotSleep(robot)); + startDelegateAI(new AIRobotGotoSleep(robot)); } } } - private ArrayList tryCraft(boolean doRemove) { - Object[] items = new Object[0]; - - if (recipe instanceof ShapedRecipes) { - items = ((ShapedRecipes) recipe).recipeItems; - } else if (recipe instanceof ShapelessRecipes) { - items = ((ShapelessRecipes) recipe).recipeItems.toArray(); - } else if (recipe instanceof ShapedOreRecipe) { - items = ((ShapedOreRecipe) recipe).getInput(); - } else if (recipe instanceof ShapelessOreRecipe) { - items = ((ShapelessOreRecipe) recipe).getInput().toArray(); - } - - ArrayList result = new ArrayList(); - - IInventory inv = robot; - - if (!doRemove) { - inv = new InventoryCopy(robot); - } - - for (Object tmp : items) { - if (tmp == null) { - continue; - } - - int qty = 0; - ArrayStackFilter filter; - - if (tmp instanceof ItemStack) { - ItemStack stack = (ItemStack) tmp; - qty = stack.stackSize; - filter = new ArrayStackFilter(stack); - } else { - ArrayList stacks = (ArrayList) tmp; - qty = stacks.get(0).stackSize; - filter = new ArrayStackFilter(stacks.toArray(new ItemStack[stacks.size()])); - } - - for (IInvSlot s : InventoryIterator.getIterable(inv)) { - if (filter.matches(s.getStackInSlot())) { - ItemStack removed = s.decreaseStackInSlot(qty); - - qty = qty - removed.stackSize; - - if (removed.stackSize == 0) { - break; - } - } - } - - if (qty > 0) { - result.add(filter); - } - } - - return result; - } - - private IRecipe lookForRecipe(ItemStack order) { + private IRecipe lookForWorkbenchRecipe(ItemStack order) { for (Object o : CraftingManager.getInstance().getRecipeList()) { IRecipe r = (IRecipe) o; @@ -264,58 +171,4 @@ public class BoardRobotCrafter extends RedstoneBoardRobot { return null; } - private void craft() { - if (tryCraft(true).size() == 0) { - ITransactor transactor = Transactor.getTransactorFor(robot); - - transactor.add(order, ForgeDirection.UNKNOWN, true); - } - - order = null; - recipe = null; - } - - private ArrayStackFilter getStackFilter(Object o) { - if (o instanceof ArrayList) { - return new ArrayStackFilter((ItemStack[]) ((ArrayList) o).toArray()); - } else if (o instanceof ItemStack) { - return new ArrayStackFilter((ItemStack) o); - } else { - return null; - } - } - - private class ReqStackFilter implements IStackFilter { - - @Override - public boolean matches(ItemStack stack) { - for (ArrayStackFilter s : requirements) { - if (s.matches(stack)) { - return true; - } - } - - return false; - } - } - - private class StationWorkbenchFilter implements IStationFilter { - - @Override - public boolean matches(DockingStation station) { - for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { - Block nearbyBlock = robot.worldObj.getBlock(station.x() + dir.offsetX, station.y() - + dir.offsetY, station.z() - + dir.offsetZ); - - if (nearbyBlock instanceof BlockWorkbench) { - return true; - } - } - - return false; - } - - } - }