Files
Applied-Energistics-2/src/main/java/appeng/util/item/FuzzyItemVariantList.java
T
PrototypeTrousers dde87448a9 initial patch
2023-01-07 12:37:21 -03:00

207 lines
8.7 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2021, TeamAppliedEnergistics, 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>.
*/
package appeng.util.item;
import appeng.api.config.FuzzyMode;
import appeng.api.storage.data.IAEItemStack;
import appeng.integration.modules.gregtech.ToolClass;
import appeng.util.Platform;
import com.google.common.base.Preconditions;
import ic2.api.item.ICustomDamageItem;
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap;
import net.minecraft.item.ItemStack;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
/**
* This variant list is optimized for damageable items, and supports selecting durability ranges with
* {@link #findFuzzy(IAEItemStack, FuzzyMode)}.
*/
class FuzzyItemVariantList extends ItemVariantList {
static final SharedStackComparator COMPARATOR = new SharedStackComparator();
// NOTE: We only use Object as they key here so we can pass our special DamageBounds to the subMap method.
// We NEVER put any keys in this map that are not AESharedItemStacks.
private final Object2ObjectSortedMap<Object, IAEItemStack> records = new Object2ObjectAVLTreeMap<>(COMPARATOR);
@Override
public Collection<IAEItemStack> findFuzzy(final IAEItemStack filter, final FuzzyMode fuzzy) {
ItemStack itemStack = filter.getDefinition();
ItemDamageBound lowerBound = makeLowerBound(itemStack, fuzzy);
ItemDamageBound upperBound = makeUpperBound(itemStack, fuzzy);
Preconditions.checkState(lowerBound.itemDamage > upperBound.itemDamage);
return this.records.subMap(lowerBound, upperBound).values();
}
@SuppressWarnings("unchecked")
@Override
Map<AESharedItemStack, IAEItemStack> getRecords() {
// We ensure on our end that we NEVER use anything but AESharedItemStack as the key in this map
return (Map<AESharedItemStack, IAEItemStack>) (Object) this.records;
}
static class ItemDamageBound {
final int itemDamage;
public ItemDamageBound(int itemDamage) {
this.itemDamage = itemDamage;
}
}
/**
* This comparator creates a strict and total ordering over all {@link AESharedItemStack} of the same item. To
* support selecting ranges of durability, it is defined for type {@link Object} and also accepts
* {@link ItemDamageBound} as an argument to compare against.
*/
static class SharedStackComparator implements Comparator<Object> {
@Override
public int compare(Object a, Object b) {
// Either argument can either be a damage bound or a shared item stack
// Since we never put damage bounds into the map as keys, only one
// of the two arguments can possibly be a bound
ItemDamageBound boundA = null;
AESharedItemStack stackA = null;
int itemDamageA;
if (a instanceof ItemDamageBound) {
boundA = (ItemDamageBound) a;
itemDamageA = boundA.itemDamage;
} else {
stackA = (AESharedItemStack) a;
itemDamageA = stackA.getItemDamage();
}
ItemDamageBound boundB = null;
AESharedItemStack stackB = null;
int itemDamageB;
if (b instanceof ItemDamageBound) {
boundB = (ItemDamageBound) b;
itemDamageB = boundB.itemDamage;
} else {
stackB = (AESharedItemStack) b;
itemDamageB = stackB.getItemDamage();
}
// When either argument is a damage bound, we just compare the damage values because it is used
// only to get a certain damage range out of the map.
if (boundA != null || boundB != null) {
return Integer.compare(itemDamageB, itemDamageA);
}
ItemStack itemStackA = stackA.getDefinition();
ItemStack itemStackB = stackB.getDefinition();
Preconditions.checkState(itemStackA.getCount() == 1, "ItemStack#getCount() has to be 1");
Preconditions.checkArgument(itemStackB.getCount() == 1, "ItemStack#getCount() has to be 1");
if (itemStackA == itemStackB) {
return 0;
}
// Damaged items are sorted before undamaged items
final int damageValue = Integer.compare(itemDamageB, itemDamageA);
if (damageValue != 0) {
return damageValue;
}
// As a final tie breaker, order by the object identity of the item stack
// While this will order seemingly at random, we only need the order of
// damage values to be predictable, while still having to satisfy the
// complete order requirements of the sorted map
return Long.compare(System.identityHashCode(itemStackA), System.identityHashCode(itemStackB));
}
}
/**
* Minecraft reverses the damage values. So anything with a damage of 0 is undamaged and increases the more damaged
* the item is.
* <p>
* Further the used subMap follows [MAX_DAMAGE, MIN_DAMAGE), so to include undamaged items, we have to start with a
* lower damage value than 0, while it is fine to use {@link ItemStack#getMaxDamage()} for the upper bound.
*/
private static final int MIN_DAMAGE_VALUE = -1;
/*
* Keep in mind that the stack order is from most damaged to least damaged, so this lower bound will actually be a
* higher number than the upper bound.
*/
static ItemDamageBound makeLowerBound(final ItemStack stack, final FuzzyMode fuzzy) {
Preconditions.checkState(stack.getItem().isDamageable() || (Platform.isGTDamageableItem(stack.getItem())), "Item#isDamageable() has to be true");
int damage;
int maxDamage;
if (Platform.isIC2DamageableItem(stack.getItem())) {
maxDamage = ((ICustomDamageItem) stack.getItem()).getMaxCustomDamage(stack);
damage = ((ICustomDamageItem) stack.getItem()).getCustomDamage(stack);
} else if (Platform.isGTDamageableItem(stack.getItem())) {
maxDamage = ToolClass.getGTMaxDamage(stack);
damage = ToolClass.getGTitemDamage(stack);
} else {
maxDamage = stack.getMaxDamage();
damage = stack.getItemDamage();
}
if (fuzzy == FuzzyMode.IGNORE_ALL) {
if (maxDamage != 0) {
damage = maxDamage;
}
} else {
final int breakpoint = fuzzy.calculateBreakPoint(maxDamage);
damage = damage <= breakpoint ? breakpoint : maxDamage;
}
return new ItemDamageBound(damage);
}
/*
* Keep in mind that the stack order is from most damaged to least damaged, so this upper bound will actually be a
* lower number than the lower bound. It also is exclusive.
*/
static ItemDamageBound makeUpperBound(final ItemStack stack, final FuzzyMode fuzzy) {
Preconditions.checkState(stack.getItem().isDamageable() || (Platform.isGTDamageableItem(stack.getItem())), "Item#isDamageable() has to be true");
int damage;
if (fuzzy == FuzzyMode.IGNORE_ALL) {
damage = MIN_DAMAGE_VALUE;
} else {
int maxDamage;
if (Platform.isIC2DamageableItem(stack.getItem())) {
maxDamage = ((ICustomDamageItem) stack.getItem()).getMaxCustomDamage(stack);
damage = ((ICustomDamageItem) stack.getItem()).getCustomDamage(stack);
} else if (Platform.isGTDamageableItem(stack.getItem())) {
maxDamage = ToolClass.getGTMaxDamage(stack);
damage = ToolClass.getGTitemDamage(stack);
} else {
maxDamage = stack.getMaxDamage();
damage = stack.getItemDamage();
}
final int breakpoint = fuzzy.calculateBreakPoint(maxDamage);
damage = damage <= breakpoint ? MIN_DAMAGE_VALUE : breakpoint;
}
return new ItemDamageBound(damage);
}
}