2014-11-14 12:02:52 +01:00
|
|
|
/*
|
|
|
|
* This file is part of Applied Energistics 2.
|
|
|
|
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
|
|
|
|
*
|
|
|
|
* Applied Energistics 2 is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Applied Energistics 2 is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
|
|
|
*/
|
|
|
|
|
2014-06-16 07:48:43 +02:00
|
|
|
package appeng.crafting;
|
|
|
|
|
2022-12-02 17:40:47 +01:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
2014-11-28 04:36:46 +01:00
|
|
|
|
2014-06-19 08:28:45 +02:00
|
|
|
import appeng.api.AEApi;
|
2014-06-16 07:48:43 +02:00
|
|
|
import appeng.api.config.Actionable;
|
2014-06-17 05:36:35 +02:00
|
|
|
import appeng.api.config.FuzzyMode;
|
2014-07-03 06:48:18 +02:00
|
|
|
import appeng.api.networking.crafting.ICraftingGrid;
|
2014-06-16 07:48:43 +02:00
|
|
|
import appeng.api.networking.crafting.ICraftingPatternDetails;
|
|
|
|
import appeng.api.networking.security.BaseActionSource;
|
|
|
|
import appeng.api.storage.data.IAEItemStack;
|
2014-06-19 08:28:45 +02:00
|
|
|
import appeng.api.storage.data.IItemList;
|
|
|
|
import appeng.me.cluster.implementations.CraftingCPUCluster;
|
2017-04-12 16:10:13 +02:00
|
|
|
import com.google.common.collect.Lists;
|
|
|
|
import net.minecraft.world.World;
|
|
|
|
|
2022-12-02 17:40:47 +01:00
|
|
|
public class CraftingTreeNode {
|
|
|
|
// what slot!
|
|
|
|
private final int slot;
|
|
|
|
private final CraftingJob job;
|
|
|
|
private final IItemList<IAEItemStack> used
|
|
|
|
= AEApi.instance().storage().createItemList();
|
|
|
|
// parent node.
|
|
|
|
private final CraftingTreeProcess parent;
|
|
|
|
private final World world;
|
|
|
|
// what item is this?
|
|
|
|
private final IAEItemStack what;
|
|
|
|
// what are the crafting patterns for this?
|
|
|
|
private final ArrayList<CraftingTreeProcess> nodes
|
|
|
|
= new ArrayList<CraftingTreeProcess>();
|
|
|
|
private int bytes = 0;
|
|
|
|
private boolean canEmit = false;
|
|
|
|
private long missing = 0;
|
|
|
|
private long howManyEmitted = 0;
|
|
|
|
private boolean exhausted = false;
|
|
|
|
|
|
|
|
private boolean sim;
|
|
|
|
|
|
|
|
public CraftingTreeNode(
|
|
|
|
final ICraftingGrid cc,
|
|
|
|
final CraftingJob job,
|
|
|
|
final IAEItemStack wat,
|
|
|
|
final CraftingTreeProcess par,
|
|
|
|
final int slot,
|
|
|
|
final int depth
|
|
|
|
) {
|
|
|
|
this.what = wat;
|
|
|
|
this.parent = par;
|
|
|
|
this.slot = slot;
|
|
|
|
this.world = job.getWorld();
|
|
|
|
this.job = job;
|
|
|
|
this.sim = false;
|
|
|
|
|
|
|
|
this.canEmit = cc.canEmitFor(this.what);
|
|
|
|
|
|
|
|
if (this.canEmit) {
|
|
|
|
return; // if you can emit for something, you can't make it with patterns.
|
|
|
|
}
|
|
|
|
|
|
|
|
for (final ICraftingPatternDetails details : cc.getCraftingFor(
|
|
|
|
this.what,
|
|
|
|
this.parent == null ? null : this.parent.details,
|
|
|
|
slot,
|
|
|
|
this.world
|
|
|
|
)) // in
|
|
|
|
// order.
|
|
|
|
{
|
|
|
|
if (this.parent == null || this.parent.notRecursive(details)) {
|
|
|
|
this.nodes.add(new CraftingTreeProcess(cc, job, details, this, depth + 1)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean notRecursive(final ICraftingPatternDetails details) {
|
|
|
|
IAEItemStack[] o = details.getCondensedOutputs();
|
|
|
|
|
|
|
|
for (final IAEItemStack i : o) {
|
|
|
|
if (i.equals(this.what)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
o = details.getCondensedInputs();
|
|
|
|
|
|
|
|
for (final IAEItemStack i : o) {
|
|
|
|
if (i.equals(this.what)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.parent == null) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.parent.notRecursive(details);
|
|
|
|
}
|
|
|
|
|
|
|
|
IAEItemStack
|
|
|
|
request(final MECraftingInventory inv, long l, final BaseActionSource src)
|
|
|
|
throws CraftBranchFailure, InterruptedException {
|
|
|
|
this.job.handlePausing();
|
|
|
|
|
|
|
|
final List<IAEItemStack> thingsUsed = new LinkedList<IAEItemStack>();
|
|
|
|
|
|
|
|
this.what.setStackSize(l);
|
|
|
|
if (this.getSlot() >= 0 && this.parent != null
|
|
|
|
&& this.parent.details.isCraftable()) {
|
|
|
|
final Collection<IAEItemStack> itemList;
|
|
|
|
final IItemList<IAEItemStack> inventoryList = inv.getItemList();
|
|
|
|
|
|
|
|
if (this.parent.details.canSubstitute()) {
|
|
|
|
itemList = inventoryList.findFuzzy(this.what, FuzzyMode.IGNORE_ALL);
|
|
|
|
} else {
|
|
|
|
itemList = Lists.newArrayList();
|
|
|
|
|
|
|
|
final IAEItemStack item = inventoryList.findPrecise(this.what);
|
|
|
|
|
|
|
|
if (item != null) {
|
|
|
|
itemList.add(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (IAEItemStack fuzz : itemList) {
|
|
|
|
if (this.parent.details.isValidItemForSlot(
|
|
|
|
this.getSlot(), fuzz.getItemStack(), this.world
|
|
|
|
)) {
|
|
|
|
fuzz = fuzz.copy();
|
|
|
|
fuzz.setStackSize(l);
|
|
|
|
|
|
|
|
final IAEItemStack available
|
|
|
|
= inv.extractItems(fuzz, Actionable.MODULATE, src);
|
|
|
|
|
|
|
|
if (available != null) {
|
|
|
|
if (!this.exhausted) {
|
|
|
|
final IAEItemStack is = this.job.checkUse(available);
|
|
|
|
|
|
|
|
if (is != null) {
|
|
|
|
thingsUsed.add(is.copy());
|
|
|
|
this.used.add(is);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.bytes += available.getStackSize();
|
|
|
|
l -= available.getStackSize();
|
|
|
|
|
|
|
|
if (l == 0) {
|
|
|
|
return available;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
final IAEItemStack available
|
|
|
|
= inv.extractItems(this.what, Actionable.MODULATE, src);
|
|
|
|
|
|
|
|
if (available != null) {
|
|
|
|
if (!this.exhausted) {
|
|
|
|
final IAEItemStack is = this.job.checkUse(available);
|
|
|
|
|
|
|
|
if (is != null) {
|
|
|
|
thingsUsed.add(is.copy());
|
|
|
|
this.used.add(is);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.bytes += available.getStackSize();
|
|
|
|
l -= available.getStackSize();
|
|
|
|
|
|
|
|
if (l == 0) {
|
|
|
|
return available;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.canEmit) {
|
|
|
|
final IAEItemStack wat = this.what.copy();
|
|
|
|
wat.setStackSize(l);
|
|
|
|
|
|
|
|
this.howManyEmitted = wat.getStackSize();
|
|
|
|
this.bytes += wat.getStackSize();
|
|
|
|
|
|
|
|
return wat;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.exhausted = true;
|
|
|
|
|
|
|
|
if (this.nodes.size() == 1) {
|
|
|
|
final CraftingTreeProcess pro = this.nodes.get(0);
|
|
|
|
|
|
|
|
while (pro.possible && l > 0) {
|
|
|
|
final IAEItemStack madeWhat = pro.getAmountCrafted(this.what);
|
|
|
|
|
|
|
|
pro.request(inv, pro.getTimes(l, madeWhat.getStackSize()), src);
|
|
|
|
|
|
|
|
madeWhat.setStackSize(l);
|
|
|
|
|
|
|
|
final IAEItemStack available
|
|
|
|
= inv.extractItems(madeWhat, Actionable.MODULATE, src);
|
|
|
|
|
|
|
|
if (available != null) {
|
|
|
|
this.bytes += available.getStackSize();
|
|
|
|
l -= available.getStackSize();
|
|
|
|
|
|
|
|
if (l <= 0) {
|
|
|
|
return available;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pro.possible = false; // ;P
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (this.nodes.size() > 1) {
|
|
|
|
for (final CraftingTreeProcess pro : this.nodes) {
|
|
|
|
try {
|
|
|
|
while (pro.possible && l > 0) {
|
|
|
|
final MECraftingInventory subInv
|
|
|
|
= new MECraftingInventory(inv, true, true, true);
|
|
|
|
pro.request(subInv, 1, src);
|
|
|
|
|
|
|
|
this.what.setStackSize(l);
|
|
|
|
final IAEItemStack available
|
|
|
|
= subInv.extractItems(this.what, Actionable.MODULATE, src);
|
|
|
|
|
|
|
|
if (available != null) {
|
|
|
|
if (!subInv.commit(src)) {
|
|
|
|
throw new CraftBranchFailure(this.what, l);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.bytes += available.getStackSize();
|
|
|
|
l -= available.getStackSize();
|
|
|
|
|
|
|
|
if (l <= 0) {
|
|
|
|
return available;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pro.possible = false; // ;P
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (final CraftBranchFailure fail) {
|
|
|
|
pro.possible = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.sim) {
|
|
|
|
this.missing += l;
|
|
|
|
this.bytes += l;
|
|
|
|
final IAEItemStack rv = this.what.copy();
|
|
|
|
rv.setStackSize(l);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (final IAEItemStack o : thingsUsed) {
|
|
|
|
this.job.refund(o.copy());
|
|
|
|
o.setStackSize(-o.getStackSize());
|
|
|
|
this.used.add(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new CraftBranchFailure(this.what, l);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dive(final CraftingJob job) {
|
|
|
|
if (this.missing > 0) {
|
|
|
|
job.addMissing(this.getStack(this.missing));
|
|
|
|
}
|
|
|
|
// missing = 0;
|
|
|
|
|
|
|
|
job.addBytes(8 + this.bytes);
|
|
|
|
|
|
|
|
for (final CraftingTreeProcess pro : this.nodes) {
|
|
|
|
pro.dive(job);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IAEItemStack getStack(final long size) {
|
|
|
|
final IAEItemStack is = this.what.copy();
|
|
|
|
is.setStackSize(size);
|
|
|
|
return is;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setSimulate() {
|
|
|
|
this.sim = true;
|
|
|
|
this.missing = 0;
|
|
|
|
this.bytes = 0;
|
|
|
|
this.used.resetStatus();
|
|
|
|
this.exhausted = false;
|
|
|
|
|
|
|
|
for (final CraftingTreeProcess pro : this.nodes) {
|
|
|
|
pro.setSimulate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setJob(
|
|
|
|
final MECraftingInventory storage,
|
|
|
|
final CraftingCPUCluster craftingCPUCluster,
|
|
|
|
final BaseActionSource src
|
|
|
|
) throws CraftBranchFailure {
|
|
|
|
for (final IAEItemStack i : this.used) {
|
|
|
|
final IAEItemStack ex = storage.extractItems(i, Actionable.MODULATE, src);
|
|
|
|
|
|
|
|
if (ex == null || ex.getStackSize() != i.getStackSize()) {
|
|
|
|
throw new CraftBranchFailure(i, i.getStackSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
craftingCPUCluster.addStorage(ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.howManyEmitted > 0) {
|
|
|
|
final IAEItemStack i = this.what.copy();
|
|
|
|
i.setStackSize(this.howManyEmitted);
|
|
|
|
craftingCPUCluster.addEmitable(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (final CraftingTreeProcess pro : this.nodes) {
|
|
|
|
pro.setJob(storage, craftingCPUCluster, src);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void getPlan(final IItemList<IAEItemStack> plan) {
|
|
|
|
if (this.missing > 0) {
|
|
|
|
final IAEItemStack o = this.what.copy();
|
|
|
|
o.setStackSize(this.missing);
|
|
|
|
plan.add(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.howManyEmitted > 0) {
|
|
|
|
final IAEItemStack i = this.what.copy();
|
|
|
|
i.setCountRequestable(this.howManyEmitted);
|
|
|
|
plan.addRequestable(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (final IAEItemStack i : this.used) {
|
|
|
|
plan.add(i.copy());
|
|
|
|
}
|
|
|
|
|
|
|
|
for (final CraftingTreeProcess pro : this.nodes) {
|
|
|
|
pro.getPlan(plan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int getSlot() {
|
|
|
|
return this.slot;
|
|
|
|
}
|
2014-06-16 07:48:43 +02:00
|
|
|
}
|