diff --git a/gradle/tools/opti.sh b/gradle/tools/opti.sh index e4653de63..7bd327c57 100644 --- a/gradle/tools/opti.sh +++ b/gradle/tools/opti.sh @@ -12,19 +12,26 @@ processAudio () { touch $1.tmp $LOC_FFMPEG -y -i $1 -f ogg -c:a libvorbis -b:a 120k $1.tmp & wait - rm $1 - mv $1.tmp $1 + if [ -s $1.tmp ] + then + rm $1 + mv $1.tmp $1 + else + echo "Cannot convert file, output is empty." + rm $1.tmp + return; + fi } while getopts "ia" arg; do case $arg in i) echo "Processing images..." - for f in $(find ./src/main/resources/assets/immersiveintelligence/textures -name '*.png'); do ./gradle/tools/oxipng $f --zc 9 -f 0-5 --nc --strip all; done + for f in $(find ./src/main/resources/assets/*/textures -name '*.png'); do chmod 644 "$f"; ./gradle/tools/oxipng "$f" --zc 9 -f 0-5 --nc --strip all; done ;; a) echo "Processing audio..." - for f in $(find ./src/main/resources/assets/immersiveintelligence/sounds -name '*.ogg'); do processAudio $f; done + for f in $(find ./src/main/resources/assets/*/sounds -name '*.ogg'); do processAudio $f; done ;; esac done \ No newline at end of file diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/ImmersiveIntelligence.java b/src/main/java/pl/pabilo8/immersiveintelligence/ImmersiveIntelligence.java index db1fee044..259b90e95 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/ImmersiveIntelligence.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/ImmersiveIntelligence.java @@ -20,8 +20,6 @@ import pl.pabilo8.immersiveintelligence.common.IISaveData; import pl.pabilo8.immersiveintelligence.common.commands.CommandII; import pl.pabilo8.immersiveintelligence.common.compat.IICompatModule; -import pl.pabilo8.immersiveintelligence.common.event.IEOverrideEventHandler; -import pl.pabilo8.immersiveintelligence.common.event.LightEngineerEventHandler; import pl.pabilo8.immersiveintelligence.common.util.IIReflectionUtils; import pl.pabilo8.immersiveintelligence.common.util.IISkinHandler; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler; @@ -83,7 +81,6 @@ public void preInit(FMLPreInitializationEvent event) public void init(FMLInitializationEvent event) { NetworkRegistry.INSTANCE.registerGuiHandler(INSTANCE, proxy); - new LightEngineerEventHandler().registerEventHandler(); proxy.init(event); } @@ -91,10 +88,8 @@ public void init(FMLInitializationEvent event) public void postInit(FMLPostInitializationEvent event) { proxy.postInit(event); - - //Redirecting IE event to our own + //Redirecting IE event handler to our own IIReflectionUtils.getForgeEventListeners(); - IIReflectionUtils.overrideEventHandler(blusunrize.immersiveengineering.common.EventHandler.class, new IEOverrideEventHandler()); } @Mod.EventHandler diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/CorrosionHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/CorrosionHandler.java deleted file mode 100644 index 1d3877090..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/CorrosionHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package pl.pabilo8.immersiveintelligence.api; - -import net.minecraft.item.ItemStack; -import net.minecraftforge.items.ItemHandlerHelper; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Pabilo8 (pabilo@iiteam.net) - * @since 24.05.2019 - */ -public class CorrosionHandler -{ - - static List corrosionBlacklist = new ArrayList<>(); - - public static boolean canCorrode(ItemStack stack) - { - if(stack.getItem() instanceof ICorrosionProtectionEquipment) - return ((ICorrosionProtectionEquipment)stack.getItem()).canCorrode(stack); - return corrosionBlacklist.stream().noneMatch(stack1 -> ItemHandlerHelper.canItemStacksStack(stack, stack1)); - } - - public static void addItemToBlacklist(ItemStack stack) - { - corrosionBlacklist.add(stack); - } - - public interface ICorrosionProtectionEquipment - { - boolean canCorrode(ItemStack stack); - } - - public interface IAcidProtectionEquipment - { - boolean protectsFromAcid(ItemStack stack); - } -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/LogisticTag.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/LogisticTag.java index 693584d61..80080540f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/LogisticTag.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/LogisticTag.java @@ -7,6 +7,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.common.util.INBTSerializable; +import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.lwjgl.input.Keyboard; @@ -99,9 +100,13 @@ else if(packet.has('s')) //Owner try { - IIDataHandlingUtils.optionalString('o', packet) - .map(UUID::fromString) - .ifPresent(uuid -> this.owner = uuid); + if(packet.has('o')) + { + DiplomacyHandler instance = DiplomacyHandler.getInstance(FMLCommonHandler.instance().getEffectiveSide()==Side.CLIENT); + IIDataHandlingUtils.optionalString('o', packet) + .map(instance::getIdentityByName) + .ifPresent(uuid -> this.owner = uuid.getUUID()); + } } catch(IllegalArgumentException ignored) {} //Color (Paint) IIDataHandlingUtils.optionalColor('p', packet) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/PenetrationRegistry.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/PenetrationRegistry.java index 86746ab1b..8d5617a60 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/PenetrationRegistry.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/PenetrationRegistry.java @@ -72,7 +72,7 @@ public static void init() //Bedrock registerState(state -> state.getBlock().blockHardness==-1, new PenetrationHandlerInvulnerable(PenetrationHardness.BEDROCK, Integer.MAX_VALUE)); //Fluids - registerState(state -> state.getBlock().blockHardness==-1, new PenetrationHandlerInvulnerable(PenetrationHardness.FOLIAGE, 0f)); + registerState(state -> state.getMaterial().isLiquid(), new PenetrationHandlerInvulnerable(PenetrationHardness.FOLIAGE, 0f)); //Fragile metals registerMetalMaterial(PenetrationHandlerMetal.create("aluminum", PenetrationHardness.FRAGILE, 1.0f, 150f)); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/IIAmmoUtils.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/IIAmmoUtils.java index e7e4209bc..9e2fc6f21 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/IIAmmoUtils.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/IIAmmoUtils.java @@ -96,21 +96,21 @@ public static void suppress(World world, double posX, double posY, double posZ, } } - public static void breakArmour(Entity entity, int damageToArmour) + public static void breakArmor(Entity entity, int damageToArmor) { if(entity instanceof EntityLivingBase) { EntityLivingBase ent = (EntityLivingBase)entity; PotionEffect effect = ent.getActivePotionEffect(IIPotions.brokenArmor); if(effect==null) - effect = new PotionEffect(IIPotions.brokenArmor, 60, damageToArmour, false, false); + effect = new PotionEffect(IIPotions.brokenArmor, 60, damageToArmor, false, false); else { effect.duration = 10; - effect.combine(new PotionEffect(IIPotions.brokenArmor, 60, Math.min(255, effect.getAmplifier()+damageToArmour))); + effect.combine(new PotionEffect(IIPotions.brokenArmor, 60, Math.min(255, effect.getAmplifier()+damageToArmor))); } for(ItemStack stack : ent.getArmorInventoryList()) - stack.damageItem(damageToArmour, ent); + stack.damageItem(damageToArmor, ent); ent.addPotionEffect(effect); } @@ -493,7 +493,7 @@ public static float getCombinedDepth(IAmmoType ammoType, CoreType coreType * @return amount of blocks penetrated by the ammo */ public static int getPenetratedAmount(IAmmoType ammoType, AmmoCore coreMaterial, CoreType coreType, - IPenetrationHandler penHandler, PenetrationHardness blockHardness) + IPenetrationHandler penHandler, PenetrationHardness blockHardness) { float penetrationDepth = getCombinedDepth(ammoType, coreType); PenetrationHardness ammoHardness = getCombinedHardness(coreMaterial, coreType); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/PenetrationCache.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/PenetrationCache.java index c4d2dee5d..cb914272d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/PenetrationCache.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/ammo/utils/PenetrationCache.java @@ -1,5 +1,6 @@ package pl.pabilo8.immersiveintelligence.api.ammo.utils; +import blusunrize.immersiveengineering.common.blocks.TileEntityMultiblockPart; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; @@ -8,6 +9,10 @@ import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Graphics; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageBlockDamageSync; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; +import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IDamageResistantMultiblock; +import pl.pabilo8.immersiveintelligence.common.util.multiblock.TileEntityMultiblockIIBase; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIBase; import java.util.ArrayList; import java.util.List; @@ -61,26 +66,53 @@ public static void dealBlockDamage(World world, Vec3d direction, float bulletDam return; DamageBlockPos dimensionBlockPos = new DamageBlockPos(pos, world, pen.getIntegrity()); - float newHp = getBlockHitpoints(pen, pos, world)-(bulletDamage*pen.getThickness()); - if(newHp > 0) + if(world.getTileEntity(pos) instanceof IDamageResistantMultiblock) { - List list = blockDamage.stream().filter(damageBlockPos -> damageBlockPos.equals(dimensionBlockPos)).collect(Collectors.toList()); - if(!list.isEmpty()) - list.forEach(damageBlockPos -> damageBlockPos.damage = newHp); - else - blockDamage.add(new DamageBlockPos(dimensionBlockPos, newHp)); - - IIPacketHandler.sendToClient(dimensionBlockPos, world, - new MessageBlockDamageSync(new DamageBlockPos(dimensionBlockPos, newHp/(pen.getIntegrity()/pen.getThickness())), direction)); + //Damage a multiblock + IDamageResistantMultiblock mb = (IDamageResistantMultiblock)world.getTileEntity(pos); + if(mb instanceof TileEntityMultiblockPart) + mb = (IDamageResistantMultiblock)((TileEntityMultiblockPart)mb).master(); + if(mb!=null) + { + if(mb.damageHealth(bulletDamage*pen.getThickness())) + { + world.getBlockState(pos).getBlock().breakBlock(world, pos, world.getBlockState(pos)); + world.destroyBlock(dimensionBlockPos, false); + } + else + { + if(mb instanceof TileEntityMultiblockIIBase) + ((TileEntityMultiblockIIBase)mb).updateTileForEvent(SyncEvents.TILE_DAMAGED); + else if(mb instanceof TileEntityIIBase) + ((TileEntityIIBase)mb).updateTileForEvent(SyncEvents.TILE_DAMAGED); + } + } } - else if(newHp <= 0) + else { - blockDamage.removeIf(damageBlockPos -> damageBlockPos.equals(dimensionBlockPos)); - world.getBlockState(pos).getBlock().breakBlock(world, pos, world.getBlockState(pos)); - world.destroyBlock(dimensionBlockPos, false); + //Proceed with block breaking + float newHp = getBlockHitpoints(pen, pos, world)-(bulletDamage*pen.getThickness()); + if(newHp > 0) + { + List list = blockDamage.stream().filter(damageBlockPos -> damageBlockPos.equals(dimensionBlockPos)).collect(Collectors.toList()); + if(!list.isEmpty()) + list.forEach(damageBlockPos -> damageBlockPos.damage = newHp); + else + blockDamage.add(new DamageBlockPos(dimensionBlockPos, newHp)); + + IIPacketHandler.sendToClient(dimensionBlockPos, world, + new MessageBlockDamageSync(new DamageBlockPos(dimensionBlockPos, newHp/(pen.getIntegrity()/pen.getThickness())), direction)); + } + else if(newHp <= 0) + { + blockDamage.removeIf(damageBlockPos -> damageBlockPos.equals(dimensionBlockPos)); + world.getBlockState(pos).getBlock().breakBlock(world, pos, world.getBlockState(pos)); + world.destroyBlock(dimensionBlockPos, false); - IIPacketHandler.sendToClient(dimensionBlockPos, world, - new MessageBlockDamageSync(new DamageBlockPos(dimensionBlockPos, newHp/(pen.getIntegrity()/pen.getThickness())), direction)); + IIPacketHandler.sendToClient(dimensionBlockPos, world, + new MessageBlockDamageSync(new DamageBlockPos(dimensionBlockPos, newHp/(pen.getIntegrity()/pen.getThickness())), direction)); + } } + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/CorrosionHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/CorrosionHandler.java new file mode 100644 index 000000000..b73e99372 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/CorrosionHandler.java @@ -0,0 +1,41 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.items.ItemHandlerHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Registry and capability query for item corrosion. + * + * @since 24.05.2019 + */ +public final class CorrosionHandler +{ + private static final List CORROSION_BLACKLIST = new ArrayList<>(); + + private CorrosionHandler() + { + } + + public static boolean canCorrode(ItemStack stack) + { + if(stack.isEmpty()||ProtectionHandler.isProtectedFromCorrosion(stack)) + return false; + return CORROSION_BLACKLIST.stream().noneMatch(blacklisted -> + ItemHandlerHelper.canItemStacksStack(stack, blacklisted)); + } + + public static void addItemToBlacklist(ItemStack stack) + { + if(!stack.isEmpty()) + CORROSION_BLACKLIST.add(stack.copy()); + } + + public static List getCorrosionBlacklist() + { + return Collections.unmodifiableList(CORROSION_BLACKLIST); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/ProtectionHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/ProtectionHandler.java new file mode 100644 index 000000000..bb7bc20c2 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/ProtectionHandler.java @@ -0,0 +1,90 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.capabilities.Capability; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.*; + +import javax.annotation.Nullable; +import java.util.function.Predicate; + +/** + * Central capability-based handler for radiation, gas, infrared and acid protective equipment. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 22.07.2026 + */ +public final class ProtectionHandler +{ + private ProtectionHandler() + { + } + + public static boolean isProtectedFromGas(EntityLivingBase entity) + { + return hasPartialArmorProtection(entity, ProtectionCapabilities.GAS_PROTECTION, + IGasProtection::protectsFromGases); + } + + public static boolean isInvisibleToInfrared(EntityLivingBase entity) + { + return hasPartialArmorProtection(entity, ProtectionCapabilities.INFRARED_PROTECTION, + IInfraredProtection::isInvisibleToInfrared); + } + + public static boolean isProtectedFromAcid(EntityLivingBase entity) + { + return hasCompleteArmorProtection(entity, ProtectionCapabilities.ACID_PROTECTION, + IAcidProtection::protectsFromAcid); + } + + public static boolean isProtectedFromRadiation(EntityLivingBase entity) + { + return hasCompleteArmorProtection(entity, ProtectionCapabilities.RADIATION_PROTECTION, + IRadiationProtection::protectsFromRadiation); + } + + public static boolean isProtectedFromCorrosion(ItemStack stack) + { + return test(stack, ProtectionCapabilities.CORROSION_PROTECTION, ICorrosionProtection::protectsFromCorrosion); + } + + private static boolean hasPartialArmorProtection(EntityLivingBase entity, @Nullable Capability capability, + Predicate predicate) + { + //Check for the entity itself + if(capability!=null&&entity.hasCapability(capability, null) + &&predicate.test(entity.getCapability(capability, null))) + return true; + + //Check for armor pieces + for(ItemStack stack : entity.getArmorInventoryList()) + if(test(stack, capability, predicate)) + return true; + return false; + } + + private static boolean hasCompleteArmorProtection(EntityLivingBase entity, @Nullable Capability capability, + Predicate predicate) + { + //Check for the entity itself + if(capability!=null&&entity.hasCapability(capability, null) + &&predicate.test(entity.getCapability(capability, null))) + return true; + + //Check for armor pieces + for(ItemStack stack : entity.getArmorInventoryList()) + if(!test(stack, capability, predicate)) + return false; + return true; + } + + private static boolean test(ItemStack stack, @Nullable Capability capability, Predicate predicate) + { + if(stack.isEmpty()||capability==null||!stack.hasCapability(capability, null)) + return false; + T value = stack.getCapability(capability, null); + return value!=null&&predicate.test(value); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationCenter.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationCenter.java new file mode 100644 index 000000000..d009ba61b --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationCenter.java @@ -0,0 +1,100 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.common.util.INBTSerializable; + +import javax.annotation.Nonnull; + +/** + * A persistent point source of radiation. Strength falls off linearly to zero at the radius edge. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 22.07.2026 + */ +public class RadiationCenter implements INBTSerializable +{ + private int dimension; + @Nonnull + private BlockPos position = BlockPos.ORIGIN; + private float radius; + private float strength; + + public RadiationCenter() + { + } + + public RadiationCenter(int dimension, @Nonnull BlockPos position, float radius, float strength) + { + this.dimension = dimension; + this.position = position.toImmutable(); + this.radius = Math.max(0, radius); + this.strength = Math.max(0, strength); + } + + public int getDimension() + { + return dimension; + } + + @Nonnull + public BlockPos getPosition() + { + return position; + } + + public float getRadius() + { + return radius; + } + + public float getStrength() + { + return strength; + } + + void setRadius(float radius) + { + this.radius = Math.max(0, radius); + } + + void setStrength(float strength) + { + this.strength = Math.max(0, strength); + } + + public float getRadiationAt(@Nonnull Vec3d point) + { + if(radius <= 0||strength <= 0) + return 0; + + Vec3d center = new Vec3d(position).addVector(0.5, 0.5, 0.5); + double distanceSq = center.squareDistanceTo(point); + double radiusSq = radius*radius; + if(distanceSq >= radiusSq) + return 0; + return strength*(1f-(float)(Math.sqrt(distanceSq)/radius)); + } + + @Override + public NBTTagCompound serializeNBT() + { + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setInteger("dimension", dimension); + nbt.setLong("position", position.toLong()); + nbt.setFloat("radius", radius); + nbt.setFloat("strength", strength); + return nbt; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) + { + dimension = nbt.getInteger("dimension"); + position = BlockPos.fromLong(nbt.getLong("position")); + radius = Math.max(0, nbt.getFloat("radius")); + strength = Math.max(0, nbt.getFloat("strength")); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationEmitter.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationEmitter.java new file mode 100644 index 000000000..2a3fab197 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationEmitter.java @@ -0,0 +1,83 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.INBTSerializable; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.IRadiationEmitter; + +/** + * Radiation emitter implementation for tile entities and entities. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 22.07.2026 + */ +public class RadiationEmitter implements IRadiationEmitter, INBTSerializable +{ + private float radius; + private float strength; + private boolean active = true; + + public RadiationEmitter() + { + } + + public RadiationEmitter(float radius, float strength) + { + this.radius = Math.max(0, radius); + this.strength = Math.max(0, strength); + } + + public RadiationEmitter withRadius(float radius) + { + this.radius = Math.max(0, radius); + return this; + } + + public RadiationEmitter withStrength(float strength) + { + this.strength = Math.max(0, strength); + return this; + } + + public RadiationEmitter withActive(boolean active) + { + this.active = active; + return this; + } + + @Override + public float getRadiationRadius() + { + return radius; + } + + @Override + public float getRadiationStrength() + { + return strength; + } + + @Override + public boolean isRadiationActive() + { + return active; + } + + @Override + public NBTTagCompound serializeNBT() + { + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setFloat("radius", radius); + nbt.setFloat("strength", strength); + nbt.setBoolean("active", active); + return nbt; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) + { + radius = Math.max(0, nbt.getFloat("radius")); + strength = Math.max(0, nbt.getFloat("strength")); + active = !nbt.hasKey("active")||nbt.getBoolean("active"); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationHandler.java new file mode 100644 index 000000000..fe6bbcb24 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/RadiationHandler.java @@ -0,0 +1,394 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.potion.PotionEffect; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.INBTSerializable; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.IRadiationEmitter; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.ProtectionCapabilities; +import pl.pabilo8.immersiveintelligence.common.IIPotions; +import pl.pabilo8.immersiveintelligence.common.IISaveData; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +/** + * Registry and handler for nuclear radiation. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 22.07.2026 + */ +public final class RadiationHandler implements INBTSerializable +{ + public static final RadiationHandler INSTANCE = new RadiationHandler(); + private static final int UPDATE_INTERVAL = 40; + private static final float MINIMUM_EXPOSURE = 0.01f; + + private final Map centers = new LinkedHashMap<>(); + private final Map>> centerIndex = new HashMap<>(); + private final Map>> emitterIndex = new HashMap<>(); + + private RadiationHandler() + { + } + + //--- Persistent centres ---// + + @Nonnull + public RadiationCenter addRadiationCenter(@Nonnull World world, @Nonnull BlockPos position, float radius, float strength) + { + return addRadiationCenter(world.provider.getDimension(), position, radius, strength); + } + + @Nonnull + public RadiationCenter addRadiationCenter(int dimension, @Nonnull BlockPos position, float radius, float strength) + { + return putRadiationCenter(dimension, position, radius, strength, true); + } + + @Nonnull + public RadiationCenter addOrIncreaseRadiationCenter(@Nonnull World world, @Nonnull BlockPos position, float radius, float strength) + { + return addOrIncreaseRadiationCenter(world.provider.getDimension(), position, radius, strength); + } + + @Nonnull + public RadiationCenter addOrIncreaseRadiationCenter(int dimension, @Nonnull BlockPos position, float radius, float strength) + { + RadiationKey key = new RadiationKey(dimension, position); + RadiationCenter existing = centers.get(key); + if(existing==null) + return addRadiationCenter(dimension, position, radius, strength); + + unindex(existing); + existing.setRadius(Math.max(existing.getRadius(), radius)); + existing.setStrength(existing.getStrength()+Math.max(0, strength)); + index(existing); + IISaveData.setDirty(); + return existing; + } + + @Nullable + public RadiationCenter getRadiationCenter(@Nonnull World world, @Nonnull BlockPos position) + { + return getRadiationCenter(world.provider.getDimension(), position); + } + + @Nullable + public RadiationCenter getRadiationCenter(int dimension, @Nonnull BlockPos position) + { + return centers.get(new RadiationKey(dimension, position)); + } + + public boolean removeRadiationCenter(@Nonnull World world, @Nonnull BlockPos position) + { + return removeRadiationCenter(world.provider.getDimension(), position); + } + + public boolean removeRadiationCenter(int dimension, @Nonnull BlockPos position) + { + RadiationCenter removed = centers.remove(new RadiationKey(dimension, position)); + if(removed==null) + return false; + unindex(removed); + IISaveData.setDirty(); + return true; + } + + public boolean setRadiationStrength(@Nonnull World world, @Nonnull BlockPos position, float strength) + { + return setRadiationStrength(world.provider.getDimension(), position, strength); + } + + public boolean setRadiationStrength(int dimension, @Nonnull BlockPos position, float strength) + { + RadiationCenter center = getRadiationCenter(dimension, position); + if(center==null) + return false; + unindex(center); + center.setStrength(strength); + index(center); + IISaveData.setDirty(); + return true; + } + + public boolean modifyRadiationStrength(@Nonnull World world, @Nonnull BlockPos position, float change) + { + return modifyRadiationStrength(world.provider.getDimension(), position, change); + } + + public boolean modifyRadiationStrength(int dimension, @Nonnull BlockPos position, float change) + { + RadiationCenter center = getRadiationCenter(dimension, position); + return center!=null&&setRadiationStrength(dimension, position, center.getStrength()+change); + } + + public boolean setRadiationRadius(@Nonnull World world, @Nonnull BlockPos position, float radius) + { + return setRadiationRadius(world.provider.getDimension(), position, radius); + } + + public boolean setRadiationRadius(int dimension, @Nonnull BlockPos position, float radius) + { + RadiationCenter center = getRadiationCenter(dimension, position); + if(center==null) + return false; + unindex(center); + center.setRadius(radius); + index(center); + IISaveData.setDirty(); + return true; + } + + @Nonnull + public Collection getRadiationCenters() + { + return Collections.unmodifiableCollection(centers.values()); + } + + //--- Exposure processing ---// + + public void tick(@Nonnull World world) + { + if(world.isRemote||world.getTotalWorldTime()%UPDATE_INTERVAL!=0) + return; + + rebuildEmitterIndex(world); + if(IIPotions.radiation==null) + return; + + for(Entity entity : new ArrayList<>(world.loadedEntityList)) + { + if(!(entity instanceof EntityLivingBase)||!entity.isEntityAlive()) + continue; + EntityLivingBase living = (EntityLivingBase)entity; + if(living instanceof EntityPlayer) + { + EntityPlayer player = (EntityPlayer)living; + /*if(player.isCreative()||player.isSpectator()) + continue;*/ + } + if(ProtectionHandler.isProtectedFromRadiation(living)) + continue; + + float exposure = getRadiationAt(world, living.getPositionVector().addVector(0, living.height*0.5, 0)); + if(exposure < MINIMUM_EXPOSURE) + continue; + + int amplifier = MathHelper.clamp(MathHelper.ceil(exposure)-1, 0, 4); + living.addPotionEffect(new PotionEffect(IIPotions.radiation, UPDATE_INTERVAL*3, amplifier, false, false)); + } + } + + public float getRadiationAt(@Nonnull World world, @Nonnull BlockPos position) + { + return getRadiationAt(world, new Vec3d(position).addVector(0.5, 0.5, 0.5)); + } + + public float getRadiationAt(@Nonnull World world, @Nonnull Vec3d position) + { + int dimension = world.provider.getDimension(); + long chunk = chunkKey(MathHelper.floor(position.x)>>4, MathHelper.floor(position.z)>>4); + float radiation = 0; + + Map> persistent = centerIndex.get(dimension); + if(persistent!=null) + { + List candidates = persistent.get(chunk); + if(candidates!=null) + for(RadiationCenter center : candidates) + radiation += center.getRadiationAt(position); + } + + Map> dynamic = emitterIndex.get(dimension); + if(dynamic!=null) + { + List candidates = dynamic.get(chunk); + if(candidates!=null) + for(EmitterSource source : candidates) + radiation += source.getRadiationAt(position); + } + return radiation; + } + + public void clearEmitterIndex(@Nonnull World world) + { + emitterIndex.remove(world.provider.getDimension()); + } + + private void rebuildEmitterIndex(World world) + { + Map> index = new HashMap<>(); + emitterIndex.put(world.provider.getDimension(), index); + if(ProtectionCapabilities.RADIATION_EMITTER==null) + return; + + for(Entity entity : new ArrayList<>(world.loadedEntityList)) + if(entity.isEntityAlive()&&entity.hasCapability(ProtectionCapabilities.RADIATION_EMITTER, null)) + addEmitter(index, entity.getCapability(ProtectionCapabilities.RADIATION_EMITTER, null), + entity.getPositionVector().addVector(0, entity.height*0.5, 0)); + + for(TileEntity tile : new ArrayList<>(world.loadedTileEntityList)) + if(!tile.isInvalid()&&tile.hasCapability(ProtectionCapabilities.RADIATION_EMITTER, null)) + addEmitter(index, tile.getCapability(ProtectionCapabilities.RADIATION_EMITTER, null), + new Vec3d(tile.getPos()).addVector(0.5, 0.5, 0.5)); + } + + private void addEmitter(Map> index, @Nullable IRadiationEmitter emitter, Vec3d position) + { + if(emitter==null||!emitter.isRadiationActive()) + return; + float radius = emitter.getRadiationRadius(); + float strength = emitter.getRadiationStrength(); + if(radius <= 0||strength <= 0) + return; + + EmitterSource source = new EmitterSource(position, radius, strength); + index(index, position.x, position.z, radius, source); + } + + //--- Spatial index ---// + + @Nonnull + private RadiationCenter putRadiationCenter(int dimension, BlockPos position, float radius, float strength, boolean dirty) + { + RadiationKey key = new RadiationKey(dimension, position); + RadiationCenter previous = centers.remove(key); + if(previous!=null) + unindex(previous); + + RadiationCenter center = new RadiationCenter(dimension, position, radius, strength); + centers.put(key, center); + index(center); + if(dirty) + IISaveData.setDirty(); + return center; + } + + private void index(RadiationCenter center) + { + if(center.getRadius() <= 0||center.getStrength() <= 0) + return; + Map> index = centerIndex.computeIfAbsent(center.getDimension(), ignored -> new HashMap<>()); + index(index, center.getPosition().getX()+0.5, center.getPosition().getZ()+0.5, center.getRadius(), center); + } + + private void unindex(RadiationCenter center) + { + Map> index = centerIndex.get(center.getDimension()); + if(index==null) + return; + for(List bucket : index.values()) + bucket.remove(center); + index.values().removeIf(List::isEmpty); + if(index.isEmpty()) + centerIndex.remove(center.getDimension()); + } + + private static void index(Map> index, double x, double z, float radius, T source) + { + int minChunkX = MathHelper.floor(x-radius)>>4; + int maxChunkX = MathHelper.floor(x+radius)>>4; + int minChunkZ = MathHelper.floor(z-radius)>>4; + int maxChunkZ = MathHelper.floor(z+radius)>>4; + for(int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) + for(int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) + index.computeIfAbsent(chunkKey(chunkX, chunkZ), ignored -> new ArrayList<>()).add(source); + } + + private static long chunkKey(int x, int z) + { + return (x&0xffffffffL)|((z&0xffffffffL)<<32); + } + + //--- Persistence ---// + + @Override + public NBTTagCompound serializeNBT() + { + NBTTagList list = new NBTTagList(); + for(RadiationCenter center : centers.values()) + list.appendTag(center.serializeNBT()); + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setTag("centers", list); + return nbt; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) + { + centers.clear(); + centerIndex.clear(); + emitterIndex.clear(); + + NBTTagList list = nbt.getTagList("centers", Constants.NBT.TAG_COMPOUND); + for(int i = 0; i < list.tagCount(); i++) + { + RadiationCenter center = new RadiationCenter(); + center.deserializeNBT(list.getCompoundTagAt(i)); + putRadiationCenter(center.getDimension(), center.getPosition(), center.getRadius(), center.getStrength(), false); + } + } + + private static final class RadiationKey + { + private final int dimension; + private final BlockPos position; + + private RadiationKey(int dimension, BlockPos position) + { + this.dimension = dimension; + this.position = position.toImmutable(); + } + + @Override + public boolean equals(Object object) + { + if(this==object) + return true; + if(!(object instanceof RadiationKey)) + return false; + RadiationKey key = (RadiationKey)object; + return dimension==key.dimension&&position.equals(key.position); + } + + @Override + public int hashCode() + { + return 31*dimension+position.hashCode(); + } + } + + private static final class EmitterSource + { + private final Vec3d position; + private final float radius; + private final float strength; + + private EmitterSource(Vec3d position, float radius, float strength) + { + this.position = position; + this.radius = radius; + this.strength = strength; + } + + private float getRadiationAt(Vec3d point) + { + double distanceSq = position.squareDistanceTo(point); + if(distanceSq >= radius*radius) + return 0; + return strength*(1f-(float)(Math.sqrt(distanceSq)/radius)); + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IAcidProtection.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IAcidProtection.java new file mode 100644 index 000000000..1c2d7217e --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IAcidProtection.java @@ -0,0 +1,12 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +/** + * Capability exposed by equipment capable of shielding its wearer from acid. + * + * @since 0.3.1 + */ +@FunctionalInterface +public interface IAcidProtection +{ + boolean protectsFromAcid(); +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ICorrosionProtection.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ICorrosionProtection.java new file mode 100644 index 000000000..a8cc54bbf --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ICorrosionProtection.java @@ -0,0 +1,12 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +/** + * Capability exposed by item stacks resistant to corrosion damage. + * + * @since 0.3.1 + */ +@FunctionalInterface +public interface ICorrosionProtection +{ + boolean protectsFromCorrosion(); +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IGasProtection.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IGasProtection.java new file mode 100644 index 000000000..ce689b32b --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IGasProtection.java @@ -0,0 +1,12 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +/** + * Capability exposed by equipment capable of filtering harmful gases. + * + * @since 0.3.1 + */ +@FunctionalInterface +public interface IGasProtection +{ + boolean protectsFromGases(); +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IInfraredProtection.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IInfraredProtection.java new file mode 100644 index 000000000..82b8a66a0 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IInfraredProtection.java @@ -0,0 +1,12 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +/** + * Capability exposed by equipment which conceals its wearer from infrared sensors. + * + * @since 0.3.1 + */ +@FunctionalInterface +public interface IInfraredProtection +{ + boolean isInvisibleToInfrared(); +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IRadiationEmitter.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IRadiationEmitter.java new file mode 100644 index 000000000..ee594bf36 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IRadiationEmitter.java @@ -0,0 +1,16 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +/** + * Capability exposed by entities and tile entities which emit radiation. + * Block emitters are represented by their tile entity. + * + * @since 0.3.1 + */ +public interface IRadiationEmitter +{ + float getRadiationRadius(); + + float getRadiationStrength(); + + boolean isRadiationActive(); +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IRadiationProtection.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IRadiationProtection.java new file mode 100644 index 000000000..fc6fc960b --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/IRadiationProtection.java @@ -0,0 +1,12 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +/** + * Capability exposed by equipment capable of shielding its wearer from radiation. + * + * @since 0.3.1 + */ +@FunctionalInterface +public interface IRadiationProtection +{ + boolean protectsFromRadiation(); +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ProtectionCapabilities.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ProtectionCapabilities.java new file mode 100644 index 000000000..41c6f44c0 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ProtectionCapabilities.java @@ -0,0 +1,92 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.Capability.IStorage; +import net.minecraftforge.common.capabilities.CapabilityInject; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.util.INBTSerializable; +import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; +import pl.pabilo8.immersiveintelligence.api.api.protection.RadiationEmitter; + +import javax.annotation.Nullable; + +/** + * Registration point for II protection and radiation-emitter capabilities. + * + * @since 0.3.1 + */ +public final class ProtectionCapabilities +{ + public static final ResourceLocation RADIATION_EMITTER_ID = new ResourceLocation(ImmersiveIntelligence.MODID, "radiation_emitter"); + + @CapabilityInject(IAcidProtection.class) + public static Capability ACID_PROTECTION = null; + @CapabilityInject(ICorrosionProtection.class) + public static Capability CORROSION_PROTECTION = null; + @CapabilityInject(IGasProtection.class) + public static Capability GAS_PROTECTION = null; + @CapabilityInject(IInfraredProtection.class) + public static Capability INFRARED_PROTECTION = null; + @CapabilityInject(IRadiationProtection.class) + public static Capability RADIATION_PROTECTION = null; + @CapabilityInject(IRadiationEmitter.class) + public static Capability RADIATION_EMITTER = null; + + private static boolean registered; + + private ProtectionCapabilities() + { + } + + public static void register() + { + if(registered) + return; + registered = true; + + CapabilityManager.INSTANCE.register(IAcidProtection.class, new EmptyStorage<>(), () -> () -> false); + CapabilityManager.INSTANCE.register(ICorrosionProtection.class, new EmptyStorage<>(), () -> () -> false); + CapabilityManager.INSTANCE.register(IGasProtection.class, new EmptyStorage<>(), () -> () -> false); + CapabilityManager.INSTANCE.register(IInfraredProtection.class, new EmptyStorage<>(), () -> () -> false); + CapabilityManager.INSTANCE.register(IRadiationProtection.class, new EmptyStorage<>(), () -> () -> false); + CapabilityManager.INSTANCE.register(IRadiationEmitter.class, new SerializableStorage<>(), RadiationEmitter::new); + } + + private static final class EmptyStorage implements IStorage + { + @Override + public NBTBase writeNBT(Capability capability, T instance, EnumFacing side) + { + return new NBTTagCompound(); + } + + @Override + public void readNBT(Capability capability, T instance, EnumFacing side, NBTBase nbt) + { + } + } + + private static final class SerializableStorage implements IStorage + { + @Nullable + @Override + public NBTBase writeNBT(Capability capability, T instance, EnumFacing side) + { + if(instance instanceof INBTSerializable) + return ((INBTSerializable)instance).serializeNBT(); + return null; + } + + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public void readNBT(Capability capability, T instance, EnumFacing side, NBTBase nbt) + { + if(instance instanceof INBTSerializable) + ((INBTSerializable)instance).deserializeNBT(nbt); + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ProtectionCapabilityProvider.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ProtectionCapabilityProvider.java new file mode 100644 index 000000000..d47288b2e --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/api/protection/capability/ProtectionCapabilityProvider.java @@ -0,0 +1,113 @@ +package pl.pabilo8.immersiveintelligence.api.api.protection.capability; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.INBTSerializable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * Small composable capability provider which can decorate an existing provider. + * Parent data is preserved, while serialisable capabilities registered on this provider are appended. + * + * @since 0.3.1 + */ +public class ProtectionCapabilityProvider implements ICapabilitySerializable +{ + private static final String SERIALIZED_CAPABILITIES = "iiProtectionCapabilities"; + private final Map, Object> capabilities = new IdentityHashMap<>(); + @Nullable + private final ICapabilityProvider parent; + + public ProtectionCapabilityProvider() + { + this(null); + } + + public ProtectionCapabilityProvider(@Nullable ICapabilityProvider parent) + { + this.parent = parent; + } + + public ProtectionCapabilityProvider with(@Nonnull Capability capability, @Nonnull T instance) + { + capabilities.put(capability, instance); + return this; + } + + @Override + public boolean hasCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) + { + return capabilities.containsKey(capability)||(parent!=null&&parent.hasCapability(capability, facing)); + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) + { + Object value = capabilities.get(capability); + if(value!=null) + return (T)value; + return parent==null?null: parent.getCapability(capability, facing); + } + + @Override + public NBTTagCompound serializeNBT() + { + NBTTagCompound nbt = serializeParent(); + NBTTagCompound serializedCapabilities = nbt.hasKey(SERIALIZED_CAPABILITIES, Constants.NBT.TAG_COMPOUND)? + nbt.getCompoundTag(SERIALIZED_CAPABILITIES): new NBTTagCompound(); + for(Map.Entry, Object> entry : capabilities.entrySet()) + { + Object instance = entry.getValue(); + if(!(instance instanceof INBTSerializable)) + continue; + + Object serialized = ((INBTSerializable)instance).serializeNBT(); + if(serialized instanceof NBTBase) + serializedCapabilities.setTag(entry.getKey().getName(), (NBTBase)serialized); + } + if(!serializedCapabilities.hasNoTags()) + nbt.setTag(SERIALIZED_CAPABILITIES, serializedCapabilities); + return nbt; + } + + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public void deserializeNBT(NBTTagCompound nbt) + { + if(parent instanceof INBTSerializable) + ((INBTSerializable)parent).deserializeNBT(nbt); + + if(!nbt.hasKey(SERIALIZED_CAPABILITIES, Constants.NBT.TAG_COMPOUND)) + return; + NBTTagCompound serializedCapabilities = nbt.getCompoundTag(SERIALIZED_CAPABILITIES); + for(Map.Entry, Object> entry : capabilities.entrySet()) + { + Object instance = entry.getValue(); + String name = entry.getKey().getName(); + if(instance instanceof INBTSerializable&&serializedCapabilities.hasKey(name)) + ((INBTSerializable)instance).deserializeNBT(serializedCapabilities.getTag(name)); + } + } + + private NBTTagCompound serializeParent() + { + if(parent instanceof INBTSerializable) + { + Object serialized = ((INBTSerializable)parent).serializeNBT(); + if(serialized instanceof NBTTagCompound) + return (NBTTagCompound)serialized; + } + return new NBTTagCompound(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/crafting/recipe/IIMultiblockRecipe.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/crafting/recipe/IIMultiblockRecipe.java index 1e6649a86..167d4b629 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/crafting/recipe/IIMultiblockRecipe.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/crafting/recipe/IIMultiblockRecipe.java @@ -90,7 +90,8 @@ else if(stack.stackList!=null) } else sb.append(IIItemUtils.getUniqueStackString(stack.stack)); - sb.append("_").append(stack.inputSize); + if(stack.inputSize > 1) + sb.append("_").append(stack.inputSize); return sb.toString(); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/IIDataHandlingUtils.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/IIDataHandlingUtils.java index c7e7fd096..59301bcce 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/IIDataHandlingUtils.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/IIDataHandlingUtils.java @@ -2,6 +2,7 @@ import blusunrize.immersiveengineering.api.crafting.IngredientStack; import net.minecraft.item.EnumDyeColor; +import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -22,17 +23,27 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.EnumSet; +import java.util.IdentityHashMap; +import java.util.Map; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 19.07.2026 * @ii-approved 0.3.1 * @since 28.08.2024 */ public class IIDataHandlingUtils { + /** + * Maximum number of nested packet deliveries allowed in one synchronous propagation chain. + */ + private static final int MAX_PACKET_CHAIN_DEPTH = 128; + private static final ThreadLocal PACKET_DISPATCH_CONTEXT = new ThreadLocal<>(); + //--- Meta Information ---// @SuppressWarnings("unchecked") @@ -86,6 +97,19 @@ public static IngredientStack asIngredient(char variable, DataPacket packet) return ingredientFromData(packet.get(variable)); } + public static IngredientStack ingredientFromData(DataType dataType) + { + if(dataType instanceof DataTypeItemStack) + { + ItemStack stack = ((DataTypeItemStack)dataType).value.copy(); + return new IngredientStack(stack).setUseNBT(stack.hasTagCompound()); + } + else if(dataType instanceof DataTypeString) + return new IngredientStack(dataType.toString()); + else + return new IngredientStack("*"); + } + //--- Optional ---// public static Optional optionalBoolean(char variable, DataPacket packet) @@ -350,6 +374,46 @@ public static DataPacket handleCallback(DataPacket packet, Function ((IDataDevice)te).onReceive(packet.clone(), facing.getOpposite())); + //Sending to a wire network + else if(te instanceof IDataConnector) + return dispatchPacket(te, PacketOperation.CONNECTOR_SEND, + () -> ((IDataConnector)te).sendPacket(packet.clone())); + return false; + } + + private static class PacketDispatchContext + { + private final Map> activeOperations = new IdentityHashMap<>(); + private int depth = 0; + + private boolean enter(Object receiver, PacketOperation operation) { - ((IDataDevice)te).onReceive(packet.clone(), facing.getOpposite()); + if(depth >= MAX_PACKET_CHAIN_DEPTH) + return false; + + EnumSet operations = activeOperations.computeIfAbsent(receiver, + ignored -> EnumSet.noneOf(PacketOperation.class)); + if(!operations.add(operation)) + return false; + + depth++; return true; } - //Sending to a wire network - else if(te instanceof IDataConnector) + + private void exit(Object receiver, PacketOperation operation) { - ((IDataConnector)te).sendPacket(packet.clone()); - return true; + EnumSet operations = activeOperations.get(receiver); + if(operations!=null) + { + operations.remove(operation); + if(operations.isEmpty()) + activeOperations.remove(receiver); + } + depth--; } - return false; } - public static IngredientStack ingredientFromData(DataType dataType) + public enum PacketOperation { - if(dataType instanceof DataTypeItemStack) - return new IngredientStack((((DataTypeItemStack)dataType).value.copy())); - else if(dataType instanceof DataTypeString) - return new IngredientStack(dataType.toString()); - else - return new IngredientStack("*"); + DEVICE_RECEIVE, + CONNECTOR_RECEIVE, + CONNECTOR_SEND } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/device/DataWireNetwork.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/device/DataWireNetwork.java index 11f11ae76..42d88f4a7 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/device/DataWireNetwork.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/device/DataWireNetwork.java @@ -7,6 +7,8 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import pl.pabilo8.immersiveintelligence.api.data.DataPacket; +import pl.pabilo8.immersiveintelligence.api.data.IIDataHandlingUtils; +import pl.pabilo8.immersiveintelligence.api.data.IIDataHandlingUtils.PacketOperation; import pl.pabilo8.immersiveintelligence.common.wire.IIDataWireType; import java.lang.ref.WeakReference; @@ -16,6 +18,7 @@ /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 19.07.2026 * @since 31.05.2019 */ public class DataWireNetwork @@ -45,12 +48,10 @@ public static void updateConnectors(BlockPos start, World world, DataWireNetwork } if(connsAtBlock!=null&&iic!=null) for(Connection c : connsAtBlock) - { if(Objects.equals(c.cableType.getCategory(), IIDataWireType.DATA_CATEGORY)&& iic.allowEnergyToPass(c)&& !closed.contains(c.end)) open.add(c.end); - } } } @@ -60,29 +61,6 @@ public DataWireNetwork add(IDataConnector connector) return this; } - public void mergeNetwork(DataWireNetwork wireNetwork) - { - List> conns = null; - if(connectors.size() > 0) - conns = connectors; - else if(wireNetwork.connectors.size() > 0) - conns = wireNetwork.connectors; - if(conns==null)//No connectors to merge - return; - IDataConnector start = null; - for(WeakReference conn : conns) - if(conn.get()!=null) - { - start = conn.get(); - break; - } - if(start!=null) - { - BlockPos startPos = Utils.toCC(start); - updateConnectors(startPos, start.getConnectorWorld(), this); - } - } - public void removeFromNetwork(IDataConnector removedConnector) { Iterator> iterator = connectors.iterator(); @@ -108,9 +86,8 @@ public void sendPacket(DataPacket packet, IDataConnector sender) { IDataConnector connector = connectorRef.get(); if(connector!=null&&!connector.equals(sender)) - { - connector.onPacketReceive(packet); - } + IIDataHandlingUtils.dispatchPacket(connector, PacketOperation.CONNECTOR_RECEIVE, + () -> connector.onPacketReceive(packet)); } } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/operations/document/DataOperationDocumentReadAllPagesString.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/operations/document/DataOperationDocumentReadAllPagesString.java index a5512d42a..3e9b71798 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/operations/document/DataOperationDocumentReadAllPagesString.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/operations/document/DataOperationDocumentReadAllPagesString.java @@ -8,7 +8,6 @@ import net.minecraft.util.text.ITextComponent.Serializer; import pl.pabilo8.immersiveintelligence.api.data.DataPacket; import pl.pabilo8.immersiveintelligence.api.data.operations.DataOperation; -import pl.pabilo8.immersiveintelligence.api.data.types.DataTypeArray; import pl.pabilo8.immersiveintelligence.api.data.types.DataTypeExpression; import pl.pabilo8.immersiveintelligence.api.data.types.DataTypeItemStack; import pl.pabilo8.immersiveintelligence.api.data.types.DataTypeString; @@ -22,7 +21,7 @@ */ @DataOperation.DataOperationMeta(name = "document_read_all_pages_string", allowedTypes = {DataTypeItemStack.class}, params = {"document"}, - expectedResult = DataTypeArray.class) + expectedResult = DataTypeString.class) public class DataOperationDocumentReadAllPagesString extends DataOperation { @Nonnull diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/types/generic/DataType.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/types/generic/DataType.java index f4287a356..c7dbd14d6 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/data/types/generic/DataType.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/data/types/generic/DataType.java @@ -3,6 +3,7 @@ import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.resources.I18n; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.IStringSerializable; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -92,7 +93,7 @@ public ResourceLocation getTextureLocation() Class defaultType(); } - public static class TypeMetaInfo + public static class TypeMetaInfo implements IStringSerializable { public final String name; public final Class type; @@ -126,5 +127,11 @@ public boolean isAdvancedType() { return advancedType; } + + @Override + public String getName() + { + return getTranslatedName(); + } } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IIRotaryUtils.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IIRotaryUtils.java index 2af70c9c0..b994f0de4 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IIRotaryUtils.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IIRotaryUtils.java @@ -28,6 +28,8 @@ import net.minecraft.util.math.Vec3i; import net.minecraft.world.World; import net.minecraftforge.fml.common.network.NetworkRegistry; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.MechanicalDevices; import pl.pabilo8.immersiveintelligence.common.IIUtils; import pl.pabilo8.immersiveintelligence.common.block.rotary_device.tileentity.TileEntityMechanicalConnectable; @@ -52,6 +54,7 @@ * @author GabrielV (gabriel@iiteam.net) * @updated 01.08.2024 * @updated 10.10.2025 + * @updated 20.07.2026 * @ii-approved 0.3.1 * @since 26.12.2019 */ @@ -112,7 +115,7 @@ public static boolean canConnect(TileEntity start, TileEntity end, WireType wire * @return {@link EnumActionResult#SUCCESS} if the connection was successful, {@link EnumActionResult#FAIL} if the connection failed, {@link EnumActionResult#PASS} if the connection was not attempted */ public static EnumActionResult useCoil(IWireCoil coil, EntityPlayer player, World world, BlockPos pos, - EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) + EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) { TileEntity tileEntity = world.getTileEntity(pos); //Tile entity is not a rotary device @@ -287,11 +290,6 @@ else if(canConnect(tileEntity, tileEntityLinkingPos, wire)) return EnumActionResult.SUCCESS; } - public static int getRPMMax() - { - return 1200; - } - /** * @param start start of the connection * @param end end of the connection @@ -375,21 +373,6 @@ public static float getGearTorqueRatio(NonNullList inventory) return MathHelper.clamp(torque/inventory.size(), 0, 8); } - public static float getDisplayRotation(TileEntity te, RotaryStorage rotaryStorage, float partialTicks) - { - double worldRPT = (te.getWorld().getTotalWorldTime()%getRPMMax()+partialTicks)/getRPMMax(); - return (float)(worldRPT*rotaryStorage.getRotationSpeed())%1; - } - - /** - * @param facing the facing of the rotary connector - * @return whether the rotary connector should rotate clockwise ({@link AxisDirection#POSITIVE}) or counter-clockwise ({@link AxisDirection#NEGATIVE}) - */ - public static boolean shouldRotateClockwise(EnumFacing facing) - { - return facing.getAxisDirection()==AxisDirection.POSITIVE; - } - public static Collection getAllMotorBelts() { return WireType.getValues().stream() @@ -431,4 +414,36 @@ public static float[] IEToII(double rotation, TileEntity device) return new float[]{speed, torque}; } + + public static int getMaxWorldRotationTicks() + { + return 1200; + } + + //--- Client Methods ---// + + /** + * @param te rendered tile entity + * @param rotaryStorage displayed rotary energy + * @param partialTicks partial ticks + * @return an approximate rotation value from 0.0 to 1.0 + * @implNote it's based on the world time, but it's good enough for most rendering purposes + * @implNote do not use on mechanical belts and wheels + */ + @SideOnly(Side.CLIENT) + public static float getDisplayRotation(TileEntity te, RotaryStorage rotaryStorage, float partialTicks) + { + double worldTime = (te.getWorld().getTotalWorldTime()%getMaxWorldRotationTicks()+partialTicks)/getMaxWorldRotationTicks(); + return (float)((worldTime*rotaryStorage.getRotationSpeed())%1f); + } + + /** + * @param facing the facing of the rotary connector + * @return whether the rotary connector should rotate clockwise ({@link AxisDirection#POSITIVE}) or counter-clockwise ({@link AxisDirection#NEGATIVE}) + */ + @SideOnly(Side.CLIENT) + public static boolean shouldRotateClockwise(EnumFacing facing) + { + return facing.getAxisDirection()==AxisDirection.POSITIVE; + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryConnector.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryConnector.java index 6264fa19c..93f319019 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryConnector.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryConnector.java @@ -41,6 +41,18 @@ public interface IRotaryConnector */ double getOutputSpeed(); + /** + * Returns the interpolated visual rotation progress. + * + * @param belt whether the progress is intended for a connected motor belt + * @param partialTicks partial tick time used for interpolation + * @return normalized visual rotation progress + */ + default float getDisplayedRotationProgress(boolean belt, float partialTicks) + { + return 0; + } + /** * @return the rotational energy storage object */ diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryEnergy.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryEnergy.java index 11714ae9d..f7bead08c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryEnergy.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotaryEnergy.java @@ -2,6 +2,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.INBTSerializable; import javax.annotation.Nullable; @@ -13,7 +14,7 @@ * @updated 10.10.2025 * @since 06.01.2020 */ -public interface IRotaryEnergy +public interface IRotaryEnergy extends INBTSerializable { /** * @return Torque in IT @@ -110,7 +111,8 @@ default boolean handleRotation(IRotaryEnergy other, EnumFacing facing) return false; } - default NBTTagCompound toNBT() + @Override + default NBTTagCompound serializeNBT() { NBTTagCompound nbt = new NBTTagCompound(); nbt.setFloat("speed", getRotationSpeed()); @@ -118,7 +120,8 @@ default NBTTagCompound toNBT() return nbt; } - default void fromNBT(NBTTagCompound nbt) + @Override + default void deserializeNBT(NBTTagCompound nbt) { setRotationSpeed(nbt.getFloat("speed")); setTorque(nbt.getFloat("torque")); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotationalEnergyBlock.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotationalEnergyBlock.java index 74a1468b2..0cc57279c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotationalEnergyBlock.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/IRotationalEnergyBlock.java @@ -4,6 +4,7 @@ * @author Pabilo8 (pabilo@iiteam.net) * @since 02.07.2019 */ +@Deprecated public interface IRotationalEnergyBlock { void updateRotationStorage(float speed, float torque, int partID); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/MotorBeltNetwork.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/MotorBeltNetwork.java index cfa01d7df..14cba57c8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/MotorBeltNetwork.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/MotorBeltNetwork.java @@ -6,8 +6,6 @@ import blusunrize.immersiveengineering.common.util.Utils; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; import java.lang.ref.Reference; import java.lang.ref.WeakReference; @@ -172,8 +170,13 @@ public double getNetworkTorque() return torque; } - @SideOnly(Side.CLIENT) - public void setClient(float speed, float torque) + /** + * Used for deserializing the values on the client side + * + * @param speed network speed + * @param torque network torque + */ + public void setValues(float speed, float torque) { this.speed = speed; this.torque = torque; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/RotaryStorage.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/RotaryStorage.java index 37eb9844e..1c03f344d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/RotaryStorage.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/api/rotary/RotaryStorage.java @@ -19,9 +19,7 @@ package pl.pabilo8.immersiveintelligence.api.rotary; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; -import net.minecraftforge.common.util.INBTSerializable; import javax.annotation.Nullable; @@ -32,7 +30,7 @@ * @ii-approved 0.1.0 * @since 06.01.2020 */ -public class RotaryStorage implements IRotaryEnergy, INBTSerializable +public class RotaryStorage implements IRotaryEnergy { protected float torque = 0, speed = 0; @@ -82,18 +80,4 @@ public RotationSide getSide(@Nullable EnumFacing facing) { return RotationSide.NONE; } - - //--- INBTSerializable ---// - - @Override - public NBTTagCompound serializeNBT() - { - return toNBT(); - } - - @Override - public void deserializeNBT(NBTTagCompound nbt) - { - fromNBT(nbt); - } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IGasmask.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IGasmask.java deleted file mode 100644 index c4c3e2f70..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IGasmask.java +++ /dev/null @@ -1,12 +0,0 @@ -package pl.pabilo8.immersiveintelligence.api.utils.armor; - -import net.minecraft.item.ItemStack; - -/** - * @author Pabilo8 (pabilo@iiteam.net) - * @since 01.05.2021 - */ -public interface IGasmask -{ - boolean protectsFromGasses(ItemStack stack); -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IInfraredProtectionEquipment.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IInfraredProtectionEquipment.java deleted file mode 100644 index 0158160e4..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IInfraredProtectionEquipment.java +++ /dev/null @@ -1,12 +0,0 @@ -package pl.pabilo8.immersiveintelligence.api.utils.armor; - -import net.minecraft.item.ItemStack; - -/** - * @author Pabilo8 (pabilo@iiteam.net) - * @since 09.07.2021 - */ -public interface IInfraredProtectionEquipment -{ - boolean invisibleToInfrared(ItemStack stack); -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IRadiationProtectionEquipment.java b/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IRadiationProtectionEquipment.java deleted file mode 100644 index 16854a5eb..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/api/utils/armor/IRadiationProtectionEquipment.java +++ /dev/null @@ -1,12 +0,0 @@ -package pl.pabilo8.immersiveintelligence.api.utils.armor; - -import net.minecraft.item.ItemStack; - -/** - * @author Pabilo8 (pabilo@iiteam.net) - * @since 09.07.2021 - */ -public interface IRadiationProtectionEquipment -{ - boolean protectsFromRadiation(ItemStack stack); -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientEventHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientEventHandler.java index b9d2aebe7..06fd05c3c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientEventHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientEventHandler.java @@ -16,6 +16,7 @@ import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiContainerCreative; +import net.minecraft.client.gui.inventory.GuiInventory; import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.model.ModelBiped.ArmPose; @@ -48,6 +49,7 @@ import net.minecraftforge.client.event.EntityViewRenderEvent.FOVModifier; import net.minecraftforge.client.event.EntityViewRenderEvent.FogColors; import net.minecraftforge.client.event.EntityViewRenderEvent.RenderFogEvent; +import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent; import net.minecraftforge.client.event.GuiScreenEvent.InitGuiEvent.Post; import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; import net.minecraftforge.client.event.RenderGameOverlayEvent.Pre; @@ -77,6 +79,7 @@ import pl.pabilo8.immersiveintelligence.api.utils.camera.ICameraEntity; import pl.pabilo8.immersiveintelligence.client.fx.ScreenShake; import pl.pabilo8.immersiveintelligence.client.fx.utils.ParticleSystem; +import pl.pabilo8.immersiveintelligence.client.gui.GuiButtonFactionInvitations; import pl.pabilo8.immersiveintelligence.client.gui.GuiWidgetAustralianTabs; import pl.pabilo8.immersiveintelligence.client.gui.inworld_overlay.InWorldOverlayBase; import pl.pabilo8.immersiveintelligence.client.gui.inworld_overlay.OwnershipOverlay; @@ -100,6 +103,7 @@ import pl.pabilo8.immersiveintelligence.client.util.amt.parts.AMTBipedAdapter; import pl.pabilo8.immersiveintelligence.common.*; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Factions; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Graphics; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Weapons; import pl.pabilo8.immersiveintelligence.common.entity.EntityCamera; @@ -110,6 +114,7 @@ import pl.pabilo8.immersiveintelligence.common.item.weapons.ItemIIGunBase; import pl.pabilo8.immersiveintelligence.common.item.weapons.ItemIIRailgunOverride; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; +import pl.pabilo8.immersiveintelligence.common.network.messages.MessageDiplomacySync; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageItemScrollableSwitch; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageManualClose; import pl.pabilo8.immersiveintelligence.common.util.IIColor; @@ -414,7 +419,7 @@ public void onFogUpdate(RenderFogEvent event) GlStateManager.setFogEnd(0.5f); GlStateManager.setFogDensity(.015f); } - else if(world.getBiome(living.getPosition())==IIContent.biomeWasteland) + else if(living.isPotionActive(IIPotions.radiation)) { GlStateManager.setFog(FogMode.EXP2); GlStateManager.setFogStart(0); //( @@ -465,7 +470,7 @@ public void onFogColorUpdate(FogColors event) event.setGreen(v); event.setBlue(v); } - else if(world.getBiome(living.getPosition())==IIContent.biomeWasteland) + else if(living.isPotionActive(IIPotions.radiation)) { float[] rgb = IIColor.fromPackedRGB(0x64604e) .withBrightness(0.2f*event.getEntity().getEntityWorld().provider.getSunBrightnessFactor(0.25f)) @@ -969,18 +974,59 @@ else if(name!=null) @SubscribeEvent public void onInitGuiPost(Post event) { - //Add creative menu subtabs - if(event.getGui() instanceof GuiContainerCreative&&IIConfig.australianCreativeTabs) + GuiScreen gui = event.getGui(); + if(Factions.enableFactions&&gui instanceof GuiInventory&&Factions.inventoryButtonPosition[0]!=-1&&Factions.inventoryButtonPosition[1]!=-1) { - GuiContainerCreative gui = (GuiContainerCreative)event.getGui(); try { - event.getButtonList().add(new GuiWidgetAustralianTabs(gui.guiLeft-27, gui.guiTop+2, gui)); + event.getButtonList().add(new GuiButtonFactionInvitations( + ((GuiInventory)gui).guiLeft+Factions.inventoryButtonPosition[0], + ((GuiInventory)gui).guiTop+Factions.inventoryButtonPosition[1], + null)); } catch(Exception ignored) { - IILogger.warn("Failed to add subtabs to creative inventory"); + IILogger.warn("Failed to add faction invitation button to inventory"); } } + //Add creative menu subtabs + if(gui instanceof GuiContainerCreative&&IIConfig.australianCreativeTabs) + { + GuiContainerCreative creative = (GuiContainerCreative)gui; + if(Factions.enableFactions&&Factions.inventoryButtonPositionCreative[0]!=-1&&Factions.inventoryButtonPositionCreative[1]!=-1) + try + { + event.getButtonList().add(new GuiButtonFactionInvitations( + creative.guiLeft+Factions.inventoryButtonPositionCreative[0], + creative.guiTop+Factions.inventoryButtonPositionCreative[1], + creative + )); + } catch(Exception ignored) + { + IILogger.warn("Failed to add faction invitation button to creative inventory"); + } + + if(IIConfig.australianCreativeTabs) + try + { + event.getButtonList().add(new GuiWidgetAustralianTabs(creative.guiLeft-27, creative.guiTop+2, creative)); + } catch(Exception ignored) + { + IILogger.warn("Failed to add subtabs to creative inventory"); + } + } + } + + @SubscribeEvent + public void onFactionInvitationButton(ActionPerformedEvent.Post event) + { + if(!(event.getButton() instanceof GuiButtonFactionInvitations)) + return; + Minecraft mc = Minecraft.getMinecraft(); + if(mc.player!=null&&mc.world!=null) + { + IIPacketHandler.sendToAllClients(MessageDiplomacySync.requestUpdateMessage()); + mc.player.openGui(ImmersiveIntelligence.INSTANCE, IIGUI.FACTION_INVITATIONS.ordinal(), mc.world, 0, 0, 0); + } } @SubscribeEvent diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientProxy.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientProxy.java index f1c6d36b1..b3618f1c8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientProxy.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/ClientProxy.java @@ -358,10 +358,16 @@ public Object getClientGuiElement(int ID, EntityPlayer player, World world, int if(IIGUI.values().length > ID) { IIGUI guiBuilder = IIGUI.values()[ID]; + if(guiBuilder.player) + return guiBuilder.guiFromPlayer==null?null: guiBuilder.guiFromPlayer.apply(player); if(guiBuilder.item) return guiBuilder.guiFromStack.apply(player, stack, hand); - - if(te instanceof IGuiTile&&guiBuilder.teClass.isInstance(te)) + if(guiBuilder.entityClass!=null&&guiBuilder.containerFromEntity!=null) + { + if(guiBuilder.entityClass.isInstance(entity)) + return guiBuilder.guiFromEntity.apply(player, entity); + } + else if(te instanceof IGuiTile&&guiBuilder.teClass.isInstance(te)) if((gui = guiBuilder.guiFromTile.apply(player, te))!=null) ((IGuiTile)te).onGuiOpened(player, true); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/particles/ParticleGasCloud.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/particles/ParticleGasCloud.java deleted file mode 100644 index bd3371018..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/particles/ParticleGasCloud.java +++ /dev/null @@ -1,67 +0,0 @@ -package pl.pabilo8.immersiveintelligence.client.fx.particles; - -import net.minecraft.client.particle.Particle; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.entity.Entity; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; -import net.minecraftforge.fluids.Fluid; - -public class ParticleGasCloud extends Particle -{ - private final Fluid fluid; - private final float initialSize; - - public ParticleGasCloud(World world, Vec3d position, float size, Fluid fluid) - { - super(world, position.x, position.y-1.0D, position.z); // Lower the spawn point by 1 block - this.fluid = fluid; - this.initialSize = size*2.0F; // Make the initial size larger - - // Set the max age of the particle - this.particleMaxAge = (int)(80*this.initialSize); - - // Get the color from the fluid - int color = fluid.getColor(); - this.particleRed = (color>>16&255)/255.0F; - this.particleGreen = (color>>8&255)/255.0F; - this.particleBlue = (color&255)/255.0F; - - // Set the initial scale - this.particleScale = size; - } - - public static void spawnParticles(World world, Vec3d position, float size, Fluid fluid) - { - int particleCount = 10; // Adjust this to control how many particles to spawn - for(int i = 0; i < particleCount; i++) - { - // Random offsets for spreading particles - double offsetX = (world.rand.nextDouble()-0.5)*size; // Spread in X - double offsetY = (world.rand.nextDouble()-0.5)*size; // Spread in Y - double offsetZ = (world.rand.nextDouble()-0.5)*size; // Spread in Z - - // Create a new particle at the offset position - //ParticleGasCloud newParticle = new ParticleGasCloud(world, position.add(offsetX, offsetY, offsetZ), size, fluid); - // Assuming you have a method to add the particle to the world or particle manager - // Example: world.spawnParticle(newParticle); - // Make sure to replace the above line with your specific method to register particles - } - } - - @Override - public void onUpdate() - { - super.onUpdate(); - // Slowly decrease the size of the particle as it ages - float ageFactor = (float)this.particleAge/(float)this.particleMaxAge; - this.particleScale = this.initialSize*(1.0F-(ageFactor*0.5F)); // Adjust the rate of size reduction - } - - @Override - public void renderParticle(BufferBuilder buffer, Entity entity, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) - { - // Call the superclass render method or provide a custom rendering implementation - super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); - } -} \ No newline at end of file diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/IIParticleUtils.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/IIParticleUtils.java index eeb21c987..494462407 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/IIParticleUtils.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/IIParticleUtils.java @@ -1,13 +1,18 @@ package pl.pabilo8.immersiveintelligence.client.fx.utils; +import blusunrize.immersiveengineering.client.ClientUtils; import blusunrize.immersiveengineering.common.util.Utils; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import pl.pabilo8.immersiveintelligence.common.util.ISerializableEnum; import javax.vecmath.Vector2f; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.function.Supplier; /** @@ -179,4 +184,117 @@ public Vector2f generateRotation(Vec3d origin, int index, float size, int amount return new Vector2f(0, 0); } } + + /** + * @param settingValue setting value specific to a particle effect + * @return the lower detail level selected by either the effect setting or the global Video Settings option + */ + public static ParticleDetail getParticleDetailLevel(ParticleDetail settingValue) + { + int globalSetting = MathHelper.clamp( + ClientUtils.mc().gameSettings.particleSetting, + 0, ParticleDetail.values().length-1 + ); + return ParticleDetail.values()[Math.max(settingValue.ordinal(), globalSetting)]; + } + + /** + * @return particle detail level from the global Video Settings option + */ + public static ParticleDetail getParticleDetailLevel() + { + int globalSetting = MathHelper.clamp( + ClientUtils.mc().gameSettings.particleSetting, + 0, ParticleDetail.values().length-1 + ); + return ParticleDetail.values()[globalSetting]; + } + + /** + * Calculates a reusable particle budget multiplier from effect detail and viewer distance. + * The returned value is 1.0 at full detail and close range, then falls to 0.65 and 0.35. + * + * @param detail resolved detail level for the effect + * @param distance distance between the viewer and the effect + * @param nearDistance distance up to which the full budget is retained + * @param farDistance distance at which the lowest distance multiplier begins + */ + public static float getParticleBudgetScale(ParticleDetail detail, float distance, + float nearDistance, float farDistance) + { + if(!detail.isEnabled()) + return 0f; + + float safeNear = Math.max(0f, nearDistance); + float safeFar = Math.max(safeNear, farDistance); + float distanceScale = distance < safeNear?1f: (distance < safeFar?0.65f: 0.35f); + + float detailScale; + switch(detail) + { + case REDUCED: + detailScale = 0.65f; + break; + case MINIMAL: + detailScale = 0.35f; + break; + case DISABLED: + return 0f; + default: + case DETAILED: + detailScale = 1f; + } + + return distanceScale*detailScale; + } + + /** + * Calculates a bounded adaptive particle budget. The magnitude is normalised against a reference + * value, raised to the requested growth exponent, and then scaled by the resolved detail budget. + * + * @param magnitude measured size or extent of the effect + * @param referenceMagnitude magnitude corresponding to a normalised value of 1 + * @param baseBudget fixed part of the budget + * @param growthBudget amount added by the normalised growth term + * @param growthExponent 1 for linear growth, 0.5 for square-root growth, etc. + * @param budgetScale multiplier returned by {@link #getParticleBudgetScale} + * @param minimum minimum returned budget + * @param maximum maximum returned budget + */ + public static int calculateAdaptiveParticleBudget(float magnitude, float referenceMagnitude, + float baseBudget, float growthBudget, + float growthExponent, float budgetScale, + int minimum, int maximum) + { + if(maximum <= 0||budgetScale <= 0f) + return 0; + + int safeMinimum = MathHelper.clamp(minimum, 0, maximum); + float reference = Math.max(0.0001f, referenceMagnitude); + float normalisedMagnitude = Math.max(1f, magnitude/reference); + float growth = (float)Math.pow(normalisedMagnitude, growthExponent); + int budget = Math.round((baseBudget+growthBudget*growth)*budgetScale); + return MathHelper.clamp(budget, safeMinimum, maximum); + } + + /** + * Selects a fixed number of elements at regular intervals while preserving source order. + * This is useful for representative particle, sound, decal, or animation samples. + */ + public static List selectEvenlyDistributed(List elements, int amount) + { + if(elements==null||elements.isEmpty()||amount <= 0) + return Collections.emptyList(); + if(elements.size() <= amount) + return new ArrayList<>(elements); + + List selected = new ArrayList<>(amount); + float stride = elements.size()/(float)amount; + for(int i = 0; i < amount; i++) + selected.add(elements.get(Math.min( + elements.size()-1, + MathHelper.floor((i+0.5f)*stride) + ))); + return selected; + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleDetail.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleDetail.java new file mode 100644 index 000000000..d7129e8c2 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleDetail.java @@ -0,0 +1,43 @@ +package pl.pabilo8.immersiveintelligence.client.fx.utils; + + +import net.minecraftforge.common.config.Config.Comment; + +/** + * Represents particle effect details (intensity, presence of additional effects), based on vanilla minecraft settings + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 12.07.2026 + */ +public enum ParticleDetail +{ + @Comment(value = "High particle detail and count") + DETAILED, + @Comment(value = "Reduced particle count") + REDUCED, + @Comment(value = "Minimal particle effects") + MINIMAL, + @Comment(value = "No particle effects") + DISABLED; + + public boolean isHigh() + { + return this==DETAILED; + } + + public boolean isMedium() + { + return this.ordinal() < MINIMAL.ordinal(); + } + + public boolean isLow() + { + return this.ordinal() < REDUCED.ordinal(); + } + + public boolean isEnabled() + { + return this!=DISABLED; + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleDrawStages.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleDrawStages.java index 69623d40a..e22e31ad3 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleDrawStages.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleDrawStages.java @@ -26,11 +26,13 @@ public enum ParticleDrawStages implements ISerializableEnum /** * Normal particles render just like minecraft's default particles */ - VANILLA(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, false, false, ParticleSystem.PARTICLE_TEXTURES), + VANILLA(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, + false, false, ParticleSystem.PARTICLE_TEXTURES), /** * Normal particles, but uses additive blending */ - VANILLA_ADDITIVE(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, false, false, ParticleSystem.PARTICLE_TEXTURES), + VANILLA_ADDITIVE(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, + false, false, ParticleSystem.PARTICLE_TEXTURES), /** * Tracer particles are rendered on their background using additive blending and no texture @@ -40,20 +42,26 @@ public enum ParticleDrawStages implements ISerializableEnum /** * Uses the default texture map, use sprites with it */ - CUSTOM(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, false, false, TextureMap.LOCATION_BLOCKS_TEXTURE), + CUSTOM(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, + false, false, TextureMap.LOCATION_BLOCKS_TEXTURE), /** * Same as CUSTOM, but uses additive blending */ - CUSTOM_ADDITIVE(DestFactor.ONE_MINUS_CONSTANT_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, false, false, TextureMap.LOCATION_BLOCKS_TEXTURE), + CUSTOM_ADDITIVE(DestFactor.ONE_MINUS_CONSTANT_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, + false, false, TextureMap.LOCATION_BLOCKS_TEXTURE), /** * Same as CUSTOM, but applies a noise shader during rendering */ - CUSTOM_SMOKE_NOISE_SHADER(CUSTOM, Shaders.NOISE, partialTicks -> new float[]{partialTicks}), + CUSTOM_SMOKE_NOISE_SHADER(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP, + false, false, TextureMap.LOCATION_BLOCKS_TEXTURE, + Shaders.NOISE_NO_LIGHTMAP, partialTicks -> new float[]{partialTicks} + ), // CUSTOM_SMOKE_NOISE_SHADER(CUSTOM, null, partialTicks -> new float[0]), /** * Same as CUSTOM, but with normal maps, use with solid 3D models */ - CUSTOM_SOLID(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, IIParticleUtils.PARTICLE_SOLID, false, true, TextureMap.LOCATION_BLOCKS_TEXTURE); + CUSTOM_SOLID(GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, IIParticleUtils.PARTICLE_SOLID, + false, true, TextureMap.LOCATION_BLOCKS_TEXTURE); public final boolean renderThroughBlocks, applyLighting; public final boolean requiresNormals; @@ -76,21 +84,16 @@ public enum ParticleDrawStages implements ISerializableEnum .anyMatch(element -> element.getUsage()==EnumUsage.NORMAL); } - /** - * Copy constructor adding a shader - * - * @param other DrawStages to copy - * @param shader shader to use - * @param shaderParameters parameters for the shader - */ - ParticleDrawStages(ParticleDrawStages other, Shaders shader, Function shaderParameters) + ParticleDrawStages(DestFactor destFactor, VertexFormat vertexFormat, boolean renderThroughBlocks, boolean applyLighting, @Nullable ResourceLocation textureRes, + Shaders shader, Function shaderParameters) { - this.destFactor = other.destFactor; - this.vertexFormat = other.vertexFormat; - this.renderThroughBlocks = other.renderThroughBlocks; - this.applyLighting = other.applyLighting; - this.textureRes = other.textureRes; - this.requiresNormals = other.requiresNormals; + this.destFactor = destFactor; + this.vertexFormat = vertexFormat; + this.renderThroughBlocks = renderThroughBlocks; + this.applyLighting = applyLighting; + this.textureRes = textureRes; + this.requiresNormals = vertexFormat.getElements().stream() + .anyMatch(element -> element.getUsage()==EnumUsage.NORMAL); this.shader = shader; this.shaderParameters = shaderParameters; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleRegistry.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleRegistry.java index 9c8228079..3ec584459 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleRegistry.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleRegistry.java @@ -2,6 +2,8 @@ import blusunrize.immersiveengineering.client.ClientUtils; import com.google.gson.JsonObject; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; @@ -13,10 +15,12 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.api.ammo.PenetrationRegistry; +import pl.pabilo8.immersiveintelligence.api.ammo.enums.ComponentEffectShape; import pl.pabilo8.immersiveintelligence.client.IIClientUtils; import pl.pabilo8.immersiveintelligence.client.fx.factories.ParticleFactory; import pl.pabilo8.immersiveintelligence.client.fx.particles.AbstractParticle; import pl.pabilo8.immersiveintelligence.client.render.IReloadableModelContainer; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Graphics; import pl.pabilo8.immersiveintelligence.common.IILogger; import pl.pabilo8.immersiveintelligence.common.util.*; import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; @@ -47,6 +51,7 @@ public class ParticleRegistry private static final Pattern PROGRAM_PATTERN = Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*)(\\(([^)]*)\\))?"); + /** * Stores particle factories */ @@ -305,14 +310,40 @@ public static List getRegisteredNames() return new ArrayList<>(FACTORIES_REGISTRY.keySet()); } - //--- Old Methods ---// + //--- Pre-made Particle Creation Methods ---// - public static void spawnExplosionBoomFX(World world, Vec3d pos, Vec3d dir, IIExplosion explosion) + /** + * Spawns the client-side explosion effect. + */ + public static void spawnExplosionBoomFX(World world, Vec3d pos, Vec3d dir, + float radius, float power, ComponentEffectShape shape, + List affectedSurface) { float playerDistance = (float)ClientUtils.mc().player.getDistance(pos.x, pos.y, pos.z); - float size = (float)Math.min(explosion.getSize(), explosion.getPower()+1); - float logSize = 1f+MathHelper.log2((int)(size)); - boolean detailed = playerDistance < 64; + float effectExtent = Math.max(1f, Math.min(radius, power+1f)); + float logSize = 1f+MathHelper.log2(Math.max(1, (int)effectExtent)); + boolean nearby = playerDistance < 64f; + + ParticleDetail particleDetail = IIParticleUtils.getParticleDetailLevel(Graphics.explosionParticlesDetail); + ParticleDetail debrisDetail = IIParticleUtils.getParticleDetailLevel(Graphics.explosionDebrisDetail); + if(!particleDetail.isEnabled()&&!debrisDetail.isEnabled()) + return; + + boolean spawnCore = particleDetail.isEnabled(); + boolean spawnDust = nearby&&particleDetail.isMedium(); + boolean spawnGlows = particleDetail.isEnabled(); + boolean spawnDebris = debrisDetail.isEnabled(); + boolean spawnDebrisTrails = debrisDetail.isMedium(); + boolean spawnRichDebris = nearby&&debrisDetail.isHigh(); + boolean adaptiveSurface = radius > 8f; + boolean adaptiveDebris = radius > 12f&&power > 12f; + + float particleBudgetScale = IIParticleUtils.getParticleBudgetScale( + particleDetail, playerDistance, 64f, 128f); + float debrisBudgetScale = IIParticleUtils.getParticleBudgetScale( + debrisDetail, playerDistance, 64f, 128f); + + Vec3d explosionDirection = dir; //If the direction is zero, set it to up (usual direction for explosions) if(dir.equals(Vec3d.ZERO)) @@ -323,78 +354,174 @@ public static void spawnExplosionBoomFX(World world, Vec3d pos, Vec3d dir, IIExp dir = IIParticleUtils.normalizeExplosionDirection(dir); Vector2f facing = IIParticleUtils.toVector2f(dir); - //Spawn a shockwave - spawnParticle("explosion/shockwave", pos.add(dir), Vec3d.ZERO, facing) - .withProperty(ParticleProperties.SIZE, size*0.6f) - .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*logSize)+1); - scheduleSpawnParticle("explosion/glow", pos.add(dir), Vec3d.ZERO, new Vector2f(0, 0), 1) - .withProperty(ParticleProperties.SIZE, size); + if(spawnCore) + { + spawnParticle("explosion/shockwave", pos.add(dir), Vec3d.ZERO, facing) + .withProperty(ParticleProperties.SIZE, effectExtent*0.6f) + .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*logSize)+1); + scheduleSpawnParticle("explosion/glow", pos.add(dir), Vec3d.ZERO, new Vector2f(0, 0), 1) + .withProperty(ParticleProperties.SIZE, effectExtent); + + spawnParticle("explosion/main", pos.add(dir.scale(effectExtent/2f)), Vec3d.ZERO, facing) + .withProperty(ParticleProperties.SIZE, effectExtent*0.75f) + .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*logSize)+3); + } - spawnParticle("explosion/main", pos.add(dir.scale(size/2f)), Vec3d.ZERO, facing) - .withProperty(ParticleProperties.SIZE, size*0.75f) - .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*logSize)+3); + List effectBlocks; + if(adaptiveSurface) + { + if(affectedSurface==null||affectedSurface.isEmpty()) + return; + effectBlocks = new ArrayList<>(affectedSurface); + } + else + effectBlocks = getExactExplosionSurface(world, pos, explosionDirection, radius, power, shape, dir); - Set topBlocks = IIExplosion.getTopBlocks(explosion.generateAffectedBlockPositions(), EnumFacing.getFacingFromVector((float)dir.x, (float)dir.y, (float)dir.z)); + if(effectBlocks.isEmpty()) + return; - for(BlockPos destroyed : topBlocks) + float affectedExtent = getAffectedSurfaceExtent(pos, effectBlocks); + float dustSize = adaptiveSurface? + MathHelper.clamp(1.15f+0.35f*(float)Math.sqrt(Math.max(1f, affectedExtent/8f)), 1.25f, 2.75f): + 1.25f; + float debrisSize = adaptiveDebris? + MathHelper.clamp(1f+MathHelper.log2(Math.max(1, (int)(affectedExtent/12f)))*0.15f, 1f, 1.8f): + 1f; + + int dustBudget = spawnDust? + (adaptiveSurface? + IIParticleUtils.calculateAdaptiveParticleBudget( + affectedExtent, 1f, 48f, 5.5f, 1f, + particleBudgetScale, 16, 256): + effectBlocks.size()): + 0; + int glowBudget = spawnGlows? + (adaptiveSurface? + MathHelper.clamp(Math.round(72f*particleBudgetScale), 1, 72): + effectBlocks.size()): + 0; + int debrisBudget = spawnDebris? + (adaptiveDebris? + IIParticleUtils.calculateAdaptiveParticleBudget( + affectedExtent, 12f, 14f, 16f, 0.5f, + debrisBudgetScale, 8, 128): + effectBlocks.size()): + 0; + + List dustBlocks = IIParticleUtils.selectEvenlyDistributed(effectBlocks, dustBudget); + List glowBlocks = IIParticleUtils.selectEvenlyDistributed(effectBlocks, glowBudget); + List debrisBlocks = IIParticleUtils.selectEvenlyDistributed(effectBlocks, debrisBudget); + + for(BlockPos destroyed : dustBlocks) { - //Calculate distance factor - double distance = pos.distanceTo(new Vec3d(destroyed).addVector(0.5, 0, 0.5)); - - if(detailed) - scheduleSpawnParticle("smoke/dust_cloud", new Vec3d(destroyed).addVector(0.5, 0, 0.5), - Vec3d.ZERO, new Vector2f(0, 0), 10) - .withProperty(ParticleProperties.SIZE, 1.25f); - spawnParticle("explosion/glow_individual", new Vec3d(destroyed).addVector(0.5, 0, 0.5), - Vec3d.ZERO, new Vector2f(0, 0)); - - String debrisParticle = PenetrationRegistry.getPenetrationHandler(world.getBlockState(destroyed)) - .getDebrisParticle(); - if(debrisParticle!=null) - { - double factor = MathHelper.clamp(distance/size+(IIParticleUtils.randFloat.get()*0.01), 0, 1); - //Calculate direction vector - Vec3d offCenterDirection = new Vec3d(destroyed).addVector(0.5, 0, 0.5) - .subtract(pos).normalize(); - Vec3d debrisMotion = dir.scale(1-factor).add(offCenterDirection.scale(factor)) - .scale(1.05f*Math.max(1f, explosion.getPower()/6f)); - - //Spawn the debris particle - scheduleSpawnParticle(debrisParticle, new Vec3d(destroyed).addVector(0, 0f, 0), - debrisMotion, new Vector2f(IIParticleUtils.randFloat.get()*4, IIParticleUtils.randFloat.get()*4), 3) - .withProperty(ParticleProperties.TEXTURES, new ResourceLocation[]{ - ClientUtils.getSideTexture(world.getBlockState(destroyed), EnumFacing.WEST) - }); - - //Spawn a smoke trace in the same direction - scheduleSpawnParticle("smoke/smoke_trace", new Vec3d(destroyed).addVector(0.5, 0, 0.5), - Vec3d.ZERO, IIParticleUtils.toVector2f(debrisMotion), 1) - .withProperty(ParticleProperties.SIZE, logSize*0.4f) - .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*(logSize))+1); + IBlockState state = world.getBlockState(destroyed); + if(state.getMaterial()==Material.AIR) + continue; + + Vec3d destroyedCenter = new Vec3d(destroyed).addVector(0.5, 0.5, 0.5); + scheduleSpawnParticle("smoke/dust_cloud", destroyedCenter, + Vec3d.ZERO, new Vector2f(0, 0), 10) + .withProperty(ParticleProperties.SIZE, dustSize) + .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*logSize)+8); + } - //Spawn a smoke trace - scheduleSpawnParticle("smoke/smoke_trace", new Vec3d(destroyed).addVector(0.5, 0, 0.5), - Vec3d.ZERO, IIParticleUtils.toVector2f(debrisMotion), 1) + for(BlockPos destroyed : glowBlocks) + { + IBlockState state = world.getBlockState(destroyed); + if(state.getMaterial()!=Material.AIR) + spawnParticle("explosion/glow_individual", + new Vec3d(destroyed).addVector(0.5, 0.5, 0.5), + Vec3d.ZERO, new Vector2f(0, 0)); + } + + int debrisIndex = 0; + for(BlockPos destroyed : debrisBlocks) + { + IBlockState state = world.getBlockState(destroyed); + if(state.getMaterial()==Material.AIR) + continue; + + String debrisParticle = PenetrationRegistry.getPenetrationHandler(state).getDebrisParticle(); + if(debrisParticle==null) + continue; + + Vec3d destroyedCenter = new Vec3d(destroyed).addVector(0.5, 0.5, 0.5); + double distance = pos.distanceTo(destroyedCenter); + double factor = MathHelper.clamp( + distance/Math.max(1f, affectedExtent)+(IIParticleUtils.randFloat.get()*0.01), 0, 1); + Vec3d offCenterDirection = destroyedCenter.subtract(pos).normalize(); + Vec3d debrisMotion = dir.scale(1-factor).add(offCenterDirection.scale(factor)) + .scale(1.05f*Math.max(1f, power/6f)); + Vector2f debrisFacing = IIParticleUtils.toVector2f(debrisMotion); + ResourceLocation sideTexture = ClientUtils.getSideTexture(state, EnumFacing.WEST); + + scheduleSpawnParticle(debrisParticle, new Vec3d(destroyed), + debrisMotion, new Vector2f(IIParticleUtils.randFloat.get()*4, IIParticleUtils.randFloat.get()*4), 3) + .withProperty(ParticleProperties.SIZE, debrisSize) + .withProperty(ParticleProperties.TEXTURES, new ResourceLocation[]{sideTexture}); + + if(spawnDebrisTrails&&(!adaptiveDebris||debrisIndex%2==0)) + scheduleSpawnParticle("smoke/smoke_trace", destroyedCenter, + Vec3d.ZERO, debrisFacing, 1) + .withProperty(ParticleProperties.SIZE, logSize*(adaptiveDebris?0.5f: 0.4f)) + .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*logSize)+(adaptiveDebris?8: 1)); + + if(!adaptiveDebris&&spawnRichDebris) + { + scheduleSpawnParticle("smoke/smoke_trace", destroyedCenter, + Vec3d.ZERO, debrisFacing, 1) .withProperty(ParticleProperties.COLOR, IIColor.fromPackedRGB(0x3f3f3f)) .withProperty(ParticleProperties.SIZE, logSize*0.4f) - .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*(logSize))+20); - - //Spawn additional debris particles for large explosions - if(detailed) - for(int i = 0; i < 2; i++) - { - factor *= IIParticleUtils.randFloat.get()*2f; - debrisMotion = dir.scale(1-factor).add(offCenterDirection.scale(factor)) - .scale(1.05f*Math.max(1f, explosion.getPower()/6f)); - scheduleSpawnParticle(debrisParticle, new Vec3d(destroyed).addVector(0, 0f, 0), - debrisMotion, new Vector2f(IIParticleUtils.randFloat.get()*4, IIParticleUtils.randFloat.get()*4), 3*i) - .withProperty(ParticleProperties.TEXTURES, new ResourceLocation[]{ - ClientUtils.getSideTexture(world.getBlockState(destroyed), EnumFacing.DOWN) - }); - } + .withProperty(ParticleProperties.MAX_LIFETIME, (int)(4*logSize)+20); + for(int i = 0; i < 2; i++) + { + double extraFactor = MathHelper.clamp( + factor*IIParticleUtils.randFloat.get()*2f, 0, 1); + Vec3d extraMotion = dir.scale(1-extraFactor) + .add(offCenterDirection.scale(extraFactor)) + .scale(1.05f*Math.max(1f, power/6f)); + scheduleSpawnParticle(debrisParticle, new Vec3d(destroyed), + extraMotion, + new Vector2f(IIParticleUtils.randFloat.get()*4, IIParticleUtils.randFloat.get()*4), + 3*i) + .withProperty(ParticleProperties.TEXTURES, new ResourceLocation[]{ + ClientUtils.getSideTexture(state, EnumFacing.DOWN) + }); + } } + debrisIndex++; + } + } + + private static List getExactExplosionSurface(World world, Vec3d pos, Vec3d explosionDirection, + float radius, float power, ComponentEffectShape shape, + Vec3d visualDirection) + { + IIExplosion explosion = new IIExplosion(world, null, pos, explosionDirection, + radius, power, shape, false, true, false); + Set topBlocks = IIExplosion.getTopBlocks( + explosion.generateAffectedBlockPositions(), + EnumFacing.getFacingFromVector( + (float)visualDirection.x, + (float)visualDirection.y, + (float)visualDirection.z + ) + ); + return new ArrayList<>(topBlocks); + } + + private static float getAffectedSurfaceExtent(Vec3d center, List blocks) + { + double maxDistanceSq = 1.0; + for(BlockPos block : blocks) + { + double x = block.getX()+0.5-center.x; + double y = block.getY()+0.5-center.y; + double z = block.getZ()+0.5-center.z; + maxDistanceSq = Math.max(maxDistanceSq, x*x+y*y+z*z); } + return (float)Math.sqrt(maxDistanceSq); } public static void spawnGasCloud(Vec3d pos, float size, Fluid fluid) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleSystem.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleSystem.java index 5943cbd6b..2b16949e5 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleSystem.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/fx/utils/ParticleSystem.java @@ -151,6 +151,7 @@ private void privateAddEffect(AbstractParticle particle) { if(particleAmount > Graphics.maxAllowedParticles) return; + //Add to an existing stage or create a new one if it doesn't exist particles.computeIfAbsent(particle.getDrawStage(), i -> new ArrayDeque<>()).add(particle); particleAmount++; } @@ -163,6 +164,7 @@ private void privateAddEffect(AbstractParticle particle) */ public void renderParticles(float partialTicks) { + //Update static fields for rendering float x = ActiveRenderInfo.getRotationX(); float z = ActiveRenderInfo.getRotationZ(); float yz = ActiveRenderInfo.getRotationYZ(); @@ -172,6 +174,7 @@ public void renderParticles(float partialTicks) if(player!=null) { + //Simulate the particle system for the current frame, so that particles are in the correct position when rendered updateParticleFields(partialTicks, player); GlStateManager.pushMatrix(); @@ -190,12 +193,24 @@ public void renderParticles(float partialTicks) drawParticles: synchronized(particles) { - for(Map.Entry> particleStage : particles.entrySet()) + //Iterate through all layers (draw stages) + Iterator>> iterator = particles.entrySet().iterator(); + while(iterator.hasNext()) { + Map.Entry> particleStage = iterator.next(); + //If the particle stage has no particles, remove it from the map and continue + if(particleStage.getValue().isEmpty()) + { + iterator.remove(); + continue; + } + + //Prepare settings for the draw stage int particleCount = 0; particleStage.getKey().prepareRender(buffer, partialTicks); for(AbstractParticle particle : particleStage.getValue()) { + //Draw the particles for this stage until the count reaches maximum or all are drawn if(++particleCount > Graphics.maxDrawnParticles) { tess.draw(); @@ -205,11 +220,13 @@ public void renderParticles(float partialTicks) particle.preRender(partialTicks, x, xz, z, yz, xy); particle.render(buffer, partialTicks, x, xz, z, yz, xy); } + //Call the Tesselator to draw, then finalize the stage tess.draw(); particleStage.getKey().clear(); } } + //Cleanup GlStateManager.enableCull(); GlStateManager.depthMask(true); GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); @@ -217,7 +234,6 @@ public void renderParticles(float partialTicks) GlStateManager.alphaFunc(GL11.GL_GREATER, 0.1F); GlStateManager.popMatrix(); } - } //--- External ---// diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/GuiButtonFactionInvitations.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/GuiButtonFactionInvitations.java new file mode 100644 index 000000000..6fcf190d5 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/GuiButtonFactionInvitations.java @@ -0,0 +1,65 @@ +package pl.pabilo8.immersiveintelligence.client.gui; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.inventory.GuiContainerCreative; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.GlStateManager.DestFactor; +import net.minecraft.client.renderer.GlStateManager.SourceFactor; +import net.minecraft.client.resources.I18n; +import net.minecraft.creativetab.CreativeTabs; +import pl.pabilo8.immersiveintelligence.client.IIClientUtils; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.util.IIDrawUtils; +import pl.pabilo8.immersiveintelligence.common.util.IIColor; +import pl.pabilo8.immersiveintelligence.common.util.IIReference; + +import javax.annotation.Nullable; + +/** + * Inventory button opening the faction invitation list. + * + * @author Pabilo8 + * @since 22.07.2026 + */ +public class GuiButtonFactionInvitations extends GuiButton +{ + @Nullable + private final GuiContainerCreative creative; + + public GuiButtonFactionInvitations(int x, int y, @Nullable GuiContainerCreative creative) + { + super(1109, x, y, 16, 16, ""); + this.creative = creative; + } + + @Override + public void drawButton(Minecraft mc, int mouseX, int mouseY, float partialTicks) + { + //Only show in inventory tab in creative mode + if(creative!=null) + visible = creative.getSelectedTabIndex()==CreativeTabs.INVENTORY.getTabIndex(); + + if(visible) + { + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GlStateManager.translate(0, 0, 1000); + GlStateManager.tryBlendFuncSeparate(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO); + GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); + IIClientUtils.bindAtlas(); + this.hovered = mouseX >= this.x&&mouseY >= this.y&&mouseX < this.x+this.width&&mouseY < this.y+this.height; + + //Hover animation + IIDrawUtils.startTextured() + .drawTexSprite(x, y, width, height, isMouseOver()?DecoTextures.ICON_INVENTORY_FACTION_INVITES_ACTIVE: DecoTextures.ICON_INVENTORY_FACTION_INVITES) + .finish(); + if(isMouseOver()) + drawCenteredString(IIClientUtils.fontRegular, I18n.format(IIReference.GUI_TOOLTIP_KEY+"button.factions"), x+8, y+12, IIColor.WHITE.getPackedRGB()); + + GlStateManager.popMatrix(); + + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/GuiFactionInvitation.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/GuiFactionInvitation.java new file mode 100644 index 000000000..543c1171a --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/GuiFactionInvitation.java @@ -0,0 +1,189 @@ +package pl.pabilo8.immersiveintelligence.client.gui; + +import blusunrize.immersiveengineering.client.ClientProxy; +import blusunrize.immersiveengineering.client.ClientUtils; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.EntityPlayer; +import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoPlayerGui; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoList; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoTitleLabel; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanelBuilder; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBannerDisplay; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Graphics; +import pl.pabilo8.immersiveintelligence.common.IIGUI; +import pl.pabilo8.immersiveintelligence.common.gui.ContainerPlayerGui; +import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; +import pl.pabilo8.immersiveintelligence.common.network.messages.MessageDiplomacyAction; +import pl.pabilo8.immersiveintelligence.common.network.messages.MessageDiplomacySync; +import pl.pabilo8.immersiveintelligence.common.util.IIColor; +import pl.pabilo8.immersiveintelligence.common.util.IIReference; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler.PlayerInfo; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.OwnerIdentity; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.permission.PermissionRole; + +import java.util.List; + +/** + * Lists faction invitations addressed to the local player. Uses vanilla GUI style. + * + * @author Pabilo8 + * @since 22.07.2026 + */ +@DecoTemplate(name = "faction_invitations", category = DecoGuiCategory.GENERIC_PLAYER) +public class GuiFactionInvitation extends DecoPlayerGui +{ + public GuiFactionInvitation(EntityPlayer player) + { + super(player, IIGUI.FACTION_INVITATIONS); + } + + @Override + public void onInit() + { + OwnerIdentity userIdentity = DiplomacyHandler.getLocalPlayerIdentity(); + startBackground() + .conditionally(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.VANILLA, + b -> b.withBox(DecoTextures.BG_VANILLA, DecoTextures.TEMPLATE_ROUND, 0, 0, 220, 144)) + .conditionally(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.WOODEN, + b -> b.withBox(DecoTextures.BG_WOODEN, DecoTextures.TEMPLATE_ROUND_WOODEN, 0, 0, 220, 144)) + .withTitleBar(I18n.format(IIReference.GUI_LABEL_KEY+"faction_invitation")) + .build(); + + //Fallback, for when faction/player info is missing + if(userIdentity==DiplomacyHandler.NEUTRAL) + { + addLabel(IIReference.GUI_LABEL_KEY+"faction_invitation.invalid", 0, 0) + .withSize(220, 144) + .withAlign(DecoAlignment.CENTER); + IIPacketHandler.INSTANCE.sendToServer(MessageDiplomacySync.requestUpdateMessage()); + return; + } + String factionName = userIdentity.getDisplayName(); + PlayerInfo playerInfo = DiplomacyHandler.getInstance(true).getPlayerInfo(ClientUtils.mc().player); + String playerName = playerInfo.getName(); + PermissionRole role = userIdentity.getRoleOf(player.getUniqueID()); + String roleName = role==null?"unknown": role.getDisplayName(); + + //Current faction status + if(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.VANILLA) + addLabel(IIReference.GUI_LABEL_KEY+"faction_invitation.current", 4, 8); + else + addLabel(new DecoTitleLabel(this.fontRenderer, 4+4, 8).withBackgroundLocation(DecoTextures.LABEL_WOODEN) + .withText(IIReference.GUI_LABEL_KEY+"faction_invitation.current") + .withSize(fontRenderer.getStringWidth(I18n.format(IIReference.GUI_LABEL_KEY+"faction_invitation.current"))+4, 10) + ); + + DecoPanel userPanel = addComponent(new DecoPanel(4, 8+12-2) + .withSize(204+16-4, 22) + ); + if(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.VANILLA) + userPanel.withBackground(DecoTextures.BG_VANILLA); + else + userPanel.withBackground(DecoTextures.BG_PAPER) + .withBackgroundMask(DecoTextures.TEMPLATE_PAPER); + userPanel.addComponents( + //Player head + new DecoImage(3, 3) + .withSize(16, 16) + .withImageLocation(playerInfo.getSkin()) + .withUV(64, 8, 8, 16, 16), + //Faction banner + new DecoBannerDisplay(220-48+4, 3) + .withBanner(userIdentity.getBanner()) + ); + + //Info text + DecoLabel userLabel; + if(userIdentity.getMembers().size()==1&&factionName.equals(playerName)) + userLabel = userPanel.addLabel(new DecoLabel(ClientProxy.itemFont, 2+18+1, 0)) + .withText(I18n.format(IIReference.GUI_LABEL_KEY+"faction_invitation.current.alone", playerName)); + else + userLabel = userPanel.addLabel(new DecoLabel(ClientProxy.itemFont, 2+18+1, 0)) + .withRawText(I18n.format(IIReference.GUI_LABEL_KEY+"faction_invitation.current.faction", + playerName, roleName, userIdentity.getColor().getHexCol(factionName) + )); + userLabel.withSize(128+16+16-6, 22) + .withAlign(DecoAlignment.CENTER) + .withWrapping(true); + + //Invitation list panel + if(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.VANILLA) + addLabel(IIReference.GUI_LABEL_KEY+"faction_invitation.invitations", 4, 42+2-2); + else + addLabel(new DecoTitleLabel(this.fontRenderer, 4+4, 42+2-2) + .withBackgroundLocation(DecoTextures.LABEL_WOODEN) + .withText(IIReference.GUI_LABEL_KEY+"faction_invitation.invitations") + .withSize(fontRenderer.getStringWidth(I18n.format(IIReference.GUI_LABEL_KEY+"faction_invitation.invitations"))+4, 10) + .withAlign(DecoAlignment.CENTER) + ); + List invitations = DiplomacyHandler.getInstance(true) + .getPendingInvitationIdentitiesForPlayer(player.getUniqueID()); + + if(invitations.isEmpty()) + { + //No invitations info + DecoPanel cardPanel = addComponent(new DecoPanel(4, 42+12-2).withSize(204+16-4, 86)); + if(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.VANILLA) + cardPanel.withBackground(DecoTextures.BG_VANILLA); + else + cardPanel.withBackground(DecoTextures.BG_PAPER) + .withBackgroundMask(DecoTextures.TEMPLATE_PAPER); + addLabel(IIReference.GUI_LABEL_KEY+"faction_invitation.invitations.none", 8, 28+136/2-12) + .withSize(204, 16) + .withAlign(DecoAlignment.CENTER); + } + else //Invitation list + { + DecoList list = addComponent(new DecoList(4, 42+12-2) + .withSize(204+16-4, 86) + .withEntries(invitations) + .withDisplayFunction(new DecoEntryPanelBuilder() + .withHeight(22) + .withBackground(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.VANILLA?DecoTextures.BG_VANILLA: DecoTextures.BG_PAPER) + .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) + //Faction banner and name + .withComponent("banner", p -> new DecoBannerDisplay(3, 3) + .withSize(32, 20) + ) + .withLabel("name", p -> new DecoLabel(fontRenderer, 34, 4) + .withSize(128, 16) + .withAlign(DecoAlignment.LEFT)) + //Accept and Reject buttons + .withComponent("accept", p -> new DecoButton(p.width-32-2-2, 4-1) + .withTemplate(DecoTemplates.ACTION_BUTTON_ACCEPT) + .withSize(16, 16) + .withBackgroundColor(IIColor.fromPackedRGB(0x698756)) + .withOnLMBPressed(() -> { + IIPacketHandler.sendToServer(MessageDiplomacyAction.acceptInvitation(p.getCurrentElement().getUUID())); + closeGUI(); + }) + ) + .withComponent("reject", p -> new DecoButton(p.width-17-2, 4-1) + .withTemplate(DecoTemplates.ACTION_BUTTON_REJECT) + .withSize(16, 16) + .withBackgroundColor(IIColor.fromPackedRGB(0x8C5353)) + .withOnLMBPressed(() -> { + IIPacketHandler.sendToServer(MessageDiplomacyAction.denyInvitation(p.getCurrentElement().getUUID())); + closeGUI(); + }) + ) + .withElementApplyMethod((identity, entry) -> { + entry.label("name").withRawText(identity.getDisplayName()); + entry.component("banner", DecoBannerDisplay.class).withBanner(identity.getBanner()); + }) + ) + ); + if(Graphics.decoVanillaGUIStyle==DecoVanillaGUIStyle.VANILLA) + list.withBackground(DecoTextures.BG_VANILLA) + .withListBackground(DecoTextures.BG_VANILLA) + .withListBackgroundColor(IIColor.BLACK) + .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_VANILLA); + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/ITabbedGui.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/ITabbedGui.java deleted file mode 100644 index 78539a76c..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/ITabbedGui.java +++ /dev/null @@ -1,35 +0,0 @@ -package pl.pabilo8.immersiveintelligence.client.gui; - -import blusunrize.immersiveengineering.api.DimensionBlockPos; -import net.minecraft.tileentity.TileEntity; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; -import pl.pabilo8.immersiveintelligence.client.ClientProxy; -import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; - -/** - * @author Pabilo8 (pabilo@iiteam.net) - * @since 05.07.2019 - */ -@SideOnly(Side.CLIENT) -@Deprecated -public interface ITabbedGui -{ - default boolean positionEqual(TileEntity tile) - { - assert ImmersiveIntelligence.proxy instanceof ClientProxy; - EasyNBT nbt = ((ClientProxy)ImmersiveIntelligence.proxy).getStoredGuiData(); - - if(!nbt.hasKey("pos")) - return false; - return new DimensionBlockPos(tile).equals(nbt.getDimPos("pos")); - } - - default EasyNBT saveBasicData(TileEntity tile) - { - assert ImmersiveIntelligence.proxy instanceof ClientProxy; - return ((ClientProxy)ImmersiveIntelligence.proxy).setStoredGuiData() - .withDimPos("pos", new DimensionBlockPos(tile)); - } -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiAmmunitionCrate.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiAmmunitionCrate.java index 583e83e5c..d1247dacd 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiAmmunitionCrate.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiAmmunitionCrate.java @@ -3,10 +3,10 @@ import net.minecraft.entity.player.EntityPlayer; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.metal_device.tileentity.effect_crate.TileEntityAmmunitionCrate; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiChemicalBath.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiChemicalBath.java index bbb05c866..40277ecb1 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiChemicalBath.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiChemicalBath.java @@ -7,7 +7,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoFluidTank; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityChemicalBath; @@ -25,7 +24,7 @@ public class GuiChemicalBath extends DecoTileGui this.scroll = gui.getScroll()) .withDisplayFunction(new DecoEntryPanelBuilder() .withHeight(32+16-6+2) - .withLabel("from", new DecoLabel(this.fontRenderer, 4, 4) + .withLabel("from", p -> new DecoLabel(this.fontRenderer, 4, 4) .withText(IIReference.GUI_LABEL_KEY+"redstone_data_interface.from."+(redstoneToData?"redstone": "data")) .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"redstone_data_interface.from."+(redstoneToData?"redstone": "data")+".tooltip") ) - .withLabel("to", new DecoLabel(this.fontRenderer, 4, 16+2) + .withLabel("to", p -> new DecoLabel(this.fontRenderer, 4, 16+2) .withText(IIReference.GUI_LABEL_KEY+"redstone_data_interface.to."+(redstoneToData?"data": "redstone")) .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"redstone_data_interface.to."+(redstoneToData?"data": "redstone")+".tooltip") ) - .withLabel("mode", new DecoLabel(this.fontRenderer, 4, 16+14+2-1) + .withLabel("mode", p -> new DecoLabel(this.fontRenderer, 4, 16+14+2-1) .withText(IIReference.GUI_LABEL_KEY+"redstone_data_interface.mode") .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"redstone_data_interface.mode.tooltip") ) //Color - .withComponent("color_icon", new DecoImage(32+8+2+48-4+2+24+8, colorY) + .withComponent("color_icon", p -> new DecoImage(32+8+2+48-4+2+24+8, colorY) .withSize(8, 8) .withImageLocation(DecoTextures.COMPONENT_COLOR, true) .withUV(16, 4, 4, 12, 12) ) - .withLabel("color_label", + .withLabel("color_label", p -> new DecoLabel(IIClientUtils.fontRegular, 4, colorY-1) .withSize(136-48-8+2+24+8, 12) .withAlign(DecoAlignment.RIGHT) @@ -156,7 +155,7 @@ public void onInit() }) ) //Variable - .withLabel("variable_label", + .withLabel("variable_label", p -> new DecoLabel(IIClientUtils.fontRegular, 4, variableY) .withSize(136-48+4+1+24+8, 12) .withAlign(DecoAlignment.RIGHT) @@ -168,7 +167,7 @@ public void onInit() .setVariable(IIUtils.cycleDataPacketChars(builder.getCurrentElement().getVariable(), arrow, false))) ) //Mode - .withLabel("mode_label", + .withLabel("mode_label", p -> new DecoLabel(IIClientUtils.fontRegular, 4, 16+14+1-1) .withSize(136-48+4+1+24+8, 12) .withAlign(DecoAlignment.RIGHT) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiElectrolyzer.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiElectrolyzer.java index d591a1c1e..bfab95616 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiElectrolyzer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiElectrolyzer.java @@ -8,7 +8,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoFluidTank; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityElectrolyzer; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFiller.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFiller.java index f31821d98..732cb829f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFiller.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFiller.java @@ -7,7 +7,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBar; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoDustTank; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.Filler; import pl.pabilo8.immersiveintelligence.common.IIGUI; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFuelStation.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFuelStation.java index aaf86a32a..03b35cdfe 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFuelStation.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiFuelStation.java @@ -6,7 +6,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBar; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoFluidTank; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.TileEntityFuelStation; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiGearbox.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiGearbox.java index 77a952cb0..929a80381 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiGearbox.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiGearbox.java @@ -6,7 +6,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBarGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.rotary_device.tileentity.TileEntityGearbox; import pl.pabilo8.immersiveintelligence.common.gui.ContainerGearbox; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMedicalCrate.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMedicalCrate.java index c553be1e3..ba4b5061a 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMedicalCrate.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMedicalCrate.java @@ -6,11 +6,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoFluidTank; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoResource; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.metal_device.tileentity.effect_crate.TileEntityMedicalCrate; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMetalCrate.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMetalCrate.java index 906c2aaa0..4aba53853 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMetalCrate.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiMetalCrate.java @@ -2,10 +2,10 @@ import net.minecraft.entity.player.EntityPlayer; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.metal_device.tileentity.TileEntityMetalCrate; import pl.pabilo8.immersiveintelligence.common.gui.ContainerIICrate; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrecisionAssembler.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrecisionAssembler.java index c9a9a7f36..f28f06241 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrecisionAssembler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrecisionAssembler.java @@ -7,7 +7,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBar; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityPrecisionAssembler; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrintingPress.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrintingPress.java index 9495a41a8..098c7e4b3 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrintingPress.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiPrintingPress.java @@ -7,7 +7,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoFluidTank; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityPrintingPress; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiProjectileWorkshop.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiProjectileWorkshop.java index b2df81d98..a8cd74b65 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiProjectileWorkshop.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiProjectileWorkshop.java @@ -24,6 +24,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoDropdown; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoElementDisplays.DecoElementSorter; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanelBuilder; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; @@ -33,7 +34,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.util.amt.parts.AMTBullet; import pl.pabilo8.immersiveintelligence.client.util.amt.parts.AMTBullet.BulletState; import pl.pabilo8.immersiveintelligence.client.util.amt.parts.AMTLocator; @@ -47,6 +47,7 @@ import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; import pl.pabilo8.immersiveintelligence.common.util.multiblock.util.MultiblockInteractablePart; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -281,7 +282,6 @@ private void addCoreWorkshopComponents() .withDropdownWidth(144) .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_PAPER) .withBackground(DecoTextures.COMPONENT_BUTTON_PAPER) - .withListBackground(DecoTextures.COMPONENT_TEXT_FIELD) .withEntries(CoreType.values()) .withSelectedEntry(coreType) .withOnSelectedEntry((oldType, newType) -> this.coreType = newType) @@ -289,10 +289,10 @@ private void addCoreWorkshopComponents() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) //Type Icon, Label, and Letter - .withComponent("icon", new DecoItemStackDisplay(2, 2) + .withComponent("icon", p -> new DecoItemStackDisplay(2, 2) .withSize(16, 18) ) - .withLabel("label", + .withLabel("label", p -> new DecoLabel(fontRenderer, 20, 2) .withSize(48, 18) .withAlign(DecoAlignment.LEFT) @@ -308,18 +308,34 @@ private void addCoreWorkshopComponents() .withDropdownWidth(144) .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_PAPER) .withBackground(DecoTextures.COMPONENT_BUTTON_PAPER) - .withListBackground(DecoTextures.COMPONENT_TEXT_FIELD) .withEntries(AmmoRegistry.getAllAmmoItems()) + .withSortFunction(new DecoElementSorter>() + { + @Override + public List> sort(List> elements) + { + return elements; + } + + @Nullable + @Override + public List> autocomplete(List> elements, String input) + { + return elements.stream() + .filter(e -> e.getName().toLowerCase().startsWith(input.toLowerCase())) + .collect(Collectors.toList()); + } + }) .withSelectedEntry(ammoType) .withOnSelectedEntry((oldType, newType) -> this.ammoType = newType) .withDisplayFunction(new DecoEntryPanelBuilder>() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) //Type Icon, Label, and Letter - .withComponent("icon", new DecoItemStackDisplay(2, 2) + .withComponent("icon", p -> new DecoItemStackDisplay(2, 2) .withSize(16, 18) ) - .withLabel("label", + .withLabel("label", p -> new DecoLabel(fontRenderer, 20, 2) .withSize(48, 18) .withAlign(DecoAlignment.LEFT) @@ -426,15 +442,14 @@ private void updateCoreInfo() .withSelectedEntry(ammoCore) .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_PAPER) .withBackground(DecoTextures.COMPONENT_BUTTON_PAPER) - .withListBackground(DecoTextures.COMPONENT_TEXT_FIELD) .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) //Type Icon, Label, and Letter - .withComponent("icon", new DecoItemStackDisplay(2, 1) + .withComponent("icon", p -> new DecoItemStackDisplay(2, 1) .withSize(16, 16) ) - .withLabel("label", + .withLabel("label", p -> new DecoLabel(fontRenderer, 20, 2) .withSize(48, 16) .withAlign(DecoAlignment.LEFT) @@ -443,6 +458,23 @@ private void updateCoreInfo() ) .withElementApplyMethod(this::drawAmmoCoreEntry) ) + .withSortFunction(new DecoElementSorter() + { + @Override + public List sort(List elements) + { + return elements; + } + + @Nullable + @Override + public List autocomplete(List elements, String input) + { + return elements.stream() + .filter(e -> e.getName().toLowerCase().startsWith(input.toLowerCase())) + .collect(Collectors.toList()); + } + }) .withOnSelectedEntry((ammoCoreOld, ammoCoreNew) -> { this.ammoCore = ammoCoreNew; updateCoreInfo(); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiRepairCrate.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiRepairCrate.java index 980e6684a..5ea68306f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiRepairCrate.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiRepairCrate.java @@ -4,11 +4,7 @@ import net.minecraft.util.ResourceLocation; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoResource; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.metal_device.tileentity.effect_crate.TileEntityRepairCrate; import pl.pabilo8.immersiveintelligence.common.gui.ContainerRepairCrate; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSawmill.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSawmill.java index 73c4c6d49..664195a60 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSawmill.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSawmill.java @@ -6,7 +6,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBarGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.wooden_multiblock.tileentity.TileEntitySawmill; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycartStation.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycartStation.java index f653afc51..69aac948c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycartStation.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycartStation.java @@ -6,7 +6,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBarGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.wooden_multiblock.tileentity.TileEntitySkyCartStation; import pl.pabilo8.immersiveintelligence.common.gui.ContainerSkycartStation; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycrateStation.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycrateStation.java index c95d84044..1e265938d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycrateStation.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSkycrateStation.java @@ -6,7 +6,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBarGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.wooden_multiblock.tileentity.TileEntitySkyCrateStation; import pl.pabilo8.immersiveintelligence.common.gui.ContainerSkycrateStation; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSmallCrate.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSmallCrate.java index ba27b1a29..c5b3f460b 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSmallCrate.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiSmallCrate.java @@ -2,10 +2,10 @@ import net.minecraft.entity.player.EntityPlayer; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.simple.tileentity.TileEntitySmallCrate; import pl.pabilo8.immersiveintelligence.common.gui.ContainerIICrate; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiTileUpgrade.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiTileUpgrade.java index 3dba60d5e..433f29be8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiTileUpgrade.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiTileUpgrade.java @@ -14,6 +14,8 @@ import pl.pabilo8.immersiveintelligence.api.upgrade.UpgradeUtils.UpgradeOperation; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTab; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTabGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoTreeDisplay; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; @@ -25,7 +27,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.tree.upgrade.UpgradeTechTreeWrapper; import pl.pabilo8.immersiveintelligence.client.gui.deco.tree.upgrade.UpgradeTreeNodeRenderer; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.util.amt.models.AMTModel; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.gui.ContainerTileUpgrade; @@ -51,6 +52,8 @@ public class GuiTileUpgrade techTreeDisplay; private DecoPanel panelInfo; + private DecoTabGroup contentTabs; + private DecoTab infoTab; @SyncNBT(nullable = true) public String lastUpgrade; @@ -95,7 +98,7 @@ public void onInit() .withInventoryTitleBar() .build(); - //Upgrade + //Upgrade preview addComponents( new DecoPanel(4, 4+8) .withSize(108, 152) @@ -107,47 +110,52 @@ public void onInit() .withScale(0.125f) .withRotation(-12.5f, 5) .withRotationAnimation(240, 0) - .withInteractionAllowed(true), - new DecoButton(118-4, 16-8-4+14-14+8) - .withSize(69, 14) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.tech_tree") - .withOnLMBPressed(() -> { - panelInfo.visible = panelInfo.enabled = false; - techTreeDisplay.visible = techTreeDisplay.enabled = true; - refreshModelPreview(null); - }), - new DecoButton(118-4+69, 16-8-4+14-14+8) - .withSize(69, 14) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.info") - .withOnLMBPressed(() -> { - panelInfo.visible = panelInfo.enabled = true; - techTreeDisplay.visible = techTreeDisplay.enabled = false; - if(lastUpgrade!=null&&!lastUpgrade.isEmpty()) - refreshModelPreview(Upgrade.getUpgradeByID(ResLoc.of(lastUpgrade))); - }), - panelInfo = new DecoPanel(118-4, 16-8-4+14+8) - .withSize(146-8, 146-8) - .withBackground(DecoTextures.BG_STEEL) - .withBackgroundMask(DecoTextures.TEMPLATE_SQUARE), - techTreeDisplay = new DecoTreeDisplay(118-4, 16-8-4+14+8) - .withTree(new UpgradeTechTreeWrapper(techTree, tile) - { - @Override - public void onNodeClicked(@Nonnull IDecoTreeNode node) - { - panelInfo.visible = panelInfo.enabled = true; - techTreeDisplay.visible = techTreeDisplay.enabled = false; - showUpgrade(node.getUserData()); - refreshModelPreview(node.getUserData()); - } - }) - .withNodeRenderer(new UpgradeTreeNodeRenderer()) - .withSize(146-8, 146-8) - .withBackground(DecoSprite.atlasSprite(DecoTextures.BG_DARK, 64)) + .withInteractionAllowed(true) ); + final int contentX = 118-4; + final int contentY = 16-8-4+14+8; + final int contentWidth = 146-8; + final int contentHeight = 146-8; + + panelInfo = addComponent(new DecoPanel(contentX, contentY) + .withSize(contentWidth, contentHeight) + .withBackground(DecoTextures.BG_STEEL) + .withBackgroundMask(DecoTextures.TEMPLATE_SQUARE)); + + DecoPanel techTreePanel = addComponent(new DecoPanel(contentX, contentY) + .withSize(contentWidth, contentHeight) + .withBackground(null) + .withBackgroundMask(null)); + techTreeDisplay = techTreePanel.addComponent(new DecoTreeDisplay(0, 0) + .withTree(new UpgradeTechTreeWrapper(techTree, tile) + { + @Override + public void onNodeClicked(@Nonnull IDecoTreeNode node) + { + contentTabs.selectTab(infoTab, false); + showUpgrade(node.getUserData()); + refreshModelPreview(node.getUserData()); + } + }) + .withNodeRenderer(new UpgradeTreeNodeRenderer()) + .withSize(contentWidth, contentHeight) + .withBackground(DecoSprite.atlasSprite(DecoTextures.BG_DARK, 64))); + + infoTab = (DecoTab)new DecoTab() + .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.info"); + contentTabs = addComponent(new DecoTabGroup(contentX, 16-8-4+14-14+8) + .withSize(contentWidth, 14) + .withHorizontalAlignment(true) + .withTabWidth(contentWidth/2) + .withTab((DecoTab)new DecoTab() + .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.tech_tree"), techTreePanel, + () -> refreshModelPreview(null)) + .withTab(infoTab, panelInfo, () -> { + if(lastUpgrade!=null&&!lastUpgrade.isEmpty()) + refreshModelPreview(Upgrade.getUpgradeByID(ResLoc.of(lastUpgrade))); + })); + if(lastUpgrade==null) { showUpgrade(null); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiVulcanizer.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiVulcanizer.java index 865f7fce2..ac4e42a21 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiVulcanizer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/GuiVulcanizer.java @@ -9,7 +9,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBar; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoItemStackDisplay; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.client.util.amt.AMTUtils; import pl.pabilo8.immersiveintelligence.common.IIGUI; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/ammunition_production/GuiAmmunitionAssembler.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/ammunition_production/GuiAmmunitionAssembler.java index b9e5c88bc..51721fcee 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/ammunition_production/GuiAmmunitionAssembler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/ammunition_production/GuiAmmunitionAssembler.java @@ -16,12 +16,12 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.TileEntityAmmunitionAssembler; import pl.pabilo8.immersiveintelligence.common.gui.ContainerAmmunitionAssembler; import pl.pabilo8.immersiveintelligence.common.util.IIColor; import pl.pabilo8.immersiveintelligence.common.util.IIReference; +import pl.pabilo8.immersiveintelligence.common.util.IIStringUtil; import pl.pabilo8.immersiveintelligence.common.util.ResLoc; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; @@ -107,16 +107,16 @@ public void onInit() //Contact fuse has no config value if(this.fuseType==FuseType.CONTACT) textField.withText(newValue = "0"); - this.fuseConfig = newValue.isEmpty()?0: Integer.parseInt(newValue); + this.fuseConfig = IIStringUtil.parseInt(newValue); }), new DecoDropdown(4, 2) .withEntries(FuseType.values()) .withDisplayFunction(new DecoEntryPanelBuilder() //Type Icon, Label, and Letter - .withComponent("icon", new DecoImage(2, 2) + .withComponent("icon", p -> new DecoImage(2, 2) .withSize(16, 16) ) - .withLabel("label", + .withLabel("label", p -> new DecoLabel(fontRenderer, 20, 2) .withSize(48, 18) .withAlign(DecoAlignment.LEFT) @@ -126,7 +126,6 @@ public void onInit() ) .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_PAPER) .withBackground(DecoTextures.COMPONENT_BUTTON_PAPER) - .withListBackground(DecoTextures.COMPONENT_TEXT_FIELD) .withOnSelectedEntry((oldFuse, newFuse) -> { textField.withDisabled(newFuse==FuseType.CONTACT); textField.visible = newFuse!=FuseType.CONTACT; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachine.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachine.java index 951cb6604..621c0599d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachine.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachine.java @@ -24,7 +24,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBar; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.IIUtils; @@ -179,15 +178,15 @@ else if(tile.inventory.size() > editedCircuit) }) ) //Type Icon, Label, and Letter - .withComponent("image", new DecoImage(2+12, 1) + .withComponent("image", p -> new DecoImage(2+12, 1) .withSize(16, 16)) - .withLabel("typeLabel", + .withLabel("typeLabel", p -> new DecoLabel(fontRenderer, 2+12+16+2-1, -1) .withSize(48, 16) .withAlign(DecoAlignment.LEFT) .withText("Integer") ) - .withLabel("letterLabel", + .withLabel("letterLabel", p -> new DecoLabel(fontRenderer, 2, 2) .withSize(12, 16) .withAlign(DecoAlignment.CENTER) @@ -226,6 +225,7 @@ else if(tile.inventory.size() > editedCircuit) { int circuitIndex = i; addComponent(new DecoTab() + .withSelected(!isStorage&&circuitIndex==editedCircuit) .withOnPressed((gui, button, mouseX, mouseY) -> { this.editedCircuit = circuitIndex; return changeGUI(IIGUI.ARITHMETIC_LOGIC_MACHINE_VARIABLES); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineEdit.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineEdit.java index e63300f41..7d8e54cc5 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineEdit.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineEdit.java @@ -1,5 +1,6 @@ package pl.pabilo8.immersiveintelligence.client.gui.block.arithmetic_logic_machine; +import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -20,6 +21,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoSwitch; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoDropdown; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoElementDisplays.DecoElementDisplay; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoElementDisplays.DecoElementSorter; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.data_editor.DecoCodeEditor; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.data_editor.DecoDataEditor; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.data_editor.DecoDataEditorExpression; @@ -27,7 +29,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanelBuilder; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.IIUtils; @@ -40,6 +41,7 @@ import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collection; import java.util.List; @@ -150,7 +152,6 @@ public void onInit() new DecoDropdown(16+4+10+32-12+6+1-24+8, 4+8+2+1) .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_PAPER) .withBackground(DecoTextures.COMPONENT_BUTTON_PAPER) - .withListBackground(DecoTextures.COMPONENT_TEXT_FIELD) .withDropdownSymbol(DecoTextures.COMPONENT_DROPDOWN_SYMBOL_PAPER) .withSize(116+24-8, 18) .withDropdownWidth(116+24-8) @@ -158,6 +159,7 @@ public void onInit() .withEntries(circuitOperations) .withSelectedEntry(edited.getOperation().getMeta()) .withDisplayFunction(getOperationDropdownDisplayFunction()) + .withSortFunction(getOperationDropdownSortFunction()) .withOnSelectedEntry((oldMeta, newMeta) -> { cancel = true; storeEditorOutput(); @@ -345,9 +347,9 @@ private DecoElementDisplay getOperationDropdownDisplayFunctio .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) //Type Icon, Label, and Letter - .withComponent("image", new DecoImage(3, 1) + .withComponent("image", p -> new DecoImage(3, 1) .withSize(16, 16)) - .withLabel("typeLabel", + .withLabel("typeLabel", p -> new DecoLabel(fontRenderer, 2+16+2, 1) .withSize(48, 18) .withAlign(DecoAlignment.LEFT) @@ -367,6 +369,28 @@ private DecoElementDisplay getOperationDropdownDisplayFunctio .withElementTooltip(operation -> "datasystem.immersiveintelligence.function."+operation.name()+".desc"); } + private DecoElementSorter getOperationDropdownSortFunction() + { + return new DecoElementSorter() + { + @Override + public List sort(List elements) + { + return elements; + } + + @Nonnull + @Override + public List autocomplete(List elements, String input) + { + return elements.stream() + .filter(e -> I18n.format("datasystem.immersiveintelligence.function."+e.name()) + .toLowerCase().contains(input.toLowerCase())) + .collect(Collectors.toList()); + } + }; + } + @Override public void onGuiClosed() { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemory.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemory.java index 973e9fb4b..3878d9ba3 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemory.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemory.java @@ -14,7 +14,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoTaskList; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoTaskList.ListMode; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityArithmeticLogicMachine; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityArithmeticLogicMachine.MemoryTransferRule; @@ -93,22 +92,22 @@ public void onInit() .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_TICKET) - .withComponent("source", new DecoButton(3, 2) + .withComponent("source", p -> new DecoButton(3, 2) .withSize(16, 16) .withDisabled(true) .withTextDisabledColor(IIColor.fromPackedRGB(0xafafaf)) ) - .withLabel("route", new DecoLabel(fontRenderer, 3+16, 5) + .withLabel("route", p -> new DecoLabel(fontRenderer, 3+16, 5) .withRawText(" into ") .withSize(24, 12) .withAlign(DecoAlignment.CENTER) ) - .withComponent("destination", new DecoButton(3+16+24, 2) + .withComponent("destination", p -> new DecoButton(3+16+24, 2) .withSize(16, 16) .withDisabled(true) .withTextDisabledColor(IIColor.fromPackedRGB(0xafafaf)) ) - .withLabel("behavior", new DecoLabel(fontRenderer, 3+16+24+16+3, 5) + .withLabel("behavior", p -> new DecoLabel(fontRenderer, 3+16+24+16+3, 5) .withSize(24, 12) .withAlign(DecoAlignment.LEFT) ) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemoryEdit.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemoryEdit.java index fa20d507e..bfc312b6d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemoryEdit.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/arithmetic_logic_machine/GuiArithmeticLogicMachineMemoryEdit.java @@ -12,7 +12,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityArithmeticLogicMachine; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityArithmeticLogicMachine.MemoryTransferRule; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachine.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachine.java index 3f5d2c629..032d374ad 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachine.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachine.java @@ -22,7 +22,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage.ImageAnimationDirection; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.IIUtils; @@ -205,15 +204,15 @@ public void onInit() }) ) //Type Icon, Label, and Letter - .withComponent("image", new DecoImage(2+12, 1) + .withComponent("image", p -> new DecoImage(2+12, 1) .withSize(16, 16)) - .withLabel("typeLabel", + .withLabel("typeLabel", p -> new DecoLabel(fontRenderer, 2+12+16+2, 2) .withSize(48, 16) .withAlign(DecoAlignment.LEFT) .withText("Integer") ) - .withLabel("letterLabel", + .withLabel("letterLabel", p -> new DecoLabel(fontRenderer, 2, 2) .withSize(12, 16) .withAlign(DecoAlignment.CENTER) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachineEdit.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachineEdit.java index fe2fff698..839e4d044 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachineEdit.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_input_machine/GuiDataInputMachineEdit.java @@ -12,7 +12,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoDropdownDataLetters; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoDropdown; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.data_editor.DecoDataEditor; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; @@ -120,13 +119,13 @@ public void onInit() new DecoDropdown>(73, 15) .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_PAPER) .withBackground(DecoTextures.COMPONENT_BUTTON_PAPER) - .withListBackground(DecoTextures.COMPONENT_TEXT_FIELD) .withSize(116, 18) .withDropdownWidth(116) .withMaxDisplayedEntries(5) .withEntries(DecoDataEditor.getEditorTypes(tile.isUpgradeInstalled(IIContent.UPGRADE_ADVANCED_DATA))) .withSelectedEntry(variableToEdit.getValue().getTypeMeta()) .withDisplayFunction(DecoTemplates.getDataTypeEntryDisplay()) + .withSortFunction(DecoTemplates.getDataTypeEntrySorter()) .withOnSelectedEntry((typeMetaInfo, typeMetaInfo2) -> { cancel = true; variableToEdit = new DataVariable(variableToEdit.getName(), typeMetaInfo2.supplier.get()); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMerger.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMerger.java index bc16e0d9d..af32f7d77 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMerger.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMerger.java @@ -10,7 +10,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoTaskList; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoTaskList.ListMode; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataMerger; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataMerger.DataMergeRule; @@ -79,12 +78,12 @@ public void onInit() .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_TICKET) - .withComponent("variable", new DecoButton(3, 2) + .withComponent("variable", p -> new DecoButton(3, 2) .withSize(20, 16) .withDisabled(true) .withTextDisabledColor(IIColor.fromPackedRGB(0xafafaf)) ) - .withLabel("route", new DecoLabel(fontRenderer, 26, 5) + .withLabel("route", p -> new DecoLabel(fontRenderer, 26, 5) .withSize(116, 12) .withAlign(DecoAlignment.LEFT) ) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMergerEdit.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMergerEdit.java index 42180bb7d..ea2270ebc 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMergerEdit.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_merger/GuiDataMergerEdit.java @@ -12,7 +12,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataMerger; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataMerger.DataMergeRule; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouter.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouter.java index 2395e7878..7eebb49cd 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouter.java @@ -10,7 +10,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoTaskList; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoTaskList.ListMode; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataRouter; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataRouter.DataRoutingRule; @@ -80,12 +79,12 @@ public void onInit() .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_TICKET) - .withComponent("action", new DecoButton(3, 2) + .withComponent("action", p -> new DecoButton(3, 2) .withSize(36, 16) .withDisabled(true) .withTextDisabledColor(IIColor.fromPackedRGB(0xafafaf)) ) - .withLabel("route", new DecoLabel(fontRenderer, 3+36+2, 5) + .withLabel("route", p -> new DecoLabel(fontRenderer, 3+36+2, 5) .withSize(58, 12) .withAlign(DecoAlignment.LEFT) ) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouterEdit.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouterEdit.java index 70f3ed855..6b173c454 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouterEdit.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/data_router/GuiDataRouterEdit.java @@ -21,7 +21,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataRouter; import pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity.TileEntityDataRouter.DataRoutingRule; @@ -238,6 +237,7 @@ private void addValueFilterSection(DecoPanel panel, int yy) .withEntries(DecoDataEditor.getEditorTypes(false)) .withSelectedEntry(edited.expectedValue.getTypeMeta()) .withDisplayFunction(DecoTemplates.getDataTypeEntryDisplay()) + .withSortFunction(DecoTemplates.getDataTypeEntrySorter()) .withOnSelectedEntry((oldType, newType) -> { storeEditorValue(); edited.expectedValue = newType==null?new DataTypeNull(): newType.supplier.get(); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/emplacement/GuiEmplacement.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/emplacement/GuiEmplacement.java index 2ee33876b..8eb75fa67 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/emplacement/GuiEmplacement.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/emplacement/GuiEmplacement.java @@ -3,8 +3,8 @@ import net.minecraft.entity.player.EntityPlayer; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoAlignment; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.emplacement.TileEntityEmplacement; import pl.pabilo8.immersiveintelligence.common.gui.ContainerEmplacement; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpole.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpole.java index 33ede1c38..48b74b7b3 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpole.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpole.java @@ -10,11 +10,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.map.DecoMapDefaultColorMapper; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.map.IDecoMapColorMapper; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.map.scanners.BlockTypeScanner; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoAlignment; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityArtilleryHowitzer; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.TileEntityFlagpole; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpoleFaction.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpoleFaction.java index ed0d734d7..d5c5b4032 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpoleFaction.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/flagpole/GuiFlagpoleFaction.java @@ -5,14 +5,16 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemBanner; import net.minecraft.item.ItemStack; +import net.minecraft.util.text.TextFormatting; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoSwitch; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTab; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTabGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoDropdown; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoElementDisplays; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoList; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoTitleLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoColorPickerPanel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanelBuilder; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; @@ -20,26 +22,27 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.DecoImage; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.TileEntityFlagpole; import pl.pabilo8.immersiveintelligence.common.gui.ContainerFlagpole; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageDiplomacyAction; import pl.pabilo8.immersiveintelligence.common.util.IIColor; +import pl.pabilo8.immersiveintelligence.common.util.IIReference; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler.PlayerInfo; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.OwnerIdentity; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.permission.PermissionCategory; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.permission.PermissionRole; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; /** * @author Pabilo8 (pabilo@iiteam.net) * @author Avalon (avalon@iiteam.net) - * @updates 03.11.2026 * @ii-approved 0.3.1 + * @updated 27.07.2026 * @since 27.12.2025 */ @DecoTemplate(name = "flagpole_faction", category = DecoGuiCategory.TERRITORY_CONTROL_TILE) @@ -63,10 +66,9 @@ public void onInit() assert connection!=null; startBackground() - .withBox(DecoTextures.BG_STEEL, 0, 0, 248+32, 152+32) + .withBox(DecoTextures.BG_STEEL, 0, 0, 152+96, 152) .withTitleBar(tile) - .withNextLayer() - .withBox(DecoTextures.BG_WOODEN, DecoTextures.TEMPLATE_ROUND_WOODEN, 32+16, 152+32, 176, 92) + .withBox(DecoTextures.BG_WOODEN, DecoTextures.TEMPLATE_ROUND_WOODEN, 32, 152, 176, 92) .withInventorySlots(SlotStyle.VANILLA, container.inventorySlots) .withInventoryTitleBar() .withFrame(DecoTextures.FRAME_WOODEN_THIN, 4, false, new boolean[]{true, false, false, false}) @@ -76,164 +78,277 @@ public void onInit() addLinkTab(IIGUI.FLAGPOLE, DecoTextures.ICON_MAP, "map_module"); addLinkTab(IIGUI.FLAGPOLE_FACTION, DecoTextures.ICON_FACTION_CONFIG, "faction_module"); - //Faction Name - addLabel(new DecoTitleLabel(fontRenderer, 6, 4) - .withBackgroundLocation(DecoTextures.LABEL_STEEL) - .withSize(60, 8) - .withRawText("Name") + //Content tabs + addComponent(new DecoTabGroup(4, 4+4+1) + .withSize(152+96-8, 14) + .withHorizontalAlignment(true) + .withSpacing(1) + .withTab((DecoTab)new DecoTab().withText(IIReference.GUI_LABEL_KEY+"faction_management.insignia") + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.insignia.tooltip"), + buildInsigniaPage(identity)) + .withTab((DecoTab)new DecoTab().withText(IIReference.GUI_LABEL_KEY+"faction_management.members") + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.members.tooltip"), + buildMembersPage(identity)) + .withTab((DecoTab)new DecoTab().withText(IIReference.GUI_LABEL_KEY+"faction_management.invitations") + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.invitations.tooltip"), + buildInvitationsPage(identity, connection)) + .withTab((DecoTab)new DecoTab().withText(IIReference.GUI_LABEL_KEY+"faction_management.permissions") + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.permissions.tooltip"), + buildRolesPage(identity)) ); + } + + private DecoPanel createContentPanel() + { + return addComponent(new DecoPanel(4, 20+2) + .withSize(152+96-8, 160-16-16+2) + .withBackground(DecoTextures.BG_STEEL) + .withBackgroundMask(DecoTextures.TEMPLATE_SQUARE) + ); + } - //Banner box - addComponent(new DecoBannerDisplay(5+1, 15+1) - .withSize(40+2, 20+2) + private DecoPanel buildInsigniaPage(OwnerIdentity identity) + { + DecoPanel panel = createContentPanel(); + boolean disabled = !identity.isPermitted(mc.player, PermissionCategory.MODIFY_INSIGNIA); + + DecoPanel topPanel = panel.addComponent(new DecoPanel(2, 2) + .withSize(panel.width-4, 36-2) + .withBackground(DecoTextures.BG_PAPER) + .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) + ); + + topPanel.addComponent(new DecoBannerDisplay(4, 4) + .withSize((int)(44*1.25f), (int)(24*1.25f)) .withBackgroundTexture(DecoSprite.atlasSprite(DecoTextures.SLOT_IE, 32, true)) .withBanner(identity.getBanner()) .withOnPressed((gui, button, mouseX, mouseY) -> { ItemStack stack = getMouseHeldItemStack(); - if(stack.getItem() instanceof ItemBanner) - { - ItemStack copy = stack.copy(); - copy.setCount(1); - gui.withBanner(copy); - this.factionBanner = copy; - return true; - } - return false; + if(!(stack.getItem() instanceof ItemBanner)) + return false; + ItemStack copy = stack.copy(); + copy.setCount(1); + gui.withBanner(copy); + factionBanner = copy; + return true; }) - .withDisabled(!identity.isPermitted(mc.player, PermissionCategory.MODIFY_INSIGNIA)) + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.insignia.banner.tooltip") + .withDisabled(disabled) ); - addComponent(new DecoTextField(26+20, 14+2) - .withSize(120-20+4, 16) + + topPanel.addLabel(new DecoLabel(fontRenderer, 64-4, 3) + .withSize(panel.width-76+8, 12) + .withAlign(DecoAlignment.LEFT) + .withText(IIReference.GUI_LABEL_KEY+"faction_management.insignia.name") + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.insignia.name.tooltip") + ); + topPanel.addComponent(new DecoTextField(64-4, 2+12) + .withSize(panel.width-76+8, 16) .withText(identity.getDisplayName()) .withMaxStringLength(32) - .withDisabled(!identity.isPermitted(mc.player, PermissionCategory.MODIFY_INSIGNIA)) - .withOnTextChanged(s -> { - this.factionName = s.isEmpty()?null: s; - }) + .withDisabled(disabled) + .withOnTextChanged(s -> factionName = s.isEmpty()?null: s) + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.insignia.name.tooltip") ); - //Color header - addLabel(new DecoTitleLabel(fontRenderer, 6, 34+4) - .withBackgroundLocation(DecoTextures.LABEL_STEEL) - .withSize(60, 8) - .withRawText("Color") + panel.addComponent(new DecoColorPickerPanel(4, 38+2) + .withOnColorChanged((oldColor, newColor) -> factionColor = newColor) + .withColor(identity.getColor()) + .withSize(panel.width-8, 82) + .withDisabled(disabled) ); + return panel; + } - //Color picker - addComponent(new DecoColorPickerPanel(4, 44+4+1) - { - @Override - protected boolean initialize() - { - if(!super.initialize()) - return false; - //Hide the dye color dropdown - dyeColor.visible = false; - dyeColor.enabled = false; - if(!labels.isEmpty()) - labels.remove(labels.size()-1); - return true; - } - } - .withOnColorChanged((oldColor, newColor) -> { - factionColor = newColor; + private DecoPanel buildMembersPage(OwnerIdentity identity) + { + DecoPanel panel = createContentPanel(); + boolean canRemove = identity.isPermitted(mc.player, PermissionCategory.REMOVE_MEMBERS); + boolean canChangeRoles = identity.isOwner(mc.player.getUniqueID()); + DiplomacyHandler handler = DiplomacyHandler.getInstance(true); + List assignableRoles = identity.getAvailableRoles().values().stream() + .filter(role -> !role.isOwner()) + .collect(Collectors.toList()); + + List members = identity.getMembers().stream() + .sorted(Comparator + .comparing((UUID uuid) -> handler.getPlayerInfo(uuid).getName(), String.CASE_INSENSITIVE_ORDER) + .thenComparing(UUID::toString)) + .collect(Collectors.toList()); + + panel.addComponent(new DecoList(2, 2) + .withSize(panel.width-4, 152-26-2) + .withEntries(members) + .withDisplayFunction(new DecoEntryPanelBuilder() + .withHeight(22) + .withBackground(DecoTextures.BG_PAPER) + .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) + .withLabel("name", () -> new DecoLabel(fontRenderer, 20, 3) + .withSize(78, 16) + .withAlign(DecoAlignment.LEFT)) + .withComponent("head", () -> new DecoImage(3, 3).withSize(16, 16)) + .withComponent("role", p -> new DecoDropdown(p.width-118, 3) + .withSize(96, 16) + .withEntries(assignableRoles) + .withDisplayFunction(DecoElementDisplays.getSimpleTextDisplay(PermissionRole::getDisplayName))) + .withComponent("remove", p -> new DecoButton(p.width-19, 3) + .withSize(16, 16) + .withTemplate(DecoTemplates.ACTION_BUTTON_REMOVE) + .withOnLMBPressed(() -> { + UUID current = p.getCurrentElement(); + IIPacketHandler.sendToServer(MessageDiplomacyAction.removeMember(current)); + p.getCurrentList().removeEntry(current); + })) + .withElementApplyMethod((uuid, entry) -> { + PlayerInfo info = handler.getPlayerInfo(uuid); + PermissionRole role = identity.getRoleOf(uuid); + entry.label("name").withRawText(info.getName()); + entry.component("head", DecoImage.class) + .withImageLocation(info.getSkin()) + .withUV(64, 8, 8, 16, 16); + + DecoDropdown dropdown = entry.component("role", DecoDropdown.class); + dropdown.withEntries(role!=null&&role.isOwner()?Collections.singletonList(role): assignableRoles) + .withSelectedEntry(role) + .withDisabled(!canChangeRoles||role==null||role.isOwner()) + .withOnSelectedEntry((oldRole, newRole) -> { + if(newRole!=null&&!newRole.equals(oldRole)) + IIPacketHandler.sendToServer(MessageDiplomacyAction.changeMemberRole(uuid, newRole)); + }); + + entry.component("remove", DecoButton.class) + .withDisabled(!canRemove||role==null||role.isOwner()||uuid.equals(mc.player.getUniqueID())); }) - .withColor(identity.getColor()) - .withSize(144+4, 44) - .withDisabled(!identity.isPermitted(mc.player, PermissionCategory.MODIFY_INSIGNIA)) + ) ); + return panel; + } - //Members Panel - DecoPanel panelMembers = addComponent(new DecoPanel(4, 90+8-1) - .withSize(144+4, 58+24+1) - .withBackground(DecoTextures.BG_PAPER) - .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) - .withTitleLabel("Members", DecoAlignment.TOP) - ); + private DecoPanel buildInvitationsPage(OwnerIdentity identity, NetHandlerPlayClient connection) + { + DecoPanel panel = createContentPanel(); + boolean canInvite = identity.isPermitted(mc.player, PermissionCategory.INVITE_MEMBERS); + DiplomacyHandler handler = DiplomacyHandler.getInstance(true); - //Invite row - DecoTextField usernameField = panelMembers.addComponent(new DecoTextField(4, 6+2) - .withSize(118+4+2, 16) + DecoTextField username = panel.addComponent(new DecoTextField(2, 2) + .withSize(panel.width-20, 16) .withMaxStringLength(16) + .withDisabled(!canInvite) + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.invitations.username.tooltip") ); - panelMembers.addComponent(new DecoButton(124+2+2, 6+2) + panel.addComponent(new DecoButton(panel.width-18, 2) .withTemplate(DecoTemplates.ACTION_BUTTON_ADD) .withSize(16, 16) + .withDisabled(!canInvite) .withOnLMBPressed(() -> { - NetworkPlayerInfo playerInfo = connection.getPlayerInfo(usernameField.getText()); - if(playerInfo!=null) + NetworkPlayerInfo playerInfo = connection.getPlayerInfo(username.getText()); + if(playerInfo!=null&&!identity.isMember(playerInfo.getGameProfile().getId())) + { IIPacketHandler.sendToServer(MessageDiplomacyAction.invitePlayer(playerInfo.getGameProfile().getId())); + username.withText(""); + } }) ); - //Member list - panelMembers.addComponent(new DecoList(4, 22+2) - .withSize(132+4+2+2, 32+24+2-2) - .withEntries(identity.getMembers()) + List invited = new ArrayList<>(identity.getInvitedPlayers()); + invited.sort(Comparator + .comparing((UUID uuid) -> handler.getPlayerInfo(uuid).getName(), String.CASE_INSENSITIVE_ORDER) + .thenComparing(UUID::toString)); + + panel.addComponent(new DecoList(2, 24-4) + .withSize(panel.width-4, 132-24-2) + .withEntries(invited) .withDisplayFunction(new DecoEntryPanelBuilder() - .withHeight(18) + .withHeight(22) .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) - .withLabel("name", new DecoLabel(fontRenderer, 16+1+1, 2) - .withSize(100, 16) - .withAlign(DecoAlignment.LEFT) - ) - .withComponent("head", new DecoImage(2, 2) - .withSize(14, 14) - ) - .withComponent(p -> new DecoButton(p.width-16, 2) + .withLabel("name", () -> new DecoLabel(fontRenderer, 20, 3) + .withSize(panel.width-62, 16) + .withAlign(DecoAlignment.LEFT)) + .withComponent("head", () -> new DecoImage(3, 3).withSize(16, 16)) + .withComponent("cancel", p -> new DecoButton(p.width-19, 3) + .withSize(16, 16) .withTemplate(DecoTemplates.ACTION_BUTTON_REMOVE) - .withOnLMBPressed(() -> IIPacketHandler.sendToServer(MessageDiplomacyAction.removeMember(p.getCurrentElement()))) - ) - .withElementApplyMethod((uuid, panel) -> { - PlayerInfo playerInfo = DiplomacyHandler.getInstance(true).getPlayerInfo(uuid); - panel.label("name").withRawText(playerInfo.getName()); - panel.component("head", DecoImage.class).withImageLocation(playerInfo.getSkin()) + .withDisabled(!canInvite) + .withOnLMBPressed(() -> IIPacketHandler.sendToServer( + MessageDiplomacyAction.cancelInvitation(p.getCurrentElement())))) + .withElementApplyMethod((uuid, entry) -> { + PlayerInfo info = handler.getPlayerInfo(uuid); + entry.label("name").withRawText(info.getName()); + entry.component("head", DecoImage.class) + .withImageLocation(info.getSkin()) .withUV(64, 8, 8, 16, 16); }) ) ); + return panel; + } - //Permissions Panel - DecoPanel panelPerms = addComponent(new DecoPanel(152+2, 4) - .withSize(92-2+32, 144+32) - .withBackground(DecoTextures.BG_STEEL) - .withBackgroundMask(DecoTextures.TEMPLATE_SQUARE) - .withTitleLabel("Permissions", DecoAlignment.TOP) + private DecoPanel buildRolesPage(OwnerIdentity identity) + { + DecoPanel panel = createContentPanel(); + List roles = new ArrayList<>(identity.getAvailableRoles().values()); + boolean canEditRoles = identity.isOwner(mc.player.getUniqueID()); + + if(selectedRole==null||!roles.contains(selectedRole)) + selectedRole = roles.stream() + .filter(role -> !role.isOwner()) + .findFirst() + .orElse(identity.getRoleOf(mc.player.getUniqueID())); + + DecoEntryPanelBuilder permissionDisplay = selectedRole==null?null: + new DecoEntryPanelBuilder() + .withHeight(20) + .withBackground(DecoTextures.BG_PAPER) + .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) + .withComponent("toggle", p -> new DecoSwitch(3, 2) + .withDisabled(!canEditRoles||selectedRole.isOwner()) + .withOnToggle(change -> IIPacketHandler.sendToServer( + MessageDiplomacyAction.changePermission(selectedRole, p.getCurrentElement(), change))) + .withTranslatedTooltip(p.getCurrentElement().getFullLocaleKey(), + TextFormatting.GRAY+p.getCurrentElement().getFullLocaleKey()+".tooltip") + ) + .withElementApplyMethod((permission, entry) -> entry.component("toggle", DecoSwitch.class) + .withCurrentState(selectedRole.isAllowed(permission)) + .withText(permission.getFullLocaleKey())); + + panel.addLabel(IIReference.GUI_LABEL_KEY+"faction_management.permissions.role", 4, 3) + .withSize(64, 16) + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.permissions.role.tooltip"); + DecoDropdown roleDropdown = panel.addComponent(new DecoDropdown(panel.width-150-2, 2) + .withSize(150, 16) + .withEntries(roles) + .withDisplayFunction(DecoElementDisplays.getSimpleTextDisplay(PermissionRole::getDisplayName)) + .withSelectedEntry(selectedRole) + .withTranslatedTooltip(IIReference.GUI_LABEL_KEY+"faction_management.permissions.role.tooltip") ); + roleDropdown.withOnSelectedEntry((oldRole, newRole) -> { + if(newRole!=null&&!newRole.equals(oldRole)) + { + selectedRole = newRole; + if(permissionDisplay!=null) + permissionDisplay.refreshCache(); + } + }); - selectedRole = identity.getRoleOf(playerContainer.player.getUniqueID()); - if(selectedRole!=null) + if(selectedRole==null) { - panelPerms.addLabel("Role:", 4, 8+2-1) - .withSize(panelPerms.width-72-2, 14) - .withAlign(DecoAlignment.LEFT); - panelPerms.addComponents( - new DecoDropdown(panelPerms.width-72, 8+2-2) - .withSize(72-4, 14) - .withEntries(identity.getAvailableRoles().values()) - .withDisplayFunction(DecoElementDisplays.getSimpleTextDisplay(PermissionRole::getDisplayName)) - .withSelectedEntry(selectedRole) - .withOnSelectedEntry((oldRole, newRole) -> selectedRole = newRole), - new DecoList(2, 12+8+2+1) - .withSize(panelPerms.width-4-2, panelPerms.height-32+8-2) - .withEntries(PermissionCategory.values()) - .withDisplayFunction(new DecoEntryPanelBuilder() - .withHeight(18) - .withComponent("toggle", p -> new DecoSwitch(2, 2) - .withOnToggle(change -> IIPacketHandler.sendToServer(MessageDiplomacyAction.changePermission(selectedRole, - p.getCurrentElement(), change))) - ) - .withElementApplyMethod((permission, panel) -> { - panel.component("toggle", DecoSwitch.class) - .withCurrentState(selectedRole.isAllowed(permission)) - .withText(permission.getFullLocaleKey()); - }) - ) + panel.addLabel(new DecoLabel(fontRenderer, 4, 30) + .withSize(panel.width-14, 16) + .withAlign(DecoAlignment.CENTER) + .withText(IIReference.GUI_LABEL_KEY+"faction_management.permissions.empty") + ); + } + else + { + panel.addComponent(new DecoList(2, 24-4) + .withSize(panel.width-4, 132-26) + .withEntries(PermissionCategory.values()) + .withDisplayFunction(permissionDisplay) ); } - + return panel; } @Override @@ -245,7 +360,6 @@ public void onGuiClosed() IIPacketHandler.sendToServer(MessageDiplomacyAction.changeColor(factionColor)); if(factionBanner!=null) IIPacketHandler.sendToServer(MessageDiplomacyAction.changeBanner(factionBanner)); - super.onGuiClosed(); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/inserter/GuiInserter.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/inserter/GuiInserter.java index 3d68e4b61..2aac01e02 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/inserter/GuiInserter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/inserter/GuiInserter.java @@ -19,7 +19,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.metal_device.tileentity.inserter.TileEntityInserterBase; import pl.pabilo8.immersiveintelligence.common.block.metal_device.tileentity.inserter.TileEntityInserterBase.InserterTask; @@ -94,14 +93,14 @@ public void onInit() .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_TICKET) - .withComponent("icon", new DecoItemStackDisplay(3, 2).withSize(16, 16)) - .withLabel("wild", new DecoLabel(fontRenderer, 3, 2) + .withComponent("icon", p -> new DecoItemStackDisplay(3, 2).withSize(16, 16)) + .withLabel("wild", p -> new DecoLabel(fontRenderer, 3, 2) .withSize(16, 16) .withAlign(DecoAlignment.CENTER) .withRawText("*") .withTextColor(IIReference.COLOR_IMMERSIVE_ORANGE) ) - .withLabel("type", new DecoLabel(fontRenderer, 3+16+4, 2) + .withLabel("type", p -> new DecoLabel(fontRenderer, 3+16+4, 2) .withSize(96-3-16-6, 16) .withAlign(DecoAlignment.LEFT) .withRawText("task") diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/overrides/GuiIECrateOverride.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/overrides/GuiIECrateOverride.java new file mode 100644 index 000000000..de311eb5b --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/overrides/GuiIECrateOverride.java @@ -0,0 +1,39 @@ +package pl.pabilo8.immersiveintelligence.client.gui.block.overrides; + +import blusunrize.immersiveengineering.common.blocks.wooden.TileEntityWoodenCrate; +import net.minecraft.entity.player.EntityPlayer; +import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; +import pl.pabilo8.immersiveintelligence.common.compat.ie.ImmersiveEngineeringHelper; +import pl.pabilo8.immersiveintelligence.common.gui.ContainerIICrate; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @since 17.05.2019 + */ +@DecoTemplate(name = "ie_crate_override", category = DecoGuiCategory.GENERIC_TILE) +public class GuiIECrateOverride extends DecoTileGui> +{ + public GuiIECrateOverride(EntityPlayer player, TileEntityWoodenCrate tile) + { + super(player, tile, ImmersiveEngineeringHelper.GUI_IE_CRATE_OVERRIDE); + } + + @Override + public void onInit() + { + startBackground() + .withBox(DecoTextures.BG_WOODEN, 0, 0, 176, 76) + .withTitleBar(tile) + .conditionally(tile.getBlockMetadata()!=0, builder -> + builder.withFrame(DecoTextures.FRAME_STEEL_THIN, 4, true) + ) + .withBox(DecoTextures.BG_WOODEN, DecoTextures.TEMPLATE_ROUND_WOODEN, 0, 76, 176, 92) + .withInventorySlots(SlotStyle.VANILLA, container.inventorySlots) + .withInventoryTitleBar() + .build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPacker.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPacker.java index 867eb7e6a..31dfb5a5f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPacker.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPacker.java @@ -9,8 +9,9 @@ import pl.pabilo8.immersiveintelligence.api.PackerHandler.PackerActionType; import pl.pabilo8.immersiveintelligence.api.PackerHandler.PackerTask; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoCheckbox; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTab; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTabGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoDropdown; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanelBuilder; @@ -26,7 +27,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.Packer; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; @@ -116,7 +116,6 @@ public void onInit() addLinkTab(IIGUI.PACKER_LABELER, ICON_LABELER, "labeler_module"); } - // Replace mode tabs + list + action buttons with a single component addComponent((taskList = new DecoTaskList<>(0, 0)) .withSize(108, 116+12-8) .withEntries(tasks) @@ -134,14 +133,14 @@ public void onInit() .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_TICKET) - .withComponent("icon", new DecoItemStackDisplay(3, 2).withSize(16, 16)) - .withLabel("wild", new DecoLabel(fontRenderer, 3, 2) + .withComponent("icon", () -> new DecoItemStackDisplay(3, 2).withSize(16, 16)) + .withLabel("wild", () -> new DecoLabel(fontRenderer, 3, 2) .withSize(16, 16) .withAlign(DecoAlignment.CENTER) .withRawText("*") .withTextColor(IIReference.COLOR_IMMERSIVE_ORANGE) ) - .withLabel("type", new DecoLabel(fontRenderer, 23, 2) + .withLabel("type", () -> new DecoLabel(fontRenderer, 23, 2) .withSize(59, 16) .withAlign(DecoAlignment.LEFT) ) @@ -214,36 +213,34 @@ private void refreshResources() break; case ITEM: { - //Add scrollable item lists - final DecoScrollableItemSlots slotsInput = panelResources.addComponent(new DecoScrollableItemSlots(0, 8+4+2+4)) + DecoPanel inputPanel = panelResources.addComponent(new DecoPanel(0, 0) + .withSize(panelResources.width, panelResources.height) + .withBackground(null) + .withBackgroundMask(null)); + DecoPanel outputPanel = panelResources.addComponent(new DecoPanel(0, 0) + .withSize(panelResources.width, panelResources.height) + .withBackground(null) + .withBackgroundMask(null)); + + inputPanel.addComponent(new DecoScrollableItemSlots(0, 8+4+2+4)) .withSlots(container.slotsInput) .withColumns(6) .withHeight(panelResources.height-8-16-8-8); - final DecoScrollableItemSlots slotsOutput = panelResources.addComponent(new DecoScrollableItemSlots(0, 8+4+2+4)) + outputPanel.addComponent(new DecoScrollableItemSlots(0, 8+4+2+4)) .withSlots(container.slotsOutput) .withColumns(6) .withHeight(panelResources.height-8-16-8-8); - slotsOutput.visible = false; - - //Add - panelResources.addComponent(new DecoButton(0, -2+4)) - .withSize(panelResources.width/2, 16) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(GUI_LABEL_KEY+"packer.item.input") - .withTranslatedTooltip(GUI_LABEL_KEY+"packer.item.input.tooltip") - .withOnLMBPressed(() -> { - slotsInput.visible = true; - slotsOutput.visible = false; - }); - panelResources.addComponent(new DecoButton(panelResources.width/2, -2+4)) - .withSize(panelResources.width/2, 16) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(GUI_LABEL_KEY+"packer.item.output") - .withTranslatedTooltip(GUI_LABEL_KEY+"packer.item.output.tooltip") - .withOnLMBPressed(() -> { - slotsInput.visible = false; - slotsOutput.visible = true; - }); + + panelResources.addComponent(new DecoTabGroup(0, -2+4) + .withSize(panelResources.width, 16) + .withHorizontalAlignment(true) + .withTabWidth(panelResources.width/2) + .withTab((DecoTab)new DecoTab() + .withText(GUI_LABEL_KEY+"packer.item.input") + .withTranslatedTooltip(GUI_LABEL_KEY+"packer.item.input.tooltip"), inputPanel) + .withTab((DecoTab)new DecoTab() + .withText(GUI_LABEL_KEY+"packer.item.output") + .withTranslatedTooltip(GUI_LABEL_KEY+"packer.item.output.tooltip"), outputPanel)); } break; } @@ -356,30 +353,19 @@ else if(task.expirationAmount==-1) .withSize(panelDetails.width-8, 56) ); - //Itemstack panel picker buttons - panelDetails.addComponents( - new DecoButton(4, panelDetails.height-56-4-14) - .withSize((panelDetails.width-8)/2, 16) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) + //Itemstack picker tabs + DecoTab stackTab = (DecoTab)new DecoTab() + .withText(GUI_LABEL_KEY+"packer.picker.stack") + .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.stack.tooltip"); + DecoTabGroup pickerTabs = panelDetails.addComponent(new DecoTabGroup(4, panelDetails.height-56-4-14) + .withSize(panelDetails.width-8, 16) + .withHorizontalAlignment(true) + .withTabWidth((panelDetails.width-8)/2) + .withTab((DecoTab)new DecoTab() .withText(GUI_LABEL_KEY+"packer.picker.container") - .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.container.tooltip") - .withOnLMBPressed(() -> { - panelContainerFilterPicker.visible = panelContainerFilterPicker.enabled = true; - panelStackFilterPicker.visible = panelStackFilterPicker.enabled = false; - }), - new DecoButton(4+(panelDetails.width-8)/2, panelDetails.height-56-4-14) - .withSize((panelDetails.width-8)/2, 16) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(GUI_LABEL_KEY+"packer.picker.stack") - .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.stack.tooltip") - .withOnLMBPressed(() -> { - panelContainerFilterPicker.visible = panelContainerFilterPicker.enabled = false; - panelStackFilterPicker.visible = panelStackFilterPicker.enabled = true; - }) - ); - - panelContainerFilterPicker.visible = panelContainerFilterPicker.enabled = false; - panelStackFilterPicker.visible = panelStackFilterPicker.enabled = true; + .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.container.tooltip"), panelContainerFilterPicker) + .withTab(stackTab, panelStackFilterPicker)); + pickerTabs.selectTab(stackTab, false); } private void updateExpiresFields() diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPackerLabeler.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPackerLabeler.java index 2267857e8..abbf7299c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPackerLabeler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/packer/GuiPackerLabeler.java @@ -7,8 +7,9 @@ import pl.pabilo8.immersiveintelligence.api.LogisticTag; import pl.pabilo8.immersiveintelligence.api.PackerHandler.LabelingTask; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoCheckbox; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTab; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTabGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanelBuilder; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoIngredientStackPickerPanel; @@ -19,11 +20,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoItemStackDisplay; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoAlignment; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock0.tileentity.TileEntityPacker; @@ -119,12 +116,12 @@ public void onInit() .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_TICKET) - .withComponent("icon", new DecoItemStackDisplay(3, 2).withSize(16, 16)) - .withLabel("type", new DecoLabel(fontRenderer, 23, 2) + .withComponent("icon", p -> new DecoItemStackDisplay(3, 2).withSize(16, 16)) + .withLabel("type", p -> new DecoLabel(fontRenderer, 23, 2) .withSize(59, 16) .withAlign(DecoAlignment.LEFT) ) - .withLabel("expires", new DecoLabel(fontRenderer, 23, 12) + .withLabel("expires", p -> new DecoLabel(fontRenderer, 23, 12) .withSize(82, 8) .withAlign(DecoAlignment.LEFT) .withTextColor(IIReference.COLOR_IMMERSIVE_ORANGE) @@ -259,29 +256,18 @@ else if(task.expirationAmount==-1) .withSize(panelDetails.width-8, 56) ); - panelDetails.addComponents( - new DecoButton(4, panelDetails.height-56-4-14) - .withSize((panelDetails.width-8)/2, 16) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) + DecoTab logiTagTab = (DecoTab)new DecoTab() + .withText(GUI_LABEL_KEY+"packer.picker.logitag") + .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.logitag.tooltip"); + DecoTabGroup pickerTabs = panelDetails.addComponent(new DecoTabGroup(4, panelDetails.height-56-4-14) + .withSize(panelDetails.width-8, 16) + .withHorizontalAlignment(true) + .withTabWidth((panelDetails.width-8)/2) + .withTab((DecoTab)new DecoTab() .withText(GUI_LABEL_KEY+"packer.picker.container") - .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.container.tooltip") - .withOnLMBPressed(() -> { - panelFilterPicker.visible = panelFilterPicker.enabled = true; - panelOutputPicker.visible = panelOutputPicker.enabled = false; - }), - new DecoButton(4+(panelDetails.width-8)/2, panelDetails.height-56-4-14) - .withSize((panelDetails.width-8)/2, 16) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(GUI_LABEL_KEY+"packer.picker.logitag") - .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.logitag.tooltip") - .withOnLMBPressed(() -> { - panelFilterPicker.visible = panelFilterPicker.enabled = false; - panelOutputPicker.visible = panelOutputPicker.enabled = true; - }) - ); - - panelFilterPicker.visible = panelFilterPicker.enabled = false; - panelOutputPicker.visible = panelOutputPicker.enabled = true; + .withTranslatedTooltip(GUI_LABEL_KEY+"packer.picker.container.tooltip"), panelFilterPicker) + .withTab(logiTagTab, panelOutputPicker)); + pickerTabs.selectTab(logiTagTab, false); } private void updateSerialBatching() diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadar.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadar.java index 0ef65986f..85eb14712 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadar.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadar.java @@ -15,7 +15,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.map.scanners.EntityScanner; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.visual.map.scanners.RadarDirectionScanner; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.Radar; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.TileEntityRadar; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarConfig.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarConfig.java index 9e826f480..287ea9bbf 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarConfig.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarConfig.java @@ -2,10 +2,10 @@ import net.minecraft.entity.player.EntityPlayer; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.TileEntityRadar; import pl.pabilo8.immersiveintelligence.common.gui.ContainerRadar; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarTargets.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarTargets.java index 0038be262..270e7243d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarTargets.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/block/radar/GuiRadarTargets.java @@ -2,10 +2,10 @@ import net.minecraft.entity.player.EntityPlayer; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoTileGui; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.TileEntityRadar; import pl.pabilo8.immersiveintelligence.common.gui.ContainerRadar; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoEntityGui.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoEntityGui.java index f1d6c69bb..8b4998c6f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoEntityGui.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoEntityGui.java @@ -24,7 +24,7 @@ public abstract class DecoEntityGui exten public DecoEntityGui(EntityPlayer player, E entity, IIGUI iigui) { //noinspection unchecked - super(player, (C)iigui.containerFromEntity.apply(player, entity), entity, iigui); + super(player, entity!=null?(C)iigui.containerFromEntity.apply(player, entity): null, entity, iigui); this.entity = entity; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoGui.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoGui.java index 0d00baacc..6d8362cdd 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoGui.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoGui.java @@ -19,6 +19,7 @@ import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.event.ClickEvent; import net.minecraft.util.text.event.ClickEvent.Action; import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent.Post; @@ -35,6 +36,7 @@ import pl.pabilo8.immersiveintelligence.client.IIClientUtils; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent.DecoGuiEvent; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent.DecoMouseCapture; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent.MouseButton; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTab; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; @@ -43,6 +45,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; import pl.pabilo8.immersiveintelligence.client.render.IReloadableModelContainer; import pl.pabilo8.immersiveintelligence.client.util.amt.AMTUtils; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.IILogger; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; @@ -60,9 +63,7 @@ import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; +import java.util.*; import java.util.List; import java.util.function.Supplier; @@ -106,7 +107,12 @@ public abstract class DecoGui extends GuiContainer private DecoBackgroundBuilder backgroundBuilder; private List takenSpace; private DecoComponent focusedElement; + private DecoMouseCapture focusedCapture, mouseCapture; private DecoComponent hoveredElement; + //OpenGL model-view translations do not affect glScissor. Virtual component trees + //therefore register their render origin here while drawing translated overlays. + private final Deque scissorOffsetStack = new ArrayDeque<>(); + private int scissorOffsetX, scissorOffsetY; //Widgets private DecoComponentWidgetBase previousWidget, currentWidget; private int widgetTime = 0; @@ -152,7 +158,11 @@ public final void initGui() this.widgetTabList.clear(); this.widgetList.clear(); this.focusedElement = null; + this.focusedCapture = null; + this.mouseCapture = null; this.hoveredElement = null; + this.scissorOffsetStack.clear(); + this.scissorOffsetX = this.scissorOffsetY = 0; this.previousWidget = null; this.currentWidget = null; this.valueListeners.clear(); @@ -277,20 +287,32 @@ protected final > B addComponent(B component) protected final DecoTab addLinkTab(IIGUI gui, ResourceLocation tabIcon, String moduleName) { - return (DecoTab)addComponent(new DecoTab() + DecoTab tab = (DecoTab)addComponent(new DecoTab() .withLink(gui) + .withSelected(gui==this.gui) .withIcon(tabIcon) .withTranslatedTooltip(IIReference.DESCRIPTION_KEY+moduleName) ); + + if(IIConfig.Graphics.decoLongTabTooltips) + tab.withTranslatedTooltip(IIReference.DESCRIPTION_KEY+moduleName, + TextFormatting.GRAY+IIReference.DESCRIPTION_KEY+moduleName+".tooltip"+TextFormatting.RESET); + return tab; } protected final DecoTab addLinkTab(IIGUI gui, ItemStack tabIcon, String moduleName) { - return (DecoTab)addComponent(new DecoTab() + DecoTab tab = (DecoTab)addComponent(new DecoTab() .withLink(gui) + .withSelected(gui==this.gui) .withIcon(tabIcon) .withTranslatedTooltip(IIReference.DESCRIPTION_KEY+moduleName) ); + + if(IIConfig.Graphics.decoLongTabTooltips) + tab.withTranslatedTooltip(IIReference.DESCRIPTION_KEY+moduleName, + TextFormatting.GRAY+IIReference.DESCRIPTION_KEY+moduleName+".tooltip"+TextFormatting.RESET); + return tab; } /** @@ -448,18 +470,21 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) //Draw tiled background, labels, and buttons super.drawScreen(mouseX, mouseY, partialTicks); + validateFocusCapture(); //Check scroll on components - float scroll = Mouse.getDWheel(); + float scroll = Math.signum(Mouse.getDWheel()); if(scroll!=0) { - if(currentWidget==null||!currentWidget.onComponentScroll(mouseX, mouseY, scroll)) - if(focusedElement==null||!focusedElement.onComponentScroll(mouseX, mouseY, scroll)) - for(int i = buttonList.size()-1; i >= 0; i--) - { - GuiButton b = buttonList.get(i); - if(b instanceof DecoComponent) - ((DecoComponent)b).onComponentScroll(mouseX, mouseY, scroll); - } + boolean handled = currentWidget!=null&¤tWidget.onComponentScroll(mouseX, mouseY, scroll); + if(!handled&&focusedCapture!=null) + handled = focusedCapture.scroll(mouseX, mouseY, scroll); + if(!handled) + for(int i = buttonList.size()-1; i >= 0; i--) + { + GuiButton b = buttonList.get(i); + if(b instanceof DecoComponent&&((DecoComponent)b).onComponentScroll(mouseX, mouseY, scroll)) + break; + } } //Draw the upper layer of buttons @@ -507,8 +532,10 @@ private void drawWidgets(int mouseX, int mouseY, float partialTicks) //Update widget tabs position int wSize = (int)(currentWidget!=null?currentWidget.getWidgetWidth()*progress: (previousWidget!=null?previousWidget.getWidgetWidth()*(1f-progress): 0)); - for(DecoTab decoTab : widgetTabList) + for(int i = 0; i < widgetTabList.size(); i++) { + DecoTab decoTab = widgetTabList.get(i); + decoTab.withSelected(currentWidget==widgetList.get(i)); decoTab.x = this.guiLeft+this.xSize+wSize; decoTab.initialize(); } @@ -613,17 +640,37 @@ else if(Keyboard.isKeyDown(Keyboard.KEY_F6)) protected final void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { MouseButton mouseButtonEnum = MouseButton.values()[mouseButton%MouseButton.values().length]; + DecoMouseCapture capture = null; - //Widgets are not a part of the button list, so we need to check them separately - boolean anyPressed = false; + //The focused component gets first refusal. This is required for popups such as + //dropdown lists, which may extend beyond the bounds of their parent panel. + if(focusedCapture!=null) + capture = focusedCapture.press(mc, mouseX, mouseY, mouseButtonEnum); - if(focusedElement!=null) - anyPressed = focusedElement.decoMousePressed(this.mc, mouseX, mouseY, mouseButtonEnum); - if(!anyPressed) + //Widgets are not part of the normal button list. + if(capture==null&¤tWidget!=null) + capture = currentWidget.decoMousePressed(mc, mouseX, mouseY, mouseButtonEnum); + + //Resolve one top-most Deco target. A consumed press is never dispatched again + //to another root or to vanilla slot handling. + if(capture==null) + for(int i = buttonList.size()-1; i >= 0; i--) + { + GuiButton guiButton = buttonList.get(i); + if(guiButton instanceof DecoComponent) + { + capture = ((DecoComponent)guiButton).decoMousePressed(mc, mouseX, mouseY, mouseButtonEnum); + if(capture!=null) + break; + } + } + + this.mouseCapture = capture; + if(capture!=null) { - if(currentWidget!=null&¤tWidget.decoMousePressed(this.mc, mouseX, mouseY, mouseButtonEnum)) + requestFocusCapture(capture); + if(currentWidget!=null&&capture.belongsTo(currentWidget)) { - anyPressed = true; Pre event = new Pre(this, currentWidget, this.buttonList); if(MinecraftForge.EVENT_BUS.post(event)) return; @@ -631,36 +678,21 @@ protected final void mouseClicked(int mouseX, int mouseY, int mouseButton) throw if(this.equals(this.mc.currentScreen)) MinecraftForge.EVENT_BUS.post(new Post(this, event.getButton(), this.buttonList)); } - - if(!anyPressed) - { - for(int i = this.buttonList.size()-1; i >= 0; i--) - { - GuiButton guiButton = this.buttonList.get(i); - if(guiButton==focusedElement) - continue; - if(guiButton instanceof DecoComponent) - anyPressed = ((DecoComponent)guiButton).decoMousePressed(this.mc, mouseX, mouseY, mouseButtonEnum)||anyPressed; - else if(mouseButtonEnum==MouseButton.LEFT) - anyPressed = guiButton.mousePressed(this.mc, mouseX, mouseY)||anyPressed; - } - } + return; } - if(!anyPressed) - requestFocus(null); - + requestFocus((DecoComponent)null); super.mouseClicked(mouseX, mouseY, mouseButton); } @Override protected final void mouseReleased(int mouseX, int mouseY, int state) { - if(focusedElement!=null) + if(mouseCapture!=null) { - MouseButton[] mouseButtons = MouseButton.values(); - if(state >= 0&&state < mouseButtons.length) - focusedElement.decoMouseReleased(mouseX, mouseY, mouseButtons[state]); + mouseCapture.release(mouseX, mouseY, MouseButton.values()[state%MouseButton.values().length]); + mouseCapture = null; + return; } super.mouseReleased(mouseX, mouseY, state); } @@ -668,11 +700,10 @@ protected final void mouseReleased(int mouseX, int mouseY, int state) @Override protected final void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { - if(focusedElement!=null) + if(mouseCapture!=null) { - MouseButton[] mouseButtons = MouseButton.values(); - if(clickedMouseButton >= 0&&clickedMouseButton < mouseButtons.length) - focusedElement.decoMouseDragged(mc, mouseX, mouseY, mouseButtons[clickedMouseButton]); + mouseCapture.drag(mc, mouseX, mouseY, MouseButton.values()[clickedMouseButton%MouseButton.values().length]); + return; } super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); } @@ -698,6 +729,8 @@ public void onGuiClosed() protected void cleanupDecoGui() { + requestFocus((DecoComponent)null); + mouseCapture = null; if(backgroundBuilder!=null) backgroundBuilder.cleanup(); for(GuiButton b : buttonList) @@ -721,6 +754,15 @@ protected void onGuiClosedWithoutTransition() protected List getTooltip() { this.hoveredElement = null; + if(focusedCapture!=null) + { + List tooltip = focusedCapture.getTooltip(); + if(!tooltip.isEmpty()) + { + this.hoveredElement = focusedCapture.getComponent(); + return tooltip; + } + } //Widget if(currentWidget!=null&¤tWidget.isMouseOver()) return currentWidget.getTooltip(); @@ -744,8 +786,22 @@ protected List getTooltip() return Collections.emptyList(); } - public void requestFocus(DecoComponent component) + private void validateFocusCapture() { + if(focusedCapture!=null&&!focusedCapture.isValid()) + requestFocusCapture(null); + if(mouseCapture!=null&&!mouseCapture.isValid()) + mouseCapture = null; + } + + public void requestFocus(@Nullable DecoComponent component) + { + requestFocusCapture(component==null?null: DecoMouseCapture.of(component)); + } + + public void requestFocusCapture(@Nullable DecoMouseCapture capture) + { + DecoComponent component = capture==null?null: capture.getComponent(); if(this.focusedElement!=component) { if(this.focusedElement!=null) @@ -755,6 +811,19 @@ public void requestFocus(DecoComponent component) } this.focusedElement = component; + this.focusedCapture = capture; + } + + /** + * Releases pointer and keyboard ownership held by a component contained in the + * supplied subtree. Cached entry panels call this before replacing their trees. + */ + public void releaseFocusWithin(DecoComponent root) + { + if(focusedCapture!=null&&focusedCapture.belongsTo(root)) + requestFocus((DecoComponent)null); + if(mouseCapture!=null&&mouseCapture.belongsTo(root)) + mouseCapture = null; } //--- NBT ---// @@ -966,6 +1035,39 @@ else if(newGUI==gui) return true; } + /** + * Adds a model-view translation to subsequent scissor rectangles. + *

+ * {@link GL11#glScissor(int, int, int, int)} works in window coordinates and + * ignores {@link GlStateManager#translate(float, float, float)}. Components + * rendered through a translated virtual tree must therefore provide the same + * offset explicitly before starting a scissor region. Calls may be nested. + *

+ */ + public void pushScissorOffset(int x, int y) + { + scissorOffsetStack.push(new Point(scissorOffsetX, scissorOffsetY)); + scissorOffsetX += x; + scissorOffsetY += y; + } + + /** + * Restores the scissor offset active before the latest + * {@link #pushScissorOffset(int, int)} call. + */ + public void popScissorOffset() + { + if(scissorOffsetStack.isEmpty()) + { + scissorOffsetX = scissorOffsetY = 0; + return; + } + + Point previous = scissorOffsetStack.pop(); + scissorOffsetX = previous.x; + scissorOffsetY = previous.y; + } + /** * Starts the scissor function, enabling OpenGL scissor test. * This is used to limit rendering to a specific area of the screen. @@ -977,6 +1079,8 @@ else if(newGUI==gui) */ public void scissorStart(int x, int y, int xSize, int ySize) { + x += scissorOffsetX; + y += scissorOffsetY; GL11.glEnable(GL11.GL_SCISSOR_TEST); if(screenshotMode) @@ -1055,9 +1159,9 @@ private void exportCurrentGui() //Configure proper blending for transparency GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(GL11.GL_GREATER, 0.003921569F); // ~1/255 + GlStateManager.alphaFunc(GL11.GL_GREATER, 0.003921569F); //~1/255 GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); // Pre-multiplied alpha + GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); //Pre-multiplied alpha GlStateManager.disableDepth(); GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); @@ -1074,9 +1178,9 @@ private void exportCurrentGui() GlStateManager.color(1f, 1f, 1f, 1f); GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(GL11.GL_GREATER, 0.003921569F); // ~1/255 + GlStateManager.alphaFunc(GL11.GL_GREATER, 0.003921569F); //~1/255 GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); // Pre-multiplied alpha + GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); //Pre-multiplied alpha GlStateManager.disableDepth(); GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoItemGui.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoItemGui.java index 3a67620ef..df43e08b4 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoItemGui.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoItemGui.java @@ -32,7 +32,7 @@ private static C createContainer(EntityPlayer pl @Override protected void onNoBackgroundBuilder() { - // Item GUIs often use a hand-drawn texture and set xSize/ySize in onInit(). + //Item GUIs often use a hand-drawn texture and set xSize/ySize in onInit(). } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoPlayerGui.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoPlayerGui.java new file mode 100644 index 000000000..69e59a452 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/DecoPlayerGui.java @@ -0,0 +1,29 @@ +package pl.pabilo8.immersiveintelligence.client.gui.deco; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import pl.pabilo8.immersiveintelligence.common.IIGUI; + +/** + * Deco GUI whose sole server-side context is the player opening it. + * + * @param player inventory container type + * @author Pabilo8 + * @since 22.07.2026 + */ +public abstract class DecoPlayerGui extends DecoGui +{ + protected final EntityPlayer player; + + protected DecoPlayerGui(EntityPlayer player, IIGUI iigui) + { + super(player, createContainer(player, iigui), player, iigui); + this.player = player; + } + + @SuppressWarnings("unchecked") + private static C createContainer(EntityPlayer player, IIGUI iigui) + { + return player==null||iigui.containerFromPlayer==null?null: (C)iigui.containerFromPlayer.apply(player); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoComponent.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoComponent.java index a1e19079d..8fab81106 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoComponent.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoComponent.java @@ -5,6 +5,7 @@ import net.minecraft.client.gui.GuiButton; import net.minecraft.client.resources.I18n; import net.minecraft.inventory.Container; +import net.minecraft.util.text.TextFormatting; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoGui; import pl.pabilo8.immersiveintelligence.common.util.IIMath; @@ -27,7 +28,7 @@ public abstract class DecoComponent> ex @Nullable protected DecoGui parentGui; protected List> children = new ArrayList<>(); - protected boolean pressed; + protected int pressTime = 0; protected boolean initialized; @SuppressWarnings("unused") private String displayString; @@ -137,14 +138,19 @@ public final void drawButton(Minecraft mc, int mouseX, int mouseY, float partial for(DecoComponent child : children) child.drawButton(mc, mouseX, mouseY, partialTicks); + + if(pressTime > 0) + this.pressTime--; } + else + updateInvisibleTree(); } public final void drawButtonUpperLayer(Minecraft mc, int mouseX, int mouseY, float partialTicks) { if(!initialized||!visible) { - updateInvisibleComponent(); + updateInvisibleTree(); return; } @@ -159,6 +165,18 @@ protected void updateInvisibleComponent() } + /** + * Notifies this component and all descendants that their containing tree is hidden. + * This is separate from cleanup: components may retain their state while releasing or + * moving external resources that would otherwise remain interactive on screen. + */ + private void updateInvisibleTree() + { + updateInvisibleComponent(); + for(DecoComponent child : children) + child.updateInvisibleTree(); + } + public void drawUpperLayer(int mouseX, int mouseY, float partialTicks) { @@ -188,43 +206,59 @@ public final void mouseDragged(Minecraft mc, int mouseX, int mouseY) } - public final boolean decoMousePressed(Minecraft mc, int mouseX, int mouseY, MouseButton button) + @Nullable + public final DecoMouseCapture decoMousePressed(Minecraft mc, int mouseX, int mouseY, MouseButton button) { - if(this.enabled&&canBeClicked(mouseX, mouseY)) + if(!visible||!enabled||!canBeClicked(mouseX, mouseY)) + return null; + + for(int i = children.size()-1; i >= 0; i--) { - Optional> childrenPressed = children.stream().filter(child -> child.decoMousePressed(mc, mouseX, mouseY, button)).findFirst(); - pressed = childrenPressed.isPresent()||(onPressed!=null&&onPressed.onMouse((TYPE)this, button, mouseX, mouseY)); - if(pressed) - { - playPressSound(mc.getSoundHandler()); - if(parentGui!=null) - { - DecoComponent component = childrenPressed.orElse(this); - parentGui.requestFocus(component); - } - } - return pressed; + DecoMouseCapture capture = children.get(i).decoMousePressed(mc, mouseX, mouseY, button); + if(capture!=null) + return capture.withAncestor(this); } + + DecoMouseCapture capture = decoMousePressedVirtualChild(mc, mouseX, mouseY, button); + if(capture!=null) + return capture.withAncestor(this); + + if(onPressed!=null&&onPressed.onMouse((TYPE)this, button, mouseX, mouseY)) + { + this.pressTime = 10; + playPressSound(mc.getSoundHandler()); + return DecoMouseCapture.of(this); + } + return null; + } + + @Nullable + protected DecoMouseCapture decoMousePressedVirtualChild(Minecraft mc, int mouseX, int mouseY, MouseButton button) + { + return null; + } + + protected boolean ownsVirtualChild(DecoComponent component) + { return false; } - public final void decoMouseReleased(int mouseX, int mouseY, MouseButton mouseButton) + private boolean ownsInputChild(DecoComponent component) { - if(this.enabled) - { - pressed = !(onReleased==null||onReleased.onMouse((TYPE)this, mouseButton, mouseX, mouseY)); - children.forEach(child -> child.mouseReleased(mouseX, mouseY)); - } + return children.contains(component)||ownsVirtualChild(component); } - public final void decoMouseDragged(Minecraft mc, int mouseX, int mouseY, MouseButton button) + private void decoMouseReleased(int mouseX, int mouseY, MouseButton mouseButton) { - if(this.enabled&&canBeClicked(mouseX, mouseY)) - { - if(onDragged!=null) - onDragged.onMouse((TYPE)this, button, mouseX, mouseY); - children.forEach(child -> child.mouseDragged(mc, mouseX, mouseY)); - } + if(enabled&&onReleased!=null) + onReleased.onMouse((TYPE)this, mouseButton, mouseX, mouseY); + this.pressTime = 0; + } + + private void decoMouseDragged(Minecraft mc, int mouseX, int mouseY, MouseButton button) + { + if(visible&&enabled&&onDragged!=null) + onDragged.onMouse((TYPE)this, button, mouseX, mouseY); } /** @@ -397,7 +431,13 @@ public final TYPE withOnTooltip(Function> onTooltip) public final TYPE withTranslatedTooltip(String... tooltip) { final List collect = Arrays.stream(tooltip) - .map(I18n::format) + .filter(Objects::nonNull) + .map(s -> { + String text = TextFormatting.getTextWithoutFormattingCodes(s); + assert text!=null; + String translated = I18n.format(text); + return s.replace(text, translated); + }) .filter(s -> !s.isEmpty()) .collect(Collectors.toList()); @@ -423,6 +463,19 @@ public TYPE withDisabled(boolean disabled) return (TYPE)this; } + /** + * Changes both visibility and interaction state. Hiding a component immediately + * propagates to descendants so external resources, such as real container slots, + * cannot remain active until the next render pass. + */ + public final void setActive(boolean active) + { + this.visible = active; + this.enabled = active; + if(!active) + updateInvisibleTree(); + } + /** * Provides an ingredient that can be used by JEI compat. * @@ -455,7 +508,123 @@ public void setFocused(boolean focused) */ public final boolean onComponentScroll(int mouseX, int mouseY, float scrolled) { - return onScroll==null||(canBeClicked(mouseX, mouseY)&&onScroll.onMouse((TYPE)this, (int)scrolled, mouseX, mouseY)); + return visible&&enabled&&onScroll!=null&&canBeClicked(mouseX, mouseY) + &&onScroll.onMouse((TYPE)this, (int)scrolled, mouseX, mouseY); + } + + /** + * Checks whether a component belongs to this ordinary child tree. + */ + public final boolean containsComponent(DecoComponent component) + { + if(this==component) + return true; + for(DecoComponent child : children) + if(child.containsComponent(component)) + return true; + return false; + } + + /** + * Immutable pointer context produced by mouse hit-testing. + * Besides the final target, it stores the coordinate transform used by virtual + * children such as cached list entry panels and the ancestor path used to + * invalidate focus when a containing panel disappears. + */ + public static final class DecoMouseCapture + { + private final DecoComponent component; + private final List> path; + private final int offsetX, offsetY; + + private DecoMouseCapture(DecoComponent component, List> path, int offsetX, int offsetY) + { + this.component = component; + this.path = path; + this.offsetX = offsetX; + this.offsetY = offsetY; + } + + public static DecoMouseCapture of(DecoComponent component) + { + return new DecoMouseCapture(component, Collections.singletonList(component), 0, 0); + } + + private DecoMouseCapture withAncestor(DecoComponent ancestor) + { + if(path.contains(ancestor)) + return this; + List> expanded = new ArrayList<>(path); + expanded.add(ancestor); + return new DecoMouseCapture(component, expanded, offsetX, offsetY); + } + + public DecoMouseCapture translated(int offsetX, int offsetY) + { + return new DecoMouseCapture(component, path, this.offsetX+offsetX, this.offsetY+offsetY); + } + + public DecoComponent getComponent() + { + return component; + } + + public boolean isValid() + { + for(int i = 0; i < path.size(); i++) + { + DecoComponent element = path.get(i); + if(!element.visible||!element.enabled) + return false; + + //A capture is stale when a rebuilt panel no longer owns the child that + //originally produced it, even if the detached objects remain enabled. + if(i+1 < path.size()&&!path.get(i+1).ownsInputChild(element)) + return false; + } + return true; + } + + public boolean belongsTo(DecoComponent root) + { + return path.contains(root)||root.containsComponent(component); + } + + @Nullable + public DecoMouseCapture press(Minecraft mc, int mouseX, int mouseY, MouseButton button) + { + if(!isValid()) + return null; + + DecoMouseCapture result = component.decoMousePressed(mc, mouseX+offsetX, mouseY+offsetY, button); + if(result==null) + return null; + + result = result.translated(offsetX, offsetY); + for(int i = 1; i < path.size(); i++) + result = result.withAncestor(path.get(i)); + return result; + } + + public void release(int mouseX, int mouseY, MouseButton button) + { + component.decoMouseReleased(mouseX+offsetX, mouseY+offsetY, button); + } + + public void drag(Minecraft mc, int mouseX, int mouseY, MouseButton button) + { + component.decoMouseDragged(mc, mouseX+offsetX, mouseY+offsetY, button); + } + + public boolean scroll(int mouseX, int mouseY, float amount) + { + return isValid()&&component.onComponentScroll(mouseX+offsetX, mouseY+offsetY, amount); + } + + public List getTooltip() + { + return isValid()&&component.isMouseOver()?component.getTooltip(): Collections.emptyList(); + } } /** diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoTextBasedComponent.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoTextBasedComponent.java index 2df64f14a..2974de724 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoTextBasedComponent.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/DecoTextBasedComponent.java @@ -103,13 +103,13 @@ public TYPE withTextDisabledColor(IIColor textDisabledColor) //--- Utilities ---// - protected final IIColor getBackgroundColor() + protected IIColor getBackgroundColor() { - return enabled?(pressed?backgroundColorPressed: (hovered?backgroundColorHovered: backgroundColor)): backgroundColorDisabled; + return enabled?(pressTime > 0?backgroundColorPressed: (hovered?backgroundColorHovered: backgroundColor)): backgroundColorDisabled; } - protected final IIColor getTextColor(boolean label) + protected IIColor getTextColor(boolean label) { - return enabled?(pressed?textPressedColor: (hovered?textHoveredColor: (label?textLabelColor: textBoxColor))): textDisabledColor; + return enabled?(pressTime > 0?textPressedColor: (hovered?textHoveredColor: (label?textLabelColor: textBoxColor))): textDisabledColor; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoButton.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoButton.java index 236f0dee9..fa0e99ed5 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoButton.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoButton.java @@ -2,14 +2,17 @@ import blusunrize.immersiveengineering.client.ClientUtils; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; +import pl.pabilo8.immersiveintelligence.client.IIClientUtils; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoTextBasedComponent; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoAlignment; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiUtils; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; import pl.pabilo8.immersiveintelligence.client.util.IIDrawUtils; +import pl.pabilo8.immersiveintelligence.common.util.IIColor; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -24,7 +27,7 @@ public class DecoButton extends DecoTextBasedComponent { protected int[] padding = new int[]{2, 2, 2, 2}; private DecoAlignment iconAlignment = DecoAlignment.CENTER; - // Cached positions + //Cached positions private int cachedIconX, cachedIconY, cachedTextX, cachedTextY; @Nullable @@ -125,7 +128,16 @@ iconSize, iconSize, getTextColor(false), GlStateManager.pushMatrix(); GlStateManager.translate(cachedIconX, cachedIconY, 0); GlStateManager.scale(16/(float)iconSize, 16/(float)iconSize, 1); + IIColor iconColor = getTextColor(false); + RenderHelper.enableGUIStandardItemLighting(); ClientUtils.mc().getRenderItem().renderItemAndEffectIntoGUI(stack, 0, 0); + GlStateManager.color(iconColor.red/255f, iconColor.green/255f, iconColor.blue/255f, iconColor.alpha/255f); + ClientUtils.mc().getRenderItem().renderItemOverlayIntoGUI(IIClientUtils.fontRegular, stack, 0, 0, null); + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableRescaleNormal(); + GlStateManager.disableDepth(); + GlStateManager.color(1f, 1f, 1f, 1f); + GlStateManager.popMatrix(); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoColorPicker.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoColorPicker.java index 3b4e1e7d7..f26308cdd 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoColorPicker.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoColorPicker.java @@ -33,7 +33,7 @@ public class DecoColorPicker extends DecoComponent private float brightness = 0.5f; int backgroundBoxes = 0; - // Cached color object - only recreated when HSB values change + //Cached color object - only recreated when HSB values change private IIColor selectedColor = IIColor.fromHSV(hue, saturation, brightness); private boolean draggingColor = false; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoSlider.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoSlider.java index 2ca9abe73..36f9349db 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoSlider.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoSlider.java @@ -179,7 +179,7 @@ public void onGuiEvent(DecoGuiEvent event) withValue(Float.parseFloat(text)); } catch(NumberFormatException e) { - // Ignore invalid input + //Ignore invalid input } } break; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTab.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTab.java index cb0144420..b024ea7f1 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTab.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTab.java @@ -21,6 +21,10 @@ */ public class DecoTab extends DecoButton { + private boolean isSelected = false; + protected IIColor textSelectedColor = IIColor.fromHex("737373"); + protected IIColor backgroundSelectedColor = IIColor.fromHex("b4b4b4"); + public DecoTab() { super(0, 0); @@ -46,8 +50,25 @@ public DecoTab withLink(IIGUI link) return this; } - //--- Overrides ---// + public DecoTab withSelected(boolean selected) + { + isSelected = selected; + return this; + } + + public DecoTab withBackgroundSelectedColor(IIColor backgroundSelectedColor) + { + this.backgroundSelectedColor = backgroundSelectedColor; + return this; + } + + public DecoTab withTextSelectedColor(IIColor textSelectedColor) + { + this.textSelectedColor = textSelectedColor; + return this; + } + //--- Overrides ---// @Override public DecoTab withIcon(@Nonnull ResourceLocation icon) @@ -112,12 +133,14 @@ public DecoTab withBackground(ResLoc backgroundLocation) @Override public DecoTab withBackgroundColor(IIColor color) { + withBackgroundSelectedColor(color); return (DecoTab)super.withBackgroundColor(color); } @Override public DecoTab withTextColor(IIColor textLabelColor, IIColor textBoxColor) { + withTextSelectedColor(textBoxColor); return (DecoTab)super.withTextColor(textLabelColor, textBoxColor); } @@ -138,4 +161,18 @@ public DecoTab withTextDisabledColor(IIColor textDisabledColor) { return (DecoTab)super.withTextDisabledColor(textDisabledColor); } + + protected final IIColor getBackgroundColor() + { + if(!isSelected) + return backgroundSelectedColor; + return super.getBackgroundColor(); + } + + protected final IIColor getTextColor(boolean label) + { + if(!isSelected) + return textSelectedColor; + return super.getTextColor(label); + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTabGroup.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTabGroup.java index 378f23947..fc9c2f7a8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTabGroup.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/button/DecoTabGroup.java @@ -4,26 +4,41 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.util.ResourceLocation; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; import pl.pabilo8.immersiveintelligence.client.util.IIDrawUtils; import pl.pabilo8.immersiveintelligence.common.util.IIColor; import pl.pabilo8.immersiveintelligence.common.util.ResLoc; +import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * A group of DecoTabs displayed in a row or column, depending on alignment. + *

+ * Tabs added with a panel or selection action are managed tabs: the group owns their selected state, + * switches associated panels and can be selected programmatically. Plain {@link #withTab(DecoTab)} + * tabs retain their own press handlers and are only laid out by the group. + *

* * @author Pabilo8 (pabilo@iiteam.net) + * @updated 30.07.2026 * @since 24.09.2025 */ public class DecoTabGroup extends DecoComponent { private final List tabs = new ArrayList<>(); + private final Map tabPanels = new LinkedHashMap<>(); + private final Map tabActions = new LinkedHashMap<>(); private ResourceLocation background = DecoTextures.COMPONENT_TAB; private boolean horizontal; private int spacing = 0; + private int fixedTabWidth = -1; + @Nullable + private DecoTab selectedTab; public DecoTabGroup(int x, int y) { @@ -35,34 +50,163 @@ public DecoTabGroup withHorizontalAlignment(boolean horizontal) this.horizontal = horizontal; if(this.background==DecoTextures.COMPONENT_TAB) this.background = DecoTextures.COMPONENT_TAB_VERTICAL; + this.initialized = false; return this; } public DecoTabGroup withBackground(ResourceLocation background) { this.background = background; + this.initialized = false; return this; } public DecoTabGroup withSpacing(int spacing) { this.spacing = spacing; + this.initialized = false; return this; } + /** + * Forces each horizontally arranged tab to use the supplied width instead of its packed text width. + */ + public DecoTabGroup withTabWidth(int width) + { + this.fixedTabWidth = Math.max(1, width); + if(horizontal) + tabs.forEach(tab -> tab.withWidth(this.fixedTabWidth)); + this.initialized = false; + return this; + } + + /** + * Adds a layout-only tab. Its existing press handler and selected state remain caller-managed. + */ public DecoTabGroup withTab(DecoTab tab) { - tab.withSize(24, 24).pack(); + tab.withSize(24, 24) + .withPadding(5, 3, 5, 2) + .pack(); if(horizontal) - tab.withSize(tab.width, this.height); + { + if(fixedTabWidth > 0) + tab.withWidth(fixedTabWidth); + tab.withHeight(this.height); + } else tab.withSize(this.width, tab.height); tabs.add(tab); children.add(tab); + this.initialized = false; + return this; + } + + /** + * Adds a managed tab that runs an action when selected. + */ + public DecoTabGroup withTab(DecoTab tab, Runnable onSelected) + { + return withManagedTab(tab, null, onSelected); + } + + /** + * Adds a tab associated with a panel. The first managed tab is selected by default; + * pressing another tab hides the previous panel and displays the selected one. + * + * @param tab tab used to select the panel + * @param panel panel controlled by the tab + * @return this + */ + public DecoTabGroup withTab(DecoTab tab, DecoPanel panel) + { + return withManagedTab(tab, panel, null); + } + + /** + * Adds a panel-backed managed tab and runs an additional action after it is selected. + */ + public DecoTabGroup withTab(DecoTab tab, DecoPanel panel, Runnable onSelected) + { + return withManagedTab(tab, panel, onSelected); + } + + private DecoTabGroup withManagedTab(DecoTab tab, @Nullable DecoPanel panel, @Nullable Runnable onSelected) + { + withTab(tab); + if(panel!=null) + tabPanels.put(tab, panel); + if(onSelected!=null) + tabActions.put(tab, onSelected); + + tab.withOnPressed((gui, button, mouseX, mouseY) -> { + if(button!=MouseButton.LEFT) + return false; + selectTab(tab, true); + return true; + }); + + if(selectedTab==null) + selectTab(tab, false); + else + { + tab.withSelected(false); + if(panel!=null) + panel.setActive(false); + } return this; } + /** + * Selects a managed tab and runs its selection action. + */ + public boolean selectTab(DecoTab tab) + { + return selectTab(tab, true); + } + + /** + * Selects a managed tab. + * + * @param tab tab to select + * @param runAction whether its optional selection action should be executed + * @return true when the tab belongs to this group and is managed + */ + public boolean selectTab(DecoTab tab, boolean runAction) + { + if(tab==null||(!tabPanels.containsKey(tab)&&!tabActions.containsKey(tab))) + return false; + + selectedTab = tab; + for(DecoTab groupedTab : tabs) + if(tabPanels.containsKey(groupedTab)||tabActions.containsKey(groupedTab)) + groupedTab.withSelected(groupedTab==tab); + tabPanels.forEach((groupedTab, panel) -> + panel.setActive(groupedTab==tab)); + + if(runAction) + { + Runnable action = tabActions.get(tab); + if(action!=null) + action.run(); + } + return true; + } + + public boolean selectTab(int tabIndex, boolean runAction) + { + if(tabIndex < 0||tabIndex >= tabs.size()) + return false; + return selectTab(tabs.get(tabIndex), runAction); + } + + @Nullable + public DecoTab getSelectedTab() + { + return selectedTab; + } + @Override protected boolean initialize() { @@ -84,13 +228,13 @@ protected boolean initialize() } if(horizontal) { - this.width = offsetX-x-spacing+(tabs.isEmpty()?0: tabs.get(tabs.size()-1).width); + this.width = tabs.isEmpty()?0: offsetX-x-spacing; this.height = maxH; } else { this.width = maxW; - this.height = offsetY-y-spacing+(tabs.isEmpty()?0: tabs.get(tabs.size()-1).height); + this.height = tabs.isEmpty()?0: offsetY-y-spacing; } return true; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoDropdown.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoDropdown.java index ce389090d..e16681150 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoDropdown.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoDropdown.java @@ -47,9 +47,13 @@ public DecoDropdown(int x, int y) if(dropped&&!IIMath.isPointInRectangle(x, y, x+width, y+height, mouseX, mouseY)) { //Click on scrollbar - if((shouldAlwaysHaveScrollbar()||maxScroll > 0)&&IIMath.isPointInRectangle(x+width-8, y, 8, height, mouseX, mouseY)) + if((shouldAlwaysHaveScrollbar()||maxScroll > 0)&&IIMath.isPointInRectangle( + x+dropdownWidth-11, y+height, + x+dropdownWidth-1, y+height+getListHeight(), + mouseX, mouseY)) { - this.scroll = (int)MathHelper.clamp((float)(mouseY-y-7)/(float)(height-14)*(float)maxScroll, 0, maxScroll); + int slideHeight = Math.max(1, getListHeight()-14); + this.scroll = (int)MathHelper.clamp((float)(mouseY-y-height-7)/(float)slideHeight*(float)maxScroll, 0, maxScroll); return true; } @@ -66,7 +70,7 @@ public DecoDropdown(int x, int y) } } dropped = !dropped; - pressed = false; + pressTime = 0; return true; } return false; @@ -291,7 +295,7 @@ protected void draw(int mouseX, int mouseY, float partialTicks) if(dropped&&!text.isEmpty()) { String drawn = blinkTime > 20?(text+"_"): text; - fontRenderer.drawString(drawn, 0, 0, getTextColor(false).getPackedRGB()); + fontRenderer.drawString(drawn, 2, 2, getTextColor(false).getPackedRGB()); } else { @@ -305,6 +309,23 @@ protected void draw(int mouseX, int mouseY, float partialTicks) @Override public void drawUpperLayer(int mouseX, int mouseY, float partialTicks) { + T selected = getSelectedEntry(); + if(selected!=null) + { + if(parentGui!=null) + parentGui.pushScissorOffset(x, y); + GlStateManager.pushMatrix(); + try + { + GlStateManager.translate(x, y, 0); + display.drawElementUpperLayer(selected, width-12, fontRenderer, mouseX-x, mouseY-y, partialTicks); + } finally + { + GlStateManager.popMatrix(); + if(parentGui!=null) + parentGui.popScissorOffset(); + } + } if(dropped) drawList(x, y+height, dropdownWidth, mouseX, mouseY, partialTicks); } @@ -312,7 +333,7 @@ public void drawUpperLayer(int mouseX, int mouseY, float partialTicks) @Override public void cleanup() { - + display.cleanupDisplay(); } @Nullable @@ -359,7 +380,7 @@ protected boolean canBeClicked(int mouseX, int mouseY) if(IIMath.isPointInRectangle(x, y, x+width, y+height, mouseX, mouseY)) return true; if(dropped) - return IIMath.isPointInRectangle(x, y+height, x+dropdownWidth, y+height+maxPossibleDropHeight, mouseX, mouseY); + return IIMath.isPointInRectangle(x, y+height, x+dropdownWidth, y+height+getListHeight(), mouseX, mouseY); return false; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoElementDisplays.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoElementDisplays.java index 94453d858..61e519468 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoElementDisplays.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoElementDisplays.java @@ -1,11 +1,15 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.IStringSerializable; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent; import pl.pabilo8.immersiveintelligence.client.util.font.IIFontRenderer; import pl.pabilo8.immersiveintelligence.common.util.IIColor; import pl.pabilo8.immersiveintelligence.common.util.ILocalizedEnum; import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @@ -90,6 +94,33 @@ default int displayElement(T t, int width, IIFontRenderer font, boolean heightPr return displayElement(t, width, font, 0, 0, 0, heightProbe); } + /** + * Draws overlays owned by an element after the collection has finished drawing + * and released its scissor. Stateful panel displays use this for dropdown lists + * and other content that must appear above neighbouring entries. + */ + default void drawElementUpperLayer(T t, int width, IIFontRenderer font, int mouseX, int mouseY, float partialTicks) + { + + } + + /** + * Returns the tooltip of the currently hovered virtual element, if any. + */ + default List getTooltip() + { + return Collections.emptyList(); + } + + /** + * Returns whether a dynamically generated component is currently owned by this display. + * Used to validate focus captures after caches or component trees are rebuilt. + */ + default boolean ownsComponent(DecoComponent component) + { + return false; + } + default boolean isSelectable(T t) { return true; @@ -109,6 +140,32 @@ default void bindCollection(DecoScrolledCollection collection) { } + + /** + * Called once for each collection display pass. + * + * @return true when the display changed in a way that requires list layout recalculation + */ + default boolean onDisplayTick() + { + return false; + } + + /** + * Called when the collection's entry set changes. + */ + default void onEntriesChanged(Collection entries) + { + + } + + /** + * Releases resources owned by this display. + */ + default void cleanupDisplay() + { + + } } @FunctionalInterface @@ -132,6 +189,15 @@ public interface DecoElementSorter @Nullable default List autocomplete(List elements, String input) { + if(elements instanceof IStringSerializable) + { + //noinspection unchecked + return elements.stream() + .map(e -> (IStringSerializable)e) + .filter(e -> e.getName().toLowerCase().startsWith(input.toLowerCase())) + .map(e -> (T)e) + .collect(Collectors.toList()); + } return elements.stream() .filter(e -> e.toString().toLowerCase().startsWith(input.toLowerCase())) .collect(Collectors.toList()); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoList.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoList.java index d5db77b09..8a85b9eb9 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoList.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoList.java @@ -1,9 +1,10 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection; -import blusunrize.immersiveengineering.client.ClientUtils; +import net.minecraft.client.Minecraft; import net.minecraft.util.Tuple; import net.minecraft.util.math.MathHelper; import org.apache.commons.lang3.tuple.Pair; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanel; import pl.pabilo8.immersiveintelligence.common.util.IIMath; @@ -27,30 +28,30 @@ public DecoList(int x, int y) { super(x, y); - //Mouse - withOnPressed((gui, mouseButton, mouseX, mouseY) -> - getHoveredPanel(mouseX, mouseY).map(pair -> - { - if(pair.getKey().decoMousePressed(ClientUtils.mc(), mouseX-gui.x, mouseY-pair.getValue(), mouseButton)) - return true; - if(this.onEntryClicked!=null) - { - this.onEntryClicked.accept(lastHoveredEntry); - return true; - } - return false; - } - ).orElseGet(() -> { - //Click on scrollbar - if(maxScroll > 0&&IIMath.isPointInRectangle(gui.x+gui.width-8, gui.y, gui.x+gui.width, gui.y+gui.height, mouseX, mouseY)) - return true; - - if(this.onEntryClicked!=null) - this.onEntryClicked.accept(lastHoveredEntry = null); - return false; - })); + withOnPressed((gui, mouseButton, mouseX, mouseY) -> { + Tuple clicked = getClickedEntryIndex(gui.x+2, gui.y-scroll+2, mouseX, mouseY); + if(clicked!=null) + { + if(clicked.getFirst()==ON_CREATE_OPTION) + return runCreateAction(); + + lastHoveredEntry = entries.get(clicked.getFirst()); + if(onEntryClicked!=null) + { + onEntryClicked.accept(lastHoveredEntry); + return true; + } + return false; + } + + if(maxScroll > 0&&IIMath.isPointInRectangle(gui.x+gui.width-8, gui.y, gui.x+gui.width, gui.y+gui.height, mouseX, mouseY)) + return true; + + if(onEntryClicked!=null) + onEntryClicked.accept(lastHoveredEntry = null); + return false; + }); withOnDragged((gui, button, mouseX, mouseY) -> { - //Click on scrollbar if(maxScroll > 0&&IIMath.isPointInRectangle(gui.x+gui.width-8, gui.y, gui.x+gui.width, gui.y+gui.height, mouseX, mouseY)) { this.scroll = (int)MathHelper.clamp((float)(mouseY-gui.y-7)/(float)(gui.height-14)*(float)maxScroll, 0, maxScroll); @@ -58,13 +59,6 @@ public DecoList(int x, int y) } return false; }); - withOnReleased((gui, mouseButton, mouseX, mouseY) -> - getHoveredPanel(mouseX, mouseY).map(pair -> - { - pair.getKey().mouseReleased(mouseX-gui.x, mouseY-pair.getValue()); - return true; - } - ).orElse(false)); } @@ -95,7 +89,25 @@ protected void draw(int mouseX, int mouseY, float partialTicks) @Override public void cleanup() { + display.cleanupDisplay(); + } + + @Override + protected boolean ownsVirtualChild(DecoComponent component) + { + return display.ownsComponent(component); + } + + @Override + protected DecoMouseCapture decoMousePressedVirtualChild(Minecraft mc, int mouseX, int mouseY, MouseButton button) + { + Optional, Integer>> hovered = getHoveredPanel(mouseX, mouseY); + if(!hovered.isPresent()) + return null; + Pair, Integer> pair = hovered.get(); + DecoMouseCapture capture = pair.getKey().decoMousePressed(mc, mouseX-x, mouseY-pair.getValue(), button); + return capture==null?null: capture.translated(-x, -pair.getValue()); } //--- Public Methods ---// @@ -156,23 +168,19 @@ public void removeIf(Predicate predicate) private Optional, Integer>> getHoveredPanel(int mouseX, int mouseY) { Tuple clicked = getClickedEntryIndex(x+2, y-scroll+2, mouseX, mouseY); - if(clicked!=null) + if(clicked!=null&&clicked.getFirst()!=ON_CREATE_OPTION) { - //Run the "create" action if the special option was clicked - if(clicked.getFirst()==ON_CREATE_OPTION) - { - runCreateAction(); - return Optional.empty(); - } - //Only panels allow more complex interactions if(!(display instanceof DecoEntryPanel)) return Optional.empty(); - DecoEntryPanel panel = (DecoEntryPanel)display; Integer index = clicked.getFirst(); Integer heightOffset = clicked.getSecond(); - //Apply the list element representation to the panel, so actions can affect it - panel.applyElement(this.lastHoveredEntry = entries.get(index)); + T entry = entries.get(index); + DecoEntryPanel panel = ((DecoEntryPanel)display).getElementPanel(entry); + if(panel==null) + return Optional.empty(); + + this.lastHoveredEntry = entry; return Optional.of(Pair.of(panel, heightOffset)); } return Optional.empty(); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoScrolledCollection.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoScrolledCollection.java index 397001c8c..b16027913 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoScrolledCollection.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/collection/DecoScrolledCollection.java @@ -3,8 +3,10 @@ import blusunrize.immersiveengineering.client.ClientUtils; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.inventory.Container; import net.minecraft.util.Tuple; import net.minecraft.util.math.MathHelper; +import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoTextBasedComponent; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoElementDisplays.DecoElementDisplay; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoElementDisplays.DecoElementSorter; @@ -28,6 +30,7 @@ public abstract class DecoScrolledCollection, T> extends DecoTextBasedComponent { protected static final int ON_CREATE_OPTION = -10; + protected IIColor listBackgroundColor = IIColor.WHITE; protected ResLoc listBackgroundLocation = DecoTextures.BG_DARK; protected ResLoc scrollBarLocation = DecoTextures.COMPONENT_SLIDER; @@ -48,13 +51,30 @@ public DecoScrolledCollection(int x, int y) withOnScroll((gui, scroll, mouseX, mouseY) -> { if(hovered&&maxScroll > 0) { - this.scroll = MathHelper.clamp(this.scroll-(scroll/scrollStep), 0, maxScroll); + this.scroll = MathHelper.clamp(this.scroll-(scroll*scrollStep), 0, maxScroll); return true; } return false; }); } + @Override + public void setParentGUI(DecoGui parent) + { + super.setParentGUI(parent); + //Displays may have created entry panels during an earlier height probe, before + //the collection was attached to its GUI. Rebinding discards those contextless + //instances so interactive children receive the proper focus owner. + display.bindCollection(this); + } + + @Override + public List getTooltip() + { + List tooltip = super.getTooltip(); + return tooltip.isEmpty()?display.getTooltip(): tooltip; + } + /** * Sets the background texture of the list * @@ -68,6 +88,19 @@ public E withListBackground(ResLoc listBackgroundLocation) return (E)this; } + /** + * Sets the background color of the list + * + * @param listBackgroundColor The background color + * @return this + */ + public E withListBackgroundColor(IIColor listBackgroundColor) + { + this.listBackgroundColor = listBackgroundColor; + //noinspection unchecked + return (E)this; + } + /** * Sets the texture of the scrollbar * @@ -89,8 +122,11 @@ public E withScrollBarBackground(ResLoc scrollBarLocation) */ public E withDisplayFunction(DecoElementDisplay display) { + this.display.cleanupDisplay(); this.display = display; display.bindCollection(this); + display.onEntriesChanged(entries); + calculateSlideLength(); //noinspection unchecked return (E)this; } @@ -104,6 +140,9 @@ public E withDisplayFunction(DecoElementDisplay display) public E withSortFunction(DecoElementSorter sorter) { this.sorter = sorter; + this.entries = sorter.sort(this.entries); + display.onEntriesChanged(entries); + calculateSlideLength(); //noinspection unchecked return (E)this; } @@ -121,6 +160,7 @@ public E withEntries(Collection entries) else this.entries = new ArrayList<>(entries); this.entries = sorter.sort(this.entries); + display.onEntriesChanged(this.entries); calculateSlideLength(); //noinspection unchecked return (E)this; @@ -265,7 +305,8 @@ protected List autocomplete() protected final void drawList(int x, int y, int listWidth, int mouseX, int mouseY, float partialTicks) { //Apply queued changes to the list - if(!toBeAdded.isEmpty()||!toBeRemoved.isEmpty()) + boolean entriesChanged = !toBeAdded.isEmpty()||!toBeRemoved.isEmpty(); + if(entriesChanged) { while(!toBeAdded.isEmpty()) { @@ -277,16 +318,20 @@ protected final void drawList(int x, int y, int listWidth, int mouseX, int mouse entries.remove(toBeRemoved.poll()); entries = sorter.sort(entries); } - calculateSlideLength(); + display.onEntriesChanged(entries); } + //Allow stateful displays to invalidate their cached layout on a controlled cadence. + if(entriesChanged||display.onDisplayTick()) + calculateSlideLength(); + //Draw list bindAtlas(); IIDrawUtils draw = IIDrawUtils.startTexturedColored(); //Background int listHeight = getListHeight(); - draw.drawConnectedTexColorRect(x, y, listWidth, listHeight, IIColor.WHITE, listBackgroundLocation, 64, 64, 8, 8); + draw.drawConnectedTexColorRect(x, y, listWidth, listHeight, listBackgroundColor, listBackgroundLocation, 64, 64, 8, 8); //Scrollbar if(shouldAlwaysHaveScrollbar()||maxScroll > 0) { @@ -321,14 +366,19 @@ protected final void drawList(int x, int y, int listWidth, int mouseX, int mouse //Filter entries based on search input List filteredEntries = autocomplete(); + List> displayedElements = new ArrayList<>(filteredEntries.size()); int alreadyDrawnHeight = 0; int currentColumn = 0; for(T filteredEntry : filteredEntries) { + int elementX = x+(currentColumn*entryMaxWidth); + int elementY = y+1+alreadyDrawnHeight; GlStateManager.pushMatrix(); - GlStateManager.translate(x+(currentColumn*entryMaxWidth), y+1+alreadyDrawnHeight, 0); - int offset = display.displayElement(filteredEntry, entryMaxWidth, fontRenderer, mouseX-(x+(currentColumn*entryMaxWidth)), mouseY+scroll-y-alreadyDrawnHeight, partialTicks, false); + GlStateManager.translate(elementX, elementY, 0); + int offset = display.displayElement(filteredEntry, entryMaxWidth, fontRenderer, + mouseX-elementX, mouseY+scroll-elementY, partialTicks, false); GlStateManager.popMatrix(); + displayedElements.add(new DisplayedElement<>(filteredEntry, elementX, elementY-scroll, offset)); currentColumn++; if(currentColumn >= entriesInGrid) @@ -349,6 +399,32 @@ protected final void drawList(int x, int y, int listWidth, int mouseX, int mouse if(parentGui!=null) parentGui.scissorEnd(); GlStateManager.popMatrix(); + + //Element overlays must be drawn after neighbouring rows and outside the parent + //collection scissor. Otherwise nested dropdown lists are clipped or overdrawn. + for(DisplayedElement element : displayedElements) + { + if(element.y+element.height < y||element.y > y+listHeight) + continue; + + //The element panel is positioned using the model-view matrix. glScissor does + //not observe that translation, so nested collections need the same origin + //supplied separately in window-coordinate calculations. + if(parentGui!=null) + parentGui.pushScissorOffset(element.x, element.y); + GlStateManager.pushMatrix(); + try + { + GlStateManager.translate(element.x, element.y, 0); + display.drawElementUpperLayer(element.entry, entryMaxWidth, fontRenderer, + mouseX-element.x, mouseY-element.y, partialTicks); + } finally + { + GlStateManager.popMatrix(); + if(parentGui!=null) + parentGui.popScissorOffset(); + } + } } @Override @@ -435,4 +511,18 @@ protected boolean runCreateAction() onCreate.run(); return onCreate!=null; } + + private static class DisplayedElement + { + private final T entry; + private final int x, y, height; + + private DisplayedElement(T entry, int x, int y, int height) + { + this.entry = entry; + this.x = x; + this.y = y; + this.height = height; + } + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoCodeEditor.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoCodeEditor.java index 784ed7f95..b53a471df 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoCodeEditor.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoCodeEditor.java @@ -1,7 +1,7 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.data_editor; import pl.pabilo8.immersiveintelligence.api.data.types.generic.DataType; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextArea; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.highlight.POLHighlighter; /** @@ -22,10 +22,10 @@ protected boolean initialize() if(!super.initialize()) return false; - addComponent(new DecoTextField(0, 0) + addComponent(new DecoTextArea(0, 0) .withSize(width, height) - .withMultiLine(true) .withHighlighter(new POLHighlighter()) + .withEditable(false) .withText(new String[]{ ";This is an editor for data variables", ";POL keywords will be highlighted, but", @@ -42,7 +42,7 @@ protected boolean initialize() @Override public DataType outputType() { - // The code view is currently a read-only preview; never return null, or applying the editor would erase the variable. + //The code view is currently a read-only preview; never return null, or applying the editor would erase the variable. return dataType; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorExpression.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorExpression.java index edf4cb510..3ff210ce5 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorExpression.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorExpression.java @@ -1,5 +1,6 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.data_editor; +import net.minecraft.util.text.TextFormatting; import pl.pabilo8.immersiveintelligence.api.data.DataPacket; import pl.pabilo8.immersiveintelligence.api.data.IIDataTypeUtils; import pl.pabilo8.immersiveintelligence.api.data.operations.DataOperation.DataOperationMeta; @@ -74,10 +75,10 @@ private DecoTabGroup buildTabs(DataOperationMeta meta) .withSize(width, 12) .withHorizontalAlignment(true) .withTab((DecoTab)new DecoTab() - .withText(IIReference.DESCRIPTION_KEY+"variable_properties") - .withPadding(4, 2, 4, 2) - .withOnPressed((gui, button, mouseX, mouseY) -> setPage(0)) - .withTranslatedTooltip(IIReference.DESCRIPTION_KEY+"variable_properties.tooltip") + .withText(IIReference.DESCRIPTION_KEY+"variable_properties") + .withPadding(4, 2, 4, 2) + .withTranslatedTooltip(IIReference.DESCRIPTION_KEY+"variable_properties.tooltip"), + () -> setPage(0) ); String[] params = meta.params(); @@ -85,11 +86,16 @@ private DecoTabGroup buildTabs(DataOperationMeta meta) { int finalParamID = paramID+1; group.withTab((DecoTab)new DecoTab() - .withText("datasystem.immersiveintelligence.function."+meta.name()+".param."+params[paramID]) - .withPadding(4, 2, 4, 2) - .withOnPressed((gui, button, mouseX, mouseY) -> setPage(finalParamID)) + .withText("datasystem.immersiveintelligence.function."+meta.name()+".param."+params[paramID]) + .withPadding(4, 2, 4, 2) + .withTranslatedTooltip( + "datasystem.immersiveintelligence.function."+meta.name()+".param."+params[paramID], + TextFormatting.GRAY+IIReference.DATA_KEY+"function."+meta.name()+".param."+params[paramID]+".desc" + ), + () -> setPage(finalParamID) ); } + group.selectTab(page, false); return group; } @@ -165,11 +171,11 @@ private void initializeArgumentPage(DataOperationMeta meta, int argumentID) .withMaxDisplayedEntries(4) .withScrollBarBackground(DecoTextures.COMPONENT_SLIDER_PAPER) .withBackground(DecoTextures.COMPONENT_BUTTON_PAPER) - .withListBackground(DecoTextures.COMPONENT_TEXT_FIELD) .withDropdownSymbol(DecoTextures.COMPONENT_DROPDOWN_SYMBOL_PAPER) .withEntries(typeEntries) .withSelectedEntry(selectedMeta) .withDisplayFunction(DecoTemplates.getDataTypeEntryDisplay()) + .withSortFunction(DecoTemplates.getDataTypeEntrySorter()) .withOnSelectedEntry((oldType, newType) -> { storeCurrentPageOutput(); DataType current = getArgument(argumentID, expectedType); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorFloat.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorFloat.java index 236f529fd..6d8be146e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorFloat.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorFloat.java @@ -33,7 +33,7 @@ protected boolean initialize() @Override public DataTypeFloat outputType() { - dataType.value = Float.parseFloat(valueEdit.getText()); + dataType.value = TextFilter.FLOAT.parseFloat(valueEdit.getText(), dataType.value); return dataType; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorInteger.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorInteger.java index 5c6017d57..11e2a5fe2 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorInteger.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorInteger.java @@ -35,8 +35,12 @@ protected boolean initialize() //Mode select dropdown valueDropdown = new DecoDropdown(2, 32+2) .withEntries(TextFilter.BINARY, TextFilter.DECIMAL, TextFilter.HEXADECIMAL) - .withOnSelectedEntry((oldValue, newValue) -> - valueEdit.withFilter(newValue)) + .withOnSelectedEntry((oldValue, newValue) -> { + TextFilter previous = Optional.ofNullable(oldValue).orElse(TextFilter.DECIMAL); + TextFilter selected = Optional.ofNullable(newValue).orElse(TextFilter.DECIMAL); + int current = previous.parseInt(valueEdit.getText(), dataType.value); + valueEdit.withFilter(selected).withText(selected.formatInt(current)); + }) .withSelectedEntry(TextFilter.DECIMAL) ); return super.initialize(); @@ -45,18 +49,8 @@ protected boolean initialize() @Override public DataTypeInteger outputType() { - switch(Optional.ofNullable(valueDropdown.getSelectedEntry()).orElse(TextFilter.DECIMAL)) - { - case DECIMAL: - dataType.value = Integer.parseInt(valueEdit.getText(), 10); - break; - case HEXADECIMAL: - dataType.value = Integer.parseInt(valueEdit.getText(), 16); - break; - case BINARY: - dataType.value = Integer.parseInt(valueEdit.getText(), 2); - break; - } + TextFilter selected = Optional.ofNullable(valueDropdown.getSelectedEntry()).orElse(TextFilter.DECIMAL); + dataType.value = selected.parseInt(valueEdit.getText(), dataType.value); return dataType; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorLogisticTag.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorLogisticTag.java index f036804b0..7bb674051 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorLogisticTag.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorLogisticTag.java @@ -92,16 +92,15 @@ protected boolean initialize() .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_STEEL) .withHeight(12) - .withComponent("icon", new DecoImage(2, 1) + .withComponent("icon", p -> new DecoImage(2, 1) .withSize(8, 8) .withImageLocation(DecoTextures.COMPONENT_COLOR, true) .withUV(16, 4, 4, 12, 12) ) - .withLabel("label", - new DecoLabel(IIClientUtils.fontRegular, 12, 1) - .withSize(48, 12) - .withAlign(DecoAlignment.LEFT) - .withRawText("Color") + .withLabel("label", p -> new DecoLabel(IIClientUtils.fontRegular, 12, 1) + .withSize(48, 12) + .withAlign(DecoAlignment.LEFT) + .withRawText("Color") ) .withElementApplyMethod((tf, builder) -> { builder.component("icon", DecoImage.class).withColor(IIColor.fromDye(tf)); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorString.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorString.java index cab51874e..ba7d86efa 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorString.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorString.java @@ -1,7 +1,7 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.data_editor; import pl.pabilo8.immersiveintelligence.api.data.types.DataTypeString; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextArea; import pl.pabilo8.immersiveintelligence.common.util.IIReference; /** @@ -10,7 +10,7 @@ */ public class DecoDataEditorString extends DecoDataEditor { - private DecoTextField valueEdit; + private DecoTextArea valueEdit; public DecoDataEditorString(int x, int y, DataTypeString dataType) { @@ -21,9 +21,8 @@ public DecoDataEditorString(int x, int y, DataTypeString dataType) protected boolean initialize() { addLabel(IIReference.DESCRIPTION_KEY+"variable_value", 2, 2); - addComponent(this.valueEdit = new DecoTextField(2, 12) + addComponent(this.valueEdit = new DecoTextArea(2, 12) .withSize(width-8, height-20) - .withMultiLine(true) .withText(dataType.toString()) ); return super.initialize(); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorVector.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorVector.java index b9787245d..11a5d4735 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorVector.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/data_editor/DecoDataEditorVector.java @@ -55,7 +55,7 @@ protected boolean initialize() private void refreshFieldFormatting(DecoTextField field) { - float currentValue = IIStringUtil.parseFloat(field.getText()); + float currentValue = TextFilter.FLOAT.parseFloat(field.getText(), IIStringUtil.parseFloat(field.getText())); field.withFilter(dataType.integerVector?TextFilter.DECIMAL: TextFilter.FLOAT) .withText(dataType.integerVector?Integer.toString((int)currentValue): Float.toString(currentValue)); } @@ -63,9 +63,10 @@ private void refreshFieldFormatting(DecoTextField field) @Override public DataTypeVector outputType() { - dataType.x = IIStringUtil.parseFloat(x.getText()); - dataType.y = IIStringUtil.parseFloat(y.getText()); - dataType.z = IIStringUtil.parseFloat(z.getText()); + TextFilter filter = dataType.integerVector?TextFilter.DECIMAL: TextFilter.FLOAT; + dataType.x = filter==TextFilter.DECIMAL?filter.parseInt(x.getText(), (int)dataType.x): filter.parseFloat(x.getText(), dataType.x); + dataType.y = filter==TextFilter.DECIMAL?filter.parseInt(y.getText(), (int)dataType.y): filter.parseFloat(y.getText(), dataType.y); + dataType.z = filter==TextFilter.DECIMAL?filter.parseInt(z.getText(), (int)dataType.z): filter.parseFloat(z.getText(), dataType.z); return dataType; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/label/DecoLabel.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/label/DecoLabel.java index 639c22469..1149bf239 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/label/DecoLabel.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/label/DecoLabel.java @@ -233,7 +233,7 @@ public void drawLabel(@Nonnull Minecraft mc, int mouseX, int mouseY) return; //Draw a highlight background for the text if(bgColor.alpha > 0) - IIDrawUtils.startColored().drawColorRect(x, y, x+width, y+height, bgColor).finish(); + IIDrawUtils.startColored().drawColorRect(x, y, width, height, bgColor).finish(); int lineOffset = y; boolean unicode = fontRenderer.getUnicodeFlag(); @@ -271,7 +271,7 @@ public void drawLabel(@Nonnull Minecraft mc, int mouseX, int mouseY) fontRenderer.drawString(subLine, currentX, currentY, textColor.getPackedARGB(), textShadow); stringHeight += fontRenderer.FONT_HEIGHT; - // Track the widest sub-line for hover detection + //Track the widest sub-line for hover detection if(subLineWidth > stringWidth) stringWidth = subLineWidth; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanel.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanel.java index 394f0d7f2..d005ec96d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanel.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanel.java @@ -11,6 +11,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; import pl.pabilo8.immersiveintelligence.client.util.font.IIFontRenderer; +import javax.annotation.Nullable; import java.util.Collections; import java.util.function.Function; @@ -22,9 +23,10 @@ public abstract class DecoEntryPanel extends DecoPanel implements DecoElementDisplay { private int displayedHeight; - private DecoButton addButton; + private final DecoButton addButton; private DecoScrolledCollection list; private T element; + private boolean hasElement; public DecoEntryPanel() { @@ -47,6 +49,8 @@ protected boolean initialize() cleanup(); initializeChildren(); this.displayedHeight = height; + if(hasElement) + applyElementToChildren(element); } return super.initialize(); } @@ -79,22 +83,28 @@ public T getCurrentElement() @Override public int displayElement(T t, int width, IIFontRenderer font, int mouseX, int mouseY, float partialTicks, boolean heightProbe) { - //If the width has changed, reinitialize the panel - if(this.width!=width) + //Initialize before probing or drawing. Direct initialize() calls must also update + //the component lifecycle flag, otherwise drawButton() rebuilds the children again. + if(this.width!=width||!initialized) { - //Readjust the width this.width = width; - if(!initialize()) + this.initialized = false; + if(!(this.initialized = initialize())) return 0; //Readjust the height - int minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE; - for(DecoComponent child : children) + if(children.isEmpty()) + this.displayedHeight = height; + else { - minY = Math.min(minY, child.y); - maxY = Math.max(maxY, child.y+child.height); + int minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE; + for(DecoComponent child : children) + { + minY = Math.min(minY, child.y); + maxY = Math.max(maxY, child.y+child.height); + } + this.displayedHeight = Math.max(2+maxY-minY, height); } - this.displayedHeight = Math.max(2+maxY-minY, height); } //If the height is being probed, return the height @@ -105,10 +115,22 @@ public int displayElement(T t, int width, IIFontRenderer font, int mouseX, int m applyElement(t); Minecraft mc = ClientUtils.mc(); drawButton(mc, mouseX, mouseY, partialTicks); - drawButtonUpperLayer(mc, mouseX, mouseY, partialTicks); return displayedHeight; } + @Override + public void drawElementUpperLayer(T t, int width, IIFontRenderer font, int mouseX, int mouseY, float partialTicks) + { + applyElement(t); + drawButtonUpperLayer(ClientUtils.mc(), mouseX, mouseY, partialTicks); + } + + @Override + public boolean ownsComponent(DecoComponent component) + { + return this==component; + } + @Override public void drawCreateOption(int width, int height, IIFontRenderer font, int mouseX, int mouseY) { @@ -130,14 +152,39 @@ public void bindCollection(DecoScrolledCollection collection) } /** - * Applies the element to the panel + * Returns the panel instance associated with an element for input routing. + * Non-caching implementations use themselves. + */ + @Nullable + public DecoEntryPanel getElementPanel(T t) + { + applyElement(t); + return this; + } + + /** + * Applies the element to the panel when it differs from the already bound instance. * * @param t The element to apply */ public final void applyElement(T t) + { + if(hasElement&&this.element==t) + return; + refreshElement(t); + } + + /** + * Forces the element representation to be reapplied. + * + * @param t The element to apply + */ + public final void refreshElement(T t) { this.element = t; - applyElementToChildren(t); + this.hasElement = true; + if(initialized) + applyElementToChildren(t); } /** @@ -147,7 +194,6 @@ public final void applyElement(T t) */ protected abstract void applyElementToChildren(T t); - /** * Sets the tooltip function for this panel using the current element. * diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanelBuilder.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanelBuilder.java index 0eb1fd270..cf4e7a657 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanelBuilder.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoEntryPanelBuilder.java @@ -1,16 +1,19 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoScrolledCollection; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoFrame; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.util.font.IIFontRenderer; import pl.pabilo8.immersiveintelligence.common.util.ResLoc; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Function; +import java.util.function.Supplier; /** * @author Pabilo8 (pabilo@iiteam.net) @@ -19,14 +22,50 @@ **/ public class DecoEntryPanelBuilder extends DecoEntryPanel { - private final Map, DecoComponent>> components = new HashMap<>(); + private final Map, DecoComponent>> components = new LinkedHashMap<>(); + private final Map, DecoLabel>> labelFactories = new LinkedHashMap<>(); private final Map> childrenMap = new HashMap<>(); - private final Map labels = new HashMap<>(); + private final Map labelsMap = new HashMap<>(); + private final Map> panelCache = new HashMap<>(); + private final boolean cachedEntry; + private int paddingX, paddingY; + private int refreshInterval, displayTicks; + private boolean layoutDirty; private BiConsumer> elementApplyMethod; + private Function elementTooltip; + @Nullable + private DecoFrame panelFrame; + @Nullable + private ResLoc panelBackground = DecoTextures.BG_PAPER; + @Nullable + private ResLoc panelBackgroundMask = DecoTextures.TEMPLATE_PAPER; public DecoEntryPanelBuilder() { + this.cachedEntry = false; + } + + private DecoEntryPanelBuilder(DecoEntryPanelBuilder template) + { + this.cachedEntry = true; + this.components.putAll(template.components); + this.labelFactories.putAll(template.labelFactories); + this.paddingX = template.paddingX; + this.paddingY = template.paddingY; + this.elementApplyMethod = template.elementApplyMethod; + this.elementTooltip = template.elementTooltip; + this.panelFrame = template.panelFrame; + this.panelBackground = template.panelBackground; + this.panelBackgroundMask = template.panelBackgroundMask; + this.height = template.height; + this.width = -1; + + super.withBackground(panelBackground); + super.withBackgroundMask(panelBackgroundMask); + super.withFrame(panelFrame); + if(elementTooltip!=null) + super.withElementTooltip(elementTooltip); } @SuppressWarnings({"rawtypes", "unchecked"}) @@ -35,16 +74,18 @@ protected void initializeChildren() { super.withPadding(paddingX, paddingY); childrenMap.clear(); + labelsMap.clear(); components.forEach((name, function) -> { DecoComponent component = function.apply(this); - if(this.parentGui!=null) - component.setParentGUI(this.parentGui); this.addComponent(component); this.childrenMap.put(name, component); }); - for(DecoLabel label : labels.values()) + labelFactories.forEach((name, function) -> { + DecoLabel label = function.apply(this); this.addLabel(label); + this.labelsMap.put(name, label); + }); } @Override @@ -54,41 +95,190 @@ protected void applyElementToChildren(TYPE type) elementApplyMethod.accept(type, this); } + @Override + public int displayElement(TYPE type, int width, IIFontRenderer font, int mouseX, int mouseY, float partialTicks, boolean heightProbe) + { + if(cachedEntry) + return super.displayElement(type, width, font, mouseX, mouseY, partialTicks, heightProbe); + + if(this.width!=width) + { + this.width = width; + clearCache(false); + } + + DecoEntryPanelBuilder panel = panelCache.get(type); + if(panel==null) + { + panel = new DecoEntryPanelBuilder<>(this); + panel.bindCollection(getCurrentList()); + panel.parentGui = getCurrentList()==null?getParentGui(): getCurrentList().getParentGui(); + panel.applyElement(type); + panelCache.put(type, panel); + } + else + panel.applyElement(type); + + return panel.displayElement(type, width, font, mouseX, mouseY, partialTicks, heightProbe); + } + + @Override + public void drawElementUpperLayer(TYPE type, int width, IIFontRenderer font, int mouseX, int mouseY, float partialTicks) + { + if(cachedEntry) + { + super.drawElementUpperLayer(type, width, font, mouseX, mouseY, partialTicks); + return; + } + + DecoEntryPanelBuilder panel = panelCache.get(type); + if(panel!=null) + panel.drawElementUpperLayer(type, width, font, mouseX, mouseY, partialTicks); + } + + @Override + public List getTooltip() + { + if(cachedEntry) + return super.getTooltip(); + + for(DecoEntryPanelBuilder panel : panelCache.values()) + if(panel.isMouseOver()) + return panel.getTooltip(); + return Collections.emptyList(); + } + + @Override + public boolean ownsComponent(DecoComponent component) + { + return cachedEntry?this==component: panelCache.containsValue(component); + } + + @Override + public void bindCollection(DecoScrolledCollection collection) + { + super.bindCollection(collection); + if(!cachedEntry) + clearCache(false); + } + + @Nullable + @Override + public DecoEntryPanel getElementPanel(TYPE type) + { + if(cachedEntry) + return super.getElementPanel(type); + return panelCache.get(type); + } + + @Override + public boolean onDisplayTick() + { + if(cachedEntry) + return false; + + if(layoutDirty) + { + layoutDirty = false; + return true; + } + if(refreshInterval <= 0||++displayTicks < refreshInterval) + return false; + + clearCache(false); + return true; + } + + @Override + public void onEntriesChanged(Collection entries) + { + if(cachedEntry||panelCache.isEmpty()) + return; + + Iterator>> iterator = panelCache.entrySet().iterator(); + while(iterator.hasNext()) + { + Map.Entry> cached = iterator.next(); + if(!entries.contains(cached.getKey())) + { + cleanupCachedPanel(cached.getValue()); + iterator.remove(); + } + } + } + + @Override + public void cleanupDisplay() + { + if(cachedEntry) + cleanup(); + else + clearCache(false); + } + @Override public DecoEntryPanelBuilder withElementTooltip(Function onTooltip) { + this.elementTooltip = onTooltip; super.withElementTooltip(onTooltip); + invalidateDefinition(); + return this; + } + + /** + * Clears all cached entry panels. Their component trees will be rebuilt lazily on the next layout or draw pass. + * + * @return this + */ + public DecoEntryPanelBuilder refreshCache() + { + clearCache(true); + return this; + } + + /** + * Sets a periodic cache refresh interval. + * + * @param displayTicks number of collection display passes between cache refreshes; zero disables periodic refresh + * @return this + */ + public DecoEntryPanelBuilder withRefreshInterval(int displayTicks) + { + this.refreshInterval = Math.max(0, displayTicks); + this.displayTicks = 0; return this; } //--- Settings ---// @Override - @SuppressWarnings("unchecked") public DecoEntryPanelBuilder withSize(int width, int height) { - return (DecoEntryPanelBuilder)super.withSize(width, height); + return withWidth(width).withHeight(height); } @Override - @SuppressWarnings("unchecked") public DecoEntryPanelBuilder withWidth(int width) { - return (DecoEntryPanelBuilder)super.withWidth(width); + super.withWidth(width); + invalidateDefinition(); + return this; } @Override - @SuppressWarnings("unchecked") public DecoEntryPanelBuilder withHeight(int height) { - return (DecoEntryPanelBuilder)super.withHeight(height); + super.withHeight(height); + invalidateDefinition(); + return this; } @Override - @SuppressWarnings("unchecked") public DecoEntryPanelBuilder withTemplate(DecoComponentTemplate template) { - return (DecoEntryPanelBuilder)super.withTemplate(template); + super.withTemplate(template); + invalidateDefinition(); + return this; } @Override @@ -96,35 +286,42 @@ public DecoEntryPanelBuilder withPadding(int x, int y) { this.paddingX = x; this.paddingY = y; + invalidateDefinition(); return this; } @Override - @SuppressWarnings("unchecked") public DecoEntryPanelBuilder withFrame(@Nullable DecoFrame frame) { - return (DecoEntryPanelBuilder)super.withFrame(frame); + this.panelFrame = frame; + super.withFrame(frame); + invalidateDefinition(); + return this; } @Override - @SuppressWarnings("unchecked") - public DecoEntryPanelBuilder withBackgroundMask(ResLoc backgroundMask) + public DecoEntryPanelBuilder withBackgroundMask(@Nullable ResLoc backgroundMask) { - return (DecoEntryPanelBuilder)super.withBackgroundMask(backgroundMask); + this.panelBackgroundMask = backgroundMask; + super.withBackgroundMask(backgroundMask); + invalidateDefinition(); + return this; } @Override - @SuppressWarnings("unchecked") - public DecoEntryPanelBuilder withBackground(ResLoc background) + public DecoEntryPanelBuilder withBackground(@Nullable ResLoc background) { - return (DecoEntryPanelBuilder)super.withBackground(background); + this.panelBackground = background; + super.withBackground(background); + invalidateDefinition(); + return this; } //--- Components ---// - public DecoEntryPanelBuilder withComponent(String name, DecoComponent component) + public DecoEntryPanelBuilder withComponent(String name, Supplier> component) { - return withComponent(name, p -> component); + return withComponent(name, p -> component.get()); } public DecoEntryPanelBuilder withComponent(Function, DecoComponent> component) @@ -135,14 +332,21 @@ public DecoEntryPanelBuilder withComponent(Function withComponent(String name, Function, DecoComponent> component) { this.components.put(name, component); + invalidateDefinition(); return this; } //--- Labels ---// - public DecoEntryPanelBuilder withLabel(String name, DecoLabel label) + public DecoEntryPanelBuilder withLabel(String name, Supplier label) { - this.labels.put(name, label); + return withLabel(name, p -> label.get()); + } + + public DecoEntryPanelBuilder withLabel(String name, Function, DecoLabel> label) + { + this.labelFactories.put(name, label); + invalidateDefinition(); return this; } @@ -151,6 +355,7 @@ public DecoEntryPanelBuilder withLabel(String name, DecoLabel label) public DecoEntryPanelBuilder withElementApplyMethod(BiConsumer> method) { this.elementApplyMethod = method; + invalidateDefinition(); return this; } @@ -162,15 +367,36 @@ public > T component(String name, @SuppressWa public DecoLabel label(String name) { - return this.labels.getOrDefault(name, null); + return this.labelsMap.getOrDefault(name, null); } //--- Utils ---// + + private void clearCache(boolean markLayoutDirty) + { + panelCache.values().forEach(this::cleanupCachedPanel); + panelCache.clear(); + displayTicks = 0; + layoutDirty |= markLayoutDirty; + } + + private void cleanupCachedPanel(DecoEntryPanelBuilder panel) + { + if(panel.getParentGui()!=null) + panel.getParentGui().releaseFocusWithin(panel); + panel.cleanup(); + } + + private void invalidateDefinition() + { + if(panelCache!=null&&!cachedEntry&&!panelCache.isEmpty()) + refreshCache(); + } + @Nonnull private String getGenericComponentName() { return String.valueOf(components.size()); } - } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoPanel.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoPanel.java index a4aa203df..dae7cb046 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoPanel.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoPanel.java @@ -169,10 +169,13 @@ protected boolean initialize() { if(background==null||backgroundMask==null) return true; + bindAtlas(); TextureAtlasSprite maskSprite = ClientUtils.getSprite(backgroundMask); //Start vbo = GlStateManager.glGenLists(1); + if(vbo <= 0) + return false; GlStateManager.glNewList(vbo, GL11.GL_COMPILE); GlStateManager.color(1f, 1f, 1f, 1f); GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); @@ -211,7 +214,7 @@ protected boolean initialize() GlStateManager.disableBlend(); GlStateManager.glEndList(); - return vbo!=-1; + return vbo > 0; } @Override @@ -219,10 +222,10 @@ protected void draw(int mouseX, int mouseY, float partialTicks) { GlStateManager.color(1, 1, 1, 1); bindAtlas(); - GlStateManager.callList(vbo); GlStateManager.pushMatrix(); GlStateManager.enableBlend(); + GlStateManager.callList(vbo); GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); GlStateManager.enableAlpha(); @@ -239,7 +242,7 @@ public void cleanup() children.clear(); labels.clear(); initialized = false; - if(vbo!=-1) + if(vbo > 0) { GlStateManager.glDeleteLists(vbo, 1); vbo = -1; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoTaskList.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoTaskList.java index e1317ede1..a914c83e0 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoTaskList.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/panel/DecoTaskList.java @@ -3,6 +3,8 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.util.INBTSerializable; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTab; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTabGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoList; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplates; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; @@ -50,6 +52,8 @@ public class DecoTaskList> extends De private EasyCollection allEntries; private Predicate isJobPredicate = t -> false; private DecoList list; + private DecoTabGroup modeTabs; + private DecoTab jobsTab, requestsTab; @Nullable private T selected; @@ -108,8 +112,9 @@ public DecoTaskList withDisplayFunction(DecoEntryPanelBuilder builder) public DecoTaskList withModeHandling(@Nullable ListMode initialMode, Consumer onModeChanged) { - this.mode = normalizeMode(initialMode); + this.mode = normalizeAvailableMode(initialMode); this.onModeChanged = onModeChanged; + syncModeTabSelection(); return this; } @@ -137,6 +142,8 @@ public DecoTaskList withBlankTaskSupplier(@Nullable Supplier blankTaskSupp public DecoTaskList withShowJobsTab(boolean showJobsTab) { this.showJobsTab = showJobsTab; + this.mode = normalizeAvailableMode(this.mode); + syncModeTabSelection(); return this; } @@ -147,17 +154,33 @@ public ListMode getMode() public void setMode(ListMode mode) { - this.mode = normalizeMode(mode); + this.mode = normalizeAvailableMode(mode); + syncModeTabSelection(); if(onModeChanged!=null) onModeChanged.accept(this.mode); refreshListEntries(); } + private ListMode normalizeAvailableMode(@Nullable ListMode mode) + { + ListMode normalized = normalizeMode(mode); + return !showJobsTab&&normalized==ListMode.JOBS?ListMode.REQUESTS: normalized; + } + private static ListMode normalizeMode(@Nullable ListMode mode) { return mode==null||mode==ListMode.TASKS?ListMode.REQUESTS: mode; } + private void syncModeTabSelection() + { + if(modeTabs==null) + return; + DecoTab selectedTab = mode==ListMode.JOBS?jobsTab: requestsTab; + if(selectedTab!=null) + modeTabs.selectTab(selectedTab, false); + } + @Nullable public T getSelected() { @@ -196,31 +219,27 @@ protected boolean initialize() return false; //Mode tabs - DecoButton tabJobs = new DecoButton(0, 4) - .withSize(listWidth/2, TAB_H) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(GUI_LABEL_KEY+"task_editor.jobs") - .withTranslatedTooltip(GUI_LABEL_KEY+"task_editor.jobs.tooltip") - .withOnLMBPressed(() -> setMode(ListMode.JOBS)); + modeTabs = addComponent(new DecoTabGroup(0, 4) + .withSize(listWidth, TAB_H) + .withHorizontalAlignment(true) + .withTabWidth(showJobsTab?listWidth/2: listWidth)); - DecoButton tabRequests = new DecoButton(listWidth/2, 4) - .withSize(listWidth/2, TAB_H) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) + jobsTab = (DecoTab)new DecoTab() + .withText(GUI_LABEL_KEY+"task_editor.jobs") + .withTranslatedTooltip(GUI_LABEL_KEY+"task_editor.jobs.tooltip"); + requestsTab = (DecoTab)new DecoTab() .withText(GUI_LABEL_KEY+"task_editor.requests") - .withTranslatedTooltip(GUI_LABEL_KEY+"task_editor.requests.tooltip") - .withOnLMBPressed(() -> setMode(ListMode.REQUESTS)); - - addComponent(tabJobs); - addComponent(tabRequests); + .withTranslatedTooltip(GUI_LABEL_KEY+"task_editor.requests.tooltip"); - if(!showJobsTab) + if(showJobsTab) + modeTabs.withTab(jobsTab, () -> setMode(ListMode.JOBS)); + else { - //stretch requests tab; hide jobs tab - tabRequests.withSize(listWidth, TAB_H); - tabRequests.x = tabJobs.x; - tabJobs.visible = tabJobs.enabled = false; + jobsTab = null; this.mode = ListMode.REQUESTS; } + modeTabs.withTab(requestsTab, () -> setMode(ListMode.REQUESTS)); + syncModeTabSelection(); //List list = addComponent(new DecoList(0, LIST_Y_OFF) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoBar.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoBar.java index e979a2ae9..616d00757 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoBar.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoBar.java @@ -314,7 +314,7 @@ void drawIcon(IIDrawUtils draw, boolean drawBackground) return; } - // Horizontal mode: draw icon on the right side of the bar, centered vertically + //Horizontal mode: draw icon on the right side of the bar, centered vertically float iconCX = x+width-9; float iconCY = y+(height*0.5f); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScenarioDisplay.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScenarioDisplay.java index 9c6e8b6d5..b4417281a 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScenarioDisplay.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScenarioDisplay.java @@ -245,7 +245,7 @@ else if(backgroundColor!=null) private boolean handleMouseScroll(DecoScenarioDisplay gui, int mouseScroll, int mouseX, int mouseY) { if(zoomAllowed) - scale = MathHelper.clamp(scale+Math.signum(mouseScroll)*0.05f, 0.5f, 2f); + scale = MathHelper.clamp(scale+mouseScroll*0.05f, 0.5f, 2f); return false; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScrollableItemSlots.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScrollableItemSlots.java index 9e5431aa4..a6b0dcf14 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScrollableItemSlots.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/storage/DecoScrollableItemSlots.java @@ -30,6 +30,7 @@ public class DecoScrollableItemSlots extends DecoComponent slots = new ArrayList<>(); private final Map originalPos = new IdentityHashMap<>(); @@ -48,9 +49,7 @@ public DecoScrollableItemSlots(int x, int y) return false; if(getMaxScrollRows() <= 0) return false; - int delta = Integer.compare(0, scroll); - if(delta!=0) - setScrollRows(scrollRows+delta); + setScrollRows(scrollRows+scroll); return true; }); withOnPressed(this::handleMouse); @@ -64,6 +63,7 @@ public DecoScrollableItemSlots(int x, int y) */ public DecoScrollableItemSlots withSlots(@Nonnull List slots) { + restoreOriginalPositions(); this.slots.clear(); this.slots.addAll(slots); this.initialized = false; @@ -75,6 +75,7 @@ public DecoScrollableItemSlots withSlots(@Nonnull List slots) */ public DecoScrollableItemSlots withSlots(@Nonnull Slot... slots) { + restoreOriginalPositions(); this.slots.clear(); for(Slot s : slots) this.slots.add(s); @@ -117,16 +118,10 @@ public int getScrollRows() @Override protected boolean initialize() { - //Hide real slots off-screen (but keep them functional) - originalPos.clear(); - for(Slot s : slots) - { - if(s==null) - continue; - originalPos.put(s, new int[]{s.xPos, s.yPos}); - s.xPos = -10000; - s.yPos = -10000; - } + //Record the container layout once, then keep every slot outside the GUI until + //the visible page explicitly assigns it a display position. + captureOriginalPositions(); + hideSlots(); //Clamp scroll in case size/columns changed setScrollRows(scrollRows); @@ -195,6 +190,10 @@ private void drawScrollbar() private void drawSlots() { + //Scrolling can change which real slots are represented by the same cells. + //Hide the previous page first so stale slots cannot remain clickable. + hideSlots(); + final int cols = Math.max(1, columns); final int visible = getVisibleRows()*cols; final int listRight = x+getListWidth(); @@ -221,28 +220,49 @@ private void drawSlots() @Override public void cleanup() { - //Restore real slot positions - for(Map.Entry e : originalPos.entrySet()) - { - Slot s = e.getKey(); - int[] pos = e.getValue(); - if(s!=null&&pos!=null&&pos.length==2) - { - s.xPos = pos[0]; - s.yPos = pos[1]; - } - } - originalPos.clear(); + restoreOriginalPositions(); + initialized = false; } @Override protected void updateInvisibleComponent() { - if(initialized) + //A hidden tab must not restore the container's original coordinates: those may + //still be inside the GUI. Keep its real slots parked outside the player's view. + captureOriginalPositions(); + hideSlots(); + } + + private void captureOriginalPositions() + { + for(Slot slot : slots) + if(slot!=null&&!originalPos.containsKey(slot)) + originalPos.put(slot, new int[]{slot.xPos, slot.yPos}); + } + + private void hideSlots() + { + for(Slot slot : slots) + if(slot!=null) + { + slot.xPos = HIDDEN_SLOT_POS; + slot.yPos = HIDDEN_SLOT_POS; + } + } + + private void restoreOriginalPositions() + { + for(Map.Entry entry : originalPos.entrySet()) { - initialized = false; - cleanup(); + Slot slot = entry.getKey(); + int[] position = entry.getValue(); + if(slot!=null&&position!=null&&position.length==2) + { + slot.xPos = position[0]; + slot.yPos = position[1]; + } } + originalPos.clear(); } //--- Utils ---// diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextArea.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextArea.java new file mode 100644 index 000000000..63f24cf66 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextArea.java @@ -0,0 +1,78 @@ +package pl.pabilo8.immersiveintelligence.client.gui.deco.component.text; + +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.highlight.TextHighlighter; +import pl.pabilo8.immersiveintelligence.client.util.font.IIFontRenderer; +import pl.pabilo8.immersiveintelligence.common.util.IIColor; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * A multi-line Deco text box supporting vertical navigation, multiple carets and syntax highlighting. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 29.07.2026 + */ +public class DecoTextArea extends DecoTextInputBase +{ + @Nullable + private TextHighlighter highlighter; + + public DecoTextArea(int x, int y) + { + super(x, y); + } + + public DecoTextArea withHighlighter(@Nullable TextHighlighter highlighter) + { + this.highlighter = highlighter; + return this; + } + + @Nullable + public TextHighlighter getHighlighter() + { + return highlighter; + } + + @Override + protected boolean isMultiLineInput() + { + return true; + } + + @Override + protected void drawTextLine(String fullLine, int sliceStart, int drawX, int lineY, int maxWidth, IIColor fallbackColor) + { + if(highlighter==null) + { + super.drawTextLine(fullLine, sliceStart, drawX, lineY, maxWidth, fallbackColor); + return; + } + + List segments = highlighter.highlightVisible(fullLine, sliceStart); + if(segments.isEmpty()) + { + super.drawTextLine(fullLine, sliceStart, drawX, lineY, maxWidth, fallbackColor); + return; + } + + IIFontRenderer font = getTextFontRenderer(); + int used = 0; + for(TextHighlighter.Segment segment : segments) + { + if(segment==null||segment.text==null||segment.text.isEmpty()||used >= maxWidth) + continue; + String visible = font.trimStringToWidth(segment.text, maxWidth-used); + if(!visible.isEmpty()) + { + IIColor color = segment.color==null?fallbackColor: segment.color; + font.drawString(visible, drawX+used, lineY, color.getPackedARGB()); + used += font.getStringWidth(visible); + } + if(visible.length() < segment.text.length()) + break; + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextField.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextField.java index 304d56c46..2990b7758 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextField.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextField.java @@ -1,1043 +1,95 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.text; -import net.minecraft.client.audio.SoundHandler; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.ChatAllowedCharacters; -import net.minecraft.util.math.MathHelper; -import org.lwjgl.input.Keyboard; -import pl.pabilo8.immersiveintelligence.client.IIClientUtils; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.highlight.TextHighlighter; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.MoveUnit; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextCaret; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextHistoryState; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiUtils; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; -import pl.pabilo8.immersiveintelligence.client.util.IIDrawUtils; -import pl.pabilo8.immersiveintelligence.client.util.font.IIFontRenderer; -import pl.pabilo8.immersiveintelligence.common.util.IIColor; -import pl.pabilo8.immersiveintelligence.common.util.IIMath; -import pl.pabilo8.immersiveintelligence.common.util.IIReference; -import pl.pabilo8.immersiveintelligence.common.util.ResLoc; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoArrows; -import javax.annotation.Nullable; -import java.util.*; +import java.util.Objects; import java.util.function.Consumer; -import java.util.function.Predicate; /** - * A multi-line, multi-caret text field component with selection, undo/redo, clipboard & syntax highlighting. + * A single-line Deco text input. + * Numeric filters right-align their values and may expose increment/decrement arrows. * * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 * @since 12.07.2025 */ -public class DecoTextField extends DecoComponent +public class DecoTextField extends DecoTextInputBase { - //Text and Carets - private final List lines = new ArrayList<>(); - private final List carets = new ArrayList<>(); - private boolean multiLine = false; - private TextFilter filter = TextFilter.NONE; - private Predicate customFilter = (s) -> true; - private Consumer onTextChanged = null; - - //Config - private IIFontRenderer fontRenderer = IIClientUtils.fontRegular; - private int maxStringLength = 32767; - private ResLoc backgroundLocation = DecoTextures.COMPONENT_TEXT_FIELD; - private IIColor textColor = IIColor.WHITE; - private IIColor cursorColor = IIReference.COLOR_IMMERSIVE_ORANGE; - private IIColor selectionColor = IIReference.COLOR_IMMERSIVE_ORANGE.withBrightness(0.35f).withAlpha(0.60f); - private TextHighlighter highlighter = null; - private int padding = 4; - - //Scroll - private int verticalScroll = 0, maxVerticalScroll = 0; - private int lineScrollOffset = 0; // char offset for single line horizontal scrolling - - //Cursor blink - private int cursorCounter = 0; - private final int blinkRate = 30; - - //History - private static final int HISTORY_LIMIT = 100; - private final Deque undoStack = new ArrayDeque<>(); - private final Deque redoStack = new ArrayDeque<>(); - - //Preferred column for vertical navigation - private int preferredColumn = -1; + private DecoArrows arrows; public DecoTextField(int x, int y) { super(x, y); - lines.add(""); - carets.add(new TextCaret(0, 0)); - withSize(160, (fontRenderer.FONT_HEIGHT+2)+1+padding*2); - withOnKeyTyped(this::onKeyTyped); - withOnPressed(this::onMousePressed); - withOnDragged(this::onMouseDragged); - withOnScroll(this::onMouseScroll); - } - - //--- Setters ---// - - public DecoTextField withMultiLine(boolean multiline) - { - this.multiLine = multiline; - calculateMaxScroll(); - return this; - } - - public DecoTextField withFilter(TextFilter f) - { - this.filter = f; - return this; - } - - public DecoTextField withCustomFilter(Predicate filter) - { - this.customFilter = filter; - return this; - } - - public DecoTextField withOnTextChanged(Consumer onTextChanged) - { - this.onTextChanged = onTextChanged; - return this; - } - - public DecoTextField withMaxStringLength(int length) - { - this.maxStringLength = length; - return this; - } - - public DecoTextField withBackgroundLocation(@Nullable ResLoc backgroundLocation) - { - this.backgroundLocation = backgroundLocation; - return this; } - public DecoTextField withTextColor(IIColor color) + /** + * Adds a configurable arrow control to the right side of this field, only visible for numeric inputs. + * + * @param configure action used to configure the created arrows, usually through + * {@link DecoArrows#withOnArrow(Consumer)} + */ + public DecoTextField withArrows(Consumer configure) { - this.textColor = color; - return this; - } - - public DecoTextField withCursorColor(IIColor color) - { - this.cursorColor = color; - return this; - } - - public DecoTextField withSelectionColor(IIColor color) - { - this.selectionColor = color; - return this; - } - - public DecoTextField withPadding(int padding) - { - this.padding = padding; - return this; - } - - public DecoTextField withHighlighter(TextHighlighter highlighter) - { - this.highlighter = highlighter; - return this; - } - - public DecoTextField withText(Object object) - { - if(object==null) - return this; - - if(object instanceof String) - return withText((String)object); - else if(object instanceof Integer) - return withText(String.valueOf(object)); - else if(object instanceof Float) - return withText(String.valueOf(object)); - else if(object instanceof Double) - return withText(String.valueOf(object)); - else if(object instanceof Long) - return withText(String.valueOf(object)); - else if(object instanceof Boolean) - return withText(String.valueOf(object)); - else if(object instanceof Character) - return withText(String.valueOf(object)); - else if(object instanceof Enum) - return withText(String.valueOf(object)); - else if(object instanceof String[]) - return withText(String.join("\n", ((String[])object))); - else - return withText(object.toString()); - } - - private DecoTextField withText(String t) - { - if(t==null) t = ""; - lines.clear(); - if(!multiLine) - { - lines.add(t.replace('\r', ' ').replace('\n', ' ').replace('\t', ' ')); - clearToSingleCaret(0, lines.get(0).length()); - } - else + Objects.requireNonNull(configure, "configure"); + if(arrows!=null) { - Collections.addAll(lines, t.split("\n", -1)); - if(lines.isEmpty()) lines.add(""); - clearToSingleCaret(0, 0); + children.remove(arrows); + arrows.cleanup(); } - calculateMaxScroll(); - return this; - } - - //--- Setters ---// - - - public TextHighlighter getHighlighter() - { - return highlighter; - } - public int getCursorPosition() - { - return primary().pos; - } - - public void setCursorPosition(int p) - { - TextCaret c = primary(); - c.pos = MathHelper.clamp(p, 0, lines.get(c.line).length()); - c.anchorLine = c.line; - c.anchorPos = c.pos; + arrows = new DecoArrows(0, 0); + configure.accept(arrows); + updateArrows(); ensureCursorVisible(); + return this; } - public int getCurrentLineIndex() - { - return primary().line; - } - - public int getLineCount() - { - return lines.size(); - } - - //--- Drawing ---// - @Override - protected boolean initialize() + protected int getTrailingDecorationWidth() { - calculateMaxScroll(); - pushHistory(); // base state - return true; + return arrows!=null&&children.contains(arrows)?arrows.width: 0; } @Override - protected void draw(int mouseX, int mouseY, float partialTicks) + protected void onBoundsChanged() { - if(backgroundLocation!=null) - { - bindAtlas(); - IIDrawUtils.startTexturedColored().drawConnectedTexColorRect(x, y, width, height, IIColor.WHITE, backgroundLocation, 32, 32, 8, 8).finish(); - } - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - assert parentGui!=null; - parentGui.scissorStart(x+padding, y+padding, width-padding*2, height-padding*2); - - int visibleLines = multiLine?(height-(padding*2))/fontRenderer.FONT_HEIGHT: 1; - int startLine = multiLine?verticalScroll: 0; - int endLine = multiLine?Math.min(startLine+visibleLines, lines.size()): 1; - boolean showCursor = isFocused()&&(cursorCounter/blinkRate%2==0); - - //Ensure global offset not negative - if(lineScrollOffset < 0) lineScrollOffset = 0; - - for(int lineIdx = startLine; lineIdx < endLine; lineIdx++) - { - String fullLine = lines.get(lineIdx); - int sliceStart = Math.min(lineScrollOffset, fullLine.length()); - String slice = fullLine.substring(sliceStart); - int lineY = y+padding+(lineIdx-startLine)*fontRenderer.FONT_HEIGHT; - - //Selections (global offset) - GlStateManager.disableTexture2D(); - IIDrawUtils selDraw = IIDrawUtils.startColored(); - for(TextCaret c : carets) - { - //Skip if no selection - if(!c.hasSelection()||(c.startLine() > lineIdx||lineIdx > c.endLine())) - continue; - - int selStart = 0, selEnd = fullLine.length(); - if(lineIdx==c.startLine()) - selStart = c.startPos(); - if(lineIdx==c.endLine()) - selEnd = c.endPos(); - - - int startX = x+padding+fontRenderer.getStringWidth(fullLine.substring(sliceStart, selStart)); - int endX = x+padding+fontRenderer.getStringWidth(fullLine.substring(sliceStart, selEnd)); - selDraw.drawColorRect(startX, lineY-1, - Math.max(1, endX-startX), fontRenderer.FONT_HEIGHT+2, selectionColor); - } - selDraw.finish(); - GlStateManager.enableTexture2D(); - - //Text / highlighting - int maxPixel = width-(padding*2); - int drawX = x+padding; - if(highlighter==null) - { - String visible = fontRenderer.trimStringToWidth(slice, maxPixel); - fontRenderer.drawString(visible, drawX, lineY, textColor.getPackedARGB()); - } - else - { - List segs = highlighter.highlight(slice); - int used = 0; - for(TextHighlighter.Segment seg : segs) - { - if(seg.text.isEmpty()) continue; - String remain = seg.text; - while(!remain.isEmpty()) - { - int len = remain.length(); - String candidate = remain; - while(len > 0&&used+fontRenderer.getStringWidth(candidate) > maxPixel) - { - len--; - candidate = remain.substring(0, len); - } - if(len==0) - break; - fontRenderer.drawString(candidate, drawX+used, lineY, seg.color.getPackedARGB()); - used += fontRenderer.getStringWidth(candidate); - remain = (len < remain.length())?"": remain.substring(len); - if(used >= maxPixel) - break; - } - if(used >= maxPixel) break; - } - } - - // Carets - if(showCursor) - { - GlStateManager.disableTexture2D(); - IIDrawUtils caretDraw = IIDrawUtils.startColored(); - for(int i = 0; i < carets.size(); i++) - { - TextCaret c = carets.get(i); - if(c.line!=lineIdx) continue; - int colPos = c.pos-sliceStart; - if(colPos < 0) continue; - if(colPos > slice.length()) colPos = slice.length(); - int caretX = x+padding+fontRenderer.getStringWidth(slice.substring(0, colPos)); - caretDraw.drawColorRect(caretX, lineY-1, 1, fontRenderer.FONT_HEIGHT+2, (i==0)?cursorColor: cursorColor.withAlpha(0.6f)); - } - caretDraw.finish(); - GlStateManager.enableTexture2D(); - } - } - - parentGui.scissorEnd(); - GlStateManager.popMatrix(); - cursorCounter++; + updateArrowsPosition(); } @Override - public void cleanup() - { - - } - - // Public text insertion - public void writeText(String text) + protected void onFilterChanged() { - if(text==null||text.isEmpty()) - return; - String filtered = filterText(text); - if(filtered.isEmpty()) - return; - pushHistory(); - deleteSelections(); - List ordered = new ArrayList<>(carets); - ordered.sort((a, b) -> (a.line==b.line?Integer.compare(b.pos, a.pos): Integer.compare(a.line, b.line))); - for(TextCaret c : ordered) - insertMultilineAtCaret(c, filtered); - - calculateMaxScroll(); - normalizeCarets(); + updateArrows(); ensureCursorVisible(); } - private void insertMultilineAtCaret(TextCaret c, String text) + @Override + public DecoTextField withDisabled(boolean disabled) { - if(!multiLine||!text.contains("\n")) - { - insertTextAtCaret(c, text); - return; - } - String[] parts = text.split("\n", -1); - String line = lines.get(c.line); - String head = line.substring(0, c.pos)+parts[0]; - String tail = line.substring(c.pos); - lines.set(c.line, head); - int insertAt = c.line+1; - for(int i = 1; i < parts.length; i++) - { - boolean last = i==parts.length-1; - String seg = last?parts[i]+tail: parts[i]; - lines.add(insertAt, seg); - insertAt++; - } - c.line = insertAt-1; - String lastLine = lines.get(c.line); - c.pos = Math.max(0, lastLine.length()-tail.length()); - c.anchorLine = c.line; - c.anchorPos = c.pos; + super.withDisabled(disabled); + updateArrows(); + return this; } - private void insertTextAtCaret(TextCaret c, String text) + private void updateArrows() { - String line = lines.get(c.line); - int allowed = Math.max(0, maxStringLength-line.length()); - String ins = text.length() > allowed?text.substring(0, allowed): text; - if(ins.isEmpty()) + if(arrows==null) return; - lines.set(c.line, line.substring(0, c.pos)+ins+line.substring(c.pos)); - c.pos += ins.length(); - c.anchorLine = c.line; - c.anchorPos = c.pos; + boolean active = getFilter().isNumeric(); + if(active&&!children.contains(arrows)) + children.add(arrows); + else if(!active) + children.remove(arrows); + arrows.visible = active; + arrows.enabled = active&&enabled; + updateArrowsPosition(); } - //Deletion - private void deleteCarets(MoveUnit moveUnit, int dir) + private void updateArrowsPosition() { - //record state - pushHistory(); - if(anySelection()) - { - deleteSelections(); - postEdit(); + if(arrows==null) return; - } - - // process carets from end -> start - List ordered = new ArrayList<>(carets); - ordered.sort(Comparator.comparingInt((TextCaret c) -> c.line).thenComparingInt(c -> c.pos).reversed()); - - for(TextCaret caret : ordered) - if(moveUnit==MoveUnit.CHAR) - //Backspace - if(dir < 0) - { - if(caret.pos > 0) - { - String line = lines.get(caret.line); - lines.set(caret.line, line.substring(0, caret.pos-1)+line.substring(caret.pos)); - caret.pos--; - } - else if(caret.line > 0) - { - int prevLen = lines.get(caret.line-1).length(); - lines.set(caret.line-1, lines.get(caret.line-1)+lines.get(caret.line)); - lines.remove(caret.line); - caret.line--; - caret.pos = prevLen; - } - caret.anchorLine = caret.line; - caret.anchorPos = caret.pos; - } - //Delete - else - { - String line = lines.get(caret.line); - if(caret.pos < line.length()) - lines.set(caret.line, line.substring(0, caret.pos)+line.substring(caret.pos+1)); - else if(caret.line < lines.size()-1) - { - lines.set(caret.line, line+lines.get(caret.line+1)); - lines.remove(caret.line+1); - } - } - else if(moveUnit==MoveUnit.WORD) - { - int origLine = caret.line, origPos = caret.pos; - caret.moveHorizontal(dir < 0, MoveUnit.WORD, lines, multiLine, this::isWordChar); - //Anchor original position to form a selection for deletion afterwards - caret.anchorLine = origLine; - caret.anchorPos = origPos; - } - - deleteSelections(); - postEdit(); - } - - private void newlineAtCarets() - { - pushHistory(); - deleteSelections(); - List ordered = new ArrayList<>(carets); - ordered.sort((a, b) -> (a.line==b.line?Integer.compare(a.pos, b.pos): Integer.compare(a.line, b.line))); - Collections.reverse(ordered); - //Process from bottom to top - for(TextCaret c : ordered) - { - String line = lines.get(c.line); - String before = line.substring(0, c.pos); - String after = line.substring(c.pos); - lines.set(c.line, before); - lines.add(c.line+1, after); - c.line++; - c.pos = 0; - c.anchorLine = c.line; - c.anchorPos = 0; - } - postEdit(); - } - - private void deleteSelections() - { - if(!anySelection()) return; - List ordered = new ArrayList<>(carets); - ordered.sort((a, b) -> { - if(a.endLine()!=b.endLine()) return Integer.compare(b.endLine(), a.endLine()); - return Integer.compare(b.endPos(), a.endPos()); - }); - for(TextCaret c : ordered) if(c.hasSelection()) deleteCaretSelection(c); - normalizeCarets(); - } - - private void deleteCaretSelection(TextCaret c) - { - int sL = c.startLine(), eL = c.endLine(); - if(sL==eL) - { - String line = lines.get(sL); - int a = Math.min(c.startPos(), line.length()); - int b = Math.min(c.endPos(), line.length()); - if(a > b) - { - int t = a; - a = b; - b = t; - } - if(a==b) return; - lines.set(sL, line.substring(0, a)+line.substring(b)); - c.line = c.anchorLine = sL; - c.pos = c.anchorPos = a; - } - else - { - String first = lines.get(sL); - String last = lines.get(eL); - int a = Math.min(c.startPos(), first.length()); - int b = Math.min(c.endPos(), last.length()); - if(a > first.length()) a = first.length(); - if(b > last.length()) b = last.length(); - String merged = first.substring(0, a)+last.substring(b); - for(int i = eL; i >= sL; i--) - if(i==sL) lines.set(i, merged); - else lines.remove(i); - c.line = c.anchorLine = sL; - c.pos = c.anchorPos = a; - } - } - - // --- Unified Caret Movement --- // - private void moveCarets(MoveUnit unit, int dx, int dy, boolean shift) - { - // capture preferred column for vertical navigation - if(dy!=0&&preferredColumn < 0) - preferredColumn = primary().pos; - if(dy==0&&dx!=0) - preferredColumn = -1; // reset when horizontal only - - boolean allowCross = multiLine; // cross-line wrapping only when multi-line - - for(TextCaret c : carets) - { - // vertical first - if(dy!=0&&multiLine) - { - int targetLine = MathHelper.clamp(c.line+dy, 0, lines.size()-1); - String tgt = lines.get(targetLine); - int col = (preferredColumn >= 0)?preferredColumn: c.pos; - c.line = targetLine; - c.pos = Math.min(col, tgt.length()); - } - // horizontal / word / end movement via caret helper - if(dx!=0) - c.moveHorizontal(dx < 0, unit, lines, allowCross, this::isWordChar); - //deselect - if(!shift) - { - c.anchorLine = c.line; - c.anchorPos = c.pos; - } - } - normalizeCarets(); - ensureCursorVisible(); - } - - private boolean isWordChar(char ch) - { - return Character.isLetterOrDigit(ch)||ch=='_'; - } - - private void addCaretVertical(int delta) - { - List added = new ArrayList<>(); - for(TextCaret c : carets) - { - int nl = c.line+delta; - if(nl < 0||nl >= lines.size()) continue; - int p = Math.min(c.pos, lines.get(nl).length()); - added.add(new TextCaret(nl, p)); - } - carets.addAll(added); - normalizeCarets(); - } - - // Mouse - private boolean onMousePressed(DecoTextField self, MouseButton button, int mx, int my) - { - if(button!=MouseButton.LEFT) - return false; - - int innerX = mx-(x+padding); - int innerY = my-(y+padding); - int line = multiLine?MathHelper.clamp(verticalScroll+(innerY/fontRenderer.FONT_HEIGHT), 0, lines.size()-1): 0; - String ln = lines.get(line); - int pos = fontRenderer.trimStringToWidth(ln, innerX).length(); - boolean shift = GuiScreen.isShiftKeyDown(); - boolean alt = Keyboard.isKeyDown(Keyboard.KEY_LMENU)||Keyboard.isKeyDown(Keyboard.KEY_RMENU); - if(!shift&&!alt) - { - carets.clear(); - carets.add(new TextCaret(line, pos)); - } - else if(shift) - { - TextCaret p = primary(); - p.line = line; - p.pos = pos; - } - else - carets.add(new TextCaret(line, pos)); - normalizeCarets(); - ensureCursorVisible(); - return true; - } - - private boolean onMouseDragged(DecoTextField self, MouseButton button, int mx, int my) - { - if(button!=MouseButton.LEFT) - return false; - - int innerX = mx-(x+padding); - int innerY = my-(y+padding); - int line = multiLine?MathHelper.clamp(verticalScroll+(innerY/fontRenderer.FONT_HEIGHT), 0, lines.size()-1): 0; - String ln = lines.get(line); - int pos = fontRenderer.trimStringToWidth(ln, innerX).length(); - TextCaret p = primary(); - p.line = line; - p.pos = MathHelper.clamp(pos, 0, ln.length()); - ensureCursorVisible(); - return true; - } - - private boolean onMouseScroll(DecoTextField self, int wheel, int mx, int my) - { - if(!multiLine||!IIMath.isPointInRectangle(x, y, x+width, y+height, mx, my)) return false; - verticalScroll = MathHelper.clamp(verticalScroll-wheel, 0, maxVerticalScroll); - return true; - } - - // Key handling - private boolean onKeyTyped(DecoTextField f, char ch, int key) - { - boolean ctrl = GuiScreen.isCtrlKeyDown(); - boolean shift = GuiScreen.isShiftKeyDown(); - boolean alt = Keyboard.isKeyDown(Keyboard.KEY_LMENU)||Keyboard.isKeyDown(Keyboard.KEY_RMENU); - if(multiLine&&alt&&(key==Keyboard.KEY_UP||key==Keyboard.KEY_DOWN)) - { - addCaretVertical(key==Keyboard.KEY_UP?-1: 1); - return true; - } - MoveUnit unit; - switch(key) - { - case Keyboard.KEY_RETURN: - if(!multiLine) - { - parentGui.requestFocus(null); - if(onTextChanged!=null) - onTextChanged.accept(getText()); - return true; - } - newlineAtCarets(); - return true; - case Keyboard.KEY_BACK: - deleteCarets(ctrl?MoveUnit.WORD: MoveUnit.CHAR, -1); - if(onTextChanged!=null) - onTextChanged.accept(getText()); - return true; - case Keyboard.KEY_DELETE: - deleteCarets(ctrl?MoveUnit.WORD: MoveUnit.CHAR, 1); - if(onTextChanged!=null) - onTextChanged.accept(getText()); - return true; - case Keyboard.KEY_LEFT: - unit = ctrl?MoveUnit.WORD: MoveUnit.CHAR; - moveCarets(unit, -1, 0, shift); - return true; - case Keyboard.KEY_RIGHT: - unit = ctrl?MoveUnit.WORD: MoveUnit.CHAR; - moveCarets(unit, 1, 0, shift); - return true; - case Keyboard.KEY_HOME: - moveCarets(MoveUnit.END, -1, 0, shift); - return true; - case Keyboard.KEY_END: - moveCarets(MoveUnit.END, 1, 0, shift); - return true; - case Keyboard.KEY_UP: - if(multiLine) - { - moveCarets(MoveUnit.CHAR, 0, -1, shift); - return true; - } - return false; - case Keyboard.KEY_DOWN: - if(multiLine) - { - moveCarets(MoveUnit.CHAR, 0, 1, shift); - return true; - } - return false; - default: - if(ChatAllowedCharacters.isAllowedCharacter(ch)) - { - writeText(Character.toString(ch)); - if(onTextChanged!=null) - onTextChanged.accept(getText()); - return true; - } - } - return false; - } - - // GUI events - @Override - public void onGuiEvent(DecoGuiEvent event) - { - switch(event) - { - case COPY: - DecoGuiUtils.setClipboardString(buildCopyString()); - break; - case CUT: - if(anySelection()) - { - pushHistory(); - DecoGuiUtils.setClipboardString(buildCopyString()); - deleteSelections(); - postEdit(); - } - break; - case PASTE: - pasteString(DecoGuiUtils.getClipboardString()); - break; - case UNDO: - { - if(undoStack.isEmpty()) - return; - redoStack.push(new TextHistoryState(lines, carets)); - TextHistoryState prev = undoStack.pop(); - restore(prev); - } - break; - case REDO: - { - if(redoStack.isEmpty()) - return; - undoStack.push(new TextHistoryState(lines, carets)); - TextHistoryState next = redoStack.pop(); - restore(next); - } - break; - case SELECT_ALL: - pushHistory(); - if(!multiLine) - { - clearToSingleCaret(0, 0); - TextCaret p = primary(); - p.pos = lines.get(0).length(); - p.anchorPos = 0; - } - else - { - clearToSingleCaret(lines.size()-1, lines.get(lines.size()-1).length()); - TextCaret p = primary(); - p.anchorLine = 0; - p.anchorPos = 0; - } - ensureCursorVisible(); - break; - default: - super.onGuiEvent(event); - } - } - - // Selection & caret utilities - private TextCaret primary() - { - if(carets.isEmpty()) - carets.add(new TextCaret(0, 0)); - return carets.get(0); - } - - private boolean anySelection() - { - return carets.stream().anyMatch(TextCaret::hasSelection); - } - - private void normalizeCarets() - { - ensureCaretsInBounds(); - Set seen = new HashSet<>(); - List uniq = new ArrayList<>(); - for(TextCaret c : carets) - { - long k = (((long)c.line)<<32)|c.pos; - if(seen.add(k)) uniq.add(c); - } - carets.clear(); - carets.addAll(uniq); - if(carets.isEmpty()) - carets.add(new TextCaret(0, 0)); - } - - private void ensureCaretsInBounds() - { - for(TextCaret c : carets) - { - c.line = MathHelper.clamp(c.line, 0, lines.size()-1); - String ln = lines.get(c.line); - c.pos = MathHelper.clamp(c.pos, 0, ln.length()); - c.anchorLine = MathHelper.clamp(c.anchorLine, 0, lines.size()-1); - String aln = lines.get(c.anchorLine); - c.anchorPos = MathHelper.clamp(c.anchorPos, 0, aln.length()); - } - } - - //--- History ---// - - private void pushHistory() - { - undoStack.push(new TextHistoryState(lines, carets)); - while(undoStack.size() > HISTORY_LIMIT) undoStack.removeLast(); - redoStack.clear(); - } - - private void restore(TextHistoryState st) - { - lines.clear(); - lines.addAll(st.lines); - carets.clear(); - for(TextCaret tc : st.carets) - { - TextCaret c = new TextCaret(tc.line, tc.pos); - c.anchorLine = tc.anchorLine; - c.anchorPos = tc.anchorPos; - carets.add(c); - } - calculateMaxScroll(); - ensureCursorVisible(); - } - - private void postEdit() - { - calculateMaxScroll(); - normalizeCarets(); - ensureCursorVisible(); - } - - // Scroll calc - private void calculateMaxScroll() - { - if(multiLine) - { - int visible = (height-(padding*2))/fontRenderer.FONT_HEIGHT; - maxVerticalScroll = Math.max(0, lines.size()-visible); - verticalScroll = MathHelper.clamp(verticalScroll, 0, maxVerticalScroll); - } - } - - //--- Filtering ---// - - private String filterText(String input) - { - if(filter==TextFilter.NONE&&customFilter==null) return input; - StringBuilder sb = new StringBuilder(); - for(char ch : input.toCharArray()) - { - String s = String.valueOf(ch); - if((filter==TextFilter.NONE||filter.test(s))&&(customFilter==null||customFilter.test(s))) sb.append(ch); - } - return sb.toString(); - } - - - public String getText() - { - if(!multiLine) - return lines.get(0); - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < lines.size(); i++) - { - sb.append(lines.get(i)); - if(i < lines.size()-1) - sb.append('\n'); - } - return sb.toString(); - } - - private void clearToSingleCaret(int line, int pos) - { - carets.clear(); - line = MathHelper.clamp(line, 0, lines.size()-1); - String ln = lines.get(line); - pos = MathHelper.clamp(pos, 0, ln.length()); - carets.add(new TextCaret(line, pos)); - } - - public void ensureCursorVisible() - { - TextCaret p = primary(); - if(multiLine) - { - int visibleLines = (height-(padding*2))/fontRenderer.FONT_HEIGHT; - if(p.line < verticalScroll) verticalScroll = p.line; - else if(p.line >= verticalScroll+visibleLines) verticalScroll = p.line-visibleLines+1; - } - String line = lines.get(p.line); - int visibleWidth = width-(padding*2); - if(p.pos < lineScrollOffset) lineScrollOffset = p.pos; - // scroll right if needed - while(lineScrollOffset < p.pos) - { - int w = fontRenderer.getStringWidth(line.substring(lineScrollOffset, p.pos)); - if(w <= visibleWidth) break; - lineScrollOffset++; - } - cursorCounter = 20; - } - - @Override - public void playPressSound(SoundHandler soundHandlerIn) - { - - } - - // === Clipboard (multi-caret) === // - private String buildCopyString() - { - if(carets.size()==1) - { - TextCaret c = primary(); - if(!c.hasSelection()) return ""; - return extractSelection(c); - } - boolean any = false; - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < carets.size(); i++) - { - TextCaret c = carets.get(i); - String part = c.hasSelection()?extractSelection(c): ""; - any |= c.hasSelection(); - sb.append(part); - if(i < carets.size()-1) sb.append('\n'); - } - return any?sb.toString(): ""; - } - - private String extractSelection(TextCaret c) - { - int sL = c.startLine(), eL = c.endLine(); - if(sL==eL) - { - String line = lines.get(sL); - int a = Math.min(c.startPos(), line.length()); - int b = Math.min(c.endPos(), line.length()); - if(a > b) - { - int t = a; - a = b; - b = t; - } - return line.substring(a, b); - } - StringBuilder sb = new StringBuilder(); - for(int l = sL; l <= eL; l++) - { - String line = lines.get(l); - int from = (l==sL)?Math.min(c.startPos(), line.length()): 0; - int to = (l==eL)?Math.min(c.endPos(), line.length()): line.length(); - if(from > to) - { - int t = from; - from = to; - to = t; - } - sb.append(line, from, to); - if(l < eL) sb.append('\n'); - } - return sb.toString(); - } - - private void pasteString(String clip) - { - if(clip==null) return; - pushHistory(); - String[] parts = clip.split("\r?\n", -1); - if(carets.size() > 1&&parts.length==carets.size()) - { - // map each selection to a part (ascending order), then insert in reverse order to keep positions stable - List ordered = new ArrayList<>(carets); - ordered.sort((a, b) -> (a.line==b.line?Integer.compare(a.pos, b.pos): Integer.compare(a.line, b.line))); - // delete selections first (in reverse so indices stable) - List rev = new ArrayList<>(ordered); - Collections.reverse(rev); - for(TextCaret c : rev) if(c.hasSelection()) deleteCaretSelection(c); - // insert per caret reverse - Collections.reverse(ordered); // now descending - for(TextCaret c : ordered) - { - int idx = ordered.size()-1-ordered.indexOf(c); // recover ascending index -> part index - String part = filterText(parts[idx]); - insertMultilineAtCaret(c, part); - } - } - else - { - // same text at all carets, process descending order - List ordered = new ArrayList<>(carets); - ordered.sort((a, b) -> (a.line==b.line?Integer.compare(a.pos, b.pos): Integer.compare(a.line, b.line))); - Collections.reverse(ordered); - for(TextCaret c : ordered) if(c.hasSelection()) deleteCaretSelection(c); - String filtered = filterText(clip); - for(TextCaret c : ordered) insertMultilineAtCaret(c, filtered); - } - postEdit(); + arrows.x = x+width-arrows.width; + arrows.y = y+Math.max(0, (height-arrows.height)/2); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextInputBase.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextInputBase.java new file mode 100644 index 000000000..48880de42 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/DecoTextInputBase.java @@ -0,0 +1,1423 @@ +package pl.pabilo8.immersiveintelligence.client.gui.deco.component.text; + +import net.minecraft.client.audio.SoundHandler; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.ChatAllowedCharacters; +import net.minecraft.util.math.MathHelper; +import org.lwjgl.input.Keyboard; +import pl.pabilo8.immersiveintelligence.client.IIClientUtils; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.MoveUnit; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextCaret; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextFilter; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util.TextHistoryState; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiUtils; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.util.IIDrawUtils; +import pl.pabilo8.immersiveintelligence.client.util.font.IIFontRenderer; +import pl.pabilo8.immersiveintelligence.common.util.IIColor; +import pl.pabilo8.immersiveintelligence.common.util.IIMath; +import pl.pabilo8.immersiveintelligence.common.util.IIReference; +import pl.pabilo8.immersiveintelligence.common.util.ResLoc; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * Shared editing, validation, selection, history, clipboard and rendering engine for Deco text inputs. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @since 12.07.2025 + */ +@SuppressWarnings("unchecked") +public abstract class DecoTextInputBase> extends DecoComponent +{ + //Text and Carets + private final List lines = new ArrayList<>(); + private final List carets = new ArrayList<>(); + private TextFilter filter = TextFilter.NONE; + /** + * Character-level compatibility filter used by the historical withCustomFilter API. + */ + @Nullable + private Predicate customCharacterFilter = null; + /** + * Whole-document validator used for contextual constraints. + */ + @Nullable + private Predicate validator = null; + private Consumer onTextChanged = null; + private boolean editable = true; + + //Config + private IIFontRenderer fontRenderer = IIClientUtils.fontRegular; + private int maxStringLength = 32767; + private ResLoc backgroundLocation = DecoTextures.COMPONENT_TEXT_FIELD; + private IIColor textColor = IIColor.WHITE; + private IIColor invalidTextColor = IIColor.MC_RED; + private IIColor cursorColor = IIReference.COLOR_IMMERSIVE_ORANGE; + private IIColor selectionColor = IIReference.COLOR_IMMERSIVE_ORANGE.withBrightness(0.35f).withAlpha(0.60f); + private int padding = 4; + + //Scroll + private int verticalScroll = 0, maxVerticalScroll = 0; + private int lineScrollOffset = 0; //char offset for single line horizontal scrolling + + //Cursor blink + private int cursorCounter = 0; + private final int blinkRate = 30; + + //History + private static final int HISTORY_LIMIT = 100; + private static final int HISTORY_CHARACTER_LIMIT = 1_000_000; + private final Deque undoStack = new ArrayDeque<>(); + private final Deque redoStack = new ArrayDeque<>(); + private int undoCharacters = 0, redoCharacters = 0; + @Nullable + private EditKind lastEditKind = null; + private int lastEditTick = Integer.MIN_VALUE; + + //Preferred column for vertical navigation + private int preferredColumn = -1; + + protected DecoTextInputBase(int x, int y) + { + super(x, y); + lines.add(""); + carets.add(new TextCaret(0, 0)); + withSize(160, (fontRenderer.FONT_HEIGHT+2)+1+padding*2); + withOnKeyTyped(this::onKeyTyped); + withOnPressed(this::onMousePressed); + withOnDragged(this::onMouseDragged); + withOnScroll(this::onMouseScroll); + } + + //--- Setters ---// + + @Override + public TYPE withPosition(int x, int y) + { + super.withPosition(x, y); + onBoundsChanged(); + return (TYPE)this; + } + + @Override + public TYPE withSize(int width, int height) + { + super.withSize(width, height); + calculateMaxScroll(); + onBoundsChanged(); + return (TYPE)this; + } + + @Override + public TYPE withWidth(int width) + { + super.withWidth(width); + ensureCursorVisible(); + onBoundsChanged(); + return (TYPE)this; + } + + @Override + public TYPE withHeight(int height) + { + super.withHeight(height); + calculateMaxScroll(); + onBoundsChanged(); + return (TYPE)this; + } + + public TYPE withFilter(@Nullable TextFilter f) + { + this.filter = f==null?TextFilter.NONE: f; + setTextInternal(getText(), false, true); + onFilterChanged(); + return (TYPE)this; + } + + /** + * Adds a character-level filter. The predicate receives one character as a one-character string. + * This preserves the historical API; use {@link #withValidator(Predicate)} for contextual validation. + */ + public TYPE withCustomFilter(@Nullable Predicate filter) + { + this.customCharacterFilter = filter; + setTextInternal(getText(), false, true); + return (TYPE)this; + } + + /** + * Adds a whole-document validator. Intermediate text rejected by this predicate is not inserted. + */ + public TYPE withValidator(@Nullable Predicate validator) + { + this.validator = validator; + return (TYPE)this; + } + + public TYPE withEditable(boolean editable) + { + this.editable = editable; + return (TYPE)this; + } + + public TYPE withOnTextChanged(Consumer onTextChanged) + { + this.onTextChanged = onTextChanged; + return (TYPE)this; + } + + public TYPE withMaxStringLength(int length) + { + this.maxStringLength = Math.max(0, length); + setTextInternal(getText(), false, true); + return (TYPE)this; + } + + public TYPE withBackgroundLocation(@Nullable ResLoc backgroundLocation) + { + this.backgroundLocation = backgroundLocation; + return (TYPE)this; + } + + public TYPE withTextColor(IIColor color) + { + this.textColor = color; + return (TYPE)this; + } + + public TYPE withInvalidTextColor(IIColor color) + { + this.invalidTextColor = color; + return (TYPE)this; + } + + public TYPE withCursorColor(IIColor color) + { + this.cursorColor = color; + return (TYPE)this; + } + + public TYPE withSelectionColor(IIColor color) + { + this.selectionColor = color; + return (TYPE)this; + } + + public TYPE withPadding(int padding) + { + this.padding = Math.max(0, padding); + calculateMaxScroll(); + ensureCursorVisible(); + return (TYPE)this; + } + + public TYPE withFontRenderer(IIFontRenderer fontRenderer) + { + this.fontRenderer = fontRenderer==null?IIClientUtils.fontRegular: fontRenderer; + calculateMaxScroll(); + ensureCursorVisible(); + return (TYPE)this; + } + + + public TYPE withText(@Nullable Object object) + { + String text; + if(object==null) + text = ""; + else if(object instanceof String[]) + text = String.join("\n", (String[])object); + else + text = String.valueOf(object); + setTextInternal(text, false, true); + return (TYPE)this; + } + + /** + * Replaces the text and emits the normal change callback. + */ + public TYPE withTextAndNotify(@Nullable Object object) + { + String text = object instanceof String[]?String.join("\n", (String[])object): String.valueOf(object==null?"": object); + setTextInternal(text, true, true); + return (TYPE)this; + } + + //--- Shared extension hooks ---// + + /** + * @return whether this concrete control supports line breaks, vertical navigation and multiple carets + */ + protected boolean isMultiLineInput() + { + return false; + } + + /** + * @return horizontal space reserved by a concrete control for trailing child components + */ + protected int getTrailingDecorationWidth() + { + return 0; + } + + protected void onBoundsChanged() + { + + } + + protected void onFilterChanged() + { + + } + + protected final TextFilter getFilter() + { + return filter; + } + + protected final IIFontRenderer getTextFontRenderer() + { + return fontRenderer; + } + + protected final int getContentWidth() + { + return Math.max(0, width-padding*2-Math.max(0, getTrailingDecorationWidth())); + } + + public int getCursorPosition() + { + return primary().pos; + } + + public void setCursorPosition(int p) + { + TextCaret c = primary(); + c.pos = MathHelper.clamp(p, 0, lines.get(c.line).length()); + c.anchorLine = c.line; + c.anchorPos = c.pos; + ensureCursorVisible(); + } + + public int getCurrentLineIndex() + { + return primary().line; + } + + public int getLineCount() + { + return lines.size(); + } + + //--- Drawing ---// + + @Override + protected boolean initialize() + { + calculateMaxScroll(); + return true; + } + + @Override + protected void draw(int mouseX, int mouseY, float partialTicks) + { + if(backgroundLocation!=null) + { + bindAtlas(); + IIDrawUtils.startTexturedColored() + .drawConnectedTexColorRect(x, y, width, height, IIColor.WHITE, backgroundLocation, 32, 32, 8, 8) + .finish(); + } + + int innerWidth = getContentWidth(); + int innerHeight = Math.max(0, height-padding*2); + if(innerWidth==0||innerHeight==0) + { + cursorCounter++; + return; + } + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + boolean scissor = parentGui!=null; + if(scissor) + parentGui.scissorStart(x+padding, y+padding, innerWidth, innerHeight); + try + { + int visibleLines = getVisibleLineCount(); + int startLine = isMultiLineInput()?verticalScroll: 0; + int endLine = isMultiLineInput()?Math.min(startLine+visibleLines, lines.size()): 1; + boolean showCursor = editable&&isFocused()&&(cursorCounter/blinkRate%2==0); + IIColor drawnTextColor = isTextValid()?textColor: invalidTextColor; + + lineScrollOffset = Math.max(0, lineScrollOffset); + for(int lineIdx = startLine; lineIdx < endLine; lineIdx++) + { + String fullLine = lines.get(lineIdx); + int sliceStart = Math.min(lineScrollOffset, fullLine.length()); + String slice = fullLine.substring(sliceStart); + int lineY = y+padding+(lineIdx-startLine)*fontRenderer.FONT_HEIGHT; + int drawX = getLineDrawX(fullLine, sliceStart, innerWidth); + + drawSelections(lineIdx, fullLine, sliceStart, drawX, lineY); + drawTextLine(fullLine, sliceStart, drawX, lineY, innerWidth, drawnTextColor); + if(showCursor) + drawCarets(lineIdx, slice, sliceStart, drawX, lineY, innerWidth); + } + } finally + { + GlStateManager.enableTexture2D(); + if(scissor) + parentGui.scissorEnd(); + GlStateManager.popMatrix(); + } + cursorCounter++; + } + + private int getLineDrawX(String fullLine, int sliceStart, int innerWidth) + { + if(filter.isNumeric()&&sliceStart==0) + { + int textWidth = fontRenderer.getStringWidth(fullLine); + if(textWidth < innerWidth) + return x+padding+innerWidth-textWidth; + } + return x+padding; + } + + private void drawSelections(int lineIdx, String fullLine, int sliceStart, int drawX, int lineY) + { + GlStateManager.disableTexture2D(); + IIDrawUtils draw = IIDrawUtils.startColored(); + for(TextCaret caret : carets) + { + if(!caret.hasSelection()||caret.startLine() > lineIdx||lineIdx > caret.endLine()) + continue; + + int selectionStart = lineIdx==caret.startLine()?caret.startPos(): 0; + int selectionEnd = lineIdx==caret.endLine()?caret.endPos(): fullLine.length(); + selectionStart = MathHelper.clamp(selectionStart, 0, fullLine.length()); + selectionEnd = MathHelper.clamp(selectionEnd, 0, fullLine.length()); + if(selectionEnd <= sliceStart) + continue; + + int visibleStart = Math.max(sliceStart, Math.min(selectionStart, selectionEnd)); + int visibleEnd = Math.max(visibleStart, Math.max(selectionStart, selectionEnd)); + visibleEnd = Math.min(visibleEnd, fullLine.length()); + if(visibleStart==visibleEnd) + continue; + + int startX = drawX+fontRenderer.getStringWidth(fullLine.substring(sliceStart, visibleStart)); + int endX = drawX+fontRenderer.getStringWidth(fullLine.substring(sliceStart, visibleEnd)); + draw.drawColorRect(startX, lineY-1, Math.max(1, endX-startX), fontRenderer.FONT_HEIGHT+2, selectionColor); + } + draw.finish(); + GlStateManager.enableTexture2D(); + } + + /** + * Draws one text line. Multiline controls may override this to provide syntax highlighting. + */ + protected void drawTextLine(String fullLine, int sliceStart, int drawX, int lineY, int maxWidth, IIColor fallbackColor) + { + String slice = fullLine.substring(Math.min(sliceStart, fullLine.length())); + fontRenderer.drawString(fontRenderer.trimStringToWidth(slice, maxWidth), drawX, lineY, fallbackColor.getPackedARGB()); + } + + private void drawCarets(int lineIdx, String slice, int sliceStart, int drawX, int lineY, int innerWidth) + { + GlStateManager.disableTexture2D(); + IIDrawUtils draw = IIDrawUtils.startColored(); + int viewportLeft = x+padding; + int viewportRight = viewportLeft+Math.max(0, innerWidth-1); + for(int i = 0; i < carets.size(); i++) + { + TextCaret caret = carets.get(i); + if(caret.line!=lineIdx||caret.pos < sliceStart) + continue; + int column = Math.min(caret.pos-sliceStart, slice.length()); + int caretX = drawX+fontRenderer.getStringWidth(slice.substring(0, column)); + //OpenGL scissor rectangles exclude their right edge, so the final insertion + //position must use the last pixel inside the text viewport. + caretX = MathHelper.clamp(caretX, viewportLeft, viewportRight); + draw.drawColorRect(caretX, lineY-1, 1, fontRenderer.FONT_HEIGHT+2, + i==0?cursorColor: cursorColor.withAlpha(0.6f)); + } + draw.finish(); + GlStateManager.enableTexture2D(); + } + + @Override + public void cleanup() + { + + } + + //Public text insertion + public void writeText(String text) + { + writeText(text, EditKind.INSERT); + } + + private void writeText(@Nullable String text, EditKind editKind) + { + if(!editable||text==null||text.isEmpty()) + return; + + TextHistoryState before = prepareHistory(editKind); + String beforeText = getText(); + deleteSelections(); + + List ordered = new ArrayList<>(carets); + ordered.sort(Comparator.comparingInt((TextCaret caret) -> caret.line) + .thenComparingInt(caret -> caret.pos).reversed()); + for(TextCaret caret : ordered) + { + String insertion = filterInsertion(caret, normalizeInput(text)); + if(!insertion.isEmpty()) + insertMultilineAtCaret(caret, insertion); + } + + finishUserEdit(before, beforeText, editKind); + } + + private void insertMultilineAtCaret(TextCaret caret, String text) + { + if(!isMultiLineInput()||!text.contains("\n")) + { + insertTextAtCaret(caret, text); + return; + } + + String[] parts = text.split("\n", -1); + String line = lines.get(caret.line); + String tail = line.substring(caret.pos); + lines.set(caret.line, line.substring(0, caret.pos)+parts[0]); + int insertAt = caret.line+1; + for(int i = 1; i < parts.length; i++) + lines.add(insertAt++, i==parts.length-1?parts[i]+tail: parts[i]); + + caret.line = insertAt-1; + caret.pos = lines.get(caret.line).length()-tail.length(); + caret.anchorLine = caret.line; + caret.anchorPos = caret.pos; + } + + private void insertTextAtCaret(TextCaret caret, String text) + { + if(text.isEmpty()) + return; + String line = lines.get(caret.line); + lines.set(caret.line, line.substring(0, caret.pos)+text+line.substring(caret.pos)); + caret.pos += text.length(); + caret.anchorLine = caret.line; + caret.anchorPos = caret.pos; + } + + //Deletion + private void deleteCarets(MoveUnit moveUnit, int dir) + { + if(!editable) + return; + TextHistoryState before = prepareHistory(EditKind.DELETE); + String beforeText = getText(); + + if(anySelection()) + deleteSelections(); + else + { + List ordered = new ArrayList<>(carets); + ordered.sort(Comparator.comparingInt((TextCaret caret) -> caret.line) + .thenComparingInt(caret -> caret.pos).reversed()); + + for(TextCaret caret : ordered) + { + if(moveUnit==MoveUnit.CHAR) + { + if(dir < 0) + { + if(caret.pos > 0) + { + String line = lines.get(caret.line); + lines.set(caret.line, line.substring(0, caret.pos-1)+line.substring(caret.pos)); + caret.pos--; + } + else if(isMultiLineInput()&&caret.line > 0) + { + int previousLength = lines.get(caret.line-1).length(); + lines.set(caret.line-1, lines.get(caret.line-1)+lines.get(caret.line)); + lines.remove(caret.line); + caret.line--; + caret.pos = previousLength; + } + } + else + { + String line = lines.get(caret.line); + if(caret.pos < line.length()) + lines.set(caret.line, line.substring(0, caret.pos)+line.substring(caret.pos+1)); + else if(isMultiLineInput()&&caret.line < lines.size()-1) + { + lines.set(caret.line, line+lines.get(caret.line+1)); + lines.remove(caret.line+1); + } + } + caret.anchorLine = caret.line; + caret.anchorPos = caret.pos; + } + else + { + int originalLine = caret.line, originalPosition = caret.pos; + caret.moveHorizontal(dir < 0, MoveUnit.WORD, lines, isMultiLineInput(), this::isWordChar); + caret.anchorLine = originalLine; + caret.anchorPos = originalPosition; + } + } + deleteSelections(); + } + + finishUserEdit(before, beforeText, EditKind.DELETE); + } + + private void newlineAtCarets() + { + if(!editable||!isMultiLineInput()||documentLength() >= maxStringLength) + return; + TextHistoryState before = prepareHistory(EditKind.STRUCTURAL); + String beforeText = getText(); + deleteSelections(); + + List ordered = new ArrayList<>(carets); + ordered.sort(Comparator.comparingInt((TextCaret caret) -> caret.line) + .thenComparingInt(caret -> caret.pos).reversed()); + for(TextCaret caret : ordered) + { + if(documentLength() >= maxStringLength) + break; + String line = lines.get(caret.line); + lines.set(caret.line, line.substring(0, caret.pos)); + lines.add(caret.line+1, line.substring(caret.pos)); + caret.line++; + caret.pos = 0; + caret.anchorLine = caret.line; + caret.anchorPos = 0; + } + finishUserEdit(before, beforeText, EditKind.STRUCTURAL); + } + + private void deleteSelections() + { + if(!anySelection()) + return; + + String document = getText(); + List ranges = new ArrayList<>(); + for(TextCaret caret : carets) + if(caret.hasSelection()) + { + int first = documentOffset(caret.anchorLine, caret.anchorPos); + int second = documentOffset(caret.line, caret.pos); + ranges.add(new TextRange(Math.min(first, second), Math.max(first, second))); + } + ranges.sort(Comparator.comparingInt(range -> range.start)); + + List merged = new ArrayList<>(); + for(TextRange range : ranges) + { + if(merged.isEmpty()||range.start > merged.get(merged.size()-1).end) + merged.add(range); + else + merged.get(merged.size()-1).end = Math.max(merged.get(merged.size()-1).end, range.end); + } + + Map resultingOffsets = new IdentityHashMap<>(); + for(TextCaret caret : carets) + { + int caretOffset = documentOffset(caret.line, caret.pos); + int anchorOffset = documentOffset(caret.anchorLine, caret.anchorPos); + int target = caret.hasSelection()?Math.min(caretOffset, anchorOffset): caretOffset; + resultingOffsets.put(caret, transformOffsetAfterDeletion(target, merged)); + } + + StringBuilder edited = new StringBuilder(document); + for(int i = merged.size()-1; i >= 0; i--) + { + TextRange range = merged.get(i); + edited.delete(range.start, range.end); + } + setDocumentRaw(edited.toString()); + for(TextCaret caret : carets) + { + setCaretFromDocumentOffset(caret, resultingOffsets.get(caret)); + caret.anchorLine = caret.line; + caret.anchorPos = caret.pos; + } + normalizeCarets(); + } + + private int transformOffsetAfterDeletion(int offset, List ranges) + { + int removed = 0; + for(TextRange range : ranges) + { + if(offset < range.start) + break; + if(offset <= range.end) + return range.start-removed; + removed += range.end-range.start; + } + return offset-removed; + } + + private void setDocumentRaw(String document) + { + lines.clear(); + if(isMultiLineInput()) + Collections.addAll(lines, document.split("\n", -1)); + else + lines.add(document.replace('\n', ' ')); + if(lines.isEmpty()) + lines.add(""); + } + + private void setCaretFromDocumentOffset(TextCaret caret, int offset) + { + int remaining = MathHelper.clamp(offset, 0, documentLength()); + for(int line = 0; line < lines.size(); line++) + { + int length = lines.get(line).length(); + if(remaining <= length||line==lines.size()-1) + { + caret.line = line; + caret.pos = Math.min(remaining, length); + return; + } + remaining -= length+1; + } + } + + //--- Unified Caret Movement --- // + private void moveCarets(MoveUnit unit, int dx, int dy, boolean shift) + { + //capture preferred column for vertical navigation + if(dy!=0&&preferredColumn < 0) + preferredColumn = primary().pos; + if(dy==0&&dx!=0) + preferredColumn = -1; //reset when horizontal only + + boolean allowCross = isMultiLineInput(); //cross-line wrapping only when multi-line + + for(TextCaret c : carets) + { + //vertical first + if(dy!=0&&isMultiLineInput()) + { + int targetLine = MathHelper.clamp(c.line+dy, 0, lines.size()-1); + String tgt = lines.get(targetLine); + int col = (preferredColumn >= 0)?preferredColumn: c.pos; + c.line = targetLine; + c.pos = Math.min(col, tgt.length()); + } + //horizontal / word / end movement via caret helper + if(dx!=0) + c.moveHorizontal(dx < 0, unit, lines, allowCross, this::isWordChar); + //deselect + if(!shift) + { + c.anchorLine = c.line; + c.anchorPos = c.pos; + } + } + normalizeCarets(); + ensureCursorVisible(); + } + + private boolean isWordChar(char ch) + { + return Character.isLetterOrDigit(ch)||ch=='_'; + } + + private void addCaretVertical(int delta) + { + List added = new ArrayList<>(); + for(TextCaret c : carets) + { + int nl = c.line+delta; + if(nl < 0||nl >= lines.size()) continue; + int p = Math.min(c.pos, lines.get(nl).length()); + added.add(new TextCaret(nl, p)); + } + carets.addAll(added); + normalizeCarets(); + } + + //Mouse + private boolean onMousePressed(TYPE self, MouseButton button, int mouseX, int mouseY) + { + if(button!=MouseButton.LEFT) + return false; + + int line = lineAt(mouseY); + int position = positionAt(lines.get(line), mouseX); + boolean shift = GuiScreen.isShiftKeyDown(); + boolean alt = isMultiLineInput()&&(Keyboard.isKeyDown(Keyboard.KEY_LMENU)||Keyboard.isKeyDown(Keyboard.KEY_RMENU)); + if(!shift&&!alt) + { + carets.clear(); + carets.add(new TextCaret(line, position)); + } + else if(shift) + { + TextCaret caret = primary(); + caret.line = line; + caret.pos = position; + } + else + carets.add(new TextCaret(line, position)); + + breakHistoryCoalescing(); + normalizeCarets(); + ensureCursorVisible(); + return true; + } + + private boolean onMouseDragged(TYPE self, MouseButton button, int mouseX, int mouseY) + { + if(button!=MouseButton.LEFT) + return false; + int line = lineAt(mouseY); + TextCaret caret = primary(); + caret.line = line; + caret.pos = positionAt(lines.get(line), mouseX); + breakHistoryCoalescing(); + ensureCursorVisible(); + return true; + } + + private int lineAt(int mouseY) + { + if(!isMultiLineInput()) + return 0; + int innerY = Math.max(0, mouseY-(y+padding)); + return MathHelper.clamp(verticalScroll+(innerY/fontRenderer.FONT_HEIGHT), 0, lines.size()-1); + } + + private int positionAt(String line, int mouseX) + { + int sliceStart = Math.min(lineScrollOffset, line.length()); + String visible = line.substring(sliceStart); + int drawX = getLineDrawX(line, sliceStart, getContentWidth()); + int innerX = Math.max(0, mouseX-drawX); + return MathHelper.clamp(sliceStart+fontRenderer.trimStringToWidth(visible, innerX).length(), 0, line.length()); + } + + private boolean onMouseScroll(TYPE self, int wheel, int mouseX, int mouseY) + { + if(!isMultiLineInput()||!IIMath.isPointInRectangle(x, y, x+width, y+height, mouseX, mouseY)) + return false; + verticalScroll = MathHelper.clamp(verticalScroll-wheel, 0, maxVerticalScroll); + return true; + } + + //Key handling + private boolean onKeyTyped(TYPE input, char character, int key) + { + boolean ctrl = GuiScreen.isCtrlKeyDown(); + boolean shift = GuiScreen.isShiftKeyDown(); + boolean alt = Keyboard.isKeyDown(Keyboard.KEY_LMENU)||Keyboard.isKeyDown(Keyboard.KEY_RMENU); + + if(ctrl&&key==Keyboard.KEY_A) + { + selectAll(); + return true; + } + if(isMultiLineInput()&&alt&&(key==Keyboard.KEY_UP||key==Keyboard.KEY_DOWN)) + { + addCaretVertical(key==Keyboard.KEY_UP?-1: 1); + breakHistoryCoalescing(); + return true; + } + + MoveUnit unit; + switch(key) + { + case Keyboard.KEY_RETURN: + if(!isMultiLineInput()) + { + if(parentGui!=null) + parentGui.requestFocus(null); + return true; + } + newlineAtCarets(); + return true; + case Keyboard.KEY_BACK: + deleteCarets(ctrl?MoveUnit.WORD: MoveUnit.CHAR, -1); + return true; + case Keyboard.KEY_DELETE: + deleteCarets(ctrl?MoveUnit.WORD: MoveUnit.CHAR, 1); + return true; + case Keyboard.KEY_LEFT: + unit = ctrl?MoveUnit.WORD: MoveUnit.CHAR; + moveCarets(unit, -1, 0, shift); + breakHistoryCoalescing(); + return true; + case Keyboard.KEY_RIGHT: + unit = ctrl?MoveUnit.WORD: MoveUnit.CHAR; + moveCarets(unit, 1, 0, shift); + breakHistoryCoalescing(); + return true; + case Keyboard.KEY_HOME: + moveCarets(MoveUnit.END, -1, 0, shift); + breakHistoryCoalescing(); + return true; + case Keyboard.KEY_END: + moveCarets(MoveUnit.END, 1, 0, shift); + breakHistoryCoalescing(); + return true; + case Keyboard.KEY_UP: + if(isMultiLineInput()) + { + moveCarets(MoveUnit.CHAR, 0, -1, shift); + breakHistoryCoalescing(); + return true; + } + return false; + case Keyboard.KEY_DOWN: + if(isMultiLineInput()) + { + moveCarets(MoveUnit.CHAR, 0, 1, shift); + breakHistoryCoalescing(); + return true; + } + return false; + default: + if(editable&&ChatAllowedCharacters.isAllowedCharacter(character)) + { + writeText(Character.toString(character)); + return true; + } + } + return false; + } + + //GUI events + @Override + public void onGuiEvent(DecoGuiEvent event) + { + switch(event) + { + case COPY: + DecoGuiUtils.setClipboardString(buildCopyString()); + break; + case CUT: + if(editable&&anySelection()) + { + TextHistoryState before = prepareHistory(EditKind.CUT); + String beforeText = getText(); + DecoGuiUtils.setClipboardString(buildCopyString()); + deleteSelections(); + finishUserEdit(before, beforeText, EditKind.CUT); + } + break; + case PASTE: + if(editable) + pasteString(DecoGuiUtils.getClipboardString()); + break; + case UNDO: + undo(); + break; + case REDO: + redo(); + break; + case SELECT_ALL: + selectAll(); + break; + default: + super.onGuiEvent(event); + } + } + + private void selectAll() + { + if(!isMultiLineInput()) + { + clearToSingleCaret(0, lines.get(0).length()); + primary().anchorPos = 0; + } + else + { + clearToSingleCaret(lines.size()-1, lines.get(lines.size()-1).length()); + primary().anchorLine = 0; + primary().anchorPos = 0; + } + breakHistoryCoalescing(); + ensureCursorVisible(); + } + + //Selection & caret utilities + private TextCaret primary() + { + if(carets.isEmpty()) + carets.add(new TextCaret(0, 0)); + return carets.get(0); + } + + private boolean anySelection() + { + return carets.stream().anyMatch(TextCaret::hasSelection); + } + + private void normalizeCarets() + { + ensureCaretsInBounds(); + Set seen = new HashSet<>(); + List uniq = new ArrayList<>(); + for(TextCaret c : carets) + { + long k = (((long)c.line)<<32)|c.pos; + if(seen.add(k)) uniq.add(c); + } + carets.clear(); + carets.addAll(uniq); + if(carets.isEmpty()) + carets.add(new TextCaret(0, 0)); + } + + private void ensureCaretsInBounds() + { + for(TextCaret c : carets) + { + c.line = MathHelper.clamp(c.line, 0, lines.size()-1); + String ln = lines.get(c.line); + c.pos = MathHelper.clamp(c.pos, 0, ln.length()); + c.anchorLine = MathHelper.clamp(c.anchorLine, 0, lines.size()-1); + String aln = lines.get(c.anchorLine); + c.anchorPos = MathHelper.clamp(c.anchorPos, 0, aln.length()); + } + } + + //--- History and edit completion ---// + + private TextHistoryState snapshot() + { + return new TextHistoryState(lines, carets); + } + + @Nullable + private TextHistoryState prepareHistory(EditKind editKind) + { + boolean coalesce = (editKind==EditKind.INSERT||editKind==EditKind.DELETE) + &&editKind==lastEditKind + &&cursorCounter-lastEditTick <= 20 + &&carets.size()==1 + &&!anySelection(); + return coalesce?null: snapshot(); + } + + private void finishUserEdit(@Nullable TextHistoryState before, String beforeText, EditKind editKind) + { + postEdit(); + if(beforeText.equals(getText())) + return; + if(before!=null) + pushUndo(before); + clearRedo(); + lastEditKind = editKind; + lastEditTick = cursorCounter; + notifyTextChanged(); + } + + private void pushUndo(TextHistoryState state) + { + undoStack.push(state); + undoCharacters += state.characterCount(); + while(undoStack.size() > HISTORY_LIMIT||undoCharacters > HISTORY_CHARACTER_LIMIT) + undoCharacters -= undoStack.removeLast().characterCount(); + } + + private void pushRedo(TextHistoryState state) + { + redoStack.push(state); + redoCharacters += state.characterCount(); + while(redoStack.size() > HISTORY_LIMIT||redoCharacters > HISTORY_CHARACTER_LIMIT) + redoCharacters -= redoStack.removeLast().characterCount(); + } + + private void clearHistory() + { + undoStack.clear(); + redoStack.clear(); + undoCharacters = redoCharacters = 0; + breakHistoryCoalescing(); + } + + private void clearRedo() + { + redoStack.clear(); + redoCharacters = 0; + } + + private void breakHistoryCoalescing() + { + lastEditKind = null; + lastEditTick = Integer.MIN_VALUE; + } + + private void undo() + { + if(!editable||undoStack.isEmpty()) + return; + TextHistoryState current = snapshot(); + TextHistoryState previous = undoStack.pop(); + undoCharacters -= previous.characterCount(); + pushRedo(current); + restore(previous); + breakHistoryCoalescing(); + notifyTextChanged(); + } + + private void redo() + { + if(!editable||redoStack.isEmpty()) + return; + TextHistoryState current = snapshot(); + TextHistoryState next = redoStack.pop(); + redoCharacters -= next.characterCount(); + pushUndo(current); + restore(next); + breakHistoryCoalescing(); + notifyTextChanged(); + } + + private void restore(TextHistoryState state) + { + lines.clear(); + lines.addAll(state.lines); + if(lines.isEmpty()) + lines.add(""); + carets.clear(); + for(TextCaret stored : state.carets) + carets.add(stored.copy()); + normalizeCarets(); + calculateMaxScroll(); + ensureCursorVisible(); + } + + private void postEdit() + { + if(lines.isEmpty()) + lines.add(""); + calculateMaxScroll(); + normalizeCarets(); + ensureCursorVisible(); + } + + private void notifyTextChanged() + { + if(onTextChanged!=null) + onTextChanged.accept(getText()); + } + + //--- Scroll ---// + + private int getVisibleLineCount() + { + return Math.max(1, Math.max(0, height-padding*2)/Math.max(1, fontRenderer.FONT_HEIGHT)); + } + + private void calculateMaxScroll() + { + if(!isMultiLineInput()) + { + verticalScroll = maxVerticalScroll = 0; + return; + } + maxVerticalScroll = Math.max(0, lines.size()-getVisibleLineCount()); + verticalScroll = MathHelper.clamp(verticalScroll, 0, maxVerticalScroll); + } + + //--- Filtering and document access ---// + + private String normalizeInput(String input) + { + String normalized = input.replace("\r\n", "\n").replace('\r', '\n').replace('\t', ' '); + return isMultiLineInput()?normalized: normalized.replace('\n', ' '); + } + + private String sanitizeWholeText(String input) + { + String normalized = normalizeInput(input==null?"": input); + StringBuilder characters = new StringBuilder(Math.min(normalized.length(), maxStringLength)); + for(int i = 0; i < normalized.length()&&characters.length() < maxStringLength; i++) + { + char character = normalized.charAt(i); + if(customCharacterFilter==null||customCharacterFilter.test(String.valueOf(character))) + characters.append(character); + } + + String candidate = characters.toString(); + if(acceptsCandidate(candidate)) + return candidate; + + StringBuilder accepted = new StringBuilder(candidate.length()); + for(int i = 0; i < candidate.length(); i++) + { + String next = accepted.toString()+candidate.charAt(i); + if(acceptsCandidate(next)) + accepted.append(candidate.charAt(i)); + } + return accepted.toString(); + } + + private String filterInsertion(TextCaret caret, String input) + { + int remaining = Math.max(0, maxStringLength-documentLength()); + if(remaining==0||input.isEmpty()) + return ""; + + StringBuilder accepted = new StringBuilder(Math.min(input.length(), remaining)); + if(filter==TextFilter.NONE&&validator==null) + { + for(int i = 0; i < input.length()&&accepted.length() < remaining; i++) + { + char character = input.charAt(i); + if(customCharacterFilter==null||customCharacterFilter.test(String.valueOf(character))) + accepted.append(character); + } + return accepted.toString(); + } + + for(int i = 0; i < input.length()&&accepted.length() < remaining; i++) + { + char character = input.charAt(i); + if(customCharacterFilter!=null&&!customCharacterFilter.test(String.valueOf(character))) + continue; + String trial = accepted.toString()+character; + if(acceptsCandidate(documentWithInsertion(caret, trial))) + accepted.append(character); + } + return accepted.toString(); + } + + private boolean acceptsCandidate(String candidate) + { + return filter.accepts(candidate)&&(validator==null||validator.test(candidate)); + } + + public boolean isTextValid() + { + String text = getText(); + return filter.isValid(text)&&(validator==null||validator.test(text)); + } + + private String documentWithInsertion(TextCaret caret, String insertion) + { + String document = getText(); + int offset = documentOffset(caret.line, caret.pos); + return document.substring(0, offset)+insertion+document.substring(offset); + } + + private int documentOffset(int line, int position) + { + int offset = 0; + for(int i = 0; i < line; i++) + offset += lines.get(i).length()+1; + return offset+position; + } + + private int documentLength() + { + int length = Math.max(0, lines.size()-1); + for(String line : lines) + length += line.length(); + return length; + } + + private void setTextInternal(String text, boolean notify, boolean resetHistory) + { + String previous = getText(); + String sanitized = sanitizeWholeText(text); + lines.clear(); + if(isMultiLineInput()) + Collections.addAll(lines, sanitized.split("\n", -1)); + else + lines.add(sanitized); + if(lines.isEmpty()) + lines.add(""); + clearToSingleCaret(isMultiLineInput()?0: lines.size()-1, isMultiLineInput()?0: lines.get(0).length()); + verticalScroll = lineScrollOffset = 0; + calculateMaxScroll(); + ensureCursorVisible(); + if(resetHistory) + clearHistory(); + if(notify&&!previous.equals(getText())) + notifyTextChanged(); + } + + public String getText() + { + if(!isMultiLineInput()) + return lines.get(0); + StringBuilder result = new StringBuilder(documentLength()); + for(int i = 0; i < lines.size(); i++) + { + if(i > 0) + result.append('\n'); + result.append(lines.get(i)); + } + return result.toString(); + } + + private void clearToSingleCaret(int line, int pos) + { + carets.clear(); + line = MathHelper.clamp(line, 0, lines.size()-1); + String ln = lines.get(line); + pos = MathHelper.clamp(pos, 0, ln.length()); + carets.add(new TextCaret(line, pos)); + } + + public void ensureCursorVisible() + { + TextCaret caret = primary(); + if(isMultiLineInput()) + { + int visibleLines = getVisibleLineCount(); + if(caret.line < verticalScroll) + verticalScroll = caret.line; + else if(caret.line >= verticalScroll+visibleLines) + verticalScroll = caret.line-visibleLines+1; + verticalScroll = MathHelper.clamp(verticalScroll, 0, maxVerticalScroll); + } + + String line = lines.get(caret.line); + int visibleWidth = Math.max(1, getContentWidth()-1); + lineScrollOffset = MathHelper.clamp(lineScrollOffset, 0, line.length()); + if(caret.pos < lineScrollOffset) + lineScrollOffset = caret.pos; + while(lineScrollOffset < caret.pos&&fontRenderer.getStringWidth(line.substring(lineScrollOffset, caret.pos)) > visibleWidth) + lineScrollOffset++; + cursorCounter = 20; + } + + @Override + public void playPressSound(SoundHandler soundHandlerIn) + { + + } + + //--- Clipboard ---// + + private String buildCopyString() + { + if(carets.size()==1) + { + TextCaret c = primary(); + if(!c.hasSelection()) return ""; + return extractSelection(c); + } + boolean any = false; + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < carets.size(); i++) + { + TextCaret c = carets.get(i); + String part = c.hasSelection()?extractSelection(c): ""; + any |= c.hasSelection(); + sb.append(part); + if(i < carets.size()-1) sb.append('\n'); + } + return any?sb.toString(): ""; + } + + private String extractSelection(TextCaret c) + { + int sL = c.startLine(), eL = c.endLine(); + if(sL==eL) + { + String line = lines.get(sL); + int a = Math.min(c.startPos(), line.length()); + int b = Math.min(c.endPos(), line.length()); + if(a > b) + { + int t = a; + a = b; + b = t; + } + return line.substring(a, b); + } + StringBuilder sb = new StringBuilder(); + for(int l = sL; l <= eL; l++) + { + String line = lines.get(l); + int from = (l==sL)?Math.min(c.startPos(), line.length()): 0; + int to = (l==eL)?Math.min(c.endPos(), line.length()): line.length(); + if(from > to) + { + int t = from; + from = to; + to = t; + } + sb.append(line, from, to); + if(l < eL) sb.append('\n'); + } + return sb.toString(); + } + + private void pasteString(@Nullable String clipboard) + { + if(!editable||clipboard==null||clipboard.isEmpty()) + return; + + String normalized = normalizeInput(clipboard); + String[] parts = normalized.split("\n", -1); + if(carets.size() <= 1||parts.length!=carets.size()) + { + writeText(normalized, EditKind.PASTE); + return; + } + + TextHistoryState before = prepareHistory(EditKind.PASTE); + String beforeText = getText(); + List ordered = new ArrayList<>(carets); + ordered.sort(Comparator.comparingInt((TextCaret caret) -> caret.line) + .thenComparingInt(caret -> caret.pos)); + deleteSelections(); + for(int i = ordered.size()-1; i >= 0; i--) + { + TextCaret caret = ordered.get(i); + String insertion = filterInsertion(caret, parts[i]); + if(!insertion.isEmpty()) + insertMultilineAtCaret(caret, insertion); + } + finishUserEdit(before, beforeText, EditKind.PASTE); + } + + private static class TextRange + { + private final int start; + private int end; + + private TextRange(int start, int end) + { + this.start = start; + this.end = end; + } + } + + private enum EditKind + { + INSERT, + DELETE, + PASTE, + CUT, + STRUCTURAL + } + +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/MarkdownHighlighter.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/MarkdownHighlighter.java index d018b62cd..400b02cef 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/MarkdownHighlighter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/MarkdownHighlighter.java @@ -7,7 +7,7 @@ import java.util.List; /** - * Very lightweight markdown-like highlighter (headings, bold, italic, code, links). + * Lightweight markdown-like highlighter for headings, emphasis, inline code and links. */ public class MarkdownHighlighter extends TextHighlighter { @@ -22,104 +22,88 @@ public class MarkdownHighlighter extends TextHighlighter @Override public List highlight(String line) { - List out = new ArrayList<>(); - if(line.isEmpty()) return out; - int i = 0; - int n = line.length(); - // heading - if(line.charAt(0)=='#') + List result = new ArrayList<>(); + if(line.isEmpty()) + return result; + + int headingEnd = headingPrefixEnd(line); + if(headingEnd > 0) { - int hashes = 0; - while(i < n&&line.charAt(i)=='#') - { - hashes++; - i++; - } - if(i < n&&line.charAt(i)==' ') - { - out.add(new Segment(line.substring(0, i+1), heading, true, false)); - String rest = line.substring(i+1); - if(!rest.isEmpty()) out.add(new Segment(rest, heading, false, false)); - return out; - } - else i = 0; // fallback + result.add(new Segment(line.substring(0, headingEnd), heading, true, false)); + if(headingEnd < line.length()) + result.add(new Segment(line.substring(headingEnd), heading)); + return result; } - StringBuilder buf = new StringBuilder(); - while(i < n) + + int plainStart = 0; + int index = 0; + while(index < line.length()) { - char c = line.charAt(i); - // inline code - if(c=='`') + int end; + if(line.charAt(index)=='`'&&(end = findClosing(line, index+1, "`")) >= 0) { - // flush - if(buf.length() > 0) - { - out.add(new Segment(buf.toString(), normal)); - buf.setLength(0); - } - i++; - int start = i; - while(i < n&&line.charAt(i)!='`') i++; - String codeText = line.substring(start, Math.min(i, n)); - out.add(new Segment(codeText, code, true, false)); - if(i < n&&line.charAt(i)=='`') i++; + flushPlain(line, plainStart, index, result); + result.add(new Segment(line.substring(index, end+1), code, true, false)); + index = end+1; + plainStart = index; continue; } - // bold or italic - if(c=='*') + + if(line.startsWith("**", index)&&(end = findClosing(line, index+2, "**")) >= 0) { - int stars = 1; - if(i+1 < n&&line.charAt(i+1)=='*') stars = 2; - // flush - if(buf.length() > 0) - { - out.add(new Segment(buf.toString(), normal)); - buf.setLength(0); - } - i += stars; - int start = i; - while(i < n) - { - if(line.charAt(i)=='*') - { - int ahead = (i+1 < n&&line.charAt(i+1)=='*')?2: 1; - if(ahead==stars) {break;} - } - i++; - } - String content = line.substring(start, Math.min(i, n)); - IIColor col = (stars==2)?strong: italic; - out.add(new Segment(content, col, stars==2, stars==1)); - if(i < n) {i += stars;} + flushPlain(line, plainStart, index, result); + result.add(new Segment(line.substring(index, end+2), strong, true, false)); + index = end+2; + plainStart = index; + continue; + } + + if(line.charAt(index)=='*'&&(end = findClosing(line, index+1, "*")) >= 0) + { + flushPlain(line, plainStart, index, result); + result.add(new Segment(line.substring(index, end+1), italic, false, true)); + index = end+1; + plainStart = index; continue; } - // link [text](url) - if(c=='[') + + if(line.charAt(index)=='[') { - int start = i+1; - int close = line.indexOf(']', start); - int openParen = (close >= 0)?line.indexOf('(', close): -1; - int closeParen = (openParen >= 0)?line.indexOf(')', openParen): -1; - if(close > 0&&openParen > 0&&closeParen > 0) + int closeBracket = line.indexOf(']', index+1); + int openParen = closeBracket >= 0&&closeBracket+1 < line.length()&&line.charAt(closeBracket+1)=='('?closeBracket+1: -1; + int closeParen = openParen >= 0?line.indexOf(')', openParen+1): -1; + if(closeParen >= 0) { - if(buf.length() > 0) - { - out.add(new Segment(buf.toString(), normal)); - buf.setLength(0); - } - String text = line.substring(start, close); - String url = line.substring(openParen+1, closeParen); - out.add(new Segment(text, linkText, false, false)); - out.add(new Segment("("+url+")", linkUrl, false, false)); - i = closeParen+1; + flushPlain(line, plainStart, index, result); + result.add(new Segment(line.substring(index, closeBracket+1), linkText)); + result.add(new Segment(line.substring(openParen, closeParen+1), linkUrl)); + index = closeParen+1; + plainStart = index; continue; } } - buf.append(c); - i++; + index++; } - if(buf.length() > 0) out.add(new Segment(buf.toString(), normal)); - return out; + flushPlain(line, plainStart, line.length(), result); + return result; + } + + private int headingPrefixEnd(String line) + { + int index = 0; + while(index < line.length()&&line.charAt(index)=='#') + index++; + return index > 0&&index < line.length()&&line.charAt(index)==' '?index+1: -1; + } + + private int findClosing(String line, int from, String marker) + { + return line.indexOf(marker, from); } -} + private void flushPlain(String line, int from, int to, List result) + { + if(to > from) + result.add(new Segment(line.substring(from, to), normal)); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/POLHighlighter.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/POLHighlighter.java index edc84fd1a..a16a0daa6 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/POLHighlighter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/POLHighlighter.java @@ -8,49 +8,87 @@ import java.util.*; /** - * Simple syntax highlighter for POL (.pol) scripts. - * Dynamically gathers keywords from алPOLKeywords enum and registered DataOperations (names & expressions). + * Syntax highlighter for POL scripts. + * Dynamically gathers language keywords and registered data-operation names. */ public class POLHighlighter extends TextHighlighter { - // Dynamic sets built once (lazy) to include enum keywords + operation names/expressions private static final Set KEYWORDS = new HashSet<>(); - private static boolean initialized = false; + private static final Set OPERATIONS = new HashSet<>(); + private static final List SYMBOLIC_TOKENS = new ArrayList<>(); + private static volatile boolean initialized = false; - // Darcula-esque palette (must match IIManualDataOperation) + //Darcula-esque palette (must match IIManualDataOperation) private static final IIColor COLOR_PLAIN = IIColor.fromPackedRGB(0xA9B7C6); private static final IIColor COLOR_KEYWORD = IIColor.fromPackedRGB(0xCC7832); - private static final IIColor COLOR_OPERATION = IIColor.fromPackedRGB(0xe89433); - private static final IIColor COLOR_VARIABLE = IIColor.fromPackedRGB(0x7e6b80); + private static final IIColor COLOR_OPERATION = IIColor.fromPackedRGB(0xE89433); + private static final IIColor COLOR_VARIABLE = IIColor.fromPackedRGB(0x7E6B80); private static final IIColor COLOR_STRING = IIColor.fromPackedRGB(0x6A8759); - private static final IIColor COLOR_COMMENT = IIColor.fromPackedRGB(0x49633f); + private static final IIColor COLOR_COMMENT = IIColor.fromPackedRGB(0x49633F); private static final IIColor COLOR_NUMBER = IIColor.fromPackedRGB(0x6897BB); private static void ensureInitialized() { - if(initialized) return; - // POL enum keywords - for(POLKeywords kw : POLKeywords.values()) - if(kw.isVisible()) - KEYWORDS.add(kw.getName()); - // Data operation names & expressions - for(String opName : IIDataOperationUtils.getAllOperationNames()) + if(initialized) + return; + synchronized(POLHighlighter.class) + { + if(initialized) + return; + + for(POLKeywords keyword : POLKeywords.values()) + if(keyword.isVisible()) + registerToken(KEYWORDS, keyword.getName()); + + for(String operationName : IIDataOperationUtils.getAllOperationNames()) + { + registerToken(OPERATIONS, operationName); + DataOperationMeta meta = IIDataOperationUtils.getOperationMeta(operationName); + if(meta!=null) + registerExpressionSymbols(meta.expression()); + } + + SYMBOLIC_TOKENS.sort(Comparator.comparingInt(String::length).reversed()); + initialized = true; + } + } + + private static void registerToken(Set target, String token) + { + if(token==null||token.isEmpty()) + return; + String normalized = token.toLowerCase(Locale.ROOT); + target.add(normalized); + if(isSymbolic(normalized)&&!SYMBOLIC_TOKENS.contains(token)) + SYMBOLIC_TOKENS.add(token); + } + + private static void registerExpressionSymbols(String expression) + { + if(expression==null||expression.isEmpty()) + return; + int start = -1; + for(int i = 0; i <= expression.length(); i++) { - KEYWORDS.add(opName.toLowerCase(Locale.ROOT)); - DataOperationMeta meta = IIDataOperationUtils.getOperationMeta(opName); - if(meta!=null) + boolean symbol = i < expression.length()&&isOperatorCharacter(expression.charAt(i)); + if(symbol&&start < 0) + start = i; + else if(!symbol&&start >= 0) { - String expr = meta.expression(); - if(!expr.isEmpty()) - KEYWORDS.add(expr); + String token = expression.substring(start, i); + OPERATIONS.add(token.toLowerCase(Locale.ROOT)); + if(!SYMBOLIC_TOKENS.contains(token)) + SYMBOLIC_TOKENS.add(token); + start = -1; } } - initialized = true; } - private static boolean isSymbolic(String s) + private static boolean isSymbolic(String token) { - for(char ch : s.toCharArray()) if(Character.isLetterOrDigit(ch)||ch=='_') return false; + for(int i = 0; i < token.length(); i++) + if(Character.isLetterOrDigit(token.charAt(i))||token.charAt(i)=='_') + return false; return true; } @@ -58,121 +96,150 @@ private static boolean isSymbolic(String s) public List highlight(String line) { ensureInitialized(); - List out = new ArrayList<>(); - if(line.isEmpty()) return out; - // comments start at ';' outside quotes - int commentIndex = indexOfOutsideQuotes(line, ';'); - String codePart = commentIndex >= 0?line.substring(0, commentIndex): line; - String commentPart = commentIndex >= 0?line.substring(commentIndex): null; - - int i = 0, n = codePart.length(); - StringBuilder token = new StringBuilder(); - while(i < n) + if(line.isEmpty()) + return Collections.emptyList(); + + List result = new ArrayList<>(); + int index = 0; + while(index < line.length()) { - char c = codePart.charAt(i); - if(c=='"') + char current = line.charAt(index); + + if(current==';') + { + result.add(new Segment(line.substring(index), COLOR_COMMENT)); + break; + } + + if(current=='"') { - if(token.length() > 0) flushToken(token, out); - int start = ++i; - boolean esc = false; - while(i < n) - { - char ch = codePart.charAt(i); - if(ch=='"'&&!esc) break; - esc = (ch=='\\')&&!esc; - i++; - } - String content = codePart.substring(start, Math.min(i, n)); - out.add(new Segment("\""+content+(i < n&&codePart.charAt(i)=='"'?"\"": ""), COLOR_STRING, false, false)); - if(i < n&&codePart.charAt(i)=='"') i++; + int end = stringEnd(line, index+1); + result.add(new Segment(line.substring(index, end), COLOR_STRING)); + index = end; continue; } - if(Character.isWhitespace(c)) + + if(Character.isWhitespace(current)) { - if(token.length() > 0) flushToken(token, out); - int ws = i; - while(i < n&&Character.isWhitespace(codePart.charAt(i))) i++; - out.add(new Segment(codePart.substring(ws, i), COLOR_PLAIN)); + int end = index+1; + while(end < line.length()&&Character.isWhitespace(line.charAt(end))) + end++; + result.add(new Segment(line.substring(index, end), COLOR_PLAIN)); + index = end; continue; } - // operator or symbolic token - if(isOpStart(c)) + + if(current=='@') { - if(token.length() > 0) flushToken(token, out); - String op = Character.toString(c); - if(!KEYWORDS.contains(op)) - { // treat lone punctuation as plain - out.add(new Segment(op, COLOR_PLAIN)); - i += op.length(); - continue; - } - out.add(new Segment(op, COLOR_KEYWORD, false, false)); - i += op.length(); + int end = index+1; + while(end < line.length()&&isIdentifierPart(line.charAt(end))) + end++; + result.add(new Segment(line.substring(index, end), COLOR_VARIABLE)); + index = end; continue; } - token.append(c); - i++; + + if(Character.isDigit(current)||(current=='.'&&index+1 < line.length()&&Character.isDigit(line.charAt(index+1)))) + { + int end = numberEnd(line, index); + result.add(new Segment(line.substring(index, end), COLOR_NUMBER)); + index = end; + continue; + } + + if(Character.isLetter(current)||current=='_') + { + int end = index+1; + while(end < line.length()&&isIdentifierPart(line.charAt(end))) + end++; + String token = line.substring(index, end); + String normalized = token.toLowerCase(Locale.ROOT); + IIColor color = KEYWORDS.contains(normalized)?COLOR_KEYWORD: + (OPERATIONS.contains(normalized)?COLOR_OPERATION: COLOR_PLAIN); + result.add(new Segment(token, color, KEYWORDS.contains(normalized), false)); + index = end; + continue; + } + + String symbolic = matchSymbol(line, index); + if(symbolic!=null) + { + String normalized = symbolic.toLowerCase(Locale.ROOT); + IIColor color = OPERATIONS.contains(normalized)?COLOR_OPERATION: + (KEYWORDS.contains(normalized)?COLOR_KEYWORD: COLOR_PLAIN); + result.add(new Segment(symbolic, color)); + index += symbolic.length(); + continue; + } + + result.add(new Segment(String.valueOf(current), COLOR_PLAIN)); + index++; } - if(token.length() > 0) flushToken(token, out); - if(commentPart!=null) out.add(new Segment(commentPart, COLOR_COMMENT, false, false)); - return out; + return result; } - private void flushToken(StringBuilder token, List out) + private int stringEnd(String line, int index) { - String t = token.toString(); - token.setLength(0); - if(t.isEmpty()) return; - String lower = t.toLowerCase(Locale.ROOT); - if(KEYWORDS.contains(lower)) + boolean escaped = false; + while(index < line.length()) { - out.add(new Segment(t, COLOR_KEYWORD, true, false)); - return; + char current = line.charAt(index++); + if(current=='"'&&!escaped) + break; + if(current=='\\') + escaped = !escaped; + else + escaped = false; } - if(isNumber(t)) - { - out.add(new Segment(t, COLOR_NUMBER, false, false)); - return; - } - if(t.startsWith("@")) - { - out.add(new Segment(t, COLOR_VARIABLE, false, false)); - return; - } - if(t.endsWith(":")) + return index; + } + + private int numberEnd(String line, int index) + { + boolean decimal = false; + boolean exponent = false; + while(index < line.length()) { - out.add(new Segment(t, COLOR_KEYWORD, false, false)); - return; + char current = line.charAt(index); + if(Character.isDigit(current)) + { + index++; + continue; + } + if(current=='.'&&!decimal&&!exponent) + { + decimal = true; + index++; + continue; + } + if((current=='e'||current=='E')&&!exponent) + { + exponent = true; + index++; + if(index < line.length()&&(line.charAt(index)=='+'||line.charAt(index)=='-')) + index++; + continue; + } + break; } - out.add(new Segment(t, COLOR_PLAIN, false, false)); + return index; } - private boolean isOpStart(char c) + private String matchSymbol(String line, int index) { - return "+-*/=!<>|&%^".indexOf(c) >= 0; + for(String token : SYMBOLIC_TOKENS) + if(index+token.length() <= line.length()&&line.regionMatches(index, token, 0, token.length())) + return token; + return null; } - private boolean isNumber(String s) + private static boolean isIdentifierPart(char character) { - if(s.isEmpty()) return false; - int dots = 0; - for(char ch : s.toCharArray()) - if(ch=='.') {if(++dots > 1) return false;} - else if(!Character.isDigit(ch)) return false; - return true; + return Character.isLetterOrDigit(character)||character=='_'; } - private int indexOfOutsideQuotes(String line, char target) + private static boolean isOperatorCharacter(char character) { - boolean inStr = false; - boolean esc = false; - for(int i = 0; i < line.length(); i++) - { - char c = line.charAt(i); - if(c=='"'&&!esc) inStr = !inStr; - if(!inStr&&c==target) return i; - esc = (c=='\\')&&!esc; - } - return -1; + return "+-*/=!<>|&%^".indexOf(character) >= 0; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/TextHighlighter.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/TextHighlighter.java index affa817f8..133f26ecb 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/TextHighlighter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/highlight/TextHighlighter.java @@ -7,7 +7,11 @@ import java.util.List; /** - * Base syntax highlighter: implement highlight(line) to return ordered colored segments. + * Base syntax highlighter. + * + *

Implementations must return ordered segments whose concatenated text is exactly equal to + * the supplied line. Preserving the source text allows the renderer to clip a horizontally + * scrolled line without reparsing a partial token and losing its original colour.

*/ public abstract class TextHighlighter { @@ -30,10 +34,55 @@ public Segment(String text, IIColor color, boolean bold, boolean italic) this.bold = bold; this.italic = italic; } + + private Segment slice(int from) + { + return new Segment(text.substring(from), color, bold, italic); + } } + /** + * Highlights a complete source line. + */ public abstract List highlight(String line); + /** + * Highlights a complete line and then removes the source characters before {@code start}. + * This deliberately does not call {@link #highlight(String)} with a substring: a visible + * suffix may start halfway through a keyword, string or comment and still needs the style + * assigned to the complete token. + */ + public final List highlightVisible(String line, int start) + { + if(line==null||line.isEmpty()) + return Collections.emptyList(); + + int clampedStart = Math.max(0, Math.min(start, line.length())); + List highlighted = highlight(line); + if(highlighted==null||highlighted.isEmpty()) + return Collections.emptyList(); + + List result = new ArrayList<>(); + int consumed = 0; + for(Segment segment : highlighted) + { + if(segment==null||segment.text==null||segment.text.isEmpty()) + continue; + + int segmentEnd = consumed+segment.text.length(); + if(segmentEnd <= clampedStart) + { + consumed = segmentEnd; + continue; + } + + int localStart = Math.max(0, clampedStart-consumed); + result.add(localStart==0?segment: segment.slice(localStart)); + consumed = segmentEnd; + } + return result; + } + protected static List single(String line, IIColor color) { return Collections.singletonList(new Segment(line, color)); @@ -43,30 +92,4 @@ protected static boolean isWord(char c) { return Character.isLetterOrDigit(c)||c=='_'||c=='#'; } - - // Utility: simple token split preserving delimiters - protected static List splitPreserve(String line) - { - List out = new ArrayList<>(); - StringBuilder cur = new StringBuilder(); - for(char ch : line.toCharArray()) - { - if(Character.isWhitespace(ch)) - { - cur.append(ch); - } - else - { - if(cur.length() > 0) - { - out.add(cur.toString()); - cur.setLength(0); - } // flush whitespace - out.add(Character.toString(ch)); - } - } - if(cur.length() > 0) out.add(cur.toString()); - return out; - } } - diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextCaret.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextCaret.java index 87886a695..d070b0638 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextCaret.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextCaret.java @@ -1,12 +1,12 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util; -import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextField; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.DecoTextInputBase; import java.util.List; import java.util.function.Predicate; /** - * Represents a text caret used inside of a {@link DecoTextField} + * Represents a text caret used inside of a {@link DecoTextInputBase} * * @author Pabilo8 (pabilo@iiteam.net) * @ii-approved 0.3.1 @@ -25,6 +25,14 @@ public TextCaret(int line, int pos) this.anchorPos = pos; } + public TextCaret copy() + { + TextCaret copy = new TextCaret(line, pos); + copy.anchorLine = anchorLine; + copy.anchorPos = anchorPos; + return copy; + } + public boolean hasSelection() { return line!=anchorLine||pos!=anchorPos; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextFilter.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextFilter.java index 4ac51e403..aa6279d7e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextFilter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextFilter.java @@ -1,32 +1,265 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.text.util; -import java.util.function.Predicate; +import pl.pabilo8.immersiveintelligence.common.util.IIReference; +import pl.pabilo8.immersiveintelligence.common.util.ILocalizedEnum; /** + * Standard validation modes for Deco text inputs. + * + *

{@link #accepts(String)} accepts both complete values and useful intermediate editing states, + * such as an empty numeric field, {@code -}, or {@code 1.}. {@link #isValid(String)} is stricter + * and reports whether the current text can safely be consumed as a finished value.

+ * * @author Pabilo8 (pabilo@iiteam.net) * @ii-approved 0.3.1 * @since 25.09.2025 */ -public enum TextFilter +public enum TextFilter implements ILocalizedEnum { - NONE((s) -> true), - ALPHANUMERIC((s) -> s.matches("[a-zA-Z0-9_]*")), - LOWERCASE((s) -> s.matches("[a-z0-9_]*")), - UPPERCASE((s) -> s.matches("[A-Z0-9_]*")), - DECIMAL((s) -> s.matches("[0-9-]*")), - HEXADECIMAL((s) -> s.matches("[0-9A-Fa-f]*")), - BINARY((s) -> s.matches("[01]*")), - FLOAT((s) -> s.matches("[0-9.-]*")); + NONE, + ALPHANUMERIC, + LOWERCASE, + UPPERCASE, + DECIMAL, + HEXADECIMAL, + BINARY, + FLOAT; - private final Predicate filter; + /** + * Checks whether text may exist in the input while it is being edited. + */ + public boolean accepts(String input) + { + if(input==null) + return false; + switch(this) + { + case NONE: + return true; + case ALPHANUMERIC: + return allCharactersMatch(input, CharacterMode.ALPHANUMERIC); + case LOWERCASE: + return allCharactersMatch(input, CharacterMode.LOWERCASE); + case UPPERCASE: + return allCharactersMatch(input, CharacterMode.UPPERCASE); + case DECIMAL: + return acceptsInteger(input, 10); + case HEXADECIMAL: + return acceptsInteger(input, 16); + case BINARY: + return acceptsInteger(input, 2); + case FLOAT: + return acceptsFloat(input); + default: + return false; + } + } + + /** + * Checks whether text is a complete value safe for consumption. + */ + public boolean isValid(String input) + { + if(!accepts(input)) + return false; + switch(this) + { + case DECIMAL: + case HEXADECIMAL: + case BINARY: + return input.length() > 0&&!"-".equals(input)&&!"+".equals(input); + case FLOAT: + if(input.isEmpty()||"-".equals(input)||"+".equals(input)||".".equals(input)||"-.".equals(input)||"+.".equals(input)) + return false; + try + { + float value = Float.parseFloat(input); + return !Float.isNaN(value)&&!Float.isInfinite(value); + } catch(NumberFormatException ignored) + { + return false; + } + default: + return true; + } + } - TextFilter(Predicate filter) + /** + * @return whether this filter represents a numeric value and should use numeric field conventions + */ + public boolean isNumeric() { - this.filter = filter; + switch(this) + { + case DECIMAL: + case HEXADECIMAL: + case BINARY: + case FLOAT: + return true; + default: + return false; + } } + /** + * Backwards-compatible alias. New code should choose {@link #accepts(String)} or + * {@link #isValid(String)} explicitly. + */ + @Deprecated public boolean test(String input) { - return filter.test(input); + return accepts(input); + } + + public int parseInt(String input, int fallback) + { + if(!isValid(input)) + return fallback; + try + { + return Integer.parseInt(input, getRadix()); + } catch(NumberFormatException ignored) + { + return fallback; + } + } + + public float parseFloat(String input, float fallback) + { + if(this!=FLOAT||!isValid(input)) + return fallback; + try + { + return Float.parseFloat(input); + } catch(NumberFormatException ignored) + { + return fallback; + } + } + + public String formatInt(int value) + { + switch(this) + { + case BINARY: + return formatSigned(value, 2); + case HEXADECIMAL: + return formatSigned(value, 16).toUpperCase(); + case DECIMAL: + default: + return Integer.toString(value); + } + } + + private int getRadix() + { + switch(this) + { + case BINARY: + return 2; + case HEXADECIMAL: + return 16; + default: + return 10; + } + } + + private static String formatSigned(int value, int radix) + { + if(value >= 0) + return Integer.toString(value, radix); + //Avoid overflowing when negating Integer.MIN_VALUE. + long magnitude = -(long)value; + return "-"+Long.toString(magnitude, radix); + } + + private static boolean acceptsInteger(String input, int radix) + { + for(int i = 0; i < input.length(); i++) + { + char c = input.charAt(i); + if(i==0&&(c=='-'||c=='+')) + continue; + if(Character.digit(c, radix) < 0) + return false; + } + return true; + } + + private static boolean acceptsFloat(String input) + { + boolean decimalPoint = false; + boolean exponent = false; + boolean exponentDigit = false; + for(int i = 0; i < input.length(); i++) + { + char c = input.charAt(i); + if(Character.isDigit(c)) + { + if(exponent) + exponentDigit = true; + continue; + } + if((c=='-'||c=='+')&&(i==0||(i > 0&&(input.charAt(i-1)=='e'||input.charAt(i-1)=='E')))) + continue; + if(c=='.'&&!decimalPoint&&!exponent) + { + decimalPoint = true; + continue; + } + if((c=='e'||c=='E')&&!exponent&&i > 0&&hasMantissaDigit(input, i)) + { + exponent = true; + continue; + } + return false; + } + //An unfinished exponent is a valid intermediate state. + return !exponent||exponentDigit||input.endsWith("e")||input.endsWith("E")||input.endsWith("e-")||input.endsWith("E-")||input.endsWith("e+")||input.endsWith("E+"); + } + + private static boolean hasMantissaDigit(String input, int end) + { + for(int i = 0; i < end; i++) + if(Character.isDigit(input.charAt(i))) + return true; + return false; + } + + private static boolean allCharactersMatch(String input, CharacterMode mode) + { + for(int i = 0; i < input.length(); i++) + { + char c = input.charAt(i); + if(c=='_'||Character.isDigit(c)) + continue; + if(mode==CharacterMode.ALPHANUMERIC&&((c >= 'a'&&c <= 'z')||(c >= 'A'&&c <= 'Z'))) + continue; + if(mode==CharacterMode.LOWERCASE&&c >= 'a'&&c <= 'z') + continue; + if(mode==CharacterMode.UPPERCASE&&c >= 'A'&&c <= 'Z') + continue; + return false; + } + return true; + } + + @Override + public String geLocaleKey() + { + return IIReference.DESCRIPTION_KEY+"text_filter."; + } + + private enum CharacterMode implements ILocalizedEnum + { + ALPHANUMERIC, + LOWERCASE, + UPPERCASE; + + @Override + public String geLocaleKey() + { + return IIReference.DESCRIPTION_KEY+"text_filter."; + } } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextHistoryState.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextHistoryState.java index 679ff6547..633323aae 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextHistoryState.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/text/util/TextHistoryState.java @@ -17,12 +17,15 @@ public TextHistoryState(List l, List c) { this.lines = new ArrayList<>(l); this.carets = new ArrayList<>(); - for(TextCaret tc : c) - { - TextCaret copy = new TextCaret(tc.line, tc.pos); - copy.anchorLine = tc.anchorLine; - copy.anchorPos = tc.anchorPos; - this.carets.add(copy); - } + for(TextCaret caret : c) + this.carets.add(caret.copy()); + } + + public int characterCount() + { + int result = Math.max(0, lines.size()-1); + for(String line : lines) + result += line.length(); + return result; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/DecoMapDisplay.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/DecoMapDisplay.java index ad9e1cdee..c81976abc 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/DecoMapDisplay.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/DecoMapDisplay.java @@ -470,7 +470,7 @@ private void normalizeView() float s = zoom*baseZoom; float half = radiusBlocks/2f; - // Horizontal + //Horizontal float mapScreenW = radiusBlocks*s; if(mapScreenW <= width) panX = 0f; @@ -481,7 +481,7 @@ private void normalizeView() panX = MathHelper.clamp(panX, minPanX, maxPanX); } - // Vertical + //Vertical float mapScreenH = radiusBlocks*s; if(mapScreenH <= height) panY = 0f; @@ -500,7 +500,7 @@ private boolean onScroll(DecoMapDisplay gui, int mouseScroll, int mouseX, int mo if(!zoomScrollEnabled) return false; float step = 0.05f; - zoom = MathHelper.clamp(zoom+Math.signum(mouseScroll)*step, zoomMin, zoomMax); + zoom = MathHelper.clamp(zoom+mouseScroll*step, zoomMin, zoomMax); normalizeView(); return true; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/IDecoMapColorMapper.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/IDecoMapColorMapper.java index de869745d..fd00d6276 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/IDecoMapColorMapper.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/IDecoMapColorMapper.java @@ -34,8 +34,8 @@ public interface IDecoMapColorMapper extends ILocalizedEnum */ default int applyHeightShading(int color, int sampleY) { - // Simple deterministic shading based on height - int shadeLevel = (sampleY%4+4)%4; // Ensure positive + //Simple deterministic shading based on height + int shadeLevel = (sampleY%4+4)%4; //Ensure positive float shadeFactor = 0.8f+(shadeLevel*0.1f); return applyShading(color, shadeFactor); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/layers/MapLayerBuilder.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/layers/MapLayerBuilder.java index 181f79d7e..e53a5522b 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/layers/MapLayerBuilder.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/visual/map/layers/MapLayerBuilder.java @@ -64,7 +64,7 @@ public MapLayerBuilder withSprite(ResLoc location, boolean usesBlockAtlas, int w } public MapLayerBuilder withSprite(ResLoc location, boolean usesBlockAtlas, int worldX, int worldZ, - float sizePx, float textureSize, IIColor color, float rotationDeg) + float sizePx, float textureSize, IIColor color, float rotationDeg) { float[] uv; if(usesBlockAtlas) @@ -110,7 +110,7 @@ public MapLayerBuilder addLine(int fromX, int fromZ, int toX, int toZ, IIColor c */ public MapLayerBuilder addDirectionalLine(int centerX, int centerZ, float angleDeg, float distance, IIColor color, float width) { - // Convert polar coordinates (angle, distance) to cartesian + //Convert polar coordinates (angle, distance) to cartesian double angleRad = Math.toRadians(angleDeg); int toX = centerX+(int)(distance*Math.cos(angleRad)); int toZ = centerZ+(int)(distance*Math.sin(angleRad)); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoManualWidget.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoManualWidget.java index 94fbcbab4..f3ddb7fd2 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoManualWidget.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoManualWidget.java @@ -39,7 +39,10 @@ public DecoManualWidget() //Initialize the default manual, if it wasn't already GuiManual trueManual = ManualHelper.getManual().getGui(); if(trueManual==null) + { trueManual = new GuiManual(ManualHelper.getManual(), ManualHelper.getManual().texture); + trueManual.initGui(); + } this.ieManualGUI = trueManual; //Initialize the wrapper diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoStyleWidget.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoStyleWidget.java index 40c02db62..ab98c4a1e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoStyleWidget.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/DecoStyleWidget.java @@ -1,6 +1,7 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.component.widget; import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; +import blusunrize.immersiveengineering.common.blocks.TileEntityMultiblockPart; import net.minecraft.client.resources.I18n; import net.minecraft.entity.Entity; import net.minecraft.util.text.TextFormatting; @@ -13,6 +14,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoAlignment; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.common.entity.vehicle.EntityVehicleBase; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageEntityNBTSync; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageIITileSync; @@ -45,8 +47,23 @@ protected boolean initialize() { if(super.initialize()) { + //Create upper text + String text = IIReference.GUI_TOOLTIP_KEY+"widget.style.main"; + if(!style.getConstraints().getStyles().isEmpty()) + text = text+".variants"; + if(style.getConstraints().getColorCustomization()!=PaintStyleConstraint.NOT_APPLICABLE) + text = text+".color"; + + String key = IIReference.GUI_TOOLTIP_KEY+"widget.style.main.tile"; + if(customizable instanceof TileEntityMultiblockPart) + key = IIReference.GUI_TOOLTIP_KEY+"widget.style.main.multiblock"; + else if(customizable instanceof EntityVehicleBase) + key = IIReference.GUI_TOOLTIP_KEY+"widget.style.main.vehicle"; + else if(customizable instanceof Entity) + key = IIReference.GUI_TOOLTIP_KEY+"widget.style.main.entity"; + withTitleLabel(IIReference.GUI_TOOLTIP_KEY+"widget.style", DecoAlignment.TOP); - DecoLabel headInfo = addLabel(TextFormatting.ITALIC+I18n.format(IIReference.GUI_TOOLTIP_KEY+"widget.style.desc"), 4, 2+4) + DecoLabel headInfo = addLabel(TextFormatting.ITALIC+I18n.format(text, I18n.format(key)), 4, 2+4) .withSize(width-4-4, 32) .withWrapping(true); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/ManualSystemWrapper.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/ManualSystemWrapper.java index a266fe7a0..c8a24daa4 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/ManualSystemWrapper.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/component/widget/ManualSystemWrapper.java @@ -61,7 +61,7 @@ public void initGui() int x = parent.x-20; int y = parent.y; - // Init + //Init manualInstance = ReflectionHelper.getPrivateValue(GuiManual.class, this, "manual"); manualInstance.openManual(); activeManual = this; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/DefaultTreeNodeRenderer.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/DefaultTreeNodeRenderer.java index 8eacdd494..f072d1678 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/DefaultTreeNodeRenderer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/DefaultTreeNodeRenderer.java @@ -35,7 +35,7 @@ public class DefaultTreeNodeRenderer implements IDecoTreeNodeRenderer @Override public void renderNode(@Nonnull IDecoTreeNode node, int x, int y, boolean isHovered, boolean isActive, boolean isAvailable) { - // Determine color based on state + //Determine color based on state IIColor color; if(isActive) color = activeColor; @@ -46,13 +46,13 @@ else if(isAvailable) else color = unavailableColor; - // Draw node background + //Draw node background ClientUtils.bindAtlas(); IIDrawUtils draw = IIDrawUtils.startTexturedColored(); draw.drawConnectedTexColorRect(x, y, NODE_WIDTH, NODE_HEIGHT, color, DecoTextures.SLOT_IE, 32, 32, 4, 4); draw.finish(); - // Draw node text (centered) + //Draw node text (centered) String text = getDisplayName(node); if(!text.isEmpty()) { @@ -87,7 +87,7 @@ public void renderRootNode(int x, int y) GlStateManager.translate(x, y, 0); IIDrawUtils draw = IIDrawUtils.startColored(); - // Draw a circle for the virtual root + //Draw a circle for the virtual root draw.drawColorRect(-3, -3, 6, 6, IIColor.fromHex("888888")); draw.finish(); @@ -98,7 +98,7 @@ public void renderRootNode(int x, int y) * Renders a connection between parent and child nodes. */ public void renderConnection(TreeLayout.NodeLayoutInfo parentInfo, TreeLayout.NodeLayoutInfo childInfo, - TreeLayout.Orientation orientation, boolean isActive) + TreeLayout.Orientation orientation, boolean isActive) { IIColor color = isActive?connectionActiveColor: connectionColor; @@ -130,94 +130,94 @@ public void renderConnection(TreeLayout.NodeLayoutInfo parentInfo, TreeLayout.No private void renderHorizontalConnection(IIDrawUtils draw, int fromX, int fromY, int toX, int toY, IIColor color, boolean reverse) { - // Calculate mid point + //Calculate mid point int midX = (fromX+toX)/2; if(!reverse) { - // Left to right connection - // Horizontal segment from parent to mid + //Left to right connection + //Horizontal segment from parent to mid draw.drawColorRect(fromX, fromY-CONNECTION_WIDTH/2, midX-fromX, CONNECTION_WIDTH, color); - // Vertical segment at mid + //Vertical segment at mid int verticalStartY = Math.min(fromY, toY); int verticalEndY = Math.max(fromY, toY); draw.drawColorRect(midX-CONNECTION_WIDTH/2, verticalStartY, CONNECTION_WIDTH, verticalEndY-verticalStartY, color); - // Horizontal segment from mid to child + //Horizontal segment from mid to child draw.drawColorRect(midX, toY-CONNECTION_WIDTH/2, toX-midX, CONNECTION_WIDTH, color); - // Draw arrow at target + //Draw arrow at target drawArrowRight(draw, toX, toY, color); } else { - // Right to left connection - // Horizontal segment from parent to mid + //Right to left connection + //Horizontal segment from parent to mid draw.drawColorRect(fromX, fromY-CONNECTION_WIDTH/2, midX-fromX, CONNECTION_WIDTH, color); - // Vertical segment at mid + //Vertical segment at mid int verticalStartY = Math.min(fromY, toY); int verticalEndY = Math.max(fromY, toY); draw.drawColorRect(midX-CONNECTION_WIDTH/2, verticalStartY, CONNECTION_WIDTH, verticalEndY-verticalStartY, color); - // Horizontal segment from mid to child + //Horizontal segment from mid to child draw.drawColorRect(midX, toY-CONNECTION_WIDTH/2, toX-midX, CONNECTION_WIDTH, color); - // Draw arrow at target + //Draw arrow at target drawArrowLeft(draw, toX, toY, color); } } private void renderVerticalConnection(IIDrawUtils draw, int fromX, int fromY, int toX, int toY, IIColor color, boolean reverse) { - // Calculate mid point + //Calculate mid point int midY = (fromY+toY)/2; if(!reverse) { - // Top to bottom connection - // Vertical segment from parent to mid + //Top to bottom connection + //Vertical segment from parent to mid draw.drawColorRect(fromX-CONNECTION_WIDTH/2, fromY, CONNECTION_WIDTH, midY-fromY, color); - // Horizontal segment at mid + //Horizontal segment at mid int horizontalStartX = Math.min(fromX, toX); int horizontalEndX = Math.max(fromX, toX); draw.drawColorRect(horizontalStartX, midY-CONNECTION_WIDTH/2, horizontalEndX-horizontalStartX, CONNECTION_WIDTH, color); - // Vertical segment from mid to child + //Vertical segment from mid to child draw.drawColorRect(toX-CONNECTION_WIDTH/2, midY, CONNECTION_WIDTH, toY-midY, color); - // Draw arrow at target + //Draw arrow at target drawArrowDown(draw, toX, toY, color); } else { - // Bottom to top connection - // Vertical segment from parent to mid + //Bottom to top connection + //Vertical segment from parent to mid draw.drawColorRect(fromX-CONNECTION_WIDTH/2, fromY, CONNECTION_WIDTH, midY-fromY, color); - // Horizontal segment at mid + //Horizontal segment at mid int horizontalStartX = Math.min(fromX, toX); int horizontalEndX = Math.max(fromX, toX); draw.drawColorRect(horizontalStartX, midY-CONNECTION_WIDTH/2, horizontalEndX-horizontalStartX, CONNECTION_WIDTH, color); - // Vertical segment from mid to child + //Vertical segment from mid to child draw.drawColorRect(toX-CONNECTION_WIDTH/2, midY, CONNECTION_WIDTH, toY-midY, color); - // Draw arrow at target + //Draw arrow at target drawArrowUp(draw, toX, toY, color); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/upgrade/UpgradeTreeNodeRenderer.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/upgrade/UpgradeTreeNodeRenderer.java index 57a2169c2..5c8e165e8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/upgrade/UpgradeTreeNodeRenderer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/tree/upgrade/UpgradeTreeNodeRenderer.java @@ -44,7 +44,7 @@ else if(isAvailable) else bg = unavailableColor; - // background + //background IIDrawUtils draw = IIDrawUtils.startColored(); draw.drawColorRect(x, y, NODE_WIDTH, NODE_HEIGHT, bg); draw.finish(); @@ -76,7 +76,10 @@ public Collection getTooltip(@Nonnull IDecoTreeNode node) if(upgrade.isWorkInProgress()) { tooltip.add(IIColor.MC_YELLOW.getHexCol(I18n.format("ie.manual.entry.wip_warning0"))); - tooltip.add(IIColor.MC_YELLOW.getHexCol(I18n.format("ie.manual.entry.wip_warning.upgrade"))); + List lines = IIClientUtils.fontRegular.listFormattedStringToWidth( + I18n.format("ie.manual.entry.wip_warning.upgrade"), 200); + for(String line : lines) + tooltip.add(IIColor.MC_YELLOW.withBrightness(0.65f).getHexCol(line)); } List description = IIClientUtils.fontRegular.listFormattedStringToWidth( diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoBackgroundBuilder.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoBackgroundBuilder.java index cf3f54a13..13e5a47bd 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoBackgroundBuilder.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoBackgroundBuilder.java @@ -2,6 +2,8 @@ import blusunrize.immersiveengineering.client.ClientUtils; import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager.DestFactor; @@ -13,6 +15,7 @@ import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraft.world.World; @@ -36,6 +39,7 @@ /** * @author Pabilo8 (pabilo@iiteam.net) * @ii-approved 0.3.1 + * @updated 23.07.2026 * @since 07.01.2025 **/ public class DecoBackgroundBuilder @@ -350,18 +354,18 @@ public void draw() GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE); GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0xFF); - DecoGuiUtils.drawBackgroundMask(tiles, minXOffset, minYOffset).finish(); + drawBackgroundMask(tiles, minXOffset, minYOffset).finish(); //Background GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); GL11.glStencilFunc(GL11.GL_EQUAL, 1, 0xFF); - DecoGuiUtils.drawBackgroundBlock(tiles, minXOffset, minYOffset).finish(); + drawBackgroundBlock(tiles, minXOffset, minYOffset).finish(); GL11.glDisable(GL11.GL_STENCIL_TEST); //Overlay GlStateManager.enableBlend(); GlStateManager.blendFunc(SourceFactor.DST_COLOR, DestFactor.SRC_COLOR); - DecoGuiUtils.drawBackgroundMask(tiles, minXOffset, minYOffset).finish(); + drawBackgroundMask(tiles, minXOffset, minYOffset).finish(); } //Blending inventory slots (using previous blend func.) @@ -435,9 +439,9 @@ private void handleFrameDrawing(IIDrawUtils draw, DecoBackgroundTile backgroundF DecoFrame frame = backgroundFrame.frame; assert frame!=null; if(frame.cornersOnly) - DecoGuiUtils.drawFrameCorners(draw, backgroundFrame.x, backgroundFrame.y, backgroundFrame.width, backgroundFrame.height, frame.style, frame.sides); + drawFrameCorners(draw, backgroundFrame.x, backgroundFrame.y, backgroundFrame.width, backgroundFrame.height, frame.style, frame.sides); else - DecoGuiUtils.drawFrame(draw, backgroundFrame.x, backgroundFrame.y, backgroundFrame.width, backgroundFrame.height, frame.style, frame.sides, frame.frameThickness); + drawFrame(draw, backgroundFrame.x, backgroundFrame.y, backgroundFrame.width, backgroundFrame.height, frame.style, frame.sides, frame.frameThickness); } @Nonnull @@ -494,61 +498,196 @@ public DecoBackgroundBuilder conditionally(boolean condition, Consumer rects, int minXOffset, int minYOffset) { - //Vanilla MC bevel mask inventory slot - VANILLA(DecoTextures.SLOT_VANILLA, true, 1), - //Immersive Engineering style inventory slot - IE(DecoTextures.SLOT_IE, DecoTextures.SLOT_IE_MARKER, 2), - IE_INPUT(IE, 2), - IE_OUTPUT(IE, 3), - IE_CUSTOM1(IE, 4), - IE_CUSTOM2(IE, 5), - IE_CUSTOM3(IE, 6), - IE_CUSTOM4(IE, 7), - //Brass Frame IE style Inventory slot - IE_BRASS(DecoTextures.SLOT_IE_BRASS, DecoTextures.SLOT_IE_BRASS_MARKER, 2), - IE_BRASS_INPUT(IE_BRASS, 2), - IE_BRASS_OUTPUT(IE_BRASS, 3), - IE_BRASS_CUSTOM1(IE_BRASS, 4), - IE_BRASS_CUSTOM2(IE_BRASS, 5), - IE_BRASS_CUSTOM3(IE_BRASS, 6), - IE_BRASS_CUSTOM4(IE_BRASS, 7), - ; - - final int borderSize; - final boolean blending; - final ResLoc backgroundLocation, markerLocation; - final int markerOffset; - - SlotStyle(ResLoc backgroundLocation, boolean blending, int borderSize) - { - this.blending = blending; - this.backgroundLocation = backgroundLocation; - this.markerLocation = null; - this.borderSize = borderSize; - this.markerOffset = -1; - } + IIDrawUtils draw = IIDrawUtils.startTextured(); + + BiMap spriteMap = HashBiMap.create(); + rects.stream() + .map(rect -> rect.mask) + .distinct() + .forEach(resLoc -> spriteMap.put((byte)(spriteMap.size()+1), resLoc)); + byte[][] outline = getBoxesOutline(rects, spriteMap.inverse(), 8, minXOffset, minYOffset); + + for(int x = 0; x < outline.length; x++) + for(int y = 0; y < outline[x].length; y++) + if(outline[x][y]!=0) + { + boolean hasRight = x!=outline.length-1&&outline[x+1][y]!=0; + boolean hasLeft = x!=0&&outline[x-1][y]!=0; + boolean hasBottom = y!=outline[x].length-1&&outline[x][y+1]!=0; + boolean hasTop = y!=0&&outline[x][y-1]!=0; + boolean hasAll = hasRight&&hasLeft&&hasTop&&hasBottom; + int tOffset = hasAll?8: 0; + + int texX = 2, texY = 2; + //Check for diagonal corners or draw central piece + if(hasAll) + { + boolean hasTL = outline[x-1][y-1]!=0; + boolean hasTR = outline[x+1][y-1]!=0; + boolean hasBL = outline[x-1][y+1]!=0; + + texX += !hasTL?-2: (!hasTR?4: 0); + texY += !hasTL?-2: (!hasBL?4: 0); + } + //Check for corner piece + else + { + texX += (!hasLeft)?-2: (!hasRight?4: 0); + texY += (!hasTop)?-2: (!hasBottom?4: 0); + } + + ResLoc res = spriteMap.get(outline[x][y]); + assert res!=null; + TextureAtlasSprite maskSprite = ClientUtils.getSprite(res); + draw.drawTexRect( + minXOffset+x*8, minYOffset+y*8, 8, 8, + maskSprite.getInterpolatedU(tOffset+texX), maskSprite.getInterpolatedU(tOffset+texX+2), + maskSprite.getInterpolatedV(texY), maskSprite.getInterpolatedV(texY+2) + ); + } + + return draw; + } + + private byte[][] getBoxesOutline(Collection rects, BiMap spriteMap, + int unit, int minXOffset, int minYOffset) + { + if(rects.isEmpty()) + return new byte[0][0]; + + int xx, yy; + + DecoBackgroundTile b = rects.stream().min((o1, o2) -> o2.x+o2.width-(o1.x+o1.width)).orElse(null); + xx = b.x+b.width-minXOffset; + b = rects.stream().min((o1, o2) -> o2.y+o2.height-(o1.y+o1.height)).orElse(null); + yy = b.y+b.height-minYOffset; + + xx /= unit; + yy /= unit; + + byte[][] fillmap = new byte[xx+1][yy+1]; + + //fill box map with 0 + for(int i = 0; i <= xx; i++) + for(int j = 0; j <= yy; j++) + fillmap[i][j] = 0; - SlotStyle(ResLoc backgroundLocation, ResLoc markerLocation, int borderSize) - { - this.blending = false; - this.backgroundLocation = backgroundLocation; - this.markerLocation = markerLocation; - this.borderSize = borderSize; - this.markerOffset = -1; - } - SlotStyle(SlotStyle base, int markerOffset) + //fill box occupied spaces with 1 + for(DecoBackgroundTile rect : rects) + for(int x = rect.x; x < rect.x+rect.width; x += unit) + for(int y = rect.y; y < rect.y+rect.height; y += unit) + fillmap[(x-minXOffset)/unit][(y-minYOffset)/unit] = spriteMap.get(rect.mask); + + return fillmap; + + } + + private IIDrawUtils drawBackgroundBlock(Collection rects, int minXOffset, int minYOffset) + { + IIDrawUtils draw = IIDrawUtils.startTexturedColored(); + for(DecoBackgroundTile rect : rects) { - this.blending = false; - this.backgroundLocation = base.backgroundLocation; - this.markerLocation = base.markerLocation; - this.borderSize = base.borderSize; - this.markerOffset = markerOffset; + float rectX = (int)Math.floor(rect.x/8f)*8f; + float rectY = (int)Math.floor(rect.y/8f)*8f; + float rectW = (int)Math.ceil(rect.width/8f)*8f; + float rectH = (int)Math.ceil(rect.height/8f)*8f; + + for(int yy = 0; yy < rectH; yy += 32) + for(int xx = 0; xx < rectW; xx += 32) + { + TextureAtlasSprite sprite = ClientUtils.getSprite(rect.style); + draw.drawTexColorRect(rectX+xx+minXOffset, rectY+yy+minYOffset, + MathHelper.clamp(rectW-xx, 8, 32), + MathHelper.clamp(rectH-yy, 8, 32), + rect.color, + sprite.getMinU(), sprite.getInterpolatedU(Math.min(rectW-xx, 32)/2f), + sprite.getMinV(), sprite.getInterpolatedV(Math.min(rectH-yy, 32)/2f) + ); + } } + + return draw; + } + + private void drawFrameCorners(IIDrawUtils draw, int x, int y, int width, int height, ResLoc style, boolean[] sides) + { + TextureAtlasSprite sprite = ClientUtils.getSprite(style); + int cornerSize = 16; + + //Top-left corner + if(sides[0]&&sides[3]) + draw.drawTexColorRect(x, y, cornerSize, cornerSize, IIColor.WHITE, + sprite.getMinU(), sprite.getInterpolatedU(8), sprite.getMinV(), sprite.getInterpolatedV(8)); + //Top-right corner + if(sides[0]&&sides[1]) + draw.drawTexColorRect(x+width-cornerSize, y, cornerSize, cornerSize, IIColor.WHITE, + sprite.getInterpolatedU(16-8), sprite.getInterpolatedU(16), sprite.getMinV(), sprite.getInterpolatedV(8)); + //Bottom-left corner + if(sides[2]&&sides[3]) + draw.drawTexColorRect(x, y+height-cornerSize, cornerSize, cornerSize, IIColor.WHITE, + sprite.getMinU(), sprite.getInterpolatedU(8), sprite.getInterpolatedV(16-8), sprite.getInterpolatedV(16)); + //Bottom-right corner + if(sides[2]&&sides[1]) + draw.drawTexColorRect(x+width-cornerSize, y+height-cornerSize, cornerSize, cornerSize, IIColor.WHITE, + sprite.getInterpolatedU(16-8), sprite.getInterpolatedU(16), sprite.getInterpolatedV(16-8), sprite.getInterpolatedV(16)); } + private void drawFrame(IIDrawUtils draw, int x, int y, int width, int height, ResLoc style, boolean[] sides, int frameThickness) + { + TextureAtlasSprite sprite = ClientUtils.getSprite(style); + + //Top-Left mappings + float minU = sprite.getMinU(); + float minUU = sprite.getInterpolatedU(frameThickness/2f); + float minV = sprite.getMinV(); + float minVV = sprite.getInterpolatedV(frameThickness/2f); + //Bottom-Right mappings + float maxU = sprite.getInterpolatedU(16-frameThickness/2f); + float maxUU = sprite.getInterpolatedU(16); + float maxV = sprite.getInterpolatedV(16-frameThickness/2f); + float maxVV = sprite.getInterpolatedV(16); + + //Draw main frame + + //Top + if(sides[0]) + draw.drawRepeatedTexColorRect(x+frameThickness, y, width-frameThickness*2, frameThickness, IIColor.WHITE, + 32-2*frameThickness, frameThickness, minUU, maxU, minV, minVV); + //Bottom + if(sides[1]) + draw.drawRepeatedTexColorRect(x+frameThickness, y+height-frameThickness, width-frameThickness*2, frameThickness, IIColor.WHITE, + 32-2*frameThickness, frameThickness, minUU, maxU, maxV, maxVV); + //Left + if(sides[2]) + draw.drawRepeatedTexColorRect(x, y+frameThickness, frameThickness, height-frameThickness*2, IIColor.WHITE, + frameThickness, 32-2*frameThickness, minU, minUU, minVV, maxV); + //Right + if(sides[3]) + draw.drawRepeatedTexColorRect(x+width-frameThickness, y+frameThickness, frameThickness, height-frameThickness*2, IIColor.WHITE, + frameThickness, 32-2*frameThickness, maxU, maxUU, minVV, maxV); + + //Draw squares on frame edges + if(sides[0]||sides[3]) + draw.drawTexColorRect(x, y, frameThickness, frameThickness, IIColor.WHITE, + minU, minUU, minV, minVV); + if(sides[0]||sides[1]) + draw.drawTexColorRect(x+width-frameThickness, y, frameThickness, frameThickness, IIColor.WHITE, + maxU, maxUU, minV, minVV); + if(sides[2]||sides[3]) + draw.drawTexColorRect(x, y+height-frameThickness, frameThickness, frameThickness, IIColor.WHITE, + minU, minUU, maxV, maxVV); + if(sides[2]||sides[1]) + draw.drawTexColorRect(x+width-frameThickness, y+height-frameThickness, frameThickness, frameThickness, IIColor.WHITE, + maxU, maxUU, maxV, maxVV); + } + + //--- Utility Classes ---// + private static class DecoSlot { final int x, y, width, height; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoColors.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoColors.java index ba7541ebb..0df4c7cd1 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoColors.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoColors.java @@ -31,6 +31,8 @@ public class DecoColors public static final IIColor ACTION_ADD = IIColor.fromPackedRGB(0x778a78); public static final IIColor ACTION_REMOVE = IIColor.fromPackedRGB(0x8a6865); public static final IIColor ACTION_EDIT = IIColor.fromPackedRGB(0x8a7d67); + public static final IIColor ACTION_ACCEPT = IIColor.fromPackedRGB(0x5e705f); + public static final IIColor ACTION_REJECT = IIColor.fromPackedRGB(0x775957); public static final IIColor ARMOR_INTEGRITY_1 = IIColor.fromPackedRGB(0x6b6b6b); public static final IIColor ARMOR_INTEGRITY_2 = IIColor.fromPackedRGB(0x3c3c3c); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiCategory.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiCategory.java index 210595821..2d0174351 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiCategory.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiCategory.java @@ -16,5 +16,6 @@ public enum DecoGuiCategory PRODUCTION_TILE, TERRITORY_CONTROL_TILE, DATA_TILE, - VEHICLE_ENTITY + VEHICLE_ENTITY, + GENERIC_PLAYER } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiUtils.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiUtils.java index ffc1e0314..ce2cc4abf 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiUtils.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoGuiUtils.java @@ -1,8 +1,6 @@ package pl.pabilo8.immersiveintelligence.client.gui.deco.util; import blusunrize.immersiveengineering.client.ClientUtils; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.nbt.NBTTagCompound; @@ -12,7 +10,6 @@ import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.client.util.IIDrawUtils; import pl.pabilo8.immersiveintelligence.common.util.IIColor; -import pl.pabilo8.immersiveintelligence.common.util.ResLoc; import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; import pl.pabilo8.immersiveintelligence.common.util.multiblock.production.TileEntityMultiblockProductionBase.IIIMultiblockRecipe; import pl.pabilo8.immersiveintelligence.common.util.multiblock.production.TileEntityMultiblockProductionMulti; @@ -22,7 +19,6 @@ import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; -import java.util.Collection; import java.util.function.Function; /** @@ -33,234 +29,6 @@ @SideOnly(Side.CLIENT) public class DecoGuiUtils { - public static IIDrawUtils drawBackgroundMask(Collection rects, int minXOffset, int minYOffset) - { - IIDrawUtils draw = IIDrawUtils.startTextured(); - - BiMap spriteMap = HashBiMap.create(); - rects.stream() - .map(rect -> rect.mask) - .distinct() - .forEach(resLoc -> spriteMap.put((byte)(spriteMap.size()+1), resLoc)); - byte[][] outline = getBoxesOutline(rects, spriteMap.inverse(), 8, minXOffset, minYOffset); - - for(int x = 0; x < outline.length; x++) - for(int y = 0; y < outline[x].length; y++) - if(outline[x][y]!=0) - { - boolean hasRight = x!=outline.length-1&&outline[x+1][y]!=0; - boolean hasLeft = x!=0&&outline[x-1][y]!=0; - boolean hasBottom = y!=outline[x].length-1&&outline[x][y+1]!=0; - boolean hasTop = y!=0&&outline[x][y-1]!=0; - boolean hasAll = hasRight&&hasLeft&&hasTop&&hasBottom; - int tOffset = hasAll?8: 0; - - int texX = 2, texY = 2; - //Check for diagonal corners or draw central piece - if(hasAll) - { - boolean hasTL = outline[x-1][y-1]!=0; - boolean hasTR = outline[x+1][y-1]!=0; - boolean hasBL = outline[x-1][y+1]!=0; - - texX += !hasTL?-2: (!hasTR?4: 0); - texY += !hasTL?-2: (!hasBL?4: 0); - } - //Check for corner piece - else - { - texX += (!hasLeft)?-2: (!hasRight?4: 0); - texY += (!hasTop)?-2: (!hasBottom?4: 0); - } - - ResLoc res = spriteMap.get(outline[x][y]); - assert res!=null; - TextureAtlasSprite maskSprite = ClientUtils.getSprite(res); - draw.drawTexRect( - minXOffset+x*8, minYOffset+y*8, 8, 8, - maskSprite.getInterpolatedU(tOffset+texX), maskSprite.getInterpolatedU(tOffset+texX+2), - maskSprite.getInterpolatedV(texY), maskSprite.getInterpolatedV(texY+2) - ); - } - - return draw; - } - - public static byte[][] getBoxesOutline(Collection rects, BiMap spriteMap, - int unit, int minXOffset, int minYOffset) - { - if(rects.isEmpty()) - return new byte[0][0]; - - int xx, yy; - - DecoBackgroundTile b = rects.stream().min((o1, o2) -> o2.x+o2.width-(o1.x+o1.width)).orElse(null); - xx = b.x+b.width-minXOffset; - b = rects.stream().min((o1, o2) -> o2.y+o2.height-(o1.y+o1.height)).orElse(null); - yy = b.y+b.height-minYOffset; - - xx /= unit; - yy /= unit; - - byte[][] fillmap = new byte[xx+1][yy+1]; - - //fill box map with 0 - for(int i = 0; i <= xx; i++) - for(int j = 0; j <= yy; j++) - fillmap[i][j] = 0; - - - //fill box occupied spaces with 1 - for(DecoBackgroundTile rect : rects) - for(int x = rect.x; x < rect.x+rect.width; x += unit) - for(int y = rect.y; y < rect.y+rect.height; y += unit) - fillmap[(x-minXOffset)/unit][(y-minYOffset)/unit] = spriteMap.get(rect.mask); - - return fillmap; - - } - - public static IIDrawUtils drawBackgroundBlock(Collection rects, int minXOffset, int minYOffset) - { - IIDrawUtils draw = IIDrawUtils.startTexturedColored(); - for(DecoBackgroundTile rect : rects) - { - float rectX = (int)Math.floor(rect.x/8f)*8f; - float rectY = (int)Math.floor(rect.y/8f)*8f; - float rectW = (int)Math.ceil(rect.width/8f)*8f; - float rectH = (int)Math.ceil(rect.height/8f)*8f; - - for(int yy = 0; yy < rectH; yy += 32) - for(int xx = 0; xx < rectW; xx += 32) - { - TextureAtlasSprite sprite = ClientUtils.getSprite(rect.style); - draw.drawTexColorRect(rectX+xx+minXOffset, rectY+yy+minYOffset, - MathHelper.clamp(rectW-xx, 8, 32), - MathHelper.clamp(rectH-yy, 8, 32), - rect.color, - sprite.getMinU(), sprite.getInterpolatedU(Math.min(rectW-xx, 32)/2f), - sprite.getMinV(), sprite.getInterpolatedV(Math.min(rectH-yy, 32)/2f) - ); - } - } - - return draw; - } - - public static void drawFrameCorners(IIDrawUtils draw, int x, int y, int width, int height, ResLoc style, boolean[] sides) - { - TextureAtlasSprite sprite = ClientUtils.getSprite(style); - int cornerSize = 16; - - //Top-left corner - if(sides[0]&&sides[3]) - draw.drawTexColorRect(x, y, cornerSize, cornerSize, IIColor.WHITE, - sprite.getMinU(), sprite.getInterpolatedU(8), sprite.getMinV(), sprite.getInterpolatedV(8)); - //Top-right corner - if(sides[0]&&sides[1]) - draw.drawTexColorRect(x+width-cornerSize, y, cornerSize, cornerSize, IIColor.WHITE, - sprite.getInterpolatedU(16-8), sprite.getInterpolatedU(16), sprite.getMinV(), sprite.getInterpolatedV(8)); - //Bottom-left corner - if(sides[2]&&sides[3]) - draw.drawTexColorRect(x, y+height-cornerSize, cornerSize, cornerSize, IIColor.WHITE, - sprite.getMinU(), sprite.getInterpolatedU(8), sprite.getInterpolatedV(16-8), sprite.getInterpolatedV(16)); - //Bottom-right corner - if(sides[2]&&sides[1]) - draw.drawTexColorRect(x+width-cornerSize, y+height-cornerSize, cornerSize, cornerSize, IIColor.WHITE, - sprite.getInterpolatedU(16-8), sprite.getInterpolatedU(16), sprite.getInterpolatedV(16-8), sprite.getInterpolatedV(16)); - } - - public static void drawFrame(IIDrawUtils draw, int x, int y, int width, int height, ResLoc style, boolean[] sides, int frameThickness) - { - TextureAtlasSprite sprite = ClientUtils.getSprite(style); - //x -= frameThickness/2; - //y -= frameThickness/2; -// width += frameThickness; -// height += frameThickness; - - //Top-Left mappings - float minU = sprite.getMinU(); - float minUU = sprite.getInterpolatedU(frameThickness/2f); - float minV = sprite.getMinV(); - float minVV = sprite.getInterpolatedV(frameThickness/2f); - //Bottom-Right mappings - float maxU = sprite.getInterpolatedU(16-frameThickness/2f); - float maxUU = sprite.getInterpolatedU(16); - float maxV = sprite.getInterpolatedV(16-frameThickness/2f); - float maxVV = sprite.getInterpolatedV(16); - - //Draw main frame - - //Top - if(sides[0]) - draw.drawRepeatedTexColorRect(x+frameThickness, y, width-frameThickness*2, frameThickness, IIColor.WHITE, - 32-2*frameThickness, frameThickness, minUU, maxU, minV, minVV); - //Bottom - if(sides[1]) - draw.drawRepeatedTexColorRect(x+frameThickness, y+height-frameThickness, width-frameThickness*2, frameThickness, IIColor.WHITE, - 32-2*frameThickness, frameThickness, minUU, maxU, maxV, maxVV); - //Left - if(sides[2]) - draw.drawRepeatedTexColorRect(x, y+frameThickness, frameThickness, height-frameThickness*2, IIColor.WHITE, - frameThickness, 32-2*frameThickness, minU, minUU, minVV, maxV); - //Right - if(sides[3]) - draw.drawRepeatedTexColorRect(x+width-frameThickness, y+frameThickness, frameThickness, height-frameThickness*2, IIColor.WHITE, - frameThickness, 32-2*frameThickness, maxU, maxUU, minVV, maxV); - - //Draw squares on frame edges - if(sides[0]||sides[3]) - draw.drawTexColorRect(x, y, frameThickness, frameThickness, IIColor.WHITE, - minU, minUU, minV, minVV); - if(sides[0]||sides[1]) - draw.drawTexColorRect(x+width-frameThickness, y, frameThickness, frameThickness, IIColor.WHITE, - maxU, maxUU, minV, minVV); - if(sides[2]||sides[3]) - draw.drawTexColorRect(x, y+height-frameThickness, frameThickness, frameThickness, IIColor.WHITE, - minU, minUU, maxV, maxVV); - if(sides[2]||sides[1]) - draw.drawTexColorRect(x+width-frameThickness, y+height-frameThickness, frameThickness, frameThickness, IIColor.WHITE, - maxU, maxUU, maxV, maxVV); - } - - - /** - * Draws a repeated texture in a rectangle, respecting corners - * - * @param draw draw utils instance - * @param texSize size of the texture to calculate corners from (it's drawn 1:1) - */ - public static void drawRepeatedRect(IIDrawUtils draw, int x, int y, int width, int height, - ResourceLocation spriteLocation, IIColor color, int texSize, int borderSize) - { - int iSize = Math.min(texSize-2*borderSize, Math.min(width, height)/2); - float tSize = (iSize/(float)texSize)*16; - float tStart = (borderSize/(float)texSize)*16; - - TextureAtlasSprite sprite = ClientUtils.getSprite(spriteLocation); - for(int yy = 0; yy < height; yy += iSize) - { - int drawHeight = Math.min(iSize, height-yy); - boolean isTop = yy==0; - boolean isBottom = yy+drawHeight >= height; - - for(int xx = 0; xx < width; xx += iSize) - { - int drawWidth = Math.min(iSize, width-xx); - boolean isLeft = xx==0; - boolean isRight = xx+drawWidth >= width; - - float texX = isLeft?0: (isRight?16-((drawWidth/(float)texSize)*16): tStart); - float texY = isTop?0: (isBottom?16-((drawHeight/(float)texSize)*16): tStart); - - draw.drawTexColorRect( - x+xx, y+yy, drawWidth, drawHeight, color, - sprite.getInterpolatedU(texX), sprite.getInterpolatedU(texX+(drawWidth/(float)texSize)*16), - sprite.getInterpolatedV(texY), sprite.getInterpolatedV(texY+(drawHeight/(float)texSize)*16) - ); - } - } - } /** * Sets the system clipboard string, with a fallback to Minecraft's clipboard handling @@ -391,4 +159,42 @@ public static EasyNBT getClipboardEasyNBT() return MathHelper.clamp((progress-startFraction)/duration, 0, 1); }; } + + /** + * Draws a repeated texture in a rectangle, respecting corners + * + * @param draw draw utils instance + * @param texSize size of the texture to calculate corners from (it's drawn 1:1) + */ + public static void drawRepeatedRect(IIDrawUtils draw, int x, int y, int width, int height, + ResourceLocation spriteLocation, IIColor color, int texSize, int borderSize) + { + int iSize = Math.min(texSize-2*borderSize, Math.min(width, height)/2); + float tSize = (iSize/(float)texSize)*16; + float tStart = (borderSize/(float)texSize)*16; + + TextureAtlasSprite sprite = ClientUtils.getSprite(spriteLocation); + for(int yy = 0; yy < height; yy += iSize) + { + int drawHeight = Math.min(iSize, height-yy); + boolean isTop = yy==0; + boolean isBottom = yy+drawHeight >= height; + + for(int xx = 0; xx < width; xx += iSize) + { + int drawWidth = Math.min(iSize, width-xx); + boolean isLeft = xx==0; + boolean isRight = xx+drawWidth >= width; + + float texX = isLeft?0: (isRight?16-((drawWidth/(float)texSize)*16): tStart); + float texY = isTop?0: (isBottom?16-((drawHeight/(float)texSize)*16): tStart); + + draw.drawTexColorRect( + x+xx, y+yy, drawWidth, drawHeight, color, + sprite.getInterpolatedU(texX), sprite.getInterpolatedU(texX+(drawWidth/(float)texSize)*16), + sprite.getInterpolatedV(texY), sprite.getInterpolatedV(texY+(drawHeight/(float)texSize)*16) + ); + } + } + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTemplates.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTemplates.java index 68fed6684..440b4c9e0 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTemplates.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTemplates.java @@ -11,6 +11,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent.DecoComponentTemplate; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoDropdown; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoElementDisplays.DecoElementSorter; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoEntryPanelBuilder; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoBar; @@ -20,7 +21,10 @@ import pl.pabilo8.immersiveintelligence.common.util.IIReference; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IDamageResistantMultiblock; +import javax.annotation.Nonnull; +import java.util.List; import java.util.function.Function; +import java.util.stream.Collectors; /** * Templates to be applied to {@link pl.pabilo8.immersiveintelligence.client.gui.deco.component.DecoComponent Deco Components} @@ -66,6 +70,18 @@ public class DecoTemplates .withIcon(DecoTextures.ICON_ACTION_EDIT) .withTranslatedTooltip(IIReference.GUI_TOOLTIP_KEY+"button.edit") ); + public static final DecoComponentTemplate ACTION_BUTTON_ACCEPT = ACTION_BUTTON.and( + component -> component + .withBackgroundColor(DecoColors.ACTION_ACCEPT) + .withIcon(DecoTextures.ICON_ACTION_ACCEPT) + .withTranslatedTooltip(IIReference.GUI_TOOLTIP_KEY+"button.accept") + ); + public static final DecoComponentTemplate ACTION_BUTTON_REJECT = ACTION_BUTTON.and( + component -> component + .withBackgroundColor(DecoColors.ACTION_REJECT) + .withIcon(DecoTextures.ICON_ACTION_REJECT) + .withTranslatedTooltip(IIReference.GUI_TOOLTIP_KEY+"button.reject") + ); //--- Mechanical Torque Bar ---// public static final Function> BAR_MECH_TORQUE = @@ -149,16 +165,15 @@ public class DecoTemplates .withDisplayFunction(new DecoEntryPanelBuilder() .withBackground(DecoTextures.BG_STEEL) .withHeight(12) - .withComponent("icon", new DecoImage(2, 1) + .withComponent("icon", p -> new DecoImage(2, 1) .withSize(8, 8) .withImageLocation(DecoTextures.COMPONENT_COLOR, true) .withUV(16, 4, 4, 12, 12) ) - .withLabel("label", - new DecoLabel(IIClientUtils.fontRegular, 12, 1) - .withSize(48, 12) - .withAlign(DecoAlignment.LEFT) - .withText("Core") + .withLabel("label", p -> new DecoLabel(IIClientUtils.fontRegular, 12, 1) + .withSize(48, 12) + .withAlign(DecoAlignment.LEFT) + .withText("Core") ) .withElementApplyMethod((dye, builder) -> { builder.component("icon", DecoImage.class).withColor(IIColor.fromDye(dye)); @@ -173,13 +188,12 @@ public static DecoEntryPanelBuilder> getDataTypeEntryDisplay() .withBackground(DecoTextures.BG_PAPER) .withBackgroundMask(DecoTextures.TEMPLATE_PAPER) //Type Icon, Label, and Letter - .withComponent("image", new DecoImage(3, 1) + .withComponent("image", p -> new DecoImage(3, 1) .withSize(16, 16)) - .withLabel("typeLabel", - new DecoLabel(IIClientUtils.fontRegular, 20, 1) - .withSize(48, 18) - .withAlign(DecoAlignment.LEFT) - .withText("Integer") + .withLabel("typeLabel", p -> new DecoLabel(IIClientUtils.fontRegular, 20, 1) + .withSize(48, 18) + .withAlign(DecoAlignment.LEFT) + .withText("Integer") ) .withElementApplyMethod((typeMeta, panel) -> { //type label (f.e. integer) @@ -190,6 +204,27 @@ public static DecoEntryPanelBuilder> getDataTypeEntryDisplay() panel.component("image", DecoImage.class) .withImageLocation(typeMeta.getTextureLocation(), true); }) - .withElementTooltip(typeMeta -> "a"); + .withElementTooltip(TypeMetaInfo::getTranslatedName); + } + + public static DecoElementSorter> getDataTypeEntrySorter() + { + return new DecoElementSorter>() + { + @Override + public List> sort(List> elements) + { + return elements; + } + + @Nonnull + @Override + public List> autocomplete(List> elements, String input) + { + return elements.stream() + .filter(e -> e.getTranslatedName().toLowerCase().contains(input.toLowerCase())) + .collect(Collectors.toList()); + } + }; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTextures.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTextures.java index b8bb74bff..b77bfd6df 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTextures.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoTextures.java @@ -15,6 +15,9 @@ */ public class DecoTextures { + //--- Special Textures ---// + public static final ResLoc TEXTURE_WHITE = IIReference.RES_IE.with("items/white"); + //--- Base Directories ---// public static final ResLoc RES_TEXTURES_DECO = ResLoc.of(IIReference.RES_II, "gui/deco/"); public static final ResLoc RES_TEXTURES_DECO_BACKGROUND = ResLoc.of(RES_TEXTURES_DECO, "background/"); @@ -31,6 +34,7 @@ public class DecoTextures public static final ResLoc BG_SANDBAGS = ResLoc.of(RES_TEXTURES_DECO_BACKGROUND, "sandbags"); public static final ResLoc BG_DARK = ResLoc.of(RES_TEXTURES_DECO_BACKGROUND, "dark"); public static final ResLoc BG_DARK_TANK = ResLoc.of(RES_TEXTURES_DECO_BACKGROUND, "dark_tank"); + public static final ResLoc BG_VANILLA = ResLoc.of(RES_TEXTURES_DECO_BACKGROUND, "vanilla"); //--- Frames ---// public static final ResLoc RES_TEXTURES_DECO_FRAME = ResLoc.of(RES_TEXTURES_DECO, "frame/"); @@ -65,6 +69,7 @@ public class DecoTextures public static final ResLoc LABEL_HAZARD = ResLoc.of(RES_TEXTURES_DECO, "label/label_hazard"); public static final ResLoc LABEL_PAPER = ResLoc.of(RES_TEXTURES_DECO, "label/label_paper"); public static final ResLoc LABEL_BLUEPRINT = ResLoc.of(RES_TEXTURES_DECO, "label/label_blueprint"); + public static final ResLoc LABEL_VANILLA = ResLoc.of(RES_TEXTURES_DECO, "label/label_vanilla"); //--- Deco Components ---// public static final ResLoc COMPONENT_BUTTON = ResLoc.of(RES_TEXTURES_DECO, "component/button"); @@ -141,6 +146,9 @@ public class DecoTextures public static final ResLoc ICON_ACTION_REMOVE = ResLoc.of(RES_TEXTURES_DECO_ICON, "action_remove"); public static final ResLoc ICON_ACTION_ADD = ResLoc.of(RES_TEXTURES_DECO_ICON, "action_add"); public static final ResLoc ICON_ACTION_CLEAR = ResLoc.of(RES_TEXTURES_DECO_ICON, "action_clear"); + public static final ResLoc ICON_ACTION_ACCEPT = ResLoc.of(RES_TEXTURES_DECO_ICON, "action_accept"); + public static final ResLoc ICON_ACTION_REJECT = ResLoc.of(RES_TEXTURES_DECO_ICON, "action_reject"); + public static final ResLoc ICON_ACTION_HELP = ResLoc.of(RES_TEXTURES_DECO_ICON, "action_help"); //--- Custom Deco Component Textures ---// public static final ResLoc COMPONENT_BUTTON_PAPER = ResLoc.of(RES_TEXTURES_DECO, "component/button_paper"); @@ -149,6 +157,7 @@ public class DecoTextures public static final ResLoc COMPONENT_ARROWS_PAPER = ResLoc.of(RES_TEXTURES_DECO, "component/arrows_paper"); public static final ResLoc COMPONENT_BUTTON_ROUND = ResLoc.of(RES_TEXTURES_DECO, "component/button_round"); public static final ResLoc COMPONENT_SLIDER_PAPER = ResLoc.of(RES_TEXTURES_DECO, "component/slider_paper"); + public static final ResLoc COMPONENT_SLIDER_VANILLA = ResLoc.of(RES_TEXTURES_DECO, "component/slider_vanilla"); public static final ResLoc COMPONENT_DROPDOWN_DATA_LETTER_PAPER = ResLoc.of(RES_TEXTURES_DECO, "component/data_letter_dropdown_paper"); public static final ResLoc COMPONENT_DROPDOWN_SYMBOL_PAPER = ResLoc.of(RES_TEXTURES_DECO, "component/dropdown_paper"); public static final ResLoc COMPONENT_TANK_PAPER = ResLoc.of(RES_TEXTURES_DECO, "component/tank_manual"); @@ -170,8 +179,8 @@ public class DecoTextures public static final ResLoc MAP_MARKER_RADAR = ResLoc.of(RES_TEXTURES_DECO, "map/marker/radar"); public static final ResLoc MAP_MARKER_RADIO_STATION = ResLoc.of(RES_TEXTURES_DECO, "map/marker/radio_station"); - //--- Special Textures ---// - public static final ResLoc TEXTURE_WHITE = IIReference.RES_IE.with("items/white"); + public static final ResLoc ICON_INVENTORY_FACTION_INVITES = ResLoc.of(RES_TEXTURES_DECO_ICON, "icon_faction_invites"); + public static final ResLoc ICON_INVENTORY_FACTION_INVITES_ACTIVE = ResLoc.of(RES_TEXTURES_DECO_ICON, "icon_faction_invites_active"); public static void registerAllTextures(TextureMap map) { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoVanillaGUIStyle.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoVanillaGUIStyle.java new file mode 100644 index 000000000..cdad4032b --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/DecoVanillaGUIStyle.java @@ -0,0 +1,15 @@ +package pl.pabilo8.immersiveintelligence.client.gui.deco.util; + + +/** + * Presets for vanilla GUIs, made because of the popular IE GUIs resource pack by Schaeferd. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 27.07.2026 + */ +public enum DecoVanillaGUIStyle +{ + VANILLA, + WOODEN +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/SlotStyle.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/SlotStyle.java new file mode 100644 index 000000000..d98747725 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/deco/util/SlotStyle.java @@ -0,0 +1,63 @@ +package pl.pabilo8.immersiveintelligence.client.gui.deco.util; + +import pl.pabilo8.immersiveintelligence.common.util.ResLoc; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 23.07.2026 + */ +public enum SlotStyle +{ + //Vanilla MC bevel mask inventory slot + VANILLA(DecoTextures.SLOT_VANILLA, true, 1), + //Immersive Engineering style inventory slot + IE(DecoTextures.SLOT_IE, DecoTextures.SLOT_IE_MARKER, 2), + IE_INPUT(IE, 2), + IE_OUTPUT(IE, 3), + IE_CUSTOM1(IE, 4), + IE_CUSTOM2(IE, 5), + IE_CUSTOM3(IE, 6), + IE_CUSTOM4(IE, 7), + //Brass Frame IE style Inventory slot + IE_BRASS(DecoTextures.SLOT_IE_BRASS, DecoTextures.SLOT_IE_BRASS_MARKER, 2), + IE_BRASS_INPUT(IE_BRASS, 2), + IE_BRASS_OUTPUT(IE_BRASS, 3), + IE_BRASS_CUSTOM1(IE_BRASS, 4), + IE_BRASS_CUSTOM2(IE_BRASS, 5), + IE_BRASS_CUSTOM3(IE_BRASS, 6), + IE_BRASS_CUSTOM4(IE_BRASS, 7), + ; + + final int borderSize; + final boolean blending; + final ResLoc backgroundLocation, markerLocation; + final int markerOffset; + + SlotStyle(ResLoc backgroundLocation, boolean blending, int borderSize) + { + this.blending = blending; + this.backgroundLocation = backgroundLocation; + this.markerLocation = null; + this.borderSize = borderSize; + this.markerOffset = -1; + } + + SlotStyle(ResLoc backgroundLocation, ResLoc markerLocation, int borderSize) + { + this.blending = false; + this.backgroundLocation = backgroundLocation; + this.markerLocation = markerLocation; + this.borderSize = borderSize; + this.markerOffset = -1; + } + + SlotStyle(SlotStyle base, int markerOffset) + { + this.blending = false; + this.backgroundLocation = base.backgroundLocation; + this.markerLocation = base.markerLocation; + this.borderSize = base.borderSize; + this.markerOffset = markerOffset; + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/entity/GuiEntityUpgrade.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/entity/GuiEntityUpgrade.java index 2d67789e2..f04a74a54 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/entity/GuiEntityUpgrade.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/entity/GuiEntityUpgrade.java @@ -14,6 +14,8 @@ import pl.pabilo8.immersiveintelligence.api.upgrade.UpgradeUtils.UpgradeOperation; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoEntityGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoButton; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTab; +import pl.pabilo8.immersiveintelligence.client.gui.deco.component.button.DecoTabGroup; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.collection.DecoTreeDisplay; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.label.DecoLabel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; @@ -25,7 +27,6 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.tree.upgrade.UpgradeTechTreeWrapper; import pl.pabilo8.immersiveintelligence.client.gui.deco.tree.upgrade.UpgradeTreeNodeRenderer; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.*; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.util.amt.models.AMTModel; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.gui.ContainerEntityUpgrade; @@ -51,15 +52,17 @@ public class GuiEntityUpgrade techTreeDisplay; private DecoPanel panelInfo; + private DecoTabGroup contentTabs; + private DecoTab infoTab; @SyncNBT(nullable = true) public String lastUpgrade; private DecoScenarioDisplay scenario; - public GuiEntityUpgrade(EntityPlayer player, T tile) + public GuiEntityUpgrade(EntityPlayer player, T entity) { - super(player, tile, IIGUI.UPGRADE_ENTITY); - this.techTree = tile!=null?UpgradeTechTree.getTreeFor(tile): null; + super(player, entity, IIGUI.UPGRADE_ENTITY); + this.techTree = entity!=null?UpgradeTechTree.getTreeFor(entity): null; } @Override @@ -95,7 +98,7 @@ public void onInit() .withInventoryTitleBar() .build(); - //Upgrade + //Upgrade preview addComponents( new DecoPanel(4, 4+8) .withSize(108, 152) @@ -107,47 +110,52 @@ public void onInit() .withScale(0.125f) .withRotation(-12.5f, 5) .withRotationAnimation(240, 0) - .withInteractionAllowed(true), - new DecoButton(118-4, 16-8-4+14-14+8) - .withSize(69, 14) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.tech_tree") - .withOnLMBPressed(() -> { - panelInfo.visible = panelInfo.enabled = false; - techTreeDisplay.visible = techTreeDisplay.enabled = true; - refreshModelPreview(null); - }), - new DecoButton(118-4+69, 16-8-4+14-14+8) - .withSize(69, 14) - .withBackground(DecoTextures.COMPONENT_TAB_VERTICAL) - .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.info") - .withOnLMBPressed(() -> { - panelInfo.visible = panelInfo.enabled = true; - techTreeDisplay.visible = techTreeDisplay.enabled = false; - if(lastUpgrade!=null&&!lastUpgrade.isEmpty()) - refreshModelPreview(Upgrade.getUpgradeByID(ResLoc.of(lastUpgrade))); - }), - panelInfo = new DecoPanel(118-4, 16-8-4+14+8) - .withSize(146-8, 146-8) - .withBackground(DecoTextures.BG_STEEL) - .withBackgroundMask(DecoTextures.TEMPLATE_SQUARE), - techTreeDisplay = new DecoTreeDisplay(118-4, 16-8-4+14+8) - .withTree(new UpgradeTechTreeWrapper(techTree, entity) - { - @Override - public void onNodeClicked(@Nonnull IDecoTreeNode node) - { - panelInfo.visible = panelInfo.enabled = true; - techTreeDisplay.visible = techTreeDisplay.enabled = false; - showUpgrade(node.getUserData()); - refreshModelPreview(node.getUserData()); - } - }) - .withNodeRenderer(new UpgradeTreeNodeRenderer()) - .withSize(146-8, 146-8) - .withBackground(DecoSprite.atlasSprite(DecoTextures.BG_DARK, 64)) + .withInteractionAllowed(true) ); + final int contentX = 118-4; + final int contentY = 16-8-4+14+8; + final int contentWidth = 146-8; + final int contentHeight = 146-8; + + panelInfo = addComponent(new DecoPanel(contentX, contentY) + .withSize(contentWidth, contentHeight) + .withBackground(DecoTextures.BG_STEEL) + .withBackgroundMask(DecoTextures.TEMPLATE_SQUARE)); + + DecoPanel techTreePanel = addComponent(new DecoPanel(contentX, contentY) + .withSize(contentWidth, contentHeight) + .withBackground(null) + .withBackgroundMask(null)); + techTreeDisplay = techTreePanel.addComponent(new DecoTreeDisplay(0, 0) + .withTree(new UpgradeTechTreeWrapper(techTree, entity) + { + @Override + public void onNodeClicked(@Nonnull IDecoTreeNode node) + { + contentTabs.selectTab(infoTab, false); + showUpgrade(node.getUserData()); + refreshModelPreview(node.getUserData()); + } + }) + .withNodeRenderer(new UpgradeTreeNodeRenderer()) + .withSize(contentWidth, contentHeight) + .withBackground(DecoSprite.atlasSprite(DecoTextures.BG_DARK, 64))); + + infoTab = (DecoTab)new DecoTab() + .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.info"); + contentTabs = addComponent(new DecoTabGroup(contentX, 16-8-4+14-14+8) + .withSize(contentWidth, 14) + .withHorizontalAlignment(true) + .withTabWidth(contentWidth/2) + .withTab((DecoTab)new DecoTab() + .withText(IIReference.DESCRIPTION_KEY+"upgrade_gui.tech_tree"), techTreePanel, + () -> refreshModelPreview(null)) + .withTab(infoTab, panelInfo, () -> { + if(lastUpgrade!=null&&!lastUpgrade.isEmpty()) + refreshModelPreview(Upgrade.getUpgradeByID(ResLoc.of(lastUpgrade))); + })); + if(lastUpgrade==null) { showUpgrade(null); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiCasingPouch.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiCasingPouch.java index deceefd8d..7f5e8972e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiCasingPouch.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiCasingPouch.java @@ -6,10 +6,10 @@ import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoItemGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.storage.DecoItemStackDisplay; -import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoBackgroundBuilder.SlotStyle; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoGuiCategory; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTextures; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.SlotStyle; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.gui.ContainerCasingPouch; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiPrintedPage.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiPrintedPage.java index c4b49943e..d3f8ece57 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiPrintedPage.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/gui/item/GuiPrintedPage.java @@ -44,8 +44,8 @@ public class GuiPrintedPage extends GuiScreen private static final Pattern patternUnderline = Pattern.compile("__(.+?)__"); private static final Pattern patternStrikethrough = Pattern.compile("~~(.+?)~~"); - private static final String PAGE_TEXTURE_PAGE = ImmersiveIntelligence.MODID + ":textures/gui/printed_page/page.png"; - private static final String PAGE_TEXTURE_BG = ImmersiveIntelligence.MODID + ":textures/gui/printed_page/"; + private static final String PAGE_TEXTURE_PAGE = ImmersiveIntelligence.MODID+":textures/gui/printed_page/page.png"; + private static final String PAGE_TEXTURE_BG = ImmersiveIntelligence.MODID+":textures/gui/printed_page/"; private static final ResourceLocation BOOK_GUI_TEXTURES = new ResourceLocation("textures/gui/book.png"); private int guiLeft = 0, guiTop = 0, topOffset = 0; private int currentPage = 0; @@ -70,20 +70,20 @@ public GuiPrintedPage(EntityPlayer player, ItemStack heldStack, EnumHand hand) public GuiPrintedPage(EntityPlayer player, ItemStack heldStack, EnumHand hand, boolean onlyTitle) { generalPageType = PageType.fromStack(heldStack); - pageTexture = PAGE_TEXTURE_BG + generalPageType.getUITextureName(heldStack); + pageTexture = PAGE_TEXTURE_BG+generalPageType.getUITextureName(heldStack); displayedPagesNum = generalPageType.getDisplayedPages(); initiatedFromHand = hand; author = ItemNBTHelper.getString(heldStack, "author"); title = ItemNBTHelper.getString(heldStack, "title"); - switch (generalPageType) + switch(generalPageType) { - // No display; No text + //No display; No text case BLANK: case LETTER: break; - // Single page display (TODO: currently all of them display just text, need to adjust later) + //Single page display (TODO: currently all of them display just text, need to adjust later) case TEXT: case CODE: case BLUEPRINT: @@ -100,38 +100,39 @@ public GuiPrintedPage(EntityPlayer player, ItemStack heldStack, EnumHand hand, b pageTypes = new PageType[]{ PageType.valueOf( - ItemNBTHelper.hasTag(heldStack) ? - ItemNBTHelper.getTag(heldStack).getString("type") : + ItemNBTHelper.hasTag(heldStack)? + ItemNBTHelper.getTag(heldStack).getString("type"): PageType.TEXT.toString() ) }; break; } - // Multiple Pages display + //Multiple Pages display case BOUND_PAGES: case NEWSPAPER: case BOOK: { - topOffset = - 10; - if (heldStack.hasTagCompound()) + topOffset = -10; + if(heldStack.hasTagCompound()) { NBTTagCompound nbttagcompound = heldStack.getTagCompound(); net.minecraft.nbt.NBTTagList pagesNBT = nbttagcompound.getTagList("pages", 10).copy(); int pagesLength = pagesNBT.tagCount(); - // If the title only is needed, we don't need to load any additional pages - if (onlyTitle) pagesLength = Math.min(pagesLength, displayedPagesNum); + //If the title only is needed, we don't need to load any additional pages + if(onlyTitle) pagesLength = Math.min(pagesLength, displayedPagesNum); - if (pagesLength < 1) + if(pagesLength < 1) { - // Has no data, create single empty page + //Has no data, create single empty page pages = new FormattedTextLine[1][]; pages[0] = new FormattedTextLine[]{}; pageTypes = new PageType[]{PageType.TEXT}; - } else + } + else { - // Create array of pages and page types + //Create array of pages and page types pages = new FormattedTextLine[pagesLength][]; pageTypes = new PageType[pagesLength]; @@ -143,19 +144,20 @@ public GuiPrintedPage(EntityPlayer player, ItemStack heldStack, EnumHand hand, b NBTTagCompound pageNBT = pagesNBT.getCompoundTagAt(index); pageTypes[index] = PageType.valueOf(pageNBT.getString("type")); pages[index] = prepareLines(pageNBT.getString("text")); - } catch (Exception e) + } catch(Exception e) { pages[index] = new FormattedTextLine[]{}; pageTypes[index] = PageType.TEXT; - } - finally + } finally { index += 1; } } } - } else { - // Has no data, create single empty page + } + else + { + //Has no data, create single empty page pages = new FormattedTextLine[1][]; pages[0] = new FormattedTextLine[]{}; pageTypes = new PageType[]{PageType.TEXT}; @@ -190,19 +192,20 @@ public void initGui() { super.initGui(); - guiLeft = (this.width-(149 * displayedPagesNum))/2; + guiLeft = (this.width-(149*displayedPagesNum))/2; guiTop = (this.height-196)/2; - int buttonLeft = (this.width - 188) / 2; - int buttonPagesOffset = 74 * (displayedPagesNum - 1); - this.buttonNextPage = this.addButton(new NextPageButton(1, buttonLeft + buttonPagesOffset + 141, guiTop + 182, true)); - this.buttonPreviousPage = this.addButton(new NextPageButton(2, buttonLeft - buttonPagesOffset + 20, guiTop + 182, false)); - this.buttonDone = this.addButton(new GuiButton(0, this.width / 2 - 100, guiTop + 220, 200, 20, I18n.format("gui.done", new Object[0]))); + int buttonLeft = (this.width-188)/2; + int buttonPagesOffset = 74*(displayedPagesNum-1); + this.buttonNextPage = this.addButton(new NextPageButton(1, buttonLeft+buttonPagesOffset+141, guiTop+182, true)); + this.buttonPreviousPage = this.addButton(new NextPageButton(2, buttonLeft-buttonPagesOffset+20, guiTop+182, false)); + this.buttonDone = this.addButton(new GuiButton(0, this.width/2-100, guiTop+220, 200, 20, I18n.format("gui.done", new Object[0]))); this.updateButtons(); } - private void updateButtons() { - buttonNextPage.visible = (currentPage + displayedPagesNum) < pages.length; + private void updateButtons() + { + buttonNextPage.visible = (currentPage+displayedPagesNum) < pages.length; buttonPreviousPage.visible = currentPage > 0; } @@ -219,46 +222,48 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { drawPageBackground(); int leftOffset = 0; - for (int renderedPage = currentPage; renderedPage < currentPage + displayedPagesNum; renderedPage += 1) + for(int renderedPage = currentPage; renderedPage < currentPage+displayedPagesNum; renderedPage += 1) { - try { + try + { drawPageContent(pages[renderedPage], pageTypes[renderedPage], leftOffset); - } catch (Exception r) {} + } catch(Exception r) {} leftOffset += 149; } drawPageForeground(); drawPageIndicator(); - if (generalPageType == PageType.NEWSPAPER) + if(generalPageType==PageType.NEWSPAPER) drawPageHeader(); super.drawScreen(mouseX, mouseY, partialTicks); } private void drawPageHeader() { - int leftLabel = (width - 220) / 2; - int rightLabel = (width + 220) / 2; + int leftLabel = (width-220)/2; + int rightLabel = (width+220)/2; - int titleWidthHalf = (this.fontRenderer.getStringWidth(title) / 2); - this.fontRenderer.drawString(title, leftLabel - titleWidthHalf, guiTop - 10, 0); + int titleWidthHalf = (this.fontRenderer.getStringWidth(title)/2); + this.fontRenderer.drawString(title, leftLabel-titleWidthHalf, guiTop-10, 0); - if (!StringUtils.isNullOrEmpty(author)) { + if(!StringUtils.isNullOrEmpty(author)) + { String authorLabel = net.minecraft.util.text.translation.I18n.translateToLocalFormatted("book.byAuthor", new Object[]{author}); - int labelWidthHalf = (this.fontRenderer.getStringWidth(authorLabel) / 2); - this.fontRenderer.drawString(authorLabel, rightLabel - labelWidthHalf, guiTop - 10, 0); + int labelWidthHalf = (this.fontRenderer.getStringWidth(authorLabel)/2); + this.fontRenderer.drawString(authorLabel, rightLabel-labelWidthHalf, guiTop-10, 0); } } private void drawPageIndicator() { - if (pages.length > 1) + if(pages.length > 1) { - int left = (width - 149 * (displayedPagesNum - 1)) / 2; - for (int labelledPage = currentPage; labelledPage < currentPage + displayedPagesNum; labelledPage++) + int left = (width-149*(displayedPagesNum-1))/2; + for(int labelledPage = currentPage; labelledPage < currentPage+displayedPagesNum; labelledPage++) { - String pagesLabel = String.valueOf(labelledPage + 1); - int labelWidthHalf = (this.fontRenderer.getStringWidth(pagesLabel) / 2); - this.fontRenderer.drawString(pagesLabel, left - labelWidthHalf, guiTop + 185, 0); + String pagesLabel = String.valueOf(labelledPage+1); + int labelWidthHalf = (this.fontRenderer.getStringWidth(pagesLabel)/2); + this.fontRenderer.drawString(pagesLabel, left-labelWidthHalf, guiTop+185, 0); left += 149; } } @@ -270,19 +275,19 @@ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOEx super.mouseClicked(mouseX, mouseY, mouseButton); Minecraft mc = Minecraft.getMinecraft(); - if (buttonNextPage.mousePressed(mc, mouseX, mouseY)) + if(buttonNextPage.mousePressed(mc, mouseX, mouseY)) { currentPage += displayedPagesNum; updateButtons(); } - if (buttonPreviousPage.mousePressed(mc, mouseX, mouseY)) + if(buttonPreviousPage.mousePressed(mc, mouseX, mouseY)) { currentPage -= displayedPagesNum; updateButtons(); } - if (buttonDone.mousePressed(mc, mouseX, mouseY)) + if(buttonDone.mousePressed(mc, mouseX, mouseY)) mc.displayGuiScreen(null); } @@ -293,11 +298,12 @@ public void drawTitlePage() { drawPageBackground(); int leftOffset = 0; - for (int renderedPage = 0; renderedPage < displayedPagesNum; renderedPage += 1) + for(int renderedPage = 0; renderedPage < displayedPagesNum; renderedPage += 1) { - try { + try + { drawPageContent(pages[renderedPage], pageTypes[renderedPage], leftOffset); - } catch (Exception r) {} + } catch(Exception r) {} leftOffset += 149; } @@ -306,10 +312,10 @@ public void drawTitlePage() public void drawPageBackground() { - // Draw the initial background + //Draw the initial background GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); ClientUtils.bindTexture(pageTexture); - switch (generalPageType) + switch(generalPageType) { case TEXT: case CODE: @@ -318,40 +324,42 @@ public void drawPageBackground() this.drawTexturedModalRect(guiLeft, guiTop, 0, 0, 146, 196); break; case BOUND_PAGES: - this.drawTexturedModalRect(guiLeft - 8, guiTop, 0, 0, 155, 207); + this.drawTexturedModalRect(guiLeft-8, guiTop, 0, 0, 155, 207); break; case NEWSPAPER: - this.drawTexturedModalRect(guiLeft - 5, guiTop - 22, 0, 0, 218, 226); - this.drawTexturedModalRect(guiLeft + 213, guiTop - 22, 20, 0, 31, 226); - this.drawTexturedModalRect(guiLeft + 244, guiTop - 22, 20, 0, 221, 226); + this.drawTexturedModalRect(guiLeft-5, guiTop-22, 0, 0, 218, 226); + this.drawTexturedModalRect(guiLeft+213, guiTop-22, 20, 0, 31, 226); + this.drawTexturedModalRect(guiLeft+244, guiTop-22, 20, 0, 221, 226); break; case BOOK: - this.drawTexturedModalRect(guiLeft - 6, guiTop - 1, 0, 0, 166, 217); - this.drawTexturedModalRectXMirrored(guiLeft + 160, guiTop - 1, 0, 0, 141, 217); + this.drawTexturedModalRect(guiLeft-6, guiTop-1, 0, 0, 166, 217); + this.drawTexturedModalRectXMirrored(guiLeft+160, guiTop-1, 0, 0, 141, 217); + break; + default: break; - default: break; } } - private void drawTexturedModalRectXMirrored(int x, int y, int textureX, int textureY, int width, int height) { + private void drawTexturedModalRectXMirrored(int x, int y, int textureX, int textureY, int width, int height) + { float f = 0.00390625F; float f1 = 0.00390625F; Tessellator tessellator = Tessellator.getInstance(); BufferBuilder bufferbuilder = tessellator.getBuffer(); bufferbuilder.begin(7, DefaultVertexFormats.POSITION_TEX); - bufferbuilder.pos((double)(x + 0), (double)(y + height), (double)this.zLevel).tex((double)((float)(textureX + width) * 0.00390625F), (double)((float)(textureY + height) * 0.00390625F)).endVertex(); - bufferbuilder.pos((double)(x + width), (double)(y + height), (double)this.zLevel).tex((double)((float)(textureX + 0) * 0.00390625F), (double)((float)(textureY + height) * 0.00390625F)).endVertex(); - bufferbuilder.pos((double)(x + width), (double)(y + 0), (double)this.zLevel).tex((double)((float)(textureX + 0) * 0.00390625F), (double)((float)(textureY + 0) * 0.00390625F)).endVertex(); - bufferbuilder.pos((double)(x + 0), (double)(y + 0), (double)this.zLevel).tex((double)((float)(textureX + width) * 0.00390625F), (double)((float)(textureY + 0) * 0.00390625F)).endVertex(); + bufferbuilder.pos((double)(x+0), (double)(y+height), (double)this.zLevel).tex((double)((float)(textureX+width)*0.00390625F), (double)((float)(textureY+height)*0.00390625F)).endVertex(); + bufferbuilder.pos((double)(x+width), (double)(y+height), (double)this.zLevel).tex((double)((float)(textureX+0)*0.00390625F), (double)((float)(textureY+height)*0.00390625F)).endVertex(); + bufferbuilder.pos((double)(x+width), (double)(y+0), (double)this.zLevel).tex((double)((float)(textureX+0)*0.00390625F), (double)((float)(textureY+0)*0.00390625F)).endVertex(); + bufferbuilder.pos((double)(x+0), (double)(y+0), (double)this.zLevel).tex((double)((float)(textureX+width)*0.00390625F), (double)((float)(textureY+0)*0.00390625F)).endVertex(); tessellator.draw(); } public void drawPageForeground() { - // The overlay should always go over the text. + //The overlay should always go over the text. GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); ClientUtils.bindTexture(pageTexture); - switch (generalPageType) + switch(generalPageType) { case TEXT: case CODE: @@ -360,13 +368,14 @@ public void drawPageForeground() this.drawTexturedModalRect(guiLeft, guiTop, 0, 196, 21, 19); break; case BOUND_PAGES: - this.drawTexturedModalRect(guiLeft - 7, guiTop, 155, 0, 14, 207); + this.drawTexturedModalRect(guiLeft-7, guiTop, 155, 0, 14, 207); break; case NEWSPAPER: break; case BOOK: break; - default: break; + default: + break; } } @@ -375,10 +384,18 @@ public void drawPageContent(FormattedTextLine[] lines, PageType pageType, int le int pageOffset = -1; int bgHeight = 1; - if (pageType == PageType.BLUEPRINT) {pageOffset = 0; bgHeight = 48;} - if (pageType == PageType.CODE) {pageOffset = 48; bgHeight = 36;} + if(pageType==PageType.BLUEPRINT) + { + pageOffset = 0; + bgHeight = 48; + } + if(pageType==PageType.CODE) + { + pageOffset = 48; + bgHeight = 36; + } - // Draw additional Page Background + //Draw additional Page Background if(pageOffset >= 0) { GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); @@ -386,16 +403,16 @@ public void drawPageContent(FormattedTextLine[] lines, PageType pageType, int le GlStateManager.enableBlend(); int cutoff_y = 179; int cutoff_x = 138; - for(int grid_y = 0; grid_y < 48 * 4; grid_y += bgHeight ) + for(int grid_y = 0; grid_y < 48*4; grid_y += bgHeight) { - for(int grid_x = 0; grid_x < 48 * 3; grid_x += 48 ) + for(int grid_x = 0; grid_x < 48*3; grid_x += 48) { this.drawTexturedModalRect( - guiLeft + leftOffset + grid_x + 4, - guiTop + topOffset + grid_y + 13, + guiLeft+leftOffset+grid_x+4, + guiTop+topOffset+grid_y+13, 146, pageOffset, - Math.min(48, cutoff_x - grid_x), - Math.min(bgHeight, cutoff_y - grid_y) + Math.min(48, cutoff_x-grid_x), + Math.min(bgHeight, cutoff_y-grid_y) ); } } @@ -406,10 +423,10 @@ public void drawPageContent(FormattedTextLine[] lines, PageType pageType, int le for(FormattedTextLine line : lines) { int lineHeight = (int)((line.font.getWordWrappedHeight(line.text, (int)(133/line.size)))*line.size); - if (y + lineHeight > 192) break; + if(y+lineHeight > 192) break; GlStateManager.pushMatrix(); - GlStateManager.translate(guiLeft + leftOffset + 8, guiTop + topOffset + y, 0); + GlStateManager.translate(guiLeft+leftOffset+8, guiTop+topOffset+y, 0); GlStateManager.scale(line.size, line.size, line.size); line.font.drawSplitString(line.text, 0, 0, (int)(133/line.size), DecoColors.H1.getPackedRGB()); y += lineHeight; @@ -528,16 +545,16 @@ public NextPageButton(int buttonId, int x, int y, boolean isForwardIn) public void drawButton(Minecraft mc, int mouseX, int mouseY, float partialTicks) { - if (this.visible) + if(this.visible) { - boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; + boolean flag = mouseX >= this.x&&mouseY >= this.y&&mouseX < this.x+this.width&&mouseY < this.y+this.height; GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); mc.getTextureManager().bindTexture(BOOK_GUI_TEXTURES); int i = 0; int j = 192; - if (flag) i += 23; - if (!this.isForward) j += 13; + if(flag) i += 23; + if(!this.isForward) j += 13; this.drawTexturedModalRect(this.x, this.y, i, j, 23, 13); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualEntry.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualEntry.java index 084afd7d2..67e2b1041 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualEntry.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualEntry.java @@ -8,7 +8,6 @@ import net.minecraft.client.resources.Language; import net.minecraft.client.resources.Locale; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.commons.io.IOUtils; @@ -177,4 +176,9 @@ public void setFolder(IIManualPageFolder folder) { this.folder = folder; } + + public String getFullName() + { + return fullFilePath; + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualPage.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualPage.java index 7a5a00cdd..4c9d09cf8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualPage.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/IIManualPage.java @@ -165,30 +165,36 @@ private String addLinks(String file) sub = ";"+entry.getSubPageID(link.substring(1)); link = this.entry.getName(); } - else if(link.contains("#")) //link to a subpage of another page + else { String[] split = link.split("#"); - if(split.length > 1) + List manualEntries = manual.manualContents.values().stream() + .filter(me -> { + if(me instanceof IIManualEntry&&((IIManualEntry)me).getFullName().equals(split[0])) + return true; + return me.getName().equals(split[0]); + }) + .collect(Collectors.toList()); + + //Get the link again, II's pages in folder may use a full path link + link = manualEntries.isEmpty()?split[0]: manualEntries.get(0).getName(); + + //Link to a subpage of another page + if(split.length > 1&&!manualEntries.isEmpty()) { - List manualEntries = manual.manualContents.values().stream() - .filter(me -> me.getName().equals(split[0])) - .collect(Collectors.toList()); - if(!manualEntries.isEmpty()) - { - IManualPage[] pages = manualEntries.get(0).getPages(); - link = split[0]; + IManualPage[] pages = manualEntries.get(0).getPages(); + link = split[0]; - for(int i = 0; i < pages.length; i++) + for(int i = 0; i < pages.length; i++) + { + IManualPage page = pages[i]; + if(page instanceof ManualPages) { - IManualPage page = pages[i]; - if(page instanceof ManualPages) + String pageName = ReflectionHelper.getPrivateValue(ManualPages.class, ((ManualPages)page), "text"); + if(pageName.equals(split[1])) { - String pageName = ReflectionHelper.getPrivateValue(ManualPages.class, ((ManualPages)page), "text"); - if(pageName.equals(split[1])) - { - sub = ";"+i; - break; - } + sub = ";"+i; + break; } } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryData.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryData.java index 2fe7859f6..91aeb8ecf 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryData.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryData.java @@ -1,6 +1,8 @@ package pl.pabilo8.immersiveintelligence.client.manual.categories; import blusunrize.immersiveengineering.api.crafting.BlueprintCraftingRecipe; +import blusunrize.immersiveengineering.api.crafting.IngredientStack; +import blusunrize.immersiveengineering.common.IEContent; import net.minecraft.item.ItemStack; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; import pl.pabilo8.immersiveintelligence.client.manual.IIManualCategory; @@ -8,8 +10,8 @@ import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.block.data_device.BlockIIDataDevice.IIBlockTypes_Connector; import pl.pabilo8.immersiveintelligence.common.block.metal_device.BlockIIMetalDevice.IIBlockTypes_MetalDevice; +import pl.pabilo8.immersiveintelligence.common.compat.ie.recipe.MetalPressRecipeAdapter; import pl.pabilo8.immersiveintelligence.common.crafting.IIRecipes; -import pl.pabilo8.immersiveintelligence.common.item.ItemIIPrintedPage.PageType; import pl.pabilo8.immersiveintelligence.common.item.crafting.ItemIIMaterial.Materials; import pl.pabilo8.immersiveintelligence.common.item.crafting.ItemIIPrecisionTool.PrecisionTools; import pl.pabilo8.immersiveintelligence.common.item.crafting.material.ItemIIMaterialDust.MaterialsDust; @@ -88,7 +90,6 @@ public void addPages() IIContent.itemMaterial.getStack(Materials.PROCESSOR_ELECTRONIC_ELEMENT) )) .addSource("processor_electronic_element", getSourceForItems( - IIContent.itemMaterial.getStack(Materials.PROCESSOR_ELECTRONIC_ELEMENT) )) .addSource("cryptographic_circuit_board", getSourceForItem(IIContent.itemMaterial.getStack(Materials.CRYPTOGRAPHIC_CIRCUIT_BOARD)) @@ -170,7 +171,8 @@ public void addPages() )); addEntry("radio_backpack"); addEntry("printing_press") - .addSource("paper_page", getSourceForItem(IIContent.itemPrintedPage.getStack(PageType.BLANK))); + .addSource("paper_page", getSourceForRecipe(MetalPressRecipeAdapter.class, + new IngredientStack("paper"), new ItemStack(IEContent.itemMold, 1, 0))); addEntry("scanning_conveyor"); addEntry("programmable_speaker") .addSource("programmable_spkr", getSourceForItem(IIContent.blockDataConnector.getStack(IIBlockTypes_Connector.PROGRAMMABLE_SPEAKER) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryLogistics.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryLogistics.java index 6c24055fe..8b2faa6a5 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryLogistics.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryLogistics.java @@ -1,7 +1,9 @@ package pl.pabilo8.immersiveintelligence.client.manual.categories; import net.minecraft.init.Blocks; +import net.minecraft.item.EnumDyeColor; import net.minecraft.item.ItemStack; +import pl.pabilo8.immersiveintelligence.api.LogisticTag; import pl.pabilo8.immersiveintelligence.api.crafting.SawmillRecipe; import pl.pabilo8.immersiveintelligence.client.manual.IIManualCategory; import pl.pabilo8.immersiveintelligence.common.IIContent; @@ -44,7 +46,14 @@ public void addPages() addEntry("logistics"); addEntry("packer"); - addEntry("task_system"); + addEntry("task_system") + .addSource("logitag_item", getSourceForItem(IIContent.itemLogisticTag.getStack(new LogisticTag() + .withDescription("Very important cargo") + .withColor(EnumDyeColor.BLUE) + .withOrigin("Engineer Republic") + .withDestination("Factory 43") + .withBatchNumber(1), + 1))); addEntry("inserters") .addSource("inserter_basic", getSourceForItem(IIContent.blockDataConnector.getStack(IIBlockTypes_Connector.INSERTER))) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryWarfare.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryWarfare.java index 3d54f842f..4c50b35ef 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryWarfare.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/manual/categories/IIManualCategoryWarfare.java @@ -17,9 +17,7 @@ import pl.pabilo8.immersiveintelligence.common.block.fortification.BlockIIMetalFortification1.IIBlockTypes_MetalFortification1; import pl.pabilo8.immersiveintelligence.common.block.fortification.BlockIISandbags.IIBlockTypes_Sandbags; import pl.pabilo8.immersiveintelligence.common.block.metal_device.BlockIIMetalDevice.IIBlockTypes_MetalDevice; -import pl.pabilo8.immersiveintelligence.common.block.mines.BlockIIMine; import pl.pabilo8.immersiveintelligence.common.block.mines.BlockIIMine.ItemBlockMineBase; -import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIAmmoBase; import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIAmmoCasing.Casing; import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIBulletMagazine.Magazines; import pl.pabilo8.immersiveintelligence.common.item.armor.ItemIIArmorUpgrade.ArmorUpgrades; @@ -164,7 +162,6 @@ public void addPages() .addSource("flippers", getSourceForItems(IIContent.itemArmorUpgrade.getStack(ArmorUpgrades.FLIPPERS))) .addSource("snow_rackets", getSourceForItems(IIContent.itemArmorUpgrade.getStack(ArmorUpgrades.SNOW_RACKETS))) .addSource("internal_springs", getSourceForItems(IIContent.itemArmorUpgrade.getStack(ArmorUpgrades.INTERNAL_SPRINGS))); - addEntry("armortools/flagpole"); addEntry("armortools/mine_detector") .addSource("mine_detector", getSourceForItem(new ItemStack(IIContent.itemMineDetector))); addEntry("armortools/trench_shovel") @@ -187,5 +184,9 @@ public void addPages() .addSource("navalmine", getSourceForItem(IIContent.itemNavalMine.getAmmoStack(IIContent.ammoCoreLead, CoreType.CANISTER, FuseType.CONTACT))); addEntry("staticdefense/explosive_mine_sign") .addSource("mine_sign", getSourceForItem(new ItemStack(IIContent.blockMineSign))); + + addEntry("terrain_control/owner_identity"); + addEntry("terrain_control/properties"); + addEntry("terrain_control/flagpole"); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/render/mechanical_device/WheelRenderer.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/render/mechanical_device/WheelRenderer.java index 4c2ba10c1..d36da4034 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/render/mechanical_device/WheelRenderer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/render/mechanical_device/WheelRenderer.java @@ -42,7 +42,7 @@ public void draw(TileEntityWheelBase te, BufferBuilder buf, float partialTicks, boolean clockwise = IIRotaryUtils.shouldRotateClockwise(te.facing); //Apply rotation - float progress = IIRotaryUtils.getDisplayRotation(te, te.getNetwork().getEnergyStorage(), partialTicks); + float progress = te.getDisplayedRotationProgress(false, partialTicks); (clockwise?rotationClockwise: rotationCounterCw).apply(progress); model.render(tes, buf); @@ -56,11 +56,15 @@ public void draw(TileEntityWheelBase te, BufferBuilder buf, float partialTicks, if(!shouldRenderConnection(te, connection)) continue; AMTChain chain = IIModelRegistry.INSTANCE.getMotorBeltConnectionModel(te, connection); - //Apply rotation - use same formula as the wheel so belt and wheel stay in sync - if(te.getNetwork().getNetworkSpeed() < 1) + //Apply rotation according to the distance travelled along the entire belt loop + double speed = te.getOutputSpeed(); + if(speed < 1) chain.setProgress(0); else - chain.setProgress(clockwise?(1f-progress): progress); + { + float beltProgress = te.getDisplayedRotationProgress(true, partialTicks); + chain.setProgress(clockwise?(1f-beltProgress): beltProgress); + } chain.render(tes, buf); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCartStationRenderer.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCartStationRenderer.java index 97a4de1f8..1bf3b25ef 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCartStationRenderer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCartStationRenderer.java @@ -65,7 +65,7 @@ public void render(TileEntitySkyCartStation te, double x, double y, double z, fl if(te.hasWorld()) { - motorTick = (te.getWorld().getTotalWorldTime()%IIRotaryUtils.getRPMMax()+partialTicks)/IIRotaryUtils.getRPMMax(); + motorTick = (te.getWorld().getTotalWorldTime()%IIRotaryUtils.getMaxWorldRotationTicks()+partialTicks)/IIRotaryUtils.getMaxWorldRotationTicks(); progress = te.progress+(partialTicks*IIRotaryUtils.getEffectiveEnergy(te.rotation, SkyCrateStation.speedMin, SkyCrateStation.speedEfficient, SkyCrateStation.torqueMin, SkyCrateStation.torqueEfficient)* IIRotaryUtils.getGearEfficiency(IIItemUtils.trimInventory(te.getInventory(), 0, 3))); @@ -110,20 +110,20 @@ public void render(TileEntitySkyCartStation te, double x, double y, double z, fl rpm_grab = animProgress <= 0.3f?60f: animProgress <= 0.5f?60f-(60f*((float)animProgress-0.3f)/0.2f): - animProgress <= 0.8d?0: - -60f; + animProgress <= 0.8d?0: + -60f; rpm_pitch = animProgress <= 0.3f?45f: animProgress <= 0.8f?-45f: - 45f; + 45f; inserterAngle = animProgress <= 0.3d?Math.min(0.5, animProgress/0.3*0.65d): animProgress <= 0.8d?0.65d-((animProgress-0.3d)/0.5d*1.65d): - -1.25d+((animProgress-0.8d)/0.2d*1.25d); + -1.25d+((animProgress-0.8d)/0.2d*1.25d); inserterLength = animProgress <= 0.3d?animProgress/0.3d: animProgress <= 0.5d?1d-(((animProgress-0.3d)/0.2d)*0.75): - animProgress <= 0.8d?0.25d: - (1d-((animProgress-0.8f)/0.3d))*0.25; + animProgress <= 0.8d?0.25d: + (1d-((animProgress-0.8f)/0.3d))*0.25; break; } @@ -134,28 +134,28 @@ public void render(TileEntitySkyCartStation te, double x, double y, double z, fl inserterAngle = animProgress <= 0.15d?animProgress/0.15*-1.25: animProgress <= 0.65d?-1.25+((animProgress-0.15)/0.5*2.35): - animProgress <= 0.6d?1.25-((animProgress-0.65)/0.1*0.75): - 0.65*(1f-((animProgress-0.75)/0.25)); + animProgress <= 0.6d?1.25-((animProgress-0.65)/0.1*0.75): + 0.65*(1f-((animProgress-0.75)/0.25)); inserterLength = animProgress <= 0.15?animProgress/0.15d*0.25: animProgress <= 0.65?0.25+((animProgress-0.15)/0.5*0.75): - animProgress <= 0.75?1: 1-((animProgress-0.75)/0.25); + animProgress <= 0.75?1: 1-((animProgress-0.75)/0.25); rpm_pitch = animProgress <= 0.15?-60f: animProgress <= 0.65?60f: - animProgress <= 0.75?-60f: -80f; + animProgress <= 0.75?-60f: -80f; rpm_grab = animProgress <= 0.15?35f: animProgress <= 0.65?70f: - animProgress <= 0.75?0f: -80f; + animProgress <= 0.75?0f: -80f; cratePusher = animProgress <= 0.75?0: animProgress <= 0.95?(animProgress-0.75)/0.2: - 1-((animProgress-0.95)/0.05); + 1-((animProgress-0.95)/0.05); rpm_crate = animProgress <= 0.75?0: animProgress <= 0.95?35: - 250; + 250; break; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCrateStationRenderer.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCrateStationRenderer.java index d8f7187d2..3acfcb783 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCrateStationRenderer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/render/multiblock/wooden/SkyCrateStationRenderer.java @@ -77,7 +77,7 @@ public void render(TileEntitySkyCrateStation te, double x, double y, double z, f if(te.hasWorld()) { - motorTick = (te.getWorld().getTotalWorldTime()%IIRotaryUtils.getRPMMax()+partialTicks)/IIRotaryUtils.getRPMMax(); + motorTick = (te.getWorld().getTotalWorldTime()%IIRotaryUtils.getMaxWorldRotationTicks()+partialTicks)/IIRotaryUtils.getMaxWorldRotationTicks(); progress = te.progress+(partialTicks*IIRotaryUtils.getEffectiveEnergy(te.rotation, SkyCrateStation.speedMin, SkyCrateStation.speedEfficient, SkyCrateStation.torqueMin, SkyCrateStation.torqueEfficient) *IIRotaryUtils.getGearEfficiency(IIItemUtils.trimInventory(te.getInventory(), 0, 3))); @@ -140,20 +140,20 @@ public void render(TileEntitySkyCrateStation te, double x, double y, double z, f rpm_grab = animProgress <= 0.3f?60f: animProgress <= 0.5f?60f-(60f*((float)animProgress-0.3f)/0.2f): - animProgress <= 0.8d?0: - -60f; + animProgress <= 0.8d?0: + -60f; rpm_pitch = animProgress <= 0.3f?45f: animProgress <= 0.8f?-45f: - 45f; + 45f; inserterAngle = animProgress <= 0.3d?Math.min(0.5, animProgress/0.3*0.65d): animProgress <= 0.8d?0.65d-((animProgress-0.3d)/0.5d*1.65d): - -1.25d+((animProgress-0.8d)/0.2d*1.25d); + -1.25d+((animProgress-0.8d)/0.2d*1.25d); inserterLength = animProgress <= 0.3d?animProgress/0.3d: animProgress <= 0.5d?1d-(((animProgress-0.3d)/0.2d)*0.75): - animProgress <= 0.8d?0.25d: - (1d-((animProgress-0.8f)/0.3d))*0.25; + animProgress <= 0.8d?0.25d: + (1d-((animProgress-0.8f)/0.3d))*0.25; break; } @@ -163,28 +163,28 @@ public void render(TileEntitySkyCrateStation te, double x, double y, double z, f inserterAngle = animProgress <= 0.15d?animProgress/0.15*-1: animProgress <= 0.65d?-1+((animProgress-0.15)/0.5*1.75): - animProgress <= 0.6d?1.75-((animProgress-0.65)/0.1*0.75): - 0.65*(1f-((animProgress-0.75)/0.25)); + animProgress <= 0.6d?1.75-((animProgress-0.65)/0.1*0.75): + 0.65*(1f-((animProgress-0.75)/0.25)); inserterLength = animProgress <= 0.15?animProgress/0.15d*0.25: animProgress <= 0.65?0.25+((animProgress-0.15)/0.5): - animProgress <= 0.75?1: 1-((animProgress-0.75)/0.25); + animProgress <= 0.75?1: 1-((animProgress-0.75)/0.25); rpm_pitch = animProgress <= 0.15?-60f: animProgress <= 0.65?60f: - animProgress <= 0.75?-60f: -80f; + animProgress <= 0.75?-60f: -80f; rpm_grab = animProgress <= 0.15?35f: animProgress <= 0.65?70f: - animProgress <= 0.75?0f: -80f; + animProgress <= 0.75?0f: -80f; cratePusher = animProgress <= 0.75?0: animProgress <= 0.95?(animProgress-0.75)/0.2: - 1-((animProgress-0.95)/0.05); + 1-((animProgress-0.95)/0.05); rpm_crate = animProgress <= 0.75?0: animProgress <= 0.95?35: - 250; + 250; break; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/ShaderUtil.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/ShaderUtil.java index 448c85866..d193d737c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/ShaderUtil.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/ShaderUtil.java @@ -36,6 +36,7 @@ public static void init() createShader(BLUEPRINT, null, "blueprint"); createShader(COLOR, null, "color"); createShader(NOISE, null, "noise"); + createShader(NOISE_NO_LIGHTMAP, null, "noise_no_lightmap"); createShader(GRAYSCALE, null, "grayscale"); } @@ -113,16 +114,34 @@ private static void createShader(Shaders shader, @Nullable String vert, @Nullabl { //Attempt loading the shader if(frag!=null) + { shader.fragID = createShader(frag, FRAG); + if(shader.fragID <= 0) + { + IILogger.error("Shader Error: Unable to load fragment shader %s (%s.frag)", shader.getName(), frag); + return; + } + } if(vert!=null) + { shader.vertID = createShader(vert, VERT); + if(shader.vertID <= 0) + { + IILogger.error("Shader Error: Unable to load vertex shader %s (%s.vert)", shader.getName(), frag); + return; + } + } //Create the program, its ID will be referenced when calling the shader shader.programID = ARBShaderObjects.glCreateProgramObjectARB(); //Unable to get a program ID if(shader.programID==0) + { + IILogger.error("Shader Error: Unable to create shader program for %s", shader.getName()); + shader.vertID = shader.fragID = 0; return; + } //Attach shader(s) to the program if(frag!=null) @@ -135,6 +154,7 @@ private static void createShader(Shaders shader, @Nullable String vert, @Nullabl if(ARBShaderObjects.glGetObjectParameteriARB(shader.programID, ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB)==GL11.GL_FALSE) { IILogger.error("Shader Error: "+getLogInfo(shader.programID)); + shader.vertID = shader.fragID = 0; return; } @@ -142,6 +162,8 @@ private static void createShader(Shaders shader, @Nullable String vert, @Nullabl if(ARBShaderObjects.glGetObjectParameteriARB(shader.programID, ARBShaderObjects.GL_OBJECT_VALIDATE_STATUS_ARB)==GL11.GL_FALSE) { IILogger.error("Shader Error: "+getLogInfo(shader.programID)); + shader.vertID = shader.fragID = 0; + return; } IILogger.info(String.format("Succesfully loaded shader '%s'", shader.getName())); @@ -166,7 +188,8 @@ private static int createShader(String filename, int shaderType) if(shader==0) return 0; - ARBShaderObjects.glShaderSourceARB(shader, readFileAsString(String.format("/assets/immersiveintelligence/shaders/%s.frag", filename))); + boolean vertex = shaderType==VERT; + ARBShaderObjects.glShaderSourceARB(shader, readFileAsString(String.format("/assets/immersiveintelligence/shaders/%s."+(vertex?"vert": "frag"), filename))); ARBShaderObjects.glCompileShaderARB(shader); if(ARBShaderObjects.glGetObjectParameteriARB(shader, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB)==GL11.GL_FALSE) @@ -221,6 +244,7 @@ public enum Shaders implements ISerializableEnum BLUEPRINT, COLOR, NOISE, + NOISE_NO_LIGHTMAP, GRAYSCALE; private int programID, fragID, vertID; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/models/AMTProgressModel.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/models/AMTProgressModel.java index 7f503f578..50b79749d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/models/AMTProgressModel.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/models/AMTProgressModel.java @@ -109,7 +109,7 @@ public void render(Tessellator tes, BufferBuilder buf) public AMT getPart(String name) { - return model.getPart(name); + return model.getPartRecursive(name); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/renderer/IIItemRendererAMT.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/renderer/IIItemRendererAMT.java index a6b6c10d4..a6c66737d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/renderer/IIItemRendererAMT.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/amt/renderer/IIItemRendererAMT.java @@ -17,6 +17,7 @@ import net.minecraft.util.EnumHand; import net.minecraft.world.World; import net.minecraftforge.client.model.obj.OBJModel; +import net.minecraftforge.common.config.Config.Comment; import pl.pabilo8.immersiveintelligence.client.model.IIModelRegistry; import pl.pabilo8.immersiveintelligence.client.model.item.ModelDualPerspective; import pl.pabilo8.immersiveintelligence.client.render.IReloadableModelContainer; @@ -180,7 +181,7 @@ protected final boolean is1stPerson(TransformType transform) return true; case THIRD_PERSON_RIGHT_HAND: case THIRD_PERSON_LEFT_HAND: - return Graphics.AMTHandDisplayMode==2; + return Graphics.modelHandDisplay==HandDisplayMode.FIRST_AND_THIRD_PERSON; default: return false; } @@ -207,4 +208,14 @@ protected float getItemEquipTime(EnumHand hand, float partialTicks) { String name(); } + + public enum HandDisplayMode + { + @Comment(value = "Hands will not be rendered with the item") + DISABLED, + @Comment(value = "Hands will be rendered only in first person") + FIRST_PERSON_ONLY, + @Comment(value = "Hands will be rendered in first and third person") + FIRST_AND_THIRD_PERSON + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/CompoundSound.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/CompoundSound.java index 70ac3c630..e4d94c895 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/CompoundSound.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/CompoundSound.java @@ -5,8 +5,10 @@ import net.minecraft.client.audio.ITickableSound; import net.minecraft.client.audio.PositionedSound; import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.entity.Entity; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -23,6 +25,7 @@ public abstract class CompoundSound extends PositionedSound implements ITickable { private final SoundEvent soundBegin; private final SoundEvent soundEnd; + private float maxRange = 0; public CompoundSound(MultiSound multiSound, SoundCategory category, Vec3d pos, float volume, float pitch) { @@ -38,6 +41,26 @@ public CompoundSound(MultiSound multiSound, SoundCategory category, Vec3d pos, f this.xPosF = (float)pos.x; this.yPosF = (float)pos.y; this.zPosF = (float)pos.z; + this.attenuationType = AttenuationType.LINEAR; + + this.repeatDelay = 0; + } + + public CompoundSound(SoundEvent event, SoundCategory category, Vec3d pos, float volume, float pitch) + { + super(event, category); + + this.soundBegin = null; + this.soundEnd = null; + repeat = true; + + this.pitch = pitch; + this.volume = volume; + + this.xPosF = (float)pos.x; + this.yPosF = (float)pos.y; + this.zPosF = (float)pos.z; + this.attenuationType = AttenuationType.LINEAR; this.repeatDelay = 0; } @@ -71,9 +94,83 @@ public void setPosition(Vec3d position) this.zPosF = (float)position.z; } + public void setMaxRange(float maxRange) + { + this.maxRange = maxRange <= 0?0: maxRange; + //When the range is undefined (0), attenuation is controlled by the sound system, otherwise it's calculated by the sound itself + this.attenuationType = this.maxRange==0?AttenuationType.LINEAR: AttenuationType.NONE; + } + + public float getMaxRange() + { + return maxRange; + } + public void start() { Minecraft.getMinecraft().getSoundHandler().playSound(this); } + @Override + public float getVolume() + { + if(this.maxRange==0) + return super.getVolume(); + + //Get camera entity + Entity entity = ClientUtils.mc().getRenderViewEntity(); + if(entity==null) + return super.getVolume(); + + //Calculate inverse square law attenuation based on distance to the sound source + float distance = (float)entity.getDistance(xPosF, yPosF, zPosF); + float normalizedDistance = 1f-MathHelper.clamp(distance/maxRange, 0f, 1f); + //Apply inverse square law + return (float)(super.getVolume()*normalizedDistance); + } + + @Override + public float getXPosF() + { + if(maxRange!=0) + { + //Get camera entity + Entity entity = ClientUtils.mc().getRenderViewEntity(); + if(entity==null) + return super.getXPosF(); + //Clamp X to nearest + return (float)(entity.posX+MathHelper.clamp(xPosF-(float)entity.posX, -1, 1)); + } + return super.getXPosF(); + } + + @Override + public float getYPosF() + { + if(maxRange!=0) + { + //Get camera entity + Entity entity = ClientUtils.mc().getRenderViewEntity(); + if(entity==null) + return super.getYPosF(); + //Clamp Y to nearest + return (float)(entity.posY+MathHelper.clamp(yPosF-(float)entity.posY, -1, 1)); + } + return super.getYPosF(); + } + + @Override + public float getZPosF() + { + if(maxRange!=0) + { + //Get camera entity + Entity entity = ClientUtils.mc().getRenderViewEntity(); + if(entity==null) + return super.getZPosF(); + //Clamp Z to nearest + return (float)(entity.posZ+MathHelper.clamp(zPosF-(float)entity.posZ, -1, 1)); + } + return super.getZPosF(); + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/ConditionCompoundSound.java b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/ConditionCompoundSound.java index 231315a48..4066131fe 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/ConditionCompoundSound.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/client/util/carversound/ConditionCompoundSound.java @@ -1,6 +1,7 @@ package pl.pabilo8.immersiveintelligence.client.util.carversound; import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; import net.minecraft.util.math.Vec3d; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -36,6 +37,20 @@ public ConditionCompoundSound(MultiSound multiSound, Vec3d pos, T controller, Fu start(); } + public ConditionCompoundSound(SoundEvent event, SoundCategory category, Vec3d pos, float volume, float pitch, T controller, Function shouldPlay) + { + super(event, category, pos, volume, pitch); + this.playingVolume = volume; + this.controller = controller; + this.shouldPlay = shouldPlay; + } + + public ConditionCompoundSound(SoundEvent multiSound, Vec3d pos, T controller, Function shouldPlay) + { + this(multiSound, SoundCategory.BLOCKS, pos, 1f, 1f, controller, shouldPlay); + start(); + } + @Override public boolean isDonePlaying() { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/CommonProxy.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/CommonProxy.java index 71983b428..fd5bf0e5f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/CommonProxy.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/CommonProxy.java @@ -62,10 +62,15 @@ import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.registries.IForgeRegistryModifiable; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; -import pl.pabilo8.immersiveintelligence.api.*; +import pl.pabilo8.immersiveintelligence.api.LighterFuelHandler; +import pl.pabilo8.immersiveintelligence.api.MachinegunCoolantHandler; +import pl.pabilo8.immersiveintelligence.api.ShrapnelHandler; +import pl.pabilo8.immersiveintelligence.api.VehicleFuelHandler; import pl.pabilo8.immersiveintelligence.api.ammo.AmmoRegistry; import pl.pabilo8.immersiveintelligence.api.ammo.PenetrationRegistry; import pl.pabilo8.immersiveintelligence.api.ammo.parts.IAmmoTypeItem; +import pl.pabilo8.immersiveintelligence.api.api.protection.CorrosionHandler; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.ProtectionCapabilities; import pl.pabilo8.immersiveintelligence.api.crafting.DustUtils; import pl.pabilo8.immersiveintelligence.api.data.IIDataOperationUtils; import pl.pabilo8.immersiveintelligence.api.data.IIDataTypeUtils; @@ -483,6 +488,7 @@ public void preInit(FMLPreInitializationEvent event) NBTSerialisation.preInit(); CapabilityRotaryEnergy.register(); + ProtectionCapabilities.register(); CapabilityChunkOwnership.register(); IEApi.prefixToIngotMap.put("spring", new Integer[]{2, 1}); @@ -823,10 +829,16 @@ public Object getServerGuiElement(int ID, EntityPlayer player, World world, int { IIGUI gui = IIGUI.values()[ID]; + if(gui.player) + return gui.containerFromPlayer==null?null: gui.containerFromPlayer.apply(player); if(gui.item) return gui.containerFromStack==null?null: gui.containerFromStack.apply(player, stack, hand); - - if(gui.teClass==null||gui.containerFromTile==null) + if(gui.entityClass!=null&&gui.containerFromEntity!=null) + { + if(gui.entityClass.isInstance(entity)) + return gui.containerFromEntity.apply(player, entity); + } + else if(gui.teClass==null||gui.containerFromTile==null) return null; else if(te instanceof IGuiTile&&gui.teClass.isInstance(te)) { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/EventHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/EventHandler.java index 77e506160..6b23d42c1 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/EventHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/EventHandler.java @@ -13,7 +13,7 @@ import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.ItemStack; -import net.minecraft.potion.PotionEffect; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.DamageSource; import net.minecraft.util.EntityDamageSourceIndirect; import net.minecraft.util.EnumHand; @@ -21,7 +21,6 @@ import net.minecraft.world.GameRules; import net.minecraft.world.GameRules.ValueType; import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.GameRuleChangeEvent; @@ -43,6 +42,7 @@ import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; import net.minecraftforge.fml.common.gameevent.TickEvent.WorldTickEvent; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; @@ -51,6 +51,10 @@ import pl.pabilo8.immersiveintelligence.api.ammo.penetration.DamageBlockPos; import pl.pabilo8.immersiveintelligence.api.ammo.utils.IIAmmoUtils; import pl.pabilo8.immersiveintelligence.api.ammo.utils.PenetrationCache; +import pl.pabilo8.immersiveintelligence.api.api.protection.RadiationHandler; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.IRadiationEmitter; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.ProtectionCapabilities; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.ProtectionCapabilityProvider; import pl.pabilo8.immersiveintelligence.api.upgrade.IUpgradableDevice; import pl.pabilo8.immersiveintelligence.api.utils.IAdvancedMultiblock; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Ammunition; @@ -99,6 +103,8 @@ public static void onSave(Save event) public static void onUnload(Unload event) { IISaveData.setDirty(); + if(!event.getWorld().isRemote) + RadiationHandler.INSTANCE.clearEmitterIndex(event.getWorld()); } @SubscribeEvent @@ -183,6 +189,24 @@ public void attachCapability(AttachCapabilitiesEvent event) } } + @SubscribeEvent + public void attachEntityCapability(AttachCapabilitiesEvent event) + { + if(event.getObject() instanceof IRadiationEmitter) + event.addCapability(ProtectionCapabilities.RADIATION_EMITTER_ID, + new ProtectionCapabilityProvider() + .with(ProtectionCapabilities.RADIATION_EMITTER, (IRadiationEmitter)event.getObject())); + } + + @SubscribeEvent + public void attachTileEntityCapability(AttachCapabilitiesEvent event) + { + if(event.getObject() instanceof IRadiationEmitter) + event.addCapability(ProtectionCapabilities.RADIATION_EMITTER_ID, + new ProtectionCapabilityProvider() + .with(ProtectionCapabilities.RADIATION_EMITTER, (IRadiationEmitter)event.getObject())); + } + private void initGamerule(String ruleName, GameRules rules, ValueType valueType, Object defaultValue) { @@ -231,6 +255,8 @@ public void onGameRuleChange(GameRuleChangeEvent event) public void onWorldTick(WorldTickEvent event) { pendingExplosions.removeIf(IIExplosion::explodeBlocks); + if(event.phase==Phase.END&&!event.world.isRemote) + RadiationHandler.INSTANCE.tick(event.world); } //--- Vehicle or Gun Mounts ---// @@ -345,32 +371,18 @@ public void onLivingUpdate(LivingUpdateEvent event) EntityLivingBase living = event.getEntityLiving(); World world = living.world; - if(!world.isRemote) + if(!world.isRemote||!(living instanceof EntityPlayer)) return; - Biome biome = world.getBiome(living.getPosition()); - if(living instanceof EntityPlayer) + EntityPlayer player = (EntityPlayer)living; + //Handle powerpack crafted with armor + if(!living.getItemStackFromSlot(EntityEquipmentSlot.CHEST).isEmpty() + &&ItemNBTHelper.hasKey(living.getItemStackFromSlot(EntityEquipmentSlot.CHEST), IIContent.NBT_AdvancedPowerpack)) { - EntityPlayer player = (EntityPlayer)living; - - //Potion effects - //Apply radiation - if(world.getTotalWorldTime()%20==0) - if(!player.isCreative()&&biome==IIContent.biomeWasteland) - living.addPotionEffect(new PotionEffect(IIPotions.radiation, 2000, 0, false, false)); - - //Handle powerpack crafted with armor - if(!living.getItemStackFromSlot(EntityEquipmentSlot.CHEST).isEmpty() - &&ItemNBTHelper.hasKey(living.getItemStackFromSlot(EntityEquipmentSlot.CHEST), IIContent.NBT_AdvancedPowerpack)) - { - ItemStack powerpack = ItemNBTHelper.getItemStack(living.getItemStackFromSlot(EntityEquipmentSlot.CHEST), IIContent.NBT_AdvancedPowerpack); - if(!powerpack.isEmpty()) - powerpack.getItem().onArmorTick(living.getEntityWorld(), player, powerpack); - } + ItemStack powerpack = ItemNBTHelper.getItemStack(living.getItemStackFromSlot(EntityEquipmentSlot.CHEST), IIContent.NBT_AdvancedPowerpack); + if(!powerpack.isEmpty()) + powerpack.getItem().onArmorTick(living.getEntityWorld(), player, powerpack); } - else if(world.getTotalWorldTime()%20==0&&biome==IIContent.biomeWasteland) - living.addPotionEffect(new PotionEffect(IIPotions.radiation, 2000, 0, false, false)); - } //--- Armor ---// @@ -442,18 +454,17 @@ else if(event.getSource()==DamageSource.FALL) @SubscribeEvent public void onLivingFallEvent(LivingFallEvent event) { - if(event.getEntityLiving() instanceof EntityPlayer) - { - EntityPlayer player = (EntityPlayer)event.getEntityLiving(); - Iterable armor = player.getArmorInventoryList(); + if(!(event.getEntityLiving() instanceof EntityPlayer)) + return; - for(ItemStack piece : armor) - { - if(!(piece.getItem() instanceof ItemIILightEngineerBoots)) continue; - ItemIILightEngineerBoots boots = (ItemIILightEngineerBoots)piece.getItem(); - if(boots.hasUpgrade(piece, "internal_springs")) - event.setDistance(0); - } + EntityPlayer player = (EntityPlayer)event.getEntityLiving(); + Iterable armor = player.getArmorInventoryList(); + for(ItemStack piece : armor) + { + if(!(piece.getItem() instanceof ItemIILightEngineerBoots)) continue; + ItemIILightEngineerBoots boots = (ItemIILightEngineerBoots)piece.getItem(); + if(boots.hasUpgrade(piece, "internal_springs")) + event.setDistance(0); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIConfigHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIConfigHandler.java index daa3dbcbe..9de2bf18b 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIConfigHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIConfigHandler.java @@ -12,6 +12,9 @@ import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; +import pl.pabilo8.immersiveintelligence.client.fx.utils.ParticleDetail; +import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoVanillaGUIStyle; +import pl.pabilo8.immersiveintelligence.client.util.amt.renderer.IIItemRendererAMT.HandDisplayMode; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.RadioStation; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.Sawmill; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Tools; @@ -77,6 +80,10 @@ public static void putConfigValues() @net.minecraftforge.common.config.Config(modid = ImmersiveIntelligence.MODID) public static class IIConfig { + @SubConfig + @LangKey("ii.config.Overrides") + @Comment("Toggle II's overwrites of Immersive Engineering's recipes, multiblocks and content.") + public static Overrides overrides; @SubConfig @LangKey("ii.config.Graphics") @Comment("Customize II 3d model display, AMT, particle effects and camera options.") @@ -124,19 +131,6 @@ public static class IIConfig @LangKey("Wires") public static int radioAdvancedMaxFrequency = 256; - @Comment({"Whether basic circuits should be produced in II or IE way"}) - @RequiresMcRestart - public static boolean changeCircuitProduction = true; - - @Comment({"Whether the IE revolver should be a Early Engineering-tier weapon"}) - public static boolean changeRevolverProduction = true; - - @Comment({"Whether the the railgun should require a gun stock instead of a grip to be constructed"}) - public static boolean changeRailgunProduction = true; - - @Comment({"Whether the the chemthrower should require a gun stock instead of a grip to be constructed"}) - public static boolean changeChemthrowerProduction = true; - @Comment({"Whether Tungsten should be smeltable in the vanilla furnace"}) @RequiresMcRestart public static boolean smeltableTungsten = false; @@ -145,13 +139,6 @@ public static class IIConfig @RequiresMcRestart public static boolean smeltableAEA = false; - @Comment({"Whether Immersive Engineering liquid concrete behavior should be replaced by II."}) - @RequiresMcRestart - public static boolean concreteOverride = true; - - @Comment({"If disabled, II will not make any changes to IE villager trades."}) - public static boolean enableTradeOverride = true; - @Comment({"A list of all entities for which a fakeplayer should be used when shooter is not a player"}) public static String[] bulletFakeplayerWhitelist = new String[]{ "minecraft:ender_dragon" @@ -167,6 +154,56 @@ public static class IIConfig @RequiresMcRestart public static boolean australianCreativeTabs = true; + public static class Overrides + { + @Comment({"Whether basic circuits should be produced in II or IE way"}) + @RequiresMcRestart + public static boolean changeCircuitProduction = true; + + @Comment({"Whether the IE revolver should be a Early Engineering-tier weapon"}) + @RequiresMcRestart + public static boolean changeRevolverProduction = true; + + @Comment({"Whether the the chemthrower should require a gun stock instead of a grip to be constructed"}) + @RequiresMcRestart + public static boolean changeChemthrowerProduction = true; + + @Comment({"Whether Immersive Engineering liquid concrete behavior should be replaced by II."}) + @RequiresMcRestart + public static boolean concreteOverride = true; + + @Comment({"If disabled, II will not make any changes to IE villager trades."}) + @RequiresMcRestart + public static boolean enableTradeOverride = true; + + @Comment({"If enabled, II will replace compatible Immersive Engineering's GUIs with Deco-based ones."}) + @RequiresMcRestart + public static boolean enableDecoOverride = false; + + @SubConfig + @LangKey("desc.immersiveintelligence.toolupgrade.item.railgun") + @Comment("Config for the Railgun, allows for the toggling of II related features, such as recoil and penetration") + public static Railgun railgun; + + public static class Railgun + { + @Comment({"If disabled, II will not make any changes to IE railgun. This also disables using railgun grenades (as they use a custom entity)."}) + public static boolean enableRailgunOverride = true; + + @Comment({"Make standard railgun rods to be able to penetrate mobs (depending on metal)."}) + public static boolean enablePenetration = true; + + @Comment({"Whether the railgun has recoil (pushes the shooter to back, depending on projectile mass)."}) + public static boolean railgunRecoil = true; + + @Comment({"Whether the railgun can only be used when in mainhand."}) + public static boolean disableRailgunOffhand = true; + + @Comment({"Whether the the railgun should require a gun stock instead of a grip to be constructed"}) + public static boolean changeRailgunProduction = true; + } + } + public static class Graphics { @Comment({"Enable vehicle and equipment passenger animations by changing the passengers' entity model part angles."}) @@ -191,39 +228,50 @@ public static class Graphics "0 - disabled", "1 - first person only", "2 - 1st and 3rd person (may be incompatible with mods modifying the player model)"}) - @RangeInt(min = 0, max = 2) - public static int AMTHandDisplayMode = 1; + public static HandDisplayMode modelHandDisplay = HandDisplayMode.FIRST_PERSON_ONLY; @RequiresMcRestart @Comment({"Max amount of block penetrations that will be rendered. 0 will disable rendering."}) + @RangeInt(min = 0, max = 65345) public static int maxPenetratedBlocks = 64; @Comment({"Furthest distance II explosion effects should be visible at."}) + @RangeInt(min = 1, max = 65345) public static int explosionMessageDistance = 256; - @RangeInt(min = 0) + @RangeInt(min = 0, max = 65345) @Comment({"Max amount of particles that can exist within the particle system."}) public static int maxAllowedParticles = 20000; - @RangeInt(min = 0) + @RangeInt(min = 0, max = 65345) @Comment({"Max amount of particles that will be simulated."}) public static int maxSimulatedParticles = 6000; - @RangeInt(min = 0) + @RangeInt(min = 0, max = 65345) @Comment({"Max amount of particles that will be drawn. Should be less or equal to maxSimulatedParticles."}) public static int maxDrawnParticles = 1000; - @Comment({"Determines how look of II explosion particles", - "0 - vanilla", - "1 - vanilla enhanced with block particles", - "2 - overhauled", - "3 - overhauled + debris" - }) - @RangeInt(min = 0, max = 3) - public static int explosionParticlesStyle = 3; + @Comment({"Determines the look of II explosion particles", + "The final value will be this or the Particles option from Video Settings, whichever is lower."}) + public static ParticleDetail explosionParticlesDetail = ParticleDetail.DETAILED; + + @Comment({"Determines the look of II explosion particles", + "The final value will be this or the Particles option from Video Settings, whichever is lower."}) + public static ParticleDetail explosionDebrisDetail = ParticleDetail.DETAILED; + + @Comment({"Determines the look of II nuclear explosion particles", + "The final value will be this or the Particles option from Video Settings, whichever is lower."}) + public static ParticleDetail nukeParticlesDetail = ParticleDetail.DETAILED; @RangeInt(min = 8, max = 256) + @SlidingOption public static int dynamiclyColoredTextureVariants = 64; + + @Comment({"Enables longer tooltip descriptions for link tabs in Deco based GUIs"}) + public static boolean decoLongTabTooltips = true; + + @Comment({"Determines what style should vanilla-styled GUIs, like faction invitation look like"}) + public static DecoVanillaGUIStyle decoVanillaGUIStyle = DecoVanillaGUIStyle.VANILLA; } public static class Ores @@ -666,6 +714,13 @@ public static class Machines @Comment("Config for the Vehicle Workshop, allows for changes to energy and fuel capacity") public static VehicleWorkshop vehicleWorkshop; + @Comment({"The interval (in ticks) at which the multiblock machines will synchronize recipes with the client.", + "Setting to 0 will disable this feature.", + "Regardless of this setting, machines always synchronize them upon recipe change and when opening their GUI." + }) + @RangeInt(min = 0) + public static int recipeUpdateInterval = 200; + public static class RedstoneInterface { @@ -674,13 +729,13 @@ public static class RedstoneInterface public static class AlarmSiren { @Comment({"The distance the siren can be heard from."}) - public static int soundRange = 16; + public static int soundRange = 36; } public static class ProgrammableSpeaker { @Comment({"The distance the speaker can be heard from."}) - public static int soundRange = 24; + public static int soundRange = 36; } public static class Filler @@ -867,7 +922,7 @@ public static class DataInputMachine public static int energyCapacity = 16000; @Comment({"Energy usage when sending a signal."}) - public static int energyUsage = 2048; + public static int energyUsage = 1024; @Comment({"Energy per step of punching a tape (1/60 of the full energy needed)."}) public static int energyUsagePunchtape = 128; @@ -1288,10 +1343,6 @@ public static class Weapons @Comment("Config for Emplacement weapons, allows for the adjustment of fire rate, detection radius, movement speed and health") public static EmplacementWeapons emplacementWeapons; @SubConfig - @LangKey("desc.immersiveintelligence.toolupgrade.item.railgun") - @Comment("Config for the Railgun, allows for the toggling of II related features, such as recoil and penetration") - public static Railgun railgun; - @SubConfig @LangKey("ii.config.Grenade") @Comment("Config for Grenades, such as throwing speed") public static Grenade grenade; @@ -1576,21 +1627,6 @@ public static class HeavyRailgun } } - public static class Railgun - { - @Comment({"If disabled, II will not make any changes to IE railgun. This also disables using railgun grenades (as they use a custom entity)."}) - public static boolean enableRailgunOverride = true; - - @Comment({"Make standard railgun rods to be able to penetrate mobs (depending on metal)."}) - public static boolean enablePenetration = true; - - @Comment({"Whether the railgun has recoil (pushes the shooter to back, depending on projectile mass)."}) - public static boolean railgunRecoil = true; - - @Comment({"Whether the railgun can only be used when in mainhand."}) - public static boolean disableRailgunOffhand = true; - } - public static class Grenade { @Comment({ @@ -2054,6 +2090,12 @@ public static class Factions @RequiresMcRestart @Comment({"Determines how often properties try to claim surrounding chunks. (in ticks)"}) public static int claimTickDelay = 200; + + @Comment({"The position of the faction invites button in player's inventory screen (x,y)", "Set to -1,-1 to disable the button."}) + public static int[] inventoryButtonPosition = new int[]{61, 64}; + + @Comment({"The position of the faction invites button in player's creative inventory screen (x,y)", "Set to -1,-1 to disable the button."}) + public static int[] inventoryButtonPositionCreative = new int[]{92, 34}; } public static class MechanicalDevices diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIContent.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIContent.java index 19a1c902f..eda6a5ada 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIContent.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIContent.java @@ -75,6 +75,7 @@ import pl.pabilo8.immersiveintelligence.common.item.tools.backpack.ItemIIAdvancedPowerPack; import pl.pabilo8.immersiveintelligence.common.item.weapons.*; import pl.pabilo8.immersiveintelligence.common.util.IBatchOredictRegister; +import pl.pabilo8.immersiveintelligence.common.util.advancements.UpgradeTrigger; import pl.pabilo8.immersiveintelligence.common.util.block.BlockIIFluid; import pl.pabilo8.immersiveintelligence.common.util.block.BlockIISlab; import pl.pabilo8.immersiveintelligence.common.util.block.BlockIIStairs; @@ -100,6 +101,7 @@ public class IIContent public static final List MULTIBLOCKS = new ArrayList<>(); public static final IICreativeTab II_CREATIVE_TAB = new IICreativeTab(MODID); + public static final UpgradeTrigger UPGRADE_TRIGGER = UpgradeTrigger.INSTANCE; //--- Upgrades ---// //allows filling items with fluids @@ -311,6 +313,8 @@ public class IIContent public static final ItemIIPrintedPage itemPrintedPage = new ItemIIPrintedPage(); public static final ItemIILogisticTag itemLogisticTag = new ItemIILogisticTag(); public static final ItemIITracerPowder itemTracerPowder = new ItemIITracerPowder(); + //Icon Placeholder Item + public static final ItemIIPlaceholderIcon itemPlaceholderIcon = new ItemIIPlaceholderIcon(); //rubber public static final BlockIIRubberLog blockRubberLog = new BlockIIRubberLog(); @@ -333,6 +337,9 @@ public class IIContent public static final BlockIISandbags blockSandbags = new BlockIISandbags(); public static final BlockIIClothDecoration blockClothDecoration = new BlockIIClothDecoration(); public static final BlockIIMetalDecoration blockMetalDecoration = new BlockIIMetalDecoration(); + //harbor decorations + public static final BlockIIHarbor blockHarbor = new BlockIIHarbor(); + public static final BlockIIHarborSupport blockHarborSupport = new BlockIIHarborSupport(); //b e t o n public static final BlockIIConcreteDecoration blockConcreteDecoration = new BlockIIConcreteDecoration(); public static final BlockIISlab blockConcreteSlabs = new BlockIISlab<>(blockConcreteDecoration); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIGUI.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIGUI.java index 3753cbb75..5bdabf252 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIGUI.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIGUI.java @@ -14,6 +14,7 @@ import net.minecraftforge.fml.common.Optional.Method; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import pl.pabilo8.immersiveintelligence.client.gui.GuiFactionInvitation; import pl.pabilo8.immersiveintelligence.client.gui.block.*; import pl.pabilo8.immersiveintelligence.client.gui.block.ammunition_production.GuiAmmunitionAssembler; import pl.pabilo8.immersiveintelligence.client.gui.block.arithmetic_logic_machine.GuiArithmeticLogicMachine; @@ -40,6 +41,7 @@ import pl.pabilo8.immersiveintelligence.client.gui.block.radar.GuiRadarTargets; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoGui.DecoResourcesLoader; +import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoPlayerGui; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoResource; import pl.pabilo8.immersiveintelligence.client.gui.deco.util.DecoTemplate; import pl.pabilo8.immersiveintelligence.client.gui.entity.GuiEntityUpgrade; @@ -74,6 +76,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.BiFunction; +import java.util.function.Function; /** * @author Pabilo8 (pabilo@iiteam.net) @@ -173,21 +176,25 @@ public enum IIGUI implements ISerializableEnum RADAR(TileEntityRadar.class, ContainerRadar::new), RADAR_CONFIG(TileEntityRadar.class, ContainerRadar::new), - RADAR_TARGETS(TileEntityRadar.class, ContainerRadar::new); + RADAR_TARGETS(TileEntityRadar.class, ContainerRadar::new), + FACTION_INVITATIONS(ContainerPlayerGui::new); public final Class teClass; public final Class entityClass; public final BiFunction containerFromTile; public final BiFunction containerFromEntity; public final TriFunction containerFromStack; - public boolean item; + public final Function containerFromPlayer; + public boolean item, player; @SideOnly(Side.CLIENT) public BiFunction guiFromTile; @SideOnly(Side.CLIENT) public TriFunction guiFromStack; @SideOnly(Side.CLIENT) - private BiFunction guiFromEntity; + public Function guiFromPlayer; + @SideOnly(Side.CLIENT) + public BiFunction guiFromEntity; //Required for JEI @SideOnly(Side.CLIENT) public Class> guiClass; @@ -212,12 +219,15 @@ else if(Entity.class.isAssignableFrom(teClass)) //noinspection unchecked this.entityClass = (Class)teClass; this.containerFromTile = null; + //noinspection unchecked this.containerFromEntity = (player, entity) -> containerFunction.apply(player, (T)entity); } else throw new IllegalArgumentException("Invalid GUI subject class: "+teClass); this.containerFromStack = null; + this.containerFromPlayer = null; this.item = false; + this.player = false; } /** @@ -230,7 +240,9 @@ else if(Entity.class.isAssignableFrom(teClass)) this.containerFromTile = null; this.containerFromEntity = null; this.containerFromStack = containerFromStack; + this.containerFromPlayer = null; this.item = true; + this.player = false; } /** @@ -243,7 +255,24 @@ else if(Entity.class.isAssignableFrom(teClass)) this.containerFromTile = null; this.containerFromEntity = null; this.containerFromStack = null; + this.containerFromPlayer = null; this.item = true; + this.player = false; + } + + /** + * Player-context GUI constructor. + */ + IIGUI(@Nonnull Function containerFromPlayer) + { + this.teClass = null; + this.entityClass = null; + this.containerFromTile = null; + this.containerFromEntity = null; + this.containerFromStack = null; + this.containerFromPlayer = containerFromPlayer; + this.item = false; + this.player = true; } @SideOnly(Side.CLIENT) @@ -325,6 +354,7 @@ public static void initClientGUIs() IIGUI.RADAR_TARGETS.setClientTileGui(GuiRadarTargets::new); IIGUI.COAGULATOR.setClientTileGui(GuiCoagulator::new); IIGUI.VULCANIZER.setClientTileGui(GuiVulcanizer::new); + IIGUI.FACTION_INVITATIONS.setClientPlayerGui(GuiFactionInvitation::new); } @SideOnly(Side.CLIENT) @@ -371,6 +401,41 @@ else if(value instanceof String) } + @SideOnly(Side.CLIENT) + @SuppressWarnings("unchecked") + public void setClientPlayerGui(Function> guiFromPlayer) + { + Class> klass = (Class>)guiFromPlayer.apply(null).getClass(); + this.guiFromPlayer = guiFromPlayer::apply; + this.guiClass = klass; + + DecoTemplate annotation = klass.getAnnotation(DecoTemplate.class); + if(annotation==null) + { + IILogger.error("GUI class "+klass.getName()+" is missing @DecoTemplate annotation!"); + return; + } + + List resources = new ArrayList<>(); + for(Field field : klass.getFields()) + if(field.isAnnotationPresent(DecoResource.class)&&Modifier.isStatic(field.getModifiers())) + try + { + Object value = field.get(null); + if(value instanceof ResLoc) + resources.add((ResLoc)value); + else if(value instanceof ResourceLocation) + resources.add(ResLoc.of((ResourceLocation)value)); + else if(value instanceof String) + resources.add(ResLoc.of((String)value)); + } catch(IllegalAccessException e) + { + IILogger.error("Failed to access field "+field.getName()+" in class "+klass.getName(), e); + } + if(!resources.isEmpty()) + new DecoResourcesLoader(this.getName().replace("gui_", ""), resources); + } + @SideOnly(Side.CLIENT) public void setClientStackGui(TriFunction guiFromStack) { @@ -379,9 +444,11 @@ public void setClientStackGui(TriFunction> void setClientEntityGui(BiFunction> guiFromEntity) { this.guiFromEntity = (player, entity) -> guiFromEntity.apply(player, (T)entity); + this.guiClass = (Class>)guiFromEntity.apply(null, null).getClass(); this.item = false; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIPotions.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIPotions.java index f860fea10..596f0c171 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/IIPotions.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/IIPotions.java @@ -16,15 +16,18 @@ import net.minecraft.potion.PotionEffect; import net.minecraft.util.ResourceLocation; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; -import pl.pabilo8.immersiveintelligence.api.CorrosionHandler; -import pl.pabilo8.immersiveintelligence.api.utils.armor.IRadiationProtectionEquipment; +import pl.pabilo8.immersiveintelligence.api.api.protection.CorrosionHandler; +import pl.pabilo8.immersiveintelligence.api.api.protection.ProtectionHandler; import pl.pabilo8.immersiveintelligence.common.util.IIDamageSources; import java.util.ArrayList; import java.util.List; /** + * Registers and stores II potion information. + * * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 * @since 03.03.2020 */ public class IIPotions @@ -150,18 +153,10 @@ public void performEffect(EntityLivingBase living, int amplifier) { if(living.ticksExisted%20!=0) return; - boolean apply = false; - for(ItemStack s : living.getArmorInventoryList()) - { - if(!(s.getItem() instanceof IRadiationProtectionEquipment)) - apply = true; - else if(!((IRadiationProtectionEquipment)s.getItem()).protectsFromRadiation(s)) - apply = true; - } - if(apply) + if(!ProtectionHandler.isProtectedFromRadiation(living)) { living.hurtResistantTime = 0; - living.attackEntityFrom(IIDamageSources.RADIATION_DAMAGE, 2); + living.attackEntityFrom(IIDamageSources.RADIATION_DAMAGE, 2f*(amplifier+1)); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/IISaveData.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/IISaveData.java index 08874ae39..411732916 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/IISaveData.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/IISaveData.java @@ -7,6 +7,7 @@ import net.minecraftforge.fml.relauncher.Side; import pl.pabilo8.immersiveintelligence.api.ammo.penetration.DamageBlockPos; import pl.pabilo8.immersiveintelligence.api.ammo.utils.PenetrationCache; +import pl.pabilo8.immersiveintelligence.api.api.protection.RadiationHandler; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler; import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; @@ -61,6 +62,7 @@ public void readFromNBT(NBTTagCompound nbt) } DiplomacyHandler.getInstance(false).loadAllFromNBT(enbt.getEasyCompound("diplomacy")); + RadiationHandler.INSTANCE.deserializeNBT(enbt.getCompound("radiation")); } @Override @@ -71,6 +73,7 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) e.getX(), e.getY(), e.getZ(), e.dimension, (int)(e.damage*16) }), PenetrationCache.blockDamage) .withTag("diplomacy", DiplomacyHandler.getInstance(false).saveAllToNBT()) + .withTag("radiation", RadiationHandler.INSTANCE.serializeNBT()) .unwrap(); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/ammo/components/nuke/AmmoComponentNuke.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/ammo/components/nuke/AmmoComponentNuke.java index 8ca5c301f..59dc22470 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/ammo/components/nuke/AmmoComponentNuke.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/ammo/components/nuke/AmmoComponentNuke.java @@ -11,14 +11,12 @@ import net.minecraft.network.play.server.SPacketChunkData; import net.minecraft.potion.PotionEffect; import net.minecraft.server.management.PlayerChunkMapEntry; -import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; -import net.minecraft.world.Explosion; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraft.world.biome.Biome; @@ -35,6 +33,7 @@ import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.util.IIColor; import pl.pabilo8.immersiveintelligence.common.util.IIDamageSources; +import pl.pabilo8.immersiveintelligence.common.util.IIExplosion; import java.util.ArrayList; import java.util.List; @@ -69,14 +68,8 @@ public void onEffect(World world, Vec3d pos, Vec3d dir, ComponentEffectShape sha return; BlockPos centre = new BlockPos(pos); - for(int i = 0; i < 5; i++) - { - BlockPos localCentre = i==0?centre: (centre.offset(EnumFacing.getHorizontal(i), 25)); - Explosion explosion = new Explosion(world, owner, localCentre.getX(), localCentre.getY(), localCentre.getZ(), 56*multiplier, false, true); - explosion.doExplosionA(); - explosion.doExplosionB(false); - } - + new IIExplosion(world, owner, pos, null, 56*multiplier, 64, ComponentEffectShape.ORB, false, true, false) + .doExplosion(false); applyEntityEffects(world, centre, multiplier); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/BlockIIDataDevice.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/BlockIIDataDevice.java index 77b2a309e..03e173d38 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/BlockIIDataDevice.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/BlockIIDataDevice.java @@ -4,10 +4,8 @@ import blusunrize.immersiveengineering.api.TargetingInfo; import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.IWireCoil; -import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.client.models.IOBJModelCallback; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.properties.PropertyEnum; @@ -35,6 +33,7 @@ import pl.pabilo8.immersiveintelligence.common.util.block.IIBlockInterfaces.IITileProviderEnum; import pl.pabilo8.immersiveintelligence.common.util.block.ItemBlockIIBase; import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectionalConnectable; import java.util.ArrayList; @@ -83,8 +82,8 @@ public void neighborChanged(IBlockState state, World world, BlockPos pos, Block if(te==null) break; - TileEntityImmersiveConnectable connector = (TileEntityImmersiveConnectable & IDirectionalTile)te; - if(world.isAirBlock(pos.offset(((IDirectionalTile)connector).getFacing()))) + TileEntityIIDirectionalConnectable connector = (TileEntityIIDirectionalConnectable)te; + if(world.isAirBlock(pos.offset(connector.getFacing()))) { this.dropBlockAsItem(connector.getWorld(), pos, world.getBlockState(pos), 0); connector.getWorld().setBlockToAir(pos); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityAlarmSiren.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityAlarmSiren.java index 3983aba8e..86fa6c507 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityAlarmSiren.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityAlarmSiren.java @@ -1,38 +1,37 @@ package pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity; -import blusunrize.immersiveengineering.ImmersiveEngineering; import blusunrize.immersiveengineering.api.Lib; import blusunrize.immersiveengineering.api.TargetingInfo; import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; -import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.api.energy.wires.redstone.IRedstoneConnector; import blusunrize.immersiveengineering.api.energy.wires.redstone.RedstoneWireNetwork; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IHammerInteraction; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.ISoundTile; import blusunrize.immersiveengineering.common.util.Utils; import net.minecraft.client.resources.I18n; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumDyeColor; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.ITickable; -import net.minecraft.util.math.*; -import net.minecraft.world.World; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.api.utils.tools.IAdvancedTextOverlay; +import pl.pabilo8.immersiveintelligence.client.util.carversound.ConditionCompoundSound; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.AlarmSiren; import pl.pabilo8.immersiveintelligence.common.IISounds; -import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; -import pl.pabilo8.immersiveintelligence.common.network.messages.MessageIITileSync; -import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingLimitation; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingSettings; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectionalConnectable; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import static blusunrize.immersiveengineering.api.energy.wires.WireType.REDSTONE_CATEGORY; @@ -41,33 +40,40 @@ * @author Pabilo8 (pabilo@iiteam.net) * @since 15.06.2019 */ -public class TileEntityAlarmSiren extends TileEntityImmersiveConnectable - implements IRedstoneConnector, ITickable, IDirectionalTile, IHammerInteraction, IAdvancedTextOverlay, ISoundTile +public class TileEntityAlarmSiren extends TileEntityIIDirectionalConnectable + implements IRedstoneConnector, ITickable, IHammerInteraction, IAdvancedTextOverlay { + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL); + + @SyncNBT public int redstoneChannel = 0; - public boolean rsDirty = false; + @SyncNBT(events = SyncEvents.TILE_CUSTOM1) public boolean active = false; + @SyncNBT(events = SyncEvents.TILE_CUSTOM1) public float soundVolume = 1f; - protected RedstoneWireNetwork wireNetwork = new RedstoneWireNetwork().add(this); - EnumFacing facing = EnumFacing.NORTH; - private boolean refreshWireNetwork = false; + @SideOnly(Side.CLIENT) - private AxisAlignedBB renderAABB; + private ConditionCompoundSound loopSound; + private RedstoneWireNetwork wireNetwork = new RedstoneWireNetwork().add(this); + private boolean refreshWireNetwork = false; @Override public void update() { - if(world.isRemote) { - ImmersiveEngineering.proxy.handleTileSound(IISounds.siren, this, this.active, soundVolume*((AlarmSiren.soundRange+8)/16f), 1); - } - else if(hasWorld()) - { - boolean wasActive = active; - active = this.getNetwork().getPowerOutput(redstoneChannel) > 0; - if(active^wasActive) - sendSoundUpdate(); + if(active) + { + if(loopSound==null||loopSound.isDonePlaying()) + { + loopSound = new ConditionCompoundSound<>(IISounds.siren, new Vec3d(pos).addVector(0.5, 0.5, 0.5), + this, t -> !t.isInvalid()&&t.active); + loopSound.setMaxRange(AlarmSiren.soundRange); + } + if(loopSound!=null) + loopSound.setVolume(soundVolume); + } + } if(hasWorld()&&!world.isRemote&&!refreshWireNetwork) @@ -75,45 +81,9 @@ else if(hasWorld()) refreshWireNetwork = true; wireNetwork.removeFromNetwork(null); } - if(hasWorld()&&!world.isRemote&&rsDirty) - wireNetwork.updateValues(); - } - - @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - this.facing = facing; - } - - @Override - public int getFacingLimitation() - { - return 2; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return false; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return !entity.isSneaking(); } - @Override - public boolean canRotate(EnumFacing axis) - { - return !axis.getAxis().isVertical(); - } + //--- Wiring ---// @Override public RedstoneWireNetwork getNetwork() @@ -130,44 +100,30 @@ public void setNetwork(RedstoneWireNetwork net) @Override public void onChange() { + active = this.getNetwork().getPowerOutput(redstoneChannel) > 0; soundVolume = wireNetwork.channelValues[this.redstoneChannel]/15f; - - } - - @Override - public World getConnectorWorld() - { - return getWorld(); + if(!world.isRemote) + updateTileForAll(); } @Override public void updateInput(byte[] signals) { - rsDirty = false; } @Override public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, float hitY, float hitZ) { - // Sneaking iterates through colours, normal hammerign toggles in and out + //Sneaking iterates through colours if(player.isSneaking()) + { redstoneChannel = (redstoneChannel+1)%16; - - markDirty(); - wireNetwork.updateValues(); - onChange(); - this.markContainingBlockForUpdate(null); - world.addBlockEvent(getPos(), this.getBlockType(), 254, 0); - return true; - } - - @Override - public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) - { - if(!REDSTONE_CATEGORY.equals(cableType.getCategory())) - return false; - return limitType==null||limitType==cableType; + wireNetwork.updateValues(); + onChange(); + return true; + } + return false; } @Override @@ -185,40 +141,22 @@ public void removeCable(@Nullable ImmersiveNetHandler.Connection connection) } @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) + public void onConnectivityUpdate(BlockPos pos, int dimension) { - super.writeCustomNBT(nbt, descPacket); - nbt.setBoolean("active", active); - nbt.setFloat("volume", soundVolume); - nbt.setInteger("facing", facing.ordinal()); - nbt.setInteger("redstoneChannel", redstoneChannel); + super.onConnectivityUpdate(pos, dimension); + refreshWireNetwork = false; } @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) + public boolean acceptsWireType(WireType category) { - super.readCustomNBT(nbt, descPacket); - active = nbt.getBoolean("active"); - soundVolume = nbt.getFloat("volume"); - facing = EnumFacing.getFront(nbt.getInteger("facing")); - redstoneChannel = nbt.getInteger("redstoneChannel"); + return REDSTONE_CATEGORY.equals(category.getCategory()); } @Override - public void receiveMessageFromServer(NBTTagCompound message) + public boolean isRelay() { - if(message.hasKey("volume")) - soundVolume = message.getFloat("volume"); - if(message.hasKey("active")) - active = message.getBoolean("active"); - } - - private void sendSoundUpdate() - { - IIPacketHandler.sendToClient(this, new MessageIITileSync(this, EasyNBT.newNBT() - .withBoolean("active", active) - .withFloat("volume", soundVolume) - )); + return true; } @Override @@ -227,37 +165,16 @@ public Vec3d getConnectionOffset(Connection con) return new Vec3d(0.5f, 0.2f, 0.5f); } - @Override - public void onConnectivityUpdate(BlockPos pos, int dimension) - { - refreshWireNetwork = false; - } + //--- Facing ---// - @SideOnly(Side.CLIENT) + @Nonnull @Override - public AxisAlignedBB getRenderBoundingBox() - { - int inc = getRenderRadiusIncrease(); - return new AxisAlignedBB(this.pos.getX()-inc, this.pos.getY()-inc, this.pos.getZ()-inc, - this.pos.getX()+inc+1, this.pos.getY()+inc+1, this.pos.getZ()+inc+1); - } - - int getRenderRadiusIncrease() + protected FacingSettings getFacingSettings() { - return WireType.REDSTONE.getMaxLength(); + return FACING_SETTINGS; } - @Override - public boolean moveConnectionTo(Connection c, BlockPos newEnd) - { - return true; - } - - @Override - public boolean shoudlPlaySound(String sound) - { - return active; - } + //--- IAdvancedTextOverlay ---// @SideOnly(Side.CLIENT) @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataCallbackConnector.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataCallbackConnector.java index a8018eb76..2b33b6ad0 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataCallbackConnector.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataCallbackConnector.java @@ -6,11 +6,9 @@ import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumDyeColor; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; -import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraftforge.fml.relauncher.Side; @@ -18,17 +16,20 @@ import pl.pabilo8.immersiveintelligence.api.data.DataPacket; import pl.pabilo8.immersiveintelligence.api.data.device.IDataDevice; import pl.pabilo8.immersiveintelligence.common.IIUtils; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 18.07.2026 + * @ii-approved 0.3.1 * @since 31.05.2019 */ public class TileEntityDataCallbackConnector extends TileEntityDataConnector { + @SyncNBT(name = "colorIn") public int colorIn = 0; + @SyncNBT(name = "colorOut") public int colorOut = 1; - @SideOnly(Side.CLIENT) - private AxisAlignedBB renderAABB; @Override public void onPacketReceive(DataPacket packet) @@ -76,30 +77,6 @@ public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, f return true; } - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.writeCustomNBT(nbt, descPacket); - nbt.setInteger("colorIn", colorIn); - nbt.setInteger("colorOut", colorOut); - } - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.readCustomNBT(nbt, descPacket); - colorIn = nbt.getInteger("colorIn"); - colorOut = nbt.getInteger("colorOut"); - } - - @SideOnly(Side.CLIENT) - @Override - public AxisAlignedBB getRenderBoundingBox() - { - int inc = getRenderRadiusIncrease(); - return new AxisAlignedBB(this.pos.getX()-inc, this.pos.getY()-inc, this.pos.getZ()-inc, this.pos.getX()+inc+1, this.pos.getY()+inc+1, this.pos.getZ()+inc+1); - } - @SideOnly(Side.CLIENT) @Override public int getRenderColour(IBlockState object, String group) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataConnector.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataConnector.java index 93ad7c7cb..3aae5ad30 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataConnector.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataConnector.java @@ -4,36 +4,37 @@ import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; -import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.client.models.IOBJModelCallback; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IHammerInteraction; import blusunrize.immersiveengineering.common.util.Utils; import com.google.common.annotations.VisibleForTesting; import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumDyeColor; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.ITickable; -import net.minecraft.util.math.*; -import net.minecraft.world.World; -import net.minecraftforge.fml.common.Optional; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.api.data.DataPacket; +import pl.pabilo8.immersiveintelligence.api.data.IIDataHandlingUtils; +import pl.pabilo8.immersiveintelligence.api.data.IIDataHandlingUtils.PacketOperation; import pl.pabilo8.immersiveintelligence.api.data.device.DataWireNetwork; import pl.pabilo8.immersiveintelligence.api.data.device.IDataConnector; import pl.pabilo8.immersiveintelligence.api.data.device.IDataDevice; import pl.pabilo8.immersiveintelligence.api.utils.tools.IAdvancedTextOverlay; import pl.pabilo8.immersiveintelligence.common.IIUtils; -import pl.pabilo8.immersiveintelligence.common.compat.ComputerCraftHelper; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingLimitation; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingSettings; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectionalConnectable; import pl.pabilo8.immersiveintelligence.common.wire.IIDataWireType; import javax.annotation.Nonnull; @@ -41,24 +42,43 @@ /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 18.07.2026 + * @ii-approved 0.3.1 * @since 31.05.2019 */ -public class TileEntityDataConnector extends TileEntityImmersiveConnectable implements - ITickable, IDirectionalTile, IHammerInteraction, IBlockBounds, IDataConnector, IOBJModelCallback, IAdvancedTextOverlay +public class TileEntityDataConnector extends TileEntityIIDirectionalConnectable implements + ITickable, IHammerInteraction, IBlockBounds, IDataConnector, IOBJModelCallback, IAdvancedTextOverlay { + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.SIDE_CLICKED) + .withMirroringOnPlacement(true); + //--- OpenComputers / ComputerCraft compat ---// public DataPacket lastReceived = null; public boolean compatReceived = true; //whether a computer received the signal - protected EnumFacing facing = EnumFacing.DOWN; protected DataWireNetwork wireNetwork = new DataWireNetwork().add(this); - private int color = 0; + @SyncNBT + public int color = 0; private boolean refreshWireNetwork = false; - @SideOnly(Side.CLIENT) - private AxisAlignedBB renderAABB; - /** - * Like the old updateEntity(), except more generic. - */ + @Nonnull + @Override + public FacingSettings getFacingSettings() + { + return FACING_SETTINGS; + } + + @Override + public boolean acceptsWireType(WireType category) + { + return IIDataWireType.DATA_CATEGORY.equals(category.getCategory()); + } + + @Override + public boolean isRelay() + { + return false; + } + @Override public void update() { @@ -93,12 +113,6 @@ public void onDataChange() } } - @Override - public World getConnectorWorld() - { - return getWorld(); - } - @Override public void onPacketReceive(DataPacket packet) { @@ -114,7 +128,8 @@ public void onPacketReceive(DataPacket packet) if(world.isBlockLoaded(devicePos)&&device instanceof IDataDevice) { IDataDevice d = (IDataDevice)device; - d.onReceive(packet, facing.getOpposite()); + IIDataHandlingUtils.dispatchPacket(device, PacketOperation.DEVICE_RECEIVE, + () -> d.onReceive(packet, facing.getOpposite())); } } @@ -136,14 +151,6 @@ public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, f return true; } - @Override - public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) - { - if(!cableType.getCategory().equals(IIDataWireType.DATA_CATEGORY)) - return false; - return limitType==null; - } - @Override public void connectCable(WireType cableType, TargetingInfo target, IImmersiveConnectable other) { @@ -158,58 +165,6 @@ public void removeCable(@Nullable ImmersiveNetHandler.Connection connection) wireNetwork.removeFromNetwork(this); } - @Override - public EnumFacing getFacing() - { - return this.facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - this.facing = facing; - } - - @Override - public int getFacingLimitation() - { - return 0; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return true; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return false; - } - - @Override - public boolean canRotate(EnumFacing axis) - { - return false; - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.writeCustomNBT(nbt, descPacket); - nbt.setInteger("facing", facing.ordinal()); - nbt.setInteger("color", color); - } - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.readCustomNBT(nbt, descPacket); - facing = EnumFacing.getFront(nbt.getInteger("facing")); - color = nbt.getInteger("color"); - } - @Override public Vec3d getConnectionOffset(Connection con) { @@ -221,22 +176,10 @@ public Vec3d getConnectionOffset(Connection con) @Override public void onConnectivityUpdate(BlockPos pos, int dimension) { + super.onConnectivityUpdate(pos, dimension); refreshWireNetwork = false; } - @SideOnly(Side.CLIENT) - @Override - public AxisAlignedBB getRenderBoundingBox() - { - int inc = getRenderRadiusIncrease(); - return new AxisAlignedBB(this.pos.getX()-inc, this.pos.getY()-inc, this.pos.getZ()-inc, this.pos.getX()+inc+1, this.pos.getY()+inc+1, this.pos.getZ()+inc+1); - } - - int getRenderRadiusIncrease() - { - return IIDataWireType.DATA.getMaxLength(); - } - @Override public float[] getBlockBounds() { @@ -261,12 +204,6 @@ public float[] getBlockBounds() return new float[]{0, 0, 0, 1, 1, 1}; } - @Override - public boolean moveConnectionTo(Connection c, BlockPos newEnd) - { - return true; - } - @SideOnly(Side.CLIENT) @Override public int getRenderColour(IBlockState object, String group) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataDebugger.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataDebugger.java index 03b4d6872..bdd96db1c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataDebugger.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataDebugger.java @@ -6,14 +6,15 @@ import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; -import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.client.models.IOBJModelCallback; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.*; +import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IActiveState; +import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IHammerInteraction; +import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IRedstoneOutput; +import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IUsesBooleanProperty; import blusunrize.immersiveengineering.common.util.chickenbones.Matrix4; import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; @@ -23,9 +24,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3i; import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.world.World; import net.minecraftforge.common.model.TRSRTransformation; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -40,8 +39,14 @@ import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.util.IIReference; import pl.pabilo8.immersiveintelligence.common.util.ISerializableEnum; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingLimitation; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingSettings; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectionalConnectable; import pl.pabilo8.immersiveintelligence.common.wire.IIDataWireType; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -50,20 +55,26 @@ /** * @author Pabilo8 (pabilo@iiteam.net) * @author Avalon (avalon@iiteam.net) + * @updated 10.10.2024 + * @updated 20.07.2026 + * @ii-approved 0.3.1 * @since 11.06.2019 - * @since 10.10.2024 */ -public class TileEntityDataDebugger extends TileEntityImmersiveConnectable implements ITickable, IDataConnector, IHammerInteraction, IDirectionalTile, IOBJModelCallback, IAdvancedTextOverlay, IActiveState, IRedstoneOutput +public class TileEntityDataDebugger extends TileEntityIIDirectionalConnectable implements ITickable, IDataConnector, IHammerInteraction, + IOBJModelCallback, IAdvancedTextOverlay, IActiveState, IRedstoneOutput { - //Purely decorational, client only + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL); + @SyncNBT public int setupTime = 25; + @SyncNBT(name = "packet", events = SyncEvents.TILE_CUSTOM1) + public DataPacket lastPacket = new DataPacket(); + @SyncNBT(events = SyncEvents.TILE_CUSTOM2) + public DebuggerMode mode = DebuggerMode.TRANSCEIVER; + public int outputTime = 0; - private DebuggerMode mode = DebuggerMode.TRANSCEIVER; - private EnumFacing facing = EnumFacing.NORTH; private boolean toggle = false; private DataWireNetwork wireNetwork = new DataWireNetwork().add(this); private boolean refreshWireNetwork = false; - private DataPacket lastPacket = null; private String[] packetString = new String[0]; @Override @@ -75,17 +86,17 @@ public void update() wireNetwork.removeFromNetwork(null); } - if(world.isRemote&&setupTime > 0) + if(setupTime > 0) { setupTime -= 1; if(setupTime==0) onDataChange(); } - else if(!world.isRemote) + if(!world.isRemote) { if(mode.canReceive) { - if(outputTime-1==0) + if(outputTime==1) { outputTime = 0; markDirty(); @@ -94,14 +105,15 @@ else if(!world.isRemote) else outputTime = Math.max(outputTime-1, 0); } - if(mode.canTransmit) + if(mode.canTransmit&&outputTime==0) { + //Do not interpret the debugger's own redstone signal as a trigger to send a packet if(world.isBlockIndirectlyGettingPowered(getPos()) > 0&&!toggle) { toggle = true; - DataPacket pack = new DataPacket(); - pack.set('a', new DataTypeString("Hello World!")); - this.getDataNetwork().sendPacket(pack, this); + DataPacket packet = new DataPacket(); + packet.set('a', new DataTypeString("Hello World!")); + this.getDataNetwork().sendPacket(packet, this); this.world.playSound(null, pos, IISounds.debuggerBeep, SoundCategory.BLOCKS, 1.0f, 0.0f); } else if(world.isBlockIndirectlyGettingPowered(getPos())==0&&toggle) @@ -112,41 +124,23 @@ else if(world.isBlockIndirectlyGettingPowered(getPos())==0&&toggle) } } - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) + public FacingSettings getFacingSettings() { - mode = DebuggerMode.values()[nbt.getInteger("mode")]; - if(nbt.hasKey("noSetup")) - setupTime = 0; - setFacing(EnumFacing.getFront(nbt.getInteger("facing"))); - if(nbt.hasKey("packet")) - { - this.lastPacket = new DataPacket(nbt.getCompoundTag("packet")); - if(world!=null&&world.isRemote) - this.packetString = compilePacketString(); - } + return FACING_SETTINGS; } @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) + public void receiveMessageFromServer(@Nonnull NBTTagCompound message) { - nbt.setInteger("mode", mode.ordinal()); - if(setupTime < 25) - nbt.setBoolean("noSetup", true); - nbt.setInteger("facing", facing.ordinal()); - - if(this.lastPacket!=null) - { - if(!world.isRemote) - this.packetString = compilePacketString(); - nbt.setTag("packet", this.lastPacket.serializeNBT()); - } + super.receiveMessageFromServer(message); + if(world.isRemote) + this.packetString = compilePacketString(); } private String[] compilePacketString() { - //gets variables in format l:{Value:0} + if(lastPacket==null||lastPacket.isEmpty()) + return new String[0]; return minimizeArrays( lastPacket.stream() .map(entry -> String.format("%s %s = %s", @@ -181,51 +175,11 @@ public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, f IIPacketHandler.sendChatTranslation(player, IIReference.INFO_KEY+"debugger_mode", new TextComponentTranslation(IIReference.INFO_KEY+"debugger_mode."+mode.getName()) ); - markDirty(); - markBlockForUpdate(pos, null); + updateTileForEvent(SyncEvents.TILE_CUSTOM2); } return true; } - @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - if(facing.getAxis().isHorizontal()) - this.facing = facing; - else - this.facing = EnumFacing.NORTH; - } - - @Override - public int getFacingLimitation() - { - return 2; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return false; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return !entity.isSneaking(); - } - - @Override - public boolean canRotate(EnumFacing axis) - { - return true; - } - @Override public DataWireNetwork getDataNetwork() { @@ -248,12 +202,6 @@ public void onDataChange() } } - @Override - public World getConnectorWorld() - { - return getWorld(); - } - @Override public void onPacketReceive(DataPacket packet) { @@ -262,8 +210,7 @@ public void onPacketReceive(DataPacket packet) this.lastPacket = packet; this.outputTime = 20; this.world.playSound(null, pos, IISounds.debuggerBeep, SoundCategory.BLOCKS, 1.0f, 1.0f); - markDirty(); - markBlockForUpdate(this.pos, null); + updateTileForEvent(SyncEvents.TILE_CUSTOM1); } } @@ -274,17 +221,15 @@ public void sendPacket(DataPacket packet) } @Override - protected boolean isRelay() + public boolean isRelay() { return true; } @Override - public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) + public boolean acceptsWireType(WireType category) { - if(cableType!=IIDataWireType.DATA) - return false; - return limitType==null||limitType==cableType; + return IIDataWireType.DATA_CATEGORY.equals(category.getCategory()); } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataMerger.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataMerger.java index 052bc47f3..178e42506 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataMerger.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataMerger.java @@ -1,17 +1,13 @@ package pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IPlayerInteraction; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; -import net.minecraft.util.ITickable; import net.minecraft.util.NonNullList; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.INBTSerializable; @@ -21,8 +17,11 @@ import pl.pabilo8.immersiveintelligence.api.data.types.generic.DataType; import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyCollection; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IIIGuiMultiblockTile; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IIIInventory; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional; import javax.annotation.Nonnull; import java.util.ArrayList; @@ -31,37 +30,26 @@ /** * @author Pabilo8 (pabilo@iiteam.net) * @updated 02.06.2026 + * @ii-approved 0.3.1 * @since 17.05.2019 */ -public class TileEntityDataMerger extends TileEntityIEBase implements IPlayerInteraction, ITickable, IBlockBounds, IDirectionalTile, IDataDevice, IIIGuiMultiblockTile, IIIInventory +public class TileEntityDataMerger extends TileEntityIIDirectional implements IPlayerInteraction, IBlockBounds, IDataDevice, IIIGuiMultiblockTile, IIIInventory { - public EnumFacing facing = EnumFacing.NORTH; + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL); + @SyncNBT(name = "rules", events = {SyncEvents.TILE_CLIENT_MESSAGE, SyncEvents.TILE_GUI_OPENED}) public EasyCollection mergeRules = new EasyCollection<>(DataMergeRule::new); + @SyncNBT public DataPacket packetLeft = new DataPacket(); + @SyncNBT public DataPacket packetRight = new DataPacket(); @Override public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) { - facing = EnumFacing.getFront(nbt.getInteger("facing")); - - if(nbt.hasKey("rules", Constants.NBT.TAG_LIST)) - mergeRules.deserializeNBT(nbt.getTagList("rules", Constants.NBT.TAG_COMPOUND)); - else if(nbt.hasKey("packet", Constants.NBT.TAG_COMPOUND)) + if(nbt.hasKey("packet", Constants.NBT.TAG_COMPOUND)) migrateLegacySettings(new DataPacket(nbt.getCompoundTag("packet"))); - - packetLeft = new DataPacket(nbt.getCompoundTag("packetLeft")); - packetRight = new DataPacket(nbt.getCompoundTag("packetRight")); - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - nbt.setInteger("facing", facing.ordinal()); - nbt.setTag("rules", mergeRules.serializeNBT()); - nbt.setTag("packetLeft", packetLeft.serializeNBT()); - nbt.setTag("packetRight", packetRight.serializeNBT()); + super.readCustomNBT(nbt, descPacket); } private void migrateLegacySettings(DataPacket settingsPacket) @@ -120,71 +108,17 @@ public boolean interact(EnumFacing side, EntityPlayer player, EnumHand hand, Ite return false; } - @Override - public void receiveMessageFromServer(NBTTagCompound message) - { - super.receiveMessageFromServer(message); - } - - @Override - public void receiveMessageFromClient(NBTTagCompound message) - { - super.receiveMessageFromClient(message); - if(message.hasKey("rules", Constants.NBT.TAG_LIST)) - { - mergeRules.deserializeNBT(message.getTagList("rules", Constants.NBT.TAG_COMPOUND)); - markDirty(); - if(world!=null) - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); - } - } - - @Override - public void update() - { - - } - @Override public float[] getBlockBounds() { return new float[]{0f, 0, 0f, 1f, .875f, 1f}; } + @Nonnull @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(EnumFacing facing) + protected FacingSettings getFacingSettings() { - this.facing = facing; - } - - @Override - public int getFacingLimitation() - { - return 2; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return false; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return !entity.isSneaking(); - } - - @Override - public boolean canRotate(EnumFacing axis) - { - return !axis.getAxis().isVertical(); + return FACING_SETTINGS; } @Override @@ -268,18 +202,6 @@ public boolean isStackValid(int slot, ItemStack stack) return false; } - @Override - public int getSlotLimit(int slot) - { - return 0; - } - - @Override - public void doGraphicalUpdates(int slot) - { - - } - public static class DataMergeRule implements INBTSerializable { public char variable = 'a'; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRelay.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRelay.java index b9a2d655b..065bfbddf 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRelay.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRelay.java @@ -1,45 +1,36 @@ package pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity; -import blusunrize.immersiveengineering.api.TargetingInfo; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; -import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; -import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3i; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingLimitation; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingSettings; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectionalConnectable; import pl.pabilo8.immersiveintelligence.common.wire.IIDataWireType; +import javax.annotation.Nonnull; import java.util.Set; /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 20.07.2026 + * @ii-approved 0.3.1 * @since 31.05.2019 */ -public class TileEntityDataRelay extends TileEntityImmersiveConnectable - implements ITickable, IDirectionalTile, IBlockBounds +public class TileEntityDataRelay extends TileEntityIIDirectionalConnectable implements ITickable, IBlockBounds { - public EnumFacing facing = EnumFacing.DOWN; + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.SIDE_CLICKED) + .withMirroringOnPlacement(true); boolean firstTick = true; - @SideOnly(Side.CLIENT) - private AxisAlignedBB renderAABB; @Override public void update() { - if(!world.isRemote) - { - - } - else if(firstTick) + if(!world.isRemote&&firstTick) { Set conns = ImmersiveNetHandler.INSTANCE.getConnections(world, pos); if(conns!=null) @@ -50,69 +41,11 @@ else if(firstTick) } } + @Nonnull @Override - public EnumFacing getFacing() - { - return this.facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - this.facing = facing; - } - - @Override - public int getFacingLimitation() - { - return 0; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return true; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return false; - } - - @Override - public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) - { - if(cableType!=IIDataWireType.DATA) - return false; - return limitType==null||limitType==cableType; - } - - @Override - public boolean canRotate(EnumFacing axis) + protected FacingSettings getFacingSettings() { - return false; - } - - @Override - protected float getBaseDamage(Connection c) - { - return 8*30F/c.cableType.getTransferRate(); - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.writeCustomNBT(nbt, descPacket); - nbt.setInteger("facing", facing.ordinal()); - } - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.readCustomNBT(nbt, descPacket); - if(nbt.hasKey("facing")) - facing = EnumFacing.getFront(nbt.getInteger("facing")); + return FACING_SETTINGS; } @Override @@ -123,20 +56,6 @@ public Vec3d getConnectionOffset(Connection con) return new Vec3d(.5+side.getFrontOffsetX()*(.5-conRadius), 0.5+side.getFrontOffsetY()*(.5-conRadius), .5+side.getFrontOffsetZ()*(.5-conRadius)); } - @SideOnly(Side.CLIENT) - @Override - public AxisAlignedBB getRenderBoundingBox() - { - int inc = getRenderRadiusIncrease(); - return new AxisAlignedBB(this.pos.getX()-inc, this.pos.getY()-inc, this.pos.getZ()-inc, - this.pos.getX()+inc+1, this.pos.getY()+inc+1, this.pos.getZ()+inc+1); - } - - int getRenderRadiusIncrease() - { - return IIDataWireType.DATA.getMaxLength(); - } - @Override public float[] getBlockBounds() { @@ -160,4 +79,16 @@ public float[] getBlockBounds() } return new float[]{0, 0, 0, 1, 1, 1}; } -} \ No newline at end of file + + @Override + public boolean acceptsWireType(WireType category) + { + return IIDataWireType.DATA_CATEGORY.equals(category.getCategory()); + } + + @Override + public boolean isRelay() + { + return true; + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRouter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRouter.java index 4903ed519..0dcc91c0e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRouter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityDataRouter.java @@ -1,6 +1,5 @@ package pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; import net.minecraft.item.EnumDyeColor; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -19,8 +18,11 @@ import pl.pabilo8.immersiveintelligence.common.util.IIReference; import pl.pabilo8.immersiveintelligence.common.util.ILocalizedEnum; import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyCollection; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IIIGuiMultiblockTile; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IIIInventory; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIBase; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -31,10 +33,12 @@ * * @author Pabilo8 (pabilo@iiteam.net) * @updated 02.06.2026 + * @ii-approved 0.3.1 * @since 17.05.2019 */ -public class TileEntityDataRouter extends TileEntityIEBase implements IDataDevice, IIIGuiMultiblockTile, IIIInventory +public class TileEntityDataRouter extends TileEntityIIBase implements IDataDevice, IIIGuiMultiblockTile, IIIInventory { + @SyncNBT(name = "rules", events = {SyncEvents.TILE_CLIENT_MESSAGE, SyncEvents.TILE_GUI_OPENED}) public EasyCollection routingRules; public TileEntityDataRouter() @@ -47,29 +51,9 @@ public TileEntityDataRouter() @Override public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) { - if(nbt.hasKey("rules")) - routingRules.deserializeNBT(nbt.getTagList("rules", 10)); - else if(nbt.hasKey("variable")) + if(nbt.hasKey("variable")) migrateLegacyVariableRouter(nbt.getString("variable")); - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - nbt.setTag("rules", routingRules.serializeNBT()); - } - - @Override - public void receiveMessageFromClient(@Nonnull NBTTagCompound message) - { - super.receiveMessageFromClient(message); - if(message.hasKey("rules")) - { - routingRules.deserializeNBT(message.getTagList("rules", 10)); - markDirty(); - if(world!=null) - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); - } + super.readCustomNBT(nbt, descPacket); } @Override @@ -95,12 +79,7 @@ public void onReceive(DataPacket packet, EnumFacing side) } if(changed) - { - routingRules.removeIf(DataRoutingRule::isExpired); - markDirty(); - if(world!=null) - world.notifyBlockUpdate(pos, world.getBlockState(pos), world.getBlockState(pos), 3); - } + updateTileForEvent(SyncEvents.TILE_GUI_OPENED); } //--- IIIGuiMultiblockTile ---// diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityProgrammableSpeaker.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityProgrammableSpeaker.java index 1383452c6..c4e90fbd0 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityProgrammableSpeaker.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityProgrammableSpeaker.java @@ -5,7 +5,6 @@ import blusunrize.immersiveengineering.api.TargetingInfo; import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; -import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.api.energy.wires.redstone.IRedstoneConnector; import blusunrize.immersiveengineering.api.energy.wires.redstone.RedstoneWireNetwork; @@ -15,10 +14,8 @@ import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumDyeColor; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.*; import net.minecraft.util.math.*; -import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; @@ -28,9 +25,9 @@ import pl.pabilo8.immersiveintelligence.api.data.device.IDataConnector; import pl.pabilo8.immersiveintelligence.api.utils.tools.IAdvancedTextOverlay; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.ProgrammableSpeaker; -import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; -import pl.pabilo8.immersiveintelligence.common.network.messages.MessageIITileSync; -import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIConnectable; import pl.pabilo8.immersiveintelligence.common.wire.IIDataWireType; import java.util.Objects; @@ -39,20 +36,24 @@ * @author Pabilo8 (pabilo@iiteam.net) * @since 15.06.2019 */ -public class TileEntityProgrammableSpeaker extends TileEntityImmersiveConnectable +public class TileEntityProgrammableSpeaker extends TileEntityIIConnectable implements IRedstoneConnector, IDataConnector, ITickable, IHammerInteraction, IAdvancedTextOverlay, ISoundTile { + @SyncNBT(name = "redstoneChannel", events = SyncEvents.TILE_CUSTOM1) public int redstoneChannel = 0; - public boolean rsDirty = false; + @SyncNBT(events = SyncEvents.TILE_CUSTOM2) public boolean active = false; - public String soundID = ImmersiveIntelligence.MODID+":siren"; - public float soundVolume = 1f, tone = 1f; + @SyncNBT(events = SyncEvents.TILE_CUSTOM2) + public String sound = ImmersiveIntelligence.MODID+":siren"; + @SyncNBT(events = SyncEvents.TILE_CUSTOM2) + public float volume = 1f, tone = 1f; + + public boolean rsDirty = false; protected WireType wireData = null; protected RedstoneWireNetwork redstoneNetwork = new RedstoneWireNetwork().add(this); protected DataWireNetwork dataNetwork = new DataWireNetwork().add(this); - EnumFacing facing = EnumFacing.NORTH; @SideOnly(Side.CLIENT) - SoundEvent sound; + SoundEvent playedSound; private boolean refreshWireNetwork = false; @Override @@ -62,22 +63,16 @@ public void update() { if(active) this.updateSound(); - if(!soundID.isEmpty()) - { - - if(sound!=null) - { - ImmersiveEngineering.proxy.handleTileSound(sound, this, this.active, soundVolume*((ProgrammableSpeaker.soundRange+4)/20f), tone); - } - - } + if(!sound.isEmpty()) + if(playedSound!=null) + ImmersiveEngineering.proxy.handleTileSound(playedSound, this, this.active, volume*((ProgrammableSpeaker.soundRange+4)/20f), tone); } else if(hasWorld()) { boolean wasActive = active; active = this.getNetwork().getPowerOutput(redstoneChannel) > 0; if(active^wasActive) - sendSoundUpdate(); + updateTileForEvent(SyncEvents.TILE_CUSTOM2); } if(hasWorld()&&!world.isRemote&&!refreshWireNetwork) @@ -105,7 +100,7 @@ public void setNetwork(RedstoneWireNetwork net) @Override public void onChange() { - soundVolume = getNetwork().channelValues[this.redstoneChannel]/15f; + volume = getNetwork().channelValues[this.redstoneChannel]/15f; } @Override @@ -126,19 +121,13 @@ public void onDataChange() } - @Override - public World getConnectorWorld() - { - return getWorld(); - } - @Override public void onPacketReceive(DataPacket packet) { IIDataHandlingUtils.optionalInt('t', packet).ifPresent(t -> tone = MathHelper.clamp(t/100f, -2, 2)); IIDataHandlingUtils.optionalInt('v', packet).ifPresent(v -> - soundVolume = MathHelper.clamp(v/100f, 0, 1)); + volume = MathHelper.clamp(v/100f, 0, 1)); //Update played sound IIDataHandlingUtils.expectingStringParam('s', packet, s -> { @@ -149,38 +138,16 @@ public void onPacketReceive(DataPacket packet) world.playSound(null, getPos(), soundEvent, SoundCategory.BLOCKS, ((ProgrammableSpeaker.soundRange+4)/20f), tone); } else - soundID = packet.get('s').toString(); + sound = packet.get('s').toString(); }); - sendSoundUpdate(); - } - - @Override - public void receiveMessageFromServer(NBTTagCompound message) - { - if(message.hasKey("active")) - active = message.getBoolean("active"); - if(message.hasKey("tone")) - tone = message.getFloat("tone"); - if(message.hasKey("volume")) - soundVolume = message.getFloat("volume"); - if(message.hasKey("sound")) - soundID = message.getString("sound"); - } - - private void sendSoundUpdate() - { - IIPacketHandler.sendToClient(this, new MessageIITileSync(this, EasyNBT.newNBT() - .withBoolean("active", active) - .withFloat("tone", tone) - .withFloat("volume", soundVolume) - .withString("sound", soundID) - )); + if(!world.isRemote) + updateTileForEvent(SyncEvents.TILE_CUSTOM2); } @SideOnly(Side.CLIENT) private void updateSound() { - sound = SoundEvent.REGISTRY.getObject(new ResourceLocation(soundID)); + playedSound = SoundEvent.REGISTRY.getObject(new ResourceLocation(sound)); } @Override @@ -193,7 +160,6 @@ public void sendPacket(DataPacket packet) public void updateInput(byte[] signals) { rsDirty = false; - } @Override @@ -203,11 +169,21 @@ public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, f if(player.isSneaking()) redstoneChannel = (redstoneChannel+1)%16; - markDirty(); redstoneNetwork.updateValues(); onChange(); - this.markContainingBlockForUpdate(null); - world.addBlockEvent(getPos(), this.getBlockType(), 254, 0); + updateTileForEvent(SyncEvents.TILE_CUSTOM1); + return true; + } + + @Override + public boolean acceptsWireType(WireType category) + { + return false; + } + + @Override + public boolean isRelay() + { return true; } @@ -215,7 +191,8 @@ public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, f public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) { String category = cableType.getCategory(); - return (Objects.equals(category, WireType.REDSTONE.getCategory())&&this.limitType==null)||(Objects.equals(category, IIDataWireType.DATA.getCategory())&&this.wireData==null); + return (Objects.equals(category, WireType.REDSTONE.getCategory())&&this.limitType==null) + ||(Objects.equals(category, IIDataWireType.DATA.getCategory())&&this.wireData==null); } @Override @@ -238,7 +215,6 @@ else if(Objects.equals(cableType.getCategory(), IIDataWireType.DATA.getCategory( @Override public void removeCable(Connection connection) { - WireType type = connection!=null?connection.cableType: null; if(type==null) { @@ -272,38 +248,6 @@ private Vec3d getConnectionOffset(Connection con, boolean data) return new Vec3d(0.5f, 0.2f, 0.5f); } - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.writeCustomNBT(nbt, descPacket); - nbt.setBoolean("active", active); - - nbt.setString("sound", soundID); - nbt.setFloat("volume", soundVolume); - - nbt.setInteger("facing", facing.ordinal()); - - nbt.setInteger("redstoneChannel", redstoneChannel); - - nbt.setFloat("tone", tone); - } - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.readCustomNBT(nbt, descPacket); - active = nbt.getBoolean("active"); - - soundID = nbt.getString("sound"); - soundVolume = nbt.getFloat("volume"); - - facing = EnumFacing.getFront(nbt.getInteger("facing")); - - redstoneChannel = nbt.getInteger("redstoneChannel"); - - tone = nbt.getFloat("tone"); - } - @Override public Vec3d getConnectionOffset(Connection con) { @@ -313,29 +257,10 @@ public Vec3d getConnectionOffset(Connection con) @Override public void onConnectivityUpdate(BlockPos pos, int dimension) { + super.onConnectivityUpdate(pos, dimension); refreshWireNetwork = false; } - @SideOnly(Side.CLIENT) - @Override - public AxisAlignedBB getRenderBoundingBox() - { - int inc = getRenderRadiusIncrease(); - return new AxisAlignedBB(this.pos.getX()-inc, this.pos.getY()-inc, this.pos.getZ()-inc, - this.pos.getX()+inc+1, this.pos.getY()+inc+1, this.pos.getZ()+inc+1); - } - - int getRenderRadiusIncrease() - { - return Math.max(WireType.REDSTONE.getMaxLength(), IIDataWireType.DATA.getMaxLength()); - } - - @Override - public boolean moveConnectionTo(Connection c, BlockPos newEnd) - { - return true; - } - @SideOnly(Side.CLIENT) @Override public String[] getOverlayText(EntityPlayer player, RayTraceResult mop) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityPunchtapeReader.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityPunchtapeReader.java index ce8284446..b7912f0f9 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityPunchtapeReader.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/data_device/tileentity/TileEntityPunchtapeReader.java @@ -1,15 +1,11 @@ package pl.pabilo8.immersiveintelligence.common.block.data_device.tileentity; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IHammerInteraction; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IPlayerInteraction; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IRedstoneOutput; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.ITickable; @@ -23,20 +19,30 @@ import pl.pabilo8.immersiveintelligence.common.IIUtils; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.util.IIReference; +import pl.pabilo8.immersiveintelligence.common.util.ISerializableEnum; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional; + +import javax.annotation.Nonnull; /** * @author Pabilo8 (pabilo@iiteam.net) * @author Avalon (avalon@iiteam.net) + * @updated 20.07.2026 + * @ii-approved 0.3.1 * @since 11.06.2019 - * @updated 03.15.2026 */ -public class TileEntityPunchtapeReader extends TileEntityIEBase implements ITickable, IRedstoneOutput, IDataDevice, IPlayerInteraction, IHammerInteraction, IDirectionalTile +public class TileEntityPunchtapeReader extends TileEntityIIDirectional implements ITickable, IRedstoneOutput, IDataDevice, IPlayerInteraction, IHammerInteraction { + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL) + .withMirroringOnPlacement(true); public boolean hadRedstone = false; public int rsTime = 0; - EnumFacing facing = EnumFacing.NORTH; - DataPacket received = null; - private PunchtapeReaderMode mode = PunchtapeReaderMode.REDSTONE_INDIFFERENT; + + @SyncNBT(nullable = true) + public DataPacket received = null; + @SyncNBT + public PunchtapeReaderMode mode = PunchtapeReaderMode.REDSTONE_INDIFFERENT; @Override public void update() @@ -76,24 +82,6 @@ public boolean canConnectRedstone(IBlockState state, EnumFacing side) return mode!=PunchtapeReaderMode.REDSTONE_INDIFFERENT; } - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - mode = PunchtapeReaderMode.values()[nbt.getInteger("mode")]; - setFacing(EnumFacing.getFront(nbt.getInteger("facing"))); - if(nbt.hasKey("received")) - received = new DataPacket(nbt.getCompoundTag("received")); - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - nbt.setInteger("mode", mode.ordinal()); - nbt.setInteger("facing", facing.ordinal()); - if(received!=null) - nbt.setTag("received", received.serializeNBT()); - } - @Override public void onReceive(DataPacket packet, EnumFacing side) { @@ -114,43 +102,11 @@ public boolean hammerUseSide(EnumFacing side, EntityPlayer player, float hitX, f return true; } + @Nonnull @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - if(facing.getAxis().isHorizontal()) - this.facing = facing; - else - this.facing = EnumFacing.NORTH; - } - - @Override - public int getFacingLimitation() - { - return 2; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return true; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return !entity.isSneaking(); - } - - @Override - public boolean canRotate(EnumFacing axis) + protected FacingSettings getFacingSettings() { - return true; + return FACING_SETTINGS; } @Override @@ -168,7 +124,7 @@ public boolean interact(EnumFacing side, EntityPlayer player, EnumHand hand, Ite return true; } - private enum PunchtapeReaderMode + public enum PunchtapeReaderMode implements ISerializableEnum { REDSTONE_INDIFFERENT, PACKET_ON_REDSTONE, diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIFenceBase.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIFenceBase.java index b6d52a092..c3638a716 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIFenceBase.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIFenceBase.java @@ -43,7 +43,7 @@ public abstract class BlockIIFenceBase & IITileProviderEnum> e public static final PropertyBool FORCED_POST = PropertyBool.create("forced_post"); public BlockIIFenceBase(String name, Material material, PropertyEnum mainProperty, Function, ItemBlockIIBase> itemBlock, - Object... additionalProperties) + Object... additionalProperties) { super(name, material, mainProperty, itemBlock, BlockWall.NORTH, BlockWall.SOUTH, BlockWall.WEST, BlockWall.EAST, BlockWall.UP, FORCED_POST, @@ -62,6 +62,12 @@ protected IBlockState getInitDefaultState() return super.getInitDefaultState().withProperty(FORCED_POST, false); } + @Override + public boolean hasTileEntity() + { + return true; + } + @Nullable @Override public TileEntity createBasicTE(E type) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIWoodenChainFence.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIWoodenChainFence.java index 77e129824..9d6e94fca 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIWoodenChainFence.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/BlockIIWoodenChainFence.java @@ -27,6 +27,8 @@ public enum WoodenFortifications implements IITileProviderEnum @IIBlockProperties(needsCustomState = true) WOODEN_STEEL_CHAIN_FENCE, @IIBlockProperties(needsCustomState = true) - WOODEN_BRASS_CHAIN_FENCE + WOODEN_BRASS_CHAIN_FENCE, + @IIBlockProperties(needsCustomState = true) + WOODEN_ALUMINUM_CHAIN_FENCE } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityChainFence.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityChainFence.java index 504e32165..b4a9fee38 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityChainFence.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityChainFence.java @@ -1,21 +1,10 @@ package pl.pabilo8.immersiveintelligence.common.block.fortification.tileentity; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; -import net.minecraft.nbt.NBTTagCompound; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIBase; -public class TileEntityChainFence extends TileEntityIEBase +public class TileEntityChainFence extends TileEntityIIBase { + @SyncNBT public boolean hasPost = false; - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - nbt.setBoolean("hasPost", hasPost); - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - hasPost = nbt.getBoolean("hasPost"); - } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityMineSign.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityMineSign.java index 942848a56..569185a24 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityMineSign.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntityMineSign.java @@ -1,71 +1,27 @@ package pl.pabilo8.immersiveintelligence.common.block.fortification.tileentity; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing.Axis; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional; + +import javax.annotation.Nonnull; /** * @author Pabilo8 (pabilo@iiteam.net) * @since 06.07.2020 */ -public class TileEntityMineSign extends TileEntityIEBase implements IDirectionalTile, IBlockBounds +public class TileEntityMineSign extends TileEntityIIDirectional implements IBlockBounds { + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL) + .withMirroringOnPlacement(true); private static final float[] boundsX = new float[]{0.4375f, 0, 0, 0.5625f, 1, 1}; private static final float[] boundsY = new float[]{0, 0, 0.4375f, 1, 1, 0.5625f}; - public EnumFacing facing = EnumFacing.NORTH; - - @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - this.facing = facing; - if(facing.getAxis().isVertical()) - this.facing = EnumFacing.NORTH; - } - - @Override - public int getFacingLimitation() - { - return 2; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return true; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return true; - } @Override - public boolean canRotate(EnumFacing axis) + @Nonnull + protected FacingSettings getFacingSettings() { - return !axis.getAxis().isVertical(); - } - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - setFacing(EnumFacing.getFront(nbt.getInteger("facing"))); - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - nbt.setInteger("facing", facing.getIndex()); + return FACING_SETTINGS; } @Override @@ -73,4 +29,5 @@ public float[] getBlockBounds() { return facing.getAxis()==Axis.X?boundsX: boundsY; } + } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntitySandbags.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntitySandbags.java index 3a425e551..957c5363c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntitySandbags.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/fortification/tileentity/TileEntitySandbags.java @@ -1,18 +1,10 @@ package pl.pabilo8.immersiveintelligence.common.block.fortification.tileentity; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IAdvancedCollisionBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IAdvancedSelectionBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumFacing; import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IAdvancedBounds; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.List; @@ -20,89 +12,22 @@ * @author Pabilo8 (pabilo@iiteam.net) * @since 16.08.2019 */ -public class TileEntitySandbags extends TileEntityIEBase implements IDirectionalTile, IAdvancedCollisionBounds, IAdvancedSelectionBounds +public class TileEntitySandbags extends TileEntityIIDirectional implements IAdvancedBounds { - public EnumFacing facing = EnumFacing.NORTH; + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL) + .withRotation(true); @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) + @Nonnull + protected FacingSettings getFacingSettings() { - facing = EnumFacing.getFront(nbt.getInteger("facing")); + return FACING_SETTINGS; } @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - nbt.setInteger("facing", facing.ordinal()); - } - - @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - this.facing = facing; - } - - @Override - public int getFacingLimitation() - { - return 2; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return false; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return true; - } - - @Override - public boolean canRotate(EnumFacing axis) - { - return true; - } - - @Override - public float[] getBlockBounds() - { - return null; - } - - @Override - public List getAdvancedColisionBounds() - { - return getAdvancedSelectionBounds(); - } - - public boolean hasNeighbour() - { - BlockPos pos = getPos().offset(facing.rotateY()); - return world.getTileEntity(pos) instanceof TileEntitySandbags; - } - - public boolean isLower() - { - BlockPos up = getPos().up(); - TileEntity te = world.getTileEntity(up); - return te instanceof TileEntitySandbags; - } - - @Override - public List getAdvancedSelectionBounds() + public List getBounds(boolean collision) { List aabb = new ArrayList<>(); - // TODO: 28.12.2021 new aabb - switch(facing) { case NORTH: @@ -129,10 +54,4 @@ public List getAdvancedSelectionBounds() return aabb; } - - @Override - public boolean isOverrideBox(AxisAlignedBB box, EntityPlayer player, RayTraceResult mop, ArrayList list) - { - return false; - } -} \ No newline at end of file +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityCO2Filter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityCO2Filter.java index 316ca1b46..4c7d9dce8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityCO2Filter.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityCO2Filter.java @@ -2,16 +2,13 @@ import blusunrize.immersiveengineering.api.crafting.FermenterRecipe; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IHasDummyBlocks; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; import blusunrize.immersiveengineering.common.blocks.metal.TileEntityFermenter; import blusunrize.immersiveengineering.common.blocks.metal.TileEntityMultiblockMetal.MultiblockProcess; import blusunrize.immersiveengineering.common.blocks.stone.TileEntityBlastFurnaceAdvanced; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing.Axis; @@ -28,7 +25,10 @@ import net.minecraftforge.items.IItemHandlerModifiable; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.CO2Collector; import pl.pabilo8.immersiveintelligence.common.IIContent; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.HashMap; @@ -38,9 +38,10 @@ * @since 19.05.2021 * @since 15.12.2024 */ -public class TileEntityCO2Filter extends TileEntityIEBase implements ITickable, IBlockBounds, IDirectionalTile, IHasDummyBlocks +public class TileEntityCO2Filter extends TileEntityIIDirectional implements ITickable, IBlockBounds, IHasDummyBlocks { public static final HashMap, CO2Handler> handlerMap = new HashMap<>(); + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.SIDE_CLICKED); static { @@ -60,10 +61,8 @@ public int getOutput(TileEntity tile) return 0; int i = 0; for(MultiblockProcess process : fermenter.processQueue) - { if(process.canProcess(fermenter)&&process.processTick%CO2Collector.fermenterCollectTime==0) i += CO2Collector.fermenterCollectAmount; - } return i; } } @@ -90,33 +89,12 @@ public int getOutput(TileEntity tile) ); } + @SyncNBT(name = "dummy") public int subBlockID = 0; - public EnumFacing facing = EnumFacing.NORTH; public FluidTank tank = new FluidTank(1000); FluidWrapper fluidWrapper = new FluidWrapper(this); IItemHandler insertionHandler = new CO2ItemHandler(this); - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - subBlockID = nbt.getInteger("dummy"); - facing = EnumFacing.getFront(nbt.getInteger("facing")); - } - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - nbt.setInteger("dummy", subBlockID); - nbt.setInteger("facing", facing.ordinal()); - nbt.setBoolean("noSetup", true); - } - - @Override - public void receiveMessageFromServer(NBTTagCompound message) - { - super.receiveMessageFromServer(message); - } - @Override public void update() { @@ -134,9 +112,7 @@ public void update() { IFluidHandler capability = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, ff.getOpposite()); if(capability!=null) - { capability.fill(new FluidStack(IIContent.gasCO2, output), true); - } } } } @@ -144,55 +120,6 @@ public void update() } } - @Override - public float[] getBlockBounds() - { - return new float[]{0f, 0, 0f, 1f, 1f, 1f}; - } - - @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - this.facing = (facing==EnumFacing.DOWN)?EnumFacing.UP: facing; - } - - //All but not down - public EnumFacing getFacingForPlacement(EntityLivingBase placer, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) - { - return (side==EnumFacing.DOWN)?EnumFacing.UP: side; - } - - @Override - public int getFacingLimitation() - { - return 0; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return false; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return false; - } - - @Override - public boolean canRotate(EnumFacing axis) - { - return false; - } - - @Override public void placeDummies(BlockPos pos, IBlockState state, EnumFacing side, float hitX, float hitY, float hitZ) { @@ -232,13 +159,9 @@ public boolean hasCapability(Capability capability, @Nullable EnumFacing faci public T getCapability(Capability capability, @Nullable EnumFacing facing) { if(subBlockID==1&&facing==(this.getFacing()==EnumFacing.UP?EnumFacing.NORTH: this.facing)&&capability==CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) - { return ((T)fluidWrapper); - } if(!isDummy()&&(facing==null||facing.getAxis()!=Axis.Y)&&capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) - { return (T)insertionHandler; - } return super.getCapability(capability, facing); } @@ -313,9 +236,7 @@ public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { IItemHandler handlerBelow = getHandlerBelow(); if(handlerBelow!=null) - { return handlerBelow.insertItem(slot, stack, simulate); - } return stack; // Return full stack if no valid handler } @@ -338,9 +259,7 @@ public void setStackInSlot(int slot, ItemStack stack) { IItemHandlerModifiable handlerBelow = (IItemHandlerModifiable)getHandlerBelow(); if(handlerBelow!=null) - { handlerBelow.setStackInSlot(slot, stack); - } } @Nullable @@ -348,13 +267,34 @@ private IItemHandler getHandlerBelow() { TileEntity te = tile.getWorld().getTileEntity(tile.pos.down()); if(te!=null&&te.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP)) - { return te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); - } return null; } } + //--- IBlockBounds ---// + + @Override + public float[] getBlockBounds() + { + return new float[]{0f, 0, 0f, 1f, 1f, 1f}; + } + + //--- Facing ---// + + @Nonnull + @Override + protected FacingSettings getFacingSettings() + { + return FACING_SETTINGS; + } + + public EnumFacing getFacingForPlacement(EntityLivingBase placer, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) + { + //All but not down + return (side==EnumFacing.DOWN)?EnumFacing.UP: side; + } + public static abstract class CO2Handler { public abstract int getOutput(TileEntity tile); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityLatexCollector.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityLatexCollector.java index 9620070bd..6537058f6 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityLatexCollector.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/metal_device/tileentity/TileEntityLatexCollector.java @@ -1,11 +1,9 @@ package pl.pabilo8.immersiveintelligence.common.block.metal_device.tileentity; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IPlayerInteraction; import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; @@ -28,7 +26,7 @@ import pl.pabilo8.immersiveintelligence.common.block.simple.BlockIIRubberLog.RubberLogs; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; -import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIBase; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional; import javax.annotation.Nonnull; @@ -38,8 +36,9 @@ * @ii-approved 0.3.1 * @since 19.05.2021 */ -public class TileEntityLatexCollector extends TileEntityIIBase implements IPlayerInteraction, ITickable, IBlockBounds, IDirectionalTile, IAdvancedTextOverlay +public class TileEntityLatexCollector extends TileEntityIIDirectional implements IPlayerInteraction, ITickable, IBlockBounds, IAdvancedTextOverlay { + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL); @SyncNBT public EnumFacing facing = EnumFacing.NORTH; @SyncNBT(events = SyncEvents.TILE_CUSTOM1) @@ -84,7 +83,7 @@ public void update() if(!world.isRemote) { bucket = capability.getContainer(); - updateEntityForEvent(SyncEvents.ENTITY_CUSTOM1); + updateTileForEvent(SyncEvents.ENTITY_CUSTOM1); } } } @@ -100,7 +99,7 @@ public boolean interact(@Nonnull EnumFacing side, @Nonnull EntityPlayer player, bucket = heldItem.copy(); bucket.setCount(1); heldItem.shrink(1); - updateEntityForEvent(SyncEvents.ENTITY_CUSTOM1); + updateTileForEvent(SyncEvents.ENTITY_CUSTOM1); this.collectedLatex = 0; return true; } @@ -109,7 +108,7 @@ else if(!bucket.isEmpty()&&heldItem.isEmpty()) { player.inventory.addItemStackToInventory(bucket.copy()); bucket = ItemStack.EMPTY; - updateEntityForEvent(SyncEvents.ENTITY_CUSTOM1); + updateTileForEvent(SyncEvents.ENTITY_CUSTOM1); this.collectedLatex = 0; return true; } @@ -151,7 +150,7 @@ public int drainLatexMilliBuckets(int amountMb, boolean doDrain) this.bucket = capability.getContainer(); //Put back the remaining latex into the collector's stored amount; a bucket can only this.collectedLatex = 1000-Math.min(amountMb, 1000); - updateEntityForEvent(SyncEvents.TILE_CUSTOM2); + updateTileForEvent(SyncEvents.TILE_CUSTOM2); } return Math.min(amountMb, 1000); } @@ -161,7 +160,7 @@ public int drainLatexMilliBuckets(int amountMb, boolean doDrain) if(doDrain) { collectedLatex = collectedLatex-collected; - updateEntityForEvent(SyncEvents.TILE_CUSTOM2); + updateTileForEvent(SyncEvents.TILE_CUSTOM2); } return collected; } @@ -205,41 +204,11 @@ public String[] getOverlayText(EntityPlayer player, RayTraceResult mop) //--- Facing ---// - @Nonnull - @Override - public EnumFacing getFacing() - { - return facing; - } - - @Override - public void setFacing(@Nonnull EnumFacing facing) - { - this.facing = facing; - } - - @Override - public int getFacingLimitation() - { - return 2; - } - - @Override - public boolean mirrorFacingOnPlacement(@Nonnull EntityLivingBase placer) - { - return false; - } - @Override - public boolean canHammerRotate(@Nonnull EnumFacing side, float hitX, float hitY, float hitZ, @Nonnull EntityLivingBase entity) - { - return false; - } - - @Override - public boolean canRotate(@Nonnull EnumFacing axis) + @Nonnull + protected FacingSettings getFacingSettings() { - return false; + return FACING_SETTINGS; } //--- Block Bounds ---// diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityArtilleryHowitzer.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityArtilleryHowitzer.java index cbc8f51ff..bfb1a9ee7 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityArtilleryHowitzer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityArtilleryHowitzer.java @@ -502,16 +502,16 @@ private void handleSounds() Vec3d posDoor = new Vec3d(getBlockPosForPos(525)); soundDoorOpen = new ConditionCompoundSound<>(IISounds.slidingDoorOpenLoop, posDoor, this, - te -> hasEnergy.get()&&door.getState()&&!door.isFullyOpened()); + te -> !te.isInvalid()&&hasEnergy.get()&&door.getState()&&!door.isFullyOpened()); soundDoorClose = new ConditionCompoundSound<>(IISounds.slidingDoorCloseLoop, posDoor, this, - te -> hasEnergy.get()&&!door.getState()&&!door.isFullyClosed()); + te -> !te.isInvalid()&&hasEnergy.get()&&!door.getState()&&!door.isFullyClosed()); soundRotationH = new ConditionCompoundSound<>(IISounds.turntableHeavyForwardLoop, posDoor, this, - te -> hasActiveEnergy.get()&&platformOK.get()&&!yawOK.get()); + te -> !te.isInvalid()&&hasActiveEnergy.get()&&platformOK.get()&&!yawOK.get()); soundRotationV = new ConditionCompoundSound<>(IISounds.electricMotorHeavyForwardLoop, posDoor, this, - te -> hasActiveEnergy.get()&&platformOK.get()&&!pitchOK.get()); + te -> !te.isInvalid()&&hasActiveEnergy.get()&&platformOK.get()&&!pitchOK.get()); } @Override @@ -824,9 +824,9 @@ public enum ArtilleryHowitzerAction implements ISerializableEnum final float executeTime; ArtilleryHowitzerAction(boolean requiresPlatform, boolean platformUp, GunPosition gunPosition, - Predicate requirements, - Predicate fulfilled, - int animationTime, @Nullable String alias, float executeTime) + Predicate requirements, + Predicate fulfilled, + int animationTime, @Nullable String alias, float executeTime) { this.requiresPlatform = requiresPlatform; this.platformUp = platformUp; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityChemicalBath.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityChemicalBath.java index f449d8129..7c1f65dcc 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityChemicalBath.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityChemicalBath.java @@ -162,10 +162,18 @@ protected int[] listAllPOI(MultiblockPOI poi) } } + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) + { + if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY&&isPOI("item_in")) + return true; + return super.hasCapability(capability, facing); + } + @Override public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY&&isPOI("item_in")) //noinspection unchecked,DataFlowIssue return (T)master().inputHandler; return super.getCapability(capability, facing); @@ -204,7 +212,7 @@ public boolean isStackValid(int slot, ItemStack stack) @Override public boolean interact(@Nonnull EnumFacing side, @Nonnull EntityPlayer player, @Nonnull EnumHand hand, - @Nonnull ItemStack heldItem, float hitX, float hitY, float hitZ) + @Nonnull ItemStack heldItem, float hitX, float hitY, float hitZ) { if(!world.isRemote&&this.isPOI("tank_bucket")) { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityDataInputMachine.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityDataInputMachine.java index c822a6484..85eef315a 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityDataInputMachine.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityDataInputMachine.java @@ -187,7 +187,11 @@ protected void onUpdate() boolean currentSignal = getRedstoneAtPos(0); if(sendPacket||((prevSignal^currentSignal)¤tSignal)) { - this.sendData(storedData, getDirection("data"), getPOI(MultiblockPOI.DATA_OUTPUT)[0]); + if(energyStorage.extractEnergy(DataInputMachine.energyUsage, true)==DataInputMachine.energyUsage) + { + this.sendData(storedData, getDirection("data"), getPOI(MultiblockPOI.DATA_OUTPUT)[0]); + this.energyStorage.extractEnergy(DataInputMachine.energyUsage, false); + } sendPacket = false; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityPrecisionAssembler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityPrecisionAssembler.java index 05028eeab..fa64462b8 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityPrecisionAssembler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock0/tileentity/TileEntityPrecisionAssembler.java @@ -245,6 +245,17 @@ public void doGraphicalUpdates(int slot) } } + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) + { + if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + { + if(isPOI("item_in")||isPOI("tool1")||isPOI("tool2")||isPOI("tool3")) + return true; + } + return super.hasCapability(capability, facing); + } + @Override @SuppressWarnings("unchecked") public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityFiller.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityFiller.java index 4d78048b6..698d36d56 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityFiller.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityFiller.java @@ -118,7 +118,6 @@ public EnumFacing[] sigOutputDirections() @Override public boolean hasCapability(Capability capability, EnumFacing facing) { - //TODO: 24.12.2023 use positions instead if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { TileEntityFiller master = master(); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityVulcanizer.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityVulcanizer.java index c80ab0540..37992195b 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityVulcanizer.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/TileEntityVulcanizer.java @@ -15,6 +15,7 @@ import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import pl.pabilo8.immersiveintelligence.api.crafting.VulcanizerRecipe; import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIMultiblockRecipe; @@ -107,7 +108,7 @@ public boolean isStackValid(int i, ItemStack itemStack) @Override public T getCapability(Capability capability, EnumFacing facing) { - if(master()!=null) + if(master()!=null&&capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { TileEntityVulcanizer master = master(); if(isPOI("input_sulfur")) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/TileEntityEmplacement.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/TileEntityEmplacement.java index aac599871..2523d968e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/TileEntityEmplacement.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/TileEntityEmplacement.java @@ -268,12 +268,16 @@ protected int[] listAllPOI(MultiblockPOI poi) return getPOI("energy"); case REDSTONE_INPUT: return getPOI("redstone"); - case DATA: + case DATA_INPUT: + return getPOI("data"); + case DATA_OUTPUT: return getPOI("data"); case ITEM_INPUT: + return getPOI("input"); case FLUID_INPUT: return getPOI("input"); case ITEM_OUTPUT: + return getPOI("input"); case FLUID_OUTPUT: return getPOI("output"); case MISC_WEAPON: diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/weapon/EmplacementWeaponInfraredObserver.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/weapon/EmplacementWeaponInfraredObserver.java index bd6c2813e..733a829f7 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/weapon/EmplacementWeaponInfraredObserver.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/metal_multiblock1/tileentity/emplacement/weapon/EmplacementWeaponInfraredObserver.java @@ -1,13 +1,14 @@ package pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.emplacement.weapon; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.Vec3i; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import pl.pabilo8.immersiveintelligence.api.api.protection.ProtectionHandler; import pl.pabilo8.immersiveintelligence.api.data.DataPacket; -import pl.pabilo8.immersiveintelligence.api.utils.armor.IInfraredProtectionEquipment; import pl.pabilo8.immersiveintelligence.client.gui.deco.component.panel.DecoPanel; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Weapons.EmplacementWeapons.InfraredObserver; import pl.pabilo8.immersiveintelligence.common.block.multiblock.metal_multiblock1.tileentity.emplacement.TileEntityEmplacement; @@ -17,7 +18,6 @@ import pl.pabilo8.immersiveintelligence.common.util.multiblock.util.MultiblockInteractablePart; import javax.annotation.Nonnull; -import java.util.stream.StreamSupport; public class EmplacementWeaponInfraredObserver extends EmplacementWeapon { @@ -132,9 +132,8 @@ public int getMaxHealth() @Override public boolean canSeeEntity(Entity entity) { - return StreamSupport.stream(entity.getArmorInventoryList().spliterator(), false) - .noneMatch(stack -> stack.getItem() instanceof IInfraredProtectionEquipment - &&((IInfraredProtectionEquipment)stack.getItem()).invisibleToInfrared(stack)); + return !(entity instanceof EntityLivingBase) + ||!ProtectionHandler.isInvisibleToInfrared((EntityLivingBase)entity); } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySawmill.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySawmill.java index e64ebda7c..e91047c96 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySawmill.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySawmill.java @@ -15,7 +15,10 @@ import net.minecraftforge.items.IItemHandler; import pl.pabilo8.immersiveintelligence.api.crafting.SawmillRecipe; import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIMultiblockRecipe; -import pl.pabilo8.immersiveintelligence.api.rotary.*; +import pl.pabilo8.immersiveintelligence.api.rotary.CapabilityRotaryEnergy; +import pl.pabilo8.immersiveintelligence.api.rotary.IIRotaryUtils; +import pl.pabilo8.immersiveintelligence.api.rotary.IRotaryEnergy; +import pl.pabilo8.immersiveintelligence.api.rotary.RotaryStorage; import pl.pabilo8.immersiveintelligence.api.upgrade.IManagedUpgradableDevice; import pl.pabilo8.immersiveintelligence.api.upgrade.UpgradeManager; import pl.pabilo8.immersiveintelligence.api.utils.IBooleanAnimatedPartsBlock; @@ -27,7 +30,6 @@ import pl.pabilo8.immersiveintelligence.common.block.multiblock.wooden_multiblock.multiblock.MultiblockSawmill; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageBooleanAnimatedPartsSync; -import pl.pabilo8.immersiveintelligence.common.network.messages.MessageRotaryPowerSync; import pl.pabilo8.immersiveintelligence.common.util.IIDamageSources; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; @@ -47,11 +49,11 @@ * @author Pabilo8 (pabilo@iiteam.net) * @since 13.04.2020 */ -public class TileEntitySawmill extends TileEntityMultiblockProductionSingle implements IRotationalEnergyBlock, IBooleanAnimatedPartsBlock, IManagedUpgradableDevice +public class TileEntitySawmill extends TileEntityMultiblockProductionSingle implements IBooleanAnimatedPartsBlock, IManagedUpgradableDevice { @SyncNBT public MultiblockInteractablePart vise; - @SyncNBT + @SyncNBT(events = {SyncEvents.TILE_GUI_OPENED, SyncEvents.TILE_RECIPE_CHANGED, SyncEvents.TILE_ENERGY_CHANGED}) public RotaryStorage rotation = new RotaryStorage(0, 0) { @Override @@ -116,6 +118,19 @@ protected int[] listAllPOI(MultiblockPOI poi) } //--- Capabilities ---// + + + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) + { + if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + { + return isPOI("item_input")||(isPOI("item_output")&&getDirection("output")==facing) + ||(isPOI("sawdust")&&facing==EnumFacing.DOWN); + } + return super.hasCapability(capability, facing); + } + @Override public T getCapability(Capability capability, @Nullable EnumFacing facing) { @@ -156,7 +171,8 @@ protected void onUpdate() assert cap!=null; if(rotation.handleRotation(cap, rotaryFacing)) { - IIPacketHandler.sendToClient(new MessageRotaryPowerSync(world, getPos(), 0, rotation)); + if(!world.isRemote) + updateTileForEvent(SyncEvents.TILE_ENERGY_CHANGED); receivesPower = true; } } @@ -168,7 +184,7 @@ protected void onUpdate() { rotation.grow(0, 0, 0.98f); if(!world.isRemote) - IIPacketHandler.sendToClient(new MessageRotaryPowerSync(world, getPos(), 0, rotation)); + updateTileForEvent(SyncEvents.TILE_ENERGY_CHANGED); } //Hurt entities stepping on sawblade @@ -305,17 +321,7 @@ protected void onProductionFinish(IIMultiblockProcess process) ((ISawblade)sawblade.getItem()).damageTool(sawblade, process.recipe.getHardness()); } - //--- IRotationalEnergyBlock ---// - - @Override - public void updateRotationStorage(float speed, float torque, int partID) - { - if(world.isRemote) - { - rotation.setRotationSpeed(speed); - rotation.setTorque(torque); - } - } + //--- IBooleanAnimatedPartsBlock ---// @Override public void onAnimationChangeClient(boolean state, int part) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCartStation.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCartStation.java index e5a169002..5c243a9b5 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCartStation.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCartStation.java @@ -108,7 +108,7 @@ public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) crate = new ItemStack(nbt.getCompoundTag("crate")); mount = new ItemStack(nbt.getCompoundTag("mount")); if(nbt.hasKey("rotation")) - rotation.fromNBT(nbt.getCompoundTag("rotation")); + rotation.deserializeNBT(nbt.getCompoundTag("rotation")); } } @@ -133,7 +133,7 @@ public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) nbt.setTag("banner", banner.serializeNBT()); nbt.setTag("crate", crate.serializeNBT()); nbt.setTag("mount", mount.serializeNBT()); - nbt.setTag("rotation", rotation.toNBT()); + nbt.setTag("rotation", rotation.serializeNBT()); if(!world.isRemote) getInternalEntity(); @@ -159,7 +159,7 @@ public void receiveMessageFromServer(NBTTagCompound message) if(message.hasKey("occupied")) occupied = message.getBoolean("occupied"); if(message.hasKey("rotation")) - rotation.fromNBT(message.getCompoundTag("rotation")); + rotation.deserializeNBT(message.getCompoundTag("rotation")); super.receiveMessageFromServer(message); } @@ -582,7 +582,7 @@ public void doGraphicalUpdates(int slot) tag.withInt("animation", animation).withFloat("progress", progress).withBoolean("occupied", occupied); break; case 2: - tag.withTag("rotation", rotation.toNBT()).withItemStack("crate", crate).withItemStack("mount", mount); + tag.withTag("rotation", rotation.serializeNBT()).withItemStack("crate", crate).withItemStack("mount", mount); break; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCrateStation.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCrateStation.java index f62bb7590..aacb8713c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCrateStation.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/multiblock/wooden_multiblock/tileentity/TileEntitySkyCrateStation.java @@ -97,7 +97,7 @@ public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) animation = nbt.getInteger("animation"); progress = nbt.getFloat("progress"); if(nbt.hasKey("rotation")) - rotation.fromNBT(nbt.getCompoundTag("rotation")); + rotation.deserializeNBT(nbt.getCompoundTag("rotation")); } } @@ -118,7 +118,7 @@ public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) nbt.setInteger("animation", animation); nbt.setFloat("progress", progress); - nbt.setTag("rotation", rotation.toNBT()); + nbt.setTag("rotation", rotation.serializeNBT()); } } @@ -133,7 +133,7 @@ public void receiveMessageFromServer(NBTTagCompound message) if(message.hasKey("progress")) progress = message.getFloat("progress"); if(message.hasKey("rotation")) - rotation.fromNBT(message.getCompoundTag("rotation")); + rotation.deserializeNBT(message.getCompoundTag("rotation")); super.receiveMessageFromServer(message); } @@ -463,7 +463,7 @@ public void sendUpdate(int id) tag.withInt("animation", animation).withFloat("progress", progress); break; case 2: - tag.withTag("rotation", rotation.toNBT()).withTag("inventory", Utils.writeInventory(inventory)); + tag.withTag("rotation", rotation.serializeNBT()).withTag("inventory", Utils.writeInventory(inventory)); break; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/BlockIIMechanicalConnector.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/BlockIIMechanicalConnector.java index 838a139f1..e239ef5c2 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/BlockIIMechanicalConnector.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/BlockIIMechanicalConnector.java @@ -15,6 +15,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; import net.minecraft.util.NonNullList; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; @@ -38,6 +39,8 @@ /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 20.07.2026 + * @ii-approved 0.3.1 * @since 17.05.2019 */ public class BlockIIMechanicalConnector extends BlockIITileProvider @@ -139,7 +142,7 @@ public ItemStack getPickBlock(@Nonnull IBlockState state, @Nonnull RayTraceResul } } } - if(applicableWires.size() > 0) + if(!applicableWires.isEmpty()) { ItemStack heldItem = pInventory.get(player.inventory.currentItem); if(heldItem.getItem() instanceof IWireCoil) @@ -155,6 +158,12 @@ public ItemStack getPickBlock(@Nonnull IBlockState state, @Nonnull RayTraceResul return super.getPickBlock(state, target, world, pos, player); } + @Override + public boolean canIEBlockBePlaced(World world, BlockPos pos, IBlockState newState, EnumFacing side, float hitX, float hitY, float hitZ, EntityPlayer player, ItemStack stack) + { + return side.getAxis().isHorizontal(); + } + public enum IIBlockTypes_MechanicalConnector implements IITileProviderEnum { @IIBlockProperties(oreDict = "wheelIron", needsCustomState = true) @@ -164,4 +173,4 @@ public enum IIBlockTypes_MechanicalConnector implements IITileProviderEnum @EnumTileProvider(tile = TileEntityWheelSteel.class) STEEL_WHEEL } -} \ No newline at end of file +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityGearbox.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityGearbox.java index 7f85d515f..7cf383edf 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityGearbox.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityGearbox.java @@ -32,6 +32,7 @@ import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; import pl.pabilo8.immersiveintelligence.common.network.messages.MessageRotaryPowerSync; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; import javax.annotation.Nullable; @@ -41,9 +42,10 @@ public class TileEntityGearbox extends TileEntityIEBase implements ITickable, IA public static final int GEAR_SLOTS = 3; public SideConfig[] sideConfig = {SideConfig.NONE, SideConfig.INPUT, SideConfig.NONE, SideConfig.NONE, SideConfig.NONE, SideConfig.NONE}; - public int comparatorOutput = 0; + @SyncNBT public GearboxRotaryStorage rotation = new GearboxRotaryStorage(); - NonNullList inventory = NonNullList.withSize(GEAR_SLOTS, ItemStack.EMPTY); + @SyncNBT + public NonNullList inventory = NonNullList.withSize(GEAR_SLOTS, ItemStack.EMPTY); @Override public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) @@ -198,7 +200,7 @@ public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) nbt.setTag("inventory", Utils.writeInventory(inventory)); for(int i = 0; i < 6; i++) nbt.setInteger("sideConfig_"+i, sideConfig[i].ordinal()); - nbt.setTag("rotation", rotation.toNBT()); + nbt.setTag("rotation", rotation.serializeNBT()); } @@ -218,7 +220,7 @@ public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) for(int i = 0; i < 6; i++) sideConfig[i] = SideConfig.values()[nbt.getInteger("sideConfig_"+i)]; if(nbt.hasKey("rotation")) - rotation.fromNBT(nbt.getCompoundTag("rotation")); + rotation.deserializeNBT(nbt.getCompoundTag("rotation")); } @SideOnly(Side.CLIENT) @@ -242,7 +244,8 @@ public String[] getOverlayText(EntityPlayer player, RayTraceResult mop) @Override public int getComparatorInputOverride() { - return this.comparatorOutput; + //TODO: 19.07.2026 comparator output + return 0; } @Override @@ -330,21 +333,20 @@ public RotationSide getSide(@Nullable EnumFacing facing) } @Override - public void fromNBT(NBTTagCompound nbt) + public void deserializeNBT(NBTTagCompound nbt) { - super.fromNBT(nbt); + super.deserializeNBT(nbt); outputTorque = nbt.getFloat("outputTorque"); outputSpeed = nbt.getFloat("outputSpeed"); } @Override - public NBTTagCompound toNBT() + public NBTTagCompound serializeNBT() { - NBTTagCompound nbt = super.toNBT(); + NBTTagCompound nbt = super.serializeNBT(); nbt.setFloat("outputTorque", outputTorque); nbt.setFloat("outputSpeed", outputSpeed); return nbt; - } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityMechanicalConnectable.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityMechanicalConnectable.java index f5ca1e5f5..c594cc7db 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityMechanicalConnectable.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityMechanicalConnectable.java @@ -4,11 +4,9 @@ import blusunrize.immersiveengineering.api.energy.wires.IImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler; import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; -import blusunrize.immersiveengineering.api.energy.wires.TileEntityImmersiveConnectable; import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.client.models.IOBJModelCallback; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; -import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IHammerInteraction; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; @@ -17,29 +15,28 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; import pl.pabilo8.immersiveintelligence.api.rotary.*; -import pl.pabilo8.immersiveintelligence.common.network.IIPacketHandler; -import pl.pabilo8.immersiveintelligence.common.network.messages.MessageRotaryPowerSync; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectionalConnectable; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Set; -import static blusunrize.immersiveengineering.api.energy.wires.WireApi.canMix; - /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 20.07.2026 + * @ii-approved 0.3.1 * @since 29.12.2019 */ -public abstract class TileEntityMechanicalConnectable extends TileEntityImmersiveConnectable implements IRotaryConnector, ITickable, IDirectionalTile, IHammerInteraction, IBlockBounds, IOBJModelCallback, IRotationalEnergyBlock +public abstract class TileEntityMechanicalConnectable extends TileEntityIIDirectionalConnectable implements IRotaryConnector, ITickable, + IHammerInteraction, IBlockBounds, IOBJModelCallback { - @Nonnull - protected MotorBeltNetwork beltNetwork = new MotorBeltNetwork().add(this); + @SyncNBT(events = SyncEvents.TILE_ENERGY_CHANGED) public RotaryStorage energy = new RotaryStorage() { @Override @@ -51,80 +48,48 @@ public RotationSide getSide(@Nullable EnumFacing facing) @Override public float getOutputRotationSpeed() { - return getNetwork()!=null?(float)getNetwork().getNetworkSpeed(): this.getRotationSpeed(); + return (float)getNetwork().getNetworkSpeed(); } @Override public float getOutputTorque() { - return getNetwork()!=null?(float)getNetwork().getNetworkTorque(): this.getTorque(); + return (float)getNetwork().getNetworkTorque(); } - }; - protected boolean refreshBeltNetwork = false; - - @Override - public void updateRotationStorage(float speed, float torque, int partID) - { - if(world.isRemote) - if(partID==0) - { - energy.setRotationSpeed(speed); - energy.setTorque(torque); - } - else if(partID==1) - getNetwork().setClient(speed, torque); - } - - @Override - public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) - { - if(capability==CapabilityRotaryEnergy.ROTARY_ENERGY) - if(facing==null||facing==getFacing()) - return true; - return super.hasCapability(capability, facing); - } - - @Override - public T getCapability(Capability capability, @Nullable EnumFacing facing) - { - if(capability==CapabilityRotaryEnergy.ROTARY_ENERGY) - if(facing==null||facing==getFacing()) - return (T)energy; - return super.getCapability(capability, facing); - } - @Override - public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) - { - if(!IIRotaryUtils.isMotorBelt(cableType)||!canConnectBelt(((MotorBeltType)cableType))) - return false; - return limitType==null||(this.isRelay()&&canMix(limitType, cableType)); - } - - protected abstract boolean canConnectBelt(MotorBeltType cableType); - - @Override - public void readCustomNBT(@Nonnull NBTTagCompound nbt, boolean descPacket) - { - super.readCustomNBT(nbt, descPacket); - if(nbt.hasKey("energy")) - energy.fromNBT(nbt.getCompoundTag("energy")); - } + @Override + public NBTTagCompound serializeNBT() + { + NBTTagCompound nbt = super.serializeNBT(); + nbt.setFloat("speed_network", (float)getNetwork().getNetworkSpeed()); + nbt.setFloat("torque_network", (float)getNetwork().getNetworkTorque()); + return nbt; + } - @Override - public void writeCustomNBT(@Nonnull NBTTagCompound nbt, boolean descPacket) - { - super.writeCustomNBT(nbt, descPacket); - nbt.setTag("energy", energy.toNBT()); - } + @Override + public void deserializeNBT(NBTTagCompound nbt) + { + super.deserializeNBT(nbt); + getNetwork().setValues(nbt.getFloat("speed_network"), nbt.getFloat("torque_network")); + } + }; + @Nonnull + protected MotorBeltNetwork beltNetwork = new MotorBeltNetwork().add(this); + protected boolean refreshBeltNetwork = false; + protected double prevRotations, rotations; - /** - * Like the old updateEntity(), except more generic. - */ @Override public void update() { - if(hasWorld()&&!world.isRemote) + if(!hasWorld()) + return; + + if(world.isRemote) + { + prevRotations = rotations; + rotations += getOutputSpeed()/IIRotaryUtils.getMaxWorldRotationTicks(); + } + else { if(world.getTotalWorldTime()%20==0) getNetwork().updateValues(); @@ -146,6 +111,8 @@ public void update() beltNetwork.removeFromNetwork(null); } } + + } @Nonnull @@ -164,22 +131,39 @@ public void setNetwork(@Nonnull MotorBeltNetwork net) @Override public void onChange() { - markDirty(); - IBlockState stateHere = world.getBlockState(pos); - markContainingBlockForUpdate(stateHere); - markBlockForUpdate(getConnectionPos(), stateHere); + updateTileForEvent(SyncEvents.TILE_ENERGY_CHANGED); + } - //IIPacketHandler.sendToClient(new MessageRotaryPowerSync(energy, 0, getPos()), Utils.targetPointFromTile(this, 32)); - IIPacketHandler.sendToClient(new MessageRotaryPowerSync(world, getPos(), 1, getNetwork().getEnergyStorage())); + @Override + public boolean isRelay() + { + return false; + } + @Override + public boolean hasCapability(Capability capability, @Nullable EnumFacing facing) + { + if(capability==CapabilityRotaryEnergy.ROTARY_ENERGY) + if(facing==null||facing==getFacing()) + return true; + return super.hasCapability(capability, facing); } - public abstract BlockPos getConnectionPos(); + @Override + public T getCapability(Capability capability, @Nullable EnumFacing facing) + { + if(capability==CapabilityRotaryEnergy.ROTARY_ENERGY) + if(facing==null||facing==getFacing()) + return (T)energy; + return super.getCapability(capability, facing); + } @Override - public World getConnectorWorld() + public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) { - return getWorld(); + if(!IIRotaryUtils.isMotorBelt(cableType)) + return false; + return super.canConnectCable(cableType, target, offset); } @Override @@ -207,13 +191,7 @@ public void processDamage(Entity e, float amount, Connection c) @Override protected float getBaseDamage(Connection c) { - return 1; - } - - @Override - protected float getMaxDamage(Connection c) - { - return 20; + return 10; } @Override @@ -224,6 +202,14 @@ public void removeCable(@Nullable ImmersiveNetHandler.Connection connection) ImmersiveIntelligence.proxy.onMechanicalConnectorRemoved(connection); } + @Override + public float getDamageAmount(Entity e, Connection c) + { + if(c.cableType instanceof MotorBeltType) + return (float)beltNetwork.getNetworkTorque()/4f; + return super.getDamageAmount(e, c); + } + @Override public void invalidate() { @@ -238,12 +224,4 @@ public RotaryStorage getRotaryStorage() { return energy; } - - @Override - public float getDamageAmount(Entity e, Connection c) - { - if(c.cableType instanceof MotorBeltType) - return (float)beltNetwork.getNetworkTorque()/4f; - return super.getDamageAmount(e, c); - } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityTransmissionBox.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityTransmissionBox.java index 9a586cbde..bfe13fe3f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityTransmissionBox.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityTransmissionBox.java @@ -73,14 +73,14 @@ public T getCapability(Capability capability, @Nullable EnumFacing facing public void readCustomNBT(@Nonnull NBTTagCompound nbt, boolean descPacket) { if(nbt.hasKey("energy")) - energy.fromNBT(nbt.getCompoundTag("energy")); + energy.deserializeNBT(nbt.getCompoundTag("energy")); facing = EnumFacing.getFront(nbt.getInteger("facing")); } @Override public void writeCustomNBT(@Nonnull NBTTagCompound nbt, boolean descPacket) { - nbt.setTag("energy", energy.toNBT()); + nbt.setTag("energy", energy.serializeNBT()); nbt.setInteger("facing", facing.ordinal()); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelBase.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelBase.java index cbbca3fdb..a9dec13b4 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelBase.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelBase.java @@ -4,12 +4,8 @@ import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IBlockBounds; import blusunrize.immersiveengineering.common.util.Utils; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing.Axis; import net.minecraft.util.SoundCategory; -import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -17,16 +13,21 @@ import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.api.rotary.MotorBeltType; import pl.pabilo8.immersiveintelligence.client.util.carversound.ConditionCompoundSound; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingLimitation; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingSettings; +import javax.annotation.Nonnull; import java.util.Set; /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 20.07.2026 + * @ii-approved 0.3.1 * @since 29.12.2019 */ public abstract class TileEntityWheelBase extends TileEntityMechanicalConnectable implements IBlockBounds { - public EnumFacing facing = EnumFacing.NORTH; + private static final FacingSettings FACING_SETTINGS = new FacingSettings(FacingLimitation.HORIZONTAL_TOWARDS_CLICKED); @SideOnly(Side.CLIENT) private ConditionCompoundSound loopSound; @@ -87,7 +88,7 @@ private void updateSound() if(!(connection.cableType instanceof MotorBeltType)) continue; loopSound = new ConditionCompoundSound<>(((MotorBeltType)connection.cableType).getLoopSound(), - new Vec3d(pos).addVector(0.5, 0.5, 0.5), this, o -> o.getNetwork().getNetworkSpeed() > 1); + new Vec3d(pos).addVector(0.5, 0.5, 0.5), this, o -> !o.isInvalid()&&o.getNetwork().getNetworkSpeed() > 1); break; } @@ -96,55 +97,11 @@ private void updateSound() loopSound.setPitch(((float)MathHelper.clamp(getNetwork().getNetworkSpeed()/80f, 0, 2))); } + @Nonnull @Override - public EnumFacing getFacing() + public FacingSettings getFacingSettings() { - return this.facing; - } - - @Override - public void setFacing(EnumFacing facing) - { - this.facing = facing; - } - - @Override - public int getFacingLimitation() - { - return 5; - } - - @Override - public boolean mirrorFacingOnPlacement(EntityLivingBase placer) - { - return false; - } - - @Override - public boolean canHammerRotate(EnumFacing side, float hitX, float hitY, float hitZ, EntityLivingBase entity) - { - return false; - } - - @Override - public boolean canRotate(EnumFacing axis) - { - return false; - } - - - @Override - public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.writeCustomNBT(nbt, descPacket); - nbt.setInteger("facing", facing.ordinal()); - } - - @Override - public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) - { - super.readCustomNBT(nbt, descPacket); - facing = EnumFacing.getFront(nbt.getInteger("facing")); + return FACING_SETTINGS; } @Override @@ -156,19 +113,10 @@ public Vec3d getConnectionOffset(Connection con) @Override public void onConnectivityUpdate(BlockPos pos, int dimension) { + super.onConnectivityUpdate(pos, dimension); refreshBeltNetwork = false; } - @SideOnly(Side.CLIENT) - @Override - public AxisAlignedBB getRenderBoundingBox() - { - int inc = getRenderRadiusIncrease(); - return new AxisAlignedBB(this.pos.getX()-inc, this.pos.getY()-inc, this.pos.getZ()-inc, this.pos.getX()+inc+1, this.pos.getY()+inc+1, this.pos.getZ()+inc+1); - } - - protected abstract int getRenderRadiusIncrease(); - @Override public float[] getBlockBounds() { @@ -189,6 +137,32 @@ public float[] getBlockBounds() return new float[]{0, 0, 0, 1, 1, 1}; } + @Override + public float getDisplayedRotationProgress(boolean belt, float partialTicks) + { + double rotation = prevRotations+(rotations-prevRotations)*partialTicks; + double maximum = belt?getBeltRotationMaximum(): 1d; + double progress = rotation%maximum; + return (float)((progress < 0?progress+maximum: progress)/maximum); + } + + private double getBeltRotationMaximum() + { + Set connections = ImmersiveNetHandler.INSTANCE.getConnections(world, pos); + if(connections!=null) + for(Connection connection : connections) + if(connection.cableType instanceof MotorBeltType) + { + double x = connection.end.getX()-connection.start.getX(); + double y = connection.end.getY()-connection.start.getY(); + double z = connection.end.getZ()-connection.start.getZ(); + double circumference = 2*Math.PI*(getRadius()+1)/16d; + double beltLength = 2*Math.sqrt(x*x+y*y+z*z)+circumference; + return beltLength/circumference; + } + return 1d; + } + /** * Only for visuals */ @@ -201,22 +175,8 @@ public double getOutputSpeed() @Override public Axis getConnectionAxis() { - switch(facing) - { - case NORTH: - case SOUTH: - return Axis.X; - case EAST: - case WEST: - return Axis.Z; - } - return Axis.Y; - } - - - @Override - public BlockPos getConnectionPos() - { - return getPos().offset(facing); + if(facing.getAxis()==Axis.Y) + return Axis.Y; + return facing.rotateY().getAxis(); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelIron.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelIron.java index d81bdb813..ac246a9fc 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelIron.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelIron.java @@ -1,14 +1,20 @@ package pl.pabilo8.immersiveintelligence.common.block.rotary_device.tileentity; +import blusunrize.immersiveengineering.api.energy.wires.WireType; import pl.pabilo8.immersiveintelligence.api.rotary.IIRotaryUtils; -import pl.pabilo8.immersiveintelligence.api.rotary.MotorBeltType; +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @updated 20.07.2026 + * @ii-approved 0.3.1 + * @since 08.08.2024 + */ public class TileEntityWheelIron extends TileEntityWheelBase { @Override - protected int getRenderRadiusIncrease() + public boolean acceptsWireType(WireType category) { - return limitType!=null?limitType.getMaxLength(): 2; + return IIRotaryUtils.BELT_CATEGORY.equals(category.getCategory()); } @Override @@ -16,10 +22,4 @@ public float getRadius() { return 6; } - - @Override - protected boolean canConnectBelt(MotorBeltType cableType) - { - return cableType.getBeltCategory().equals(IIRotaryUtils.BELT_CATEGORY); - } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelSteel.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelSteel.java index c3c3c9b81..6f5a35480 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelSteel.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/rotary_device/tileentity/TileEntityWheelSteel.java @@ -1,13 +1,21 @@ package pl.pabilo8.immersiveintelligence.common.block.rotary_device.tileentity; -import pl.pabilo8.immersiveintelligence.api.rotary.MotorBeltType; +import blusunrize.immersiveengineering.api.energy.wires.WireType; +import pl.pabilo8.immersiveintelligence.api.rotary.IIRotaryUtils; +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @updated 20.07.2026 + * @ii-approved 0.3.1 + * @since 08.08.2024 + */ public class TileEntityWheelSteel extends TileEntityWheelBase { @Override - protected int getRenderRadiusIncrease() + public boolean acceptsWireType(WireType category) { - return limitType!=null?limitType.getMaxLength(): 2; + return (IIRotaryUtils.BELT_CATEGORY.equals(category.getCategory())|| + IIRotaryUtils.TRACK_CATEGORY.equals(category.getCategory())); } @Override @@ -15,10 +23,4 @@ public float getRadius() { return 8; } - - @Override - protected boolean canConnectBelt(MotorBeltType cableType) - { - return true; - } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/simple/BlockIIHarbor.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/simple/BlockIIHarbor.java new file mode 100644 index 000000000..9d0597fb7 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/simple/BlockIIHarbor.java @@ -0,0 +1,184 @@ +package pl.pabilo8.immersiveintelligence.common.block.simple; + +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.PropertyBool; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.state.BlockFaceShape; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.IStringSerializable; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import pl.pabilo8.immersiveintelligence.common.block.simple.BlockIIHarbor.HarborType; +import pl.pabilo8.immersiveintelligence.common.util.block.BlockIIBase; +import pl.pabilo8.immersiveintelligence.common.util.block.IIBlockInterfaces.IIBlockEnum; +import pl.pabilo8.immersiveintelligence.common.util.block.IIBlockInterfaces.IIBlockProperties; +import pl.pabilo8.immersiveintelligence.common.util.block.ItemBlockIIBase; +import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Locale; + +/** + * Connected harbor floor pieces. The saved block state contains only {@link HarborType}; + * the support model and quay-to-land contact are derived from neighbouring blocks. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @since 21.07.2026 + */ +public class BlockIIHarbor extends BlockIIBase +{ + public static final PropertyBool CONNECTED = PropertyBool.create("connected"); + public static final PropertyEnum SUPPORT = PropertyEnum.create("support", HarborConnection.class); + public static final PropertyBool SOLID_NEIGHBOR = PropertyBool.create("solid_neighbor"); + + public BlockIIHarbor() + { + super("harbor", PropertyEnum.create("type", HarborType.class), Material.WOOD, ItemBlockIIBase::new, + CONNECTED, SUPPORT, SOLID_NEIGHBOR); + setHardness(3.0F); + setResistance(15.0F); + setCategory(IICategory.RESOURCES); + setFullCube(false); + setLightOpacity(0); + setBlockLayer(BlockRenderLayer.CUTOUT_MIPPED); + setHarvestLevel("axe", 0); + } + + @Override + protected IBlockState getInitDefaultState() + { + return super.getInitDefaultState() + .withProperty(CONNECTED, false) + .withProperty(SUPPORT, HarborConnection.CENTER) + .withProperty(SOLID_NEIGHBOR, false); + } + + @Override + @Nonnull + @ParametersAreNonnullByDefault + public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) + { + state = super.getActualState(state, world, pos); + boolean connected = hasHarborNeighbor(world, pos); + HarborConnection connection = getConnection(world, pos); + boolean solidNeighbor = state.getValue(property)==HarborType.WOODEN_QUAY + &&connection!=HarborConnection.CENTER + &&hasSolidNeighbor(world, pos); + return state + .withProperty(CONNECTED, connected) + .withProperty(SUPPORT, connection) + .withProperty(SOLID_NEIGHBOR, solidNeighbor); + } + + private boolean hasHarborNeighbor(IBlockAccess world, BlockPos pos) + { + for(EnumFacing facing : EnumFacing.HORIZONTALS) + if(isHarbor(world, pos.offset(facing))) + return true; + return false; + } + + private HarborConnection getConnection(IBlockAccess world, BlockPos pos) + { + int x = 0; + int z = 0; + + if(!isHarbor(world, pos.north())) z--; + if(!isHarbor(world, pos.south())) z++; + if(!isHarbor(world, pos.west())) x--; + if(!isHarbor(world, pos.east())) x++; + + return HarborConnection.fromOffset(Integer.signum(x), Integer.signum(z)); + } + + private boolean isHarbor(IBlockAccess world, BlockPos pos) + { + return world.getBlockState(pos).getBlock()==this; + } + + private boolean hasSolidNeighbor(IBlockAccess world, BlockPos pos) + { + for(EnumFacing facing : EnumFacing.HORIZONTALS) + { + BlockPos neighborPos = pos.offset(facing); + IBlockState neighbor = world.getBlockState(neighborPos); + if(neighbor.getBlock()!=this&&neighbor.isSideSolid(world, neighborPos, facing.getOpposite())) + return true; + } + return false; + } + + @Override + public String getMappingsExtension(int meta, boolean itemBlock) + { + String extension = super.getMappingsExtension(meta, itemBlock); + return itemBlock&&extension!=null?extension+"_item": extension; + } + + @Override + @Nonnull + @ParametersAreNonnullByDefault + public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side) + { + return side==EnumFacing.UP?BlockFaceShape.SOLID: BlockFaceShape.UNDEFINED; + } + + @Override + @ParametersAreNonnullByDefault + public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos) + { + return true; + } + + public enum HarborType implements IIBlockEnum + { + @IIBlockProperties(needsCustomState = true) + WOODEN_PIER, + @IIBlockProperties(needsCustomState = true) + WOODEN_QUAY + } + + /** + * Direction of the exposed edge of a connected harbor surface. + * Opposite exposed edges cancel into {@link #CENTER}; this keeps lines and isolated blocks deterministic + * while preserving all nine useful states for rectangular placements. + */ + public enum HarborConnection implements IStringSerializable + { + CENTER(0, 0), + N(0, -1), + NE(1, -1), + E(1, 0), + SE(1, 1), + S(0, 1), + SW(-1, 1), + W(-1, 0), + NW(-1, -1); + + private final int x; + private final int z; + + HarborConnection(int x, int z) + { + this.x = x; + this.z = z; + } + + public static HarborConnection fromOffset(int x, int z) + { + for(HarborConnection connection : values()) + if(connection.x==x&&connection.z==z) + return connection; + return CENTER; + } + + @Override + public String getName() + { + return name().toLowerCase(Locale.ENGLISH); + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/block/simple/BlockIIHarborSupport.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/simple/BlockIIHarborSupport.java new file mode 100644 index 000000000..c5168a7ba --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/block/simple/BlockIIHarborSupport.java @@ -0,0 +1,118 @@ +package pl.pabilo8.immersiveintelligence.common.block.simple; + +import net.minecraft.block.material.Material; +import net.minecraft.block.properties.PropertyBool; +import net.minecraft.block.properties.PropertyEnum; +import net.minecraft.block.state.BlockFaceShape; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import pl.pabilo8.immersiveintelligence.common.block.simple.BlockIIHarborSupport.HarborSupportType; +import pl.pabilo8.immersiveintelligence.common.util.block.BlockIIBase; +import pl.pabilo8.immersiveintelligence.common.util.block.IIBlockInterfaces.IIBlockEnum; +import pl.pabilo8.immersiveintelligence.common.util.block.IIBlockInterfaces.IIBlockProperties; +import pl.pabilo8.immersiveintelligence.common.util.block.ItemBlockIIBase; +import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +/** + * Vertical harbor support. A column can add a concrete foot where it meets solid ground + * and a top fitting where it directly supports a harbor floor. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @since 21.07.2026 + */ +public class BlockIIHarborSupport extends BlockIIBase +{ + public static final PropertyBool BOTTOM = PropertyBool.create("bottom"); + public static final PropertyBool TOP = PropertyBool.create("top"); + private static final AxisAlignedBB SUPPORT_AABB = new AxisAlignedBB(0.25, 0, 0.25, 0.75, 1, 0.75); + + public BlockIIHarborSupport() + { + super("harbor_support", PropertyEnum.create("type", HarborSupportType.class), Material.WOOD, + ItemBlockIIBase::new, BOTTOM, TOP); + setHardness(3.0F); + setResistance(15.0F); + setCategory(IICategory.RESOURCES); + setFullCube(false); + setLightOpacity(0); + setBlockLayer(BlockRenderLayer.CUTOUT_MIPPED); + setHarvestLevel("axe", 0); + } + + @Override + protected IBlockState getInitDefaultState() + { + return super.getInitDefaultState() + .withProperty(BOTTOM, false) + .withProperty(TOP, false); + } + + @Override + @Nonnull + @ParametersAreNonnullByDefault + public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) + { + state = super.getActualState(state, world, pos); + + BlockPos belowPos = pos.down(); + IBlockState below = world.getBlockState(belowPos); + boolean bottom = below.getBlock()!=this&&below.isSideSolid(world, belowPos, EnumFacing.UP); + boolean top = world.getBlockState(pos.up()).getBlock() instanceof BlockIIHarbor; + + return state + .withProperty(BOTTOM, bottom) + .withProperty(TOP, top); + } + + @Override + public String getMappingsExtension(int meta, boolean itemBlock) + { + String extension = super.getMappingsExtension(meta, itemBlock); + return itemBlock&&extension!=null?extension+"_item": extension; + } + + @Override + @Nonnull + @ParametersAreNonnullByDefault + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos) + { + return SUPPORT_AABB; + } + + @Nullable + @Override + @ParametersAreNonnullByDefault + public AxisAlignedBB getCollisionBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos) + { + return SUPPORT_AABB; + } + + @Override + @Nonnull + @ParametersAreNonnullByDefault + public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing side) + { + return side.getAxis()==EnumFacing.Axis.Y?BlockFaceShape.CENTER_BIG: BlockFaceShape.MIDDLE_POLE; + } + + @Override + @ParametersAreNonnullByDefault + public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos) + { + return true; + } + + public enum HarborSupportType implements IIBlockEnum + { + @IIBlockProperties(needsCustomState = true) + WOODEN_PIER_SUPPORT + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteAccept.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteAccept.java index da66087c8..53ec5c3a3 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteAccept.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteAccept.java @@ -4,12 +4,18 @@ import net.minecraft.command.ICommandSender; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.TextComponentString; import net.minecraftforge.server.command.CommandTreeBase; import pl.pabilo8.immersiveintelligence.common.util.CommandIIBase; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.OwnerIdentity; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + public class CommandFactionInviteAccept extends CommandIIBase { public CommandFactionInviteAccept(CommandTreeBase parent) @@ -29,6 +35,18 @@ public String getDescription(ICommandSender sender) return "Accept a pending invitation from a faction"; } + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) + { + if(args.length==1) + return DiplomacyHandler.getInstance(false) + .getPendingInvitationIdentitiesForPlayer(((EntityPlayer)sender).getUniqueID()) + .stream() + .map(OwnerIdentity::getDisplayName) + .collect(Collectors.toList()); + return Collections.emptyList(); + } + @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteList.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteList.java index a753dc2a1..b060b11e2 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteList.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/invite/CommandFactionInviteList.java @@ -8,9 +8,11 @@ import net.minecraftforge.server.command.CommandTreeBase; import pl.pabilo8.immersiveintelligence.common.util.CommandIIBase; import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.OwnerIdentity; -import java.util.Set; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; public class CommandFactionInviteList extends CommandIIBase { @@ -37,10 +39,13 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args if(!(sender instanceof EntityPlayer)) throw new CommandException("Player only."); UUID uuid = ((EntityPlayer)sender).getUniqueID(); DiplomacyHandler diplomacy = DiplomacyHandler.getInstance(false); - Set invites = diplomacy.getPendingInvitationsForPlayer(uuid); + List invites = diplomacy.getPendingInvitationIdentitiesForPlayer(uuid); if(invites.isEmpty()) sender.sendMessage(new TextComponentString("No pending invitations.")); else - sender.sendMessage(new TextComponentString("Pending invitations: "+String.join(", ", invites))); + sender.sendMessage(new TextComponentString(invites.stream() + .map(OwnerIdentity::getDisplayName) + .collect(Collectors.joining(", ", "Pending invitations: ", "")) + )); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/set/CommandFactionSet.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/set/CommandFactionSet.java index 81b7dcf9a..87708b232 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/set/CommandFactionSet.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/set/CommandFactionSet.java @@ -10,6 +10,7 @@ public CommandFactionSet(CommandTreeBase parent) addSubcommand(new CommandFactionSetName(this)); addSubcommand(new CommandFactionSetColor(this)); addSubcommand(new CommandFactionSetBanner(this)); + addSubcommand(new CommandFactionSetLawForm(this)); } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/set/CommandFactionSetLawForm.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/set/CommandFactionSetLawForm.java new file mode 100644 index 000000000..f935acbe5 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/faction/set/CommandFactionSetLawForm.java @@ -0,0 +1,70 @@ +package pl.pabilo8.immersiveintelligence.common.commands.faction.set; + +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentString; +import net.minecraftforge.server.command.CommandTreeBase; +import pl.pabilo8.immersiveintelligence.common.util.CommandIIBase; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.DiplomacyHandler; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.LawForm; +import pl.pabilo8.immersiveintelligence.common.util.diplomacy.OwnerIdentity; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +public class CommandFactionSetLawForm extends CommandIIBase +{ + public CommandFactionSetLawForm(CommandTreeBase parent) + { + super(parent, "lawform"); + } + + @Override + public String getSyntax() + { + return ""; + } + + @Override + public String getDescription(ICommandSender sender) + { + return "Change your faction's law form"; + } + + @Override + public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) + { + if(args.length==1) + return getTabCompletionsEnum(args, LawForm.class); + return Collections.emptyList(); + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException + { + if(!(sender instanceof EntityPlayer)) + throw new CommandException("Player only."); + + if(args.length < 1) + throw new CommandException("Specify a law form."); + + DiplomacyHandler diplomacy = DiplomacyHandler.getInstance(false); + OwnerIdentity faction = diplomacy.getOwnerIdentityForEntity((EntityPlayer)sender); + if(faction.isInvalid()||!faction.isOwner(((EntityPlayer)sender).getUniqueID())) + throw new CommandException("You must be an owner to change the faction's law form."); + + try + { + faction.withLawForm(LawForm.valueOf(String.join(" ", args))); + diplomacy.saveAndSyncIdentity(faction); + sender.sendMessage(new TextComponentString("Faction law form set to "+args[0])); + } catch(Exception e) + { + throw new CommandException("Invalid law form."); + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGiveMagazine.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGiveMagazine.java index 2264a8fb1..1f37f2adc 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGiveMagazine.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGiveMagazine.java @@ -98,17 +98,11 @@ public int getRequiredPermissionLevel() public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { if(args.length==1) - { return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()); - } else if(args.length==2) - { return getListOfStringsMatchingLastWord(args, IIContent.itemBulletMagazine.getSubNames()); - } else if(args.length==3) - { return getListOfStringsMatchingLastWord(args, AmmoRegistry.getAllCores().stream().map(AmmoCore::getName).collect(Collectors.toList())); - } else if(args.length==4) { if(!ArrayUtils.contains(IIContent.itemBulletMagazine.getSubNames(), args[1])) @@ -121,9 +115,7 @@ else if(args.length==4) .collect(Collectors.toList())); } else if(args.length > 4) - { return getListOfStringsMatchingLastWord(args, AmmoRegistry.getAllComponents().stream().map(AmmoComponent::getName).collect(Collectors.toList())); - } else return Collections.emptyList(); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGivePunchtape.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGivePunchtape.java index 55cfe853a..453b9b373 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGivePunchtape.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/commands/item/CommandIIGivePunchtape.java @@ -91,9 +91,7 @@ public int getRequiredPermissionLevel() public List getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos targetPos) { if(args.length==1) - { return getListOfStringsMatchingLastWord(args, server.getOnlinePlayerNames()); - } else return Collections.emptyList(); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/FluidloggedAPIHelper.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/FluidloggedAPIHelper.java index 5d0c9b0ee..573603851 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/FluidloggedAPIHelper.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/FluidloggedAPIHelper.java @@ -33,7 +33,9 @@ public void preInit() { IILogger.info("Adding Fluidlogged-API config file."); FileWriter writer = new FileWriter(output); - writer.write(stream.read()); + int read; + while((read = stream.read())!=-1) + writer.write(read); writer.close(); } } catch(Exception e) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/IICompatModule.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/IICompatModule.java index 8e65b41fa..6f2fdffb6 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/IICompatModule.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/IICompatModule.java @@ -12,6 +12,7 @@ import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig; import pl.pabilo8.immersiveintelligence.common.IILogger; import pl.pabilo8.immersiveintelligence.common.compat.dd.DeeperDepthsHelper; +import pl.pabilo8.immersiveintelligence.common.compat.ie.ImmersiveEngineeringHelper; import pl.pabilo8.immersiveintelligence.common.compat.it.ImmersiveTechnologyHelper; import pl.pabilo8.immersiveintelligence.common.compat.nb.NetherBackportHelper; import pl.pabilo8.immersiveintelligence.common.compat.thaum.ThaumcraftHelper; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ImmersiveEngineeringHelper.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ImmersiveEngineeringHelper.java deleted file mode 100644 index fe79e1cfe..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ImmersiveEngineeringHelper.java +++ /dev/null @@ -1,199 +0,0 @@ -package pl.pabilo8.immersiveintelligence.common.compat; - -import blusunrize.immersiveengineering.api.crafting.IngredientStack; -import blusunrize.immersiveengineering.api.tool.RailgunHandler; -import blusunrize.immersiveengineering.client.ImmersiveModelRegistry; -import blusunrize.immersiveengineering.client.ImmersiveModelRegistry.ItemModelReplacement_OBJ; -import blusunrize.immersiveengineering.client.render.ItemRendererIEOBJ; -import blusunrize.immersiveengineering.common.IEContent; -import blusunrize.immersiveengineering.common.items.ItemToolUpgrade.ToolUpgrades; -import blusunrize.immersiveengineering.common.util.IEVillagerHandler; -import blusunrize.immersiveengineering.common.util.Utils; -import blusunrize.immersiveengineering.common.util.chickenbones.Matrix4; -import com.google.common.collect.ImmutableSet; -import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; -import net.minecraft.entity.IMerchant; -import net.minecraft.entity.passive.EntityVillager; -import net.minecraft.entity.passive.EntityVillager.ITradeList; -import net.minecraft.init.Items; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.village.MerchantRecipe; -import net.minecraft.village.MerchantRecipeList; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerCareer; -import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession; -import net.minecraftforge.fml.relauncher.ReflectionHelper; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig; -import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Weapons.Railgun; -import pl.pabilo8.immersiveintelligence.common.IIContent; -import pl.pabilo8.immersiveintelligence.common.IILogger; -import pl.pabilo8.immersiveintelligence.common.block.simple.BlockIEFluidConcreteOverride; -import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIAmmoCasing.Casing; -import pl.pabilo8.immersiveintelligence.common.item.crafting.ItemIIMaterial.Materials; -import pl.pabilo8.immersiveintelligence.common.item.weapons.ItemIIRailgunOverride; - -import java.util.List; -import java.util.Random; - -/** - * @author Pabilo8 (pabilo@iiteam.net) - * @updated 11.01.2026 - * @ii-approved 0.3.1 - * @since 17.08.2023 - */ -public class ImmersiveEngineeringHelper extends IICompatModule -{ - @Override - public void preInit() - { - if(Railgun.enableRailgunOverride) - { - IEContent.itemRailgun = new ItemIIRailgunOverride(); - IILogger.info("Immersive Engineering Railgun was overridden by Immersive Intelligence"); - } - if(IIConfig.concreteOverride) - { - IEContent.blockFluidConcrete = new BlockIEFluidConcreteOverride(); - ReflectionHelper.setPrivateValue(Fluid.class, IEContent.fluidConcrete, IEContent.blockFluidConcrete, "block"); - IILogger.info("Immersive Engineering Fluid Concrete was overridden by Immersive Intelligence"); - } - - ReflectionHelper.setPrivateValue(ToolUpgrades.class, ToolUpgrades.REVOLVER_BAYONET, ImmutableSet.of("REVOLVER", "SUBMACHINEGUN", "RIFLE"), "toolset"); - } - - @Override - public String getName() - { - return "ImmersiveEngineering"; - } - - @Override - public void registerRecipes() - { - - } - - @Override - public void init() - { - IILogger.info("Adding Railgun Projectiles"); - RailgunHandler.registerProjectileProperties(new IngredientStack("stickTungsten"), 32, 1.3).setColourMap(new int[][]{{0xCBD1D6, 0xCBD1D6, 0xCBD1D6, 0xCBD1D6, 0x9EA2A7, 0x9EA2A7}}); - } - - @Override - public void postInit() - { - VillagerCareer gunsmithCareer; - if(IEVillagerHandler.PROF_ENGINEER==null||(gunsmithCareer = findVillagerCareer(IEVillagerHandler.PROF_ENGINEER, "immersiveengineering.gunsmith"))==null) - { - IILogger.error("Could not modify Gunsmith villager to sell II iron gun barrels."); - return; - } - - List trades = gunsmithCareer.getTrades(1); - if(trades!=null) - { - gunsmithCareer.addTrade(1, - new ItemstackForEmerald(IIContent.itemMaterial.getStack(Materials.IRON_GUN_BARREL), new EntityVillager.PriceInfo(2, 4)) - ); - gunsmithCareer.addTrade(2, - new ItemstackForEmerald(IIContent.itemAmmoCasing.getStack(Casing.SMG_1BCAL, 12), new EntityVillager.PriceInfo(1, 5)), - new ItemstackForEmerald(IIContent.itemAmmoCasing.getStack(Casing.MG_2BCAL, 12), new EntityVillager.PriceInfo(2, 6)) - ); - gunsmithCareer.addTrade(3, - new ItemstackForEmerald(IIContent.itemAmmoCasing.getStack(Casing.STG_1BCAL, 8), new EntityVillager.PriceInfo(6, 12)) - ); - gunsmithCareer.addTrade(4, - new ItemstackForEmerald(IIContent.itemMaterial.getStack(Materials.TUNGSTEN_GUN_BARREL), new EntityVillager.PriceInfo(6, 12)) - ); - } - - } - - private VillagerCareer findVillagerCareer(VillagerProfession profession, String careerName) - { - VillagerCareer career = profession.getCareer(0), careerZero = career; - int iterator = 1; - do - { - if(career.getName().equals(careerName)) - return career; - career = profession.getCareer(iterator++); - } - while(career!=careerZero); - return career; - } - - @SideOnly(Side.CLIENT) - @Override - public void clientPreInit() - { - //Railgun overwrite Sunlight Railgun Overdrive! - ImmersiveModelRegistry.instance.registerCustomItemModel(new ItemStack(IEContent.itemRailgun, 1, 0), new ItemModelReplacement_OBJ("immersiveengineering:models/item/railgun.obj", true) - .setTransformations(TransformType.FIRST_PERSON_RIGHT_HAND, new Matrix4().scale(.125, .125, .125).translate(-.1875f, 2.5f, .25f).rotate(Math.PI*.46875, 0, 1, 0).translate(0.5, 0.25, -0.75f) - .rotate(Math.PI*.0225, 0, 0, 1).scale(1.125, 1.125, 1.125)) - .setTransformations(TransformType.FIRST_PERSON_LEFT_HAND, new Matrix4().scale(.125, .125, .125).translate(-1.75, 1.625, .875).rotate(-Math.PI*.46875, 0, 1, 0)) - .setTransformations(TransformType.THIRD_PERSON_RIGHT_HAND, new Matrix4().scale(.1875, .1875, .1875).translate(0.5, 0.5f, -3.5).rotate(Math.PI*.40125, 0, 1, 0)) - .setTransformations(TransformType.THIRD_PERSON_LEFT_HAND, new Matrix4().translate(-.1875, .5, -.3125).scale(.1875, .1875, .1875).rotate(-Math.PI*.46875, 0, 1, 0).rotate(-Math.PI*.25, 0, 0, 1)) - .setTransformations(TransformType.FIXED, new Matrix4().translate(.1875, .0625, .0625).scale(.125, .125, .125).rotate(-Math.PI*.25, 0, 0, 1)) - .setTransformations(TransformType.GUI, new Matrix4().translate(-.1875, 0, 0).scale(.1875, .1875, .1875).rotate(-Math.PI*.6875, 0, 1, 0).rotate(-Math.PI*.1875, 0, 0, 1)) - .setTransformations(TransformType.GROUND, new Matrix4().translate(.125, .125, .0625).scale(.125, .125, .125))); - IEContent.itemRailgun.setTileEntityItemStackRenderer(ItemRendererIEOBJ.INSTANCE); - } - - @SideOnly(Side.CLIENT) - @Override - public void clientInit() - { - - } - - @SideOnly(Side.CLIENT) - @Override - public void clientPostInit() - { - - } - - private static class ItemstackForEmerald implements EntityVillager.ITradeList - { - public ItemStack sellingItem; - public EntityVillager.PriceInfo priceInfo; - - public ItemstackForEmerald(Item par1Item, EntityVillager.PriceInfo priceInfo) - { - this.sellingItem = new ItemStack(par1Item); - this.priceInfo = priceInfo; - } - - public ItemstackForEmerald(ItemStack stack, EntityVillager.PriceInfo priceInfo) - { - this.sellingItem = stack; - this.priceInfo = priceInfo; - } - - @Override - public void addMerchantRecipe(IMerchant merchant, MerchantRecipeList recipeList, Random random) - { - int i = 1; - if(this.priceInfo!=null) - i = this.priceInfo.getPrice(random); - ItemStack itemstack; - ItemStack itemstack1; - if(i < 0) - { - itemstack = new ItemStack(Items.EMERALD); - itemstack1 = Utils.copyStackWithAmount(sellingItem, -i); - } - else - { - itemstack = new ItemStack(Items.EMERALD, i, 0); - itemstack1 = Utils.copyStackWithAmount(sellingItem, 1); - } - recipeList.add(new MerchantRecipe(itemstack, itemstack1)); - } - } -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/event/IEOverrideEventHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/IEOverrideEventHandler.java similarity index 95% rename from src/main/java/pl/pabilo8/immersiveintelligence/common/event/IEOverrideEventHandler.java rename to src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/IEOverrideEventHandler.java index e266a65af..52fa2b259 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/event/IEOverrideEventHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/IEOverrideEventHandler.java @@ -1,4 +1,4 @@ -package pl.pabilo8.immersiveintelligence.common.event; +package pl.pabilo8.immersiveintelligence.common.compat.ie; import blusunrize.immersiveengineering.common.EventHandler; import blusunrize.immersiveengineering.common.IEContent; @@ -31,6 +31,7 @@ public void digSpeedEvent(BreakSpeed event) event.setNewSpeed(event.getOriginalSpeed()*5); else event.setCanceled(true); + //Patch wire cutters behavior on razor wire if(event.getState().getBlock()==IEContent.blockMetalDecoration2&&IEContent.blockMetalDecoration2.getMetaFromState(event.getState())==BlockTypes_MetalDecoration2.RAZOR_WIRE.getMeta()) if(!Utils.isWirecutter(current)) { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/ImmersiveEngineeringHelper.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/ImmersiveEngineeringHelper.java new file mode 100644 index 000000000..e212919be --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/ImmersiveEngineeringHelper.java @@ -0,0 +1,307 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie; + +import blusunrize.immersiveengineering.api.crafting.*; +import blusunrize.immersiveengineering.api.tool.RailgunHandler; +import blusunrize.immersiveengineering.client.ClientUtils; +import blusunrize.immersiveengineering.client.ImmersiveModelRegistry; +import blusunrize.immersiveengineering.client.ImmersiveModelRegistry.ItemModelReplacement_OBJ; +import blusunrize.immersiveengineering.client.gui.GuiCrate; +import blusunrize.immersiveengineering.client.gui.GuiIEContainerBase; +import blusunrize.immersiveengineering.client.render.ItemRendererIEOBJ; +import blusunrize.immersiveengineering.common.IEContent; +import blusunrize.immersiveengineering.common.blocks.wooden.TileEntityWoodenCrate; +import blusunrize.immersiveengineering.common.gui.ContainerIEBase; +import blusunrize.immersiveengineering.common.items.ItemToolUpgrade.ToolUpgrades; +import blusunrize.immersiveengineering.common.util.IEVillagerHandler; +import blusunrize.immersiveengineering.common.util.Utils; +import blusunrize.immersiveengineering.common.util.chickenbones.Matrix4; +import com.google.common.collect.ImmutableSet; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; +import net.minecraft.entity.IMerchant; +import net.minecraft.entity.passive.EntityVillager.ITradeList; +import net.minecraft.entity.passive.EntityVillager.PriceInfo; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.inventory.Container; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.village.MerchantRecipe; +import net.minecraft.village.MerchantRecipeList; +import net.minecraftforge.client.event.GuiOpenEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.EnumHelper; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerCareer; +import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession; +import net.minecraftforge.fml.relauncher.ReflectionHelper; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import pl.pabilo8.immersiveintelligence.client.gui.block.overrides.GuiIECrateOverride; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Overrides; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Overrides.Railgun; +import pl.pabilo8.immersiveintelligence.common.IIContent; +import pl.pabilo8.immersiveintelligence.common.IIGUI; +import pl.pabilo8.immersiveintelligence.common.IILogger; +import pl.pabilo8.immersiveintelligence.common.block.simple.BlockIEFluidConcreteOverride; +import pl.pabilo8.immersiveintelligence.common.compat.IICompatModule; +import pl.pabilo8.immersiveintelligence.common.compat.ie.recipe.*; +import pl.pabilo8.immersiveintelligence.common.gui.ContainerIICrate; +import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIAmmoCasing.Casing; +import pl.pabilo8.immersiveintelligence.common.item.crafting.ItemIIMaterial.Materials; +import pl.pabilo8.immersiveintelligence.common.item.weapons.ItemIIRailgunOverride; +import pl.pabilo8.immersiveintelligence.common.util.IIReflectionUtils; + +import java.util.List; +import java.util.Random; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @updated 11.01.2026 + * @ii-approved 0.3.1 + * @since 17.08.2023 + */ +public class ImmersiveEngineeringHelper extends IICompatModule +{ + public static IIGUI GUI_IE_CRATE_OVERRIDE = null; + + @Override + public void preInit() + { + if(Railgun.enableRailgunOverride) + { + IEContent.itemRailgun = new ItemIIRailgunOverride(); + IILogger.info("Immersive Engineering Railgun was overridden by Immersive Intelligence"); + } + if(Overrides.concreteOverride) + { + IEContent.blockFluidConcrete = new BlockIEFluidConcreteOverride(); + ReflectionHelper.setPrivateValue(Fluid.class, IEContent.fluidConcrete, IEContent.blockFluidConcrete, "block"); + IILogger.info("Immersive Engineering Fluid Concrete was overridden by Immersive Intelligence"); + } + + if(Overrides.enableDecoOverride) + { + GUI_IE_CRATE_OVERRIDE = EnumHelper.addEnum(IIGUI.class, "IE_CRATE_OVERRIDE", new Class[]{Class.class, BiFunction.class}, + TileEntityWoodenCrate.class, (BiFunction) + (player, te) -> new ContainerIICrate<>(player, (TileEntityWoodenCrate)te) + ); + } + + ReflectionHelper.setPrivateValue(ToolUpgrades.class, ToolUpgrades.REVOLVER_BAYONET, ImmutableSet.of("REVOLVER", "SUBMACHINEGUN", "RIFLE"), "toolset"); + } + + @Override + public String getName() + { + return "ImmersiveEngineering"; + } + + @Override + public void registerRecipes() + { + + } + + @Override + public void init() + { + IILogger.info("Adding Railgun Projectiles"); + RailgunHandler.registerProjectileProperties(new IngredientStack("stickTungsten"), 32, 1.3).setColourMap(new int[][]{{0xCBD1D6, 0xCBD1D6, 0xCBD1D6, 0xCBD1D6, 0x9EA2A7, 0x9EA2A7}}); + } + + @Override + public void postInit() + { + if(Overrides.enableTradeOverride) + { + VillagerCareer gunsmithCareer; + if(IEVillagerHandler.PROF_ENGINEER==null||(gunsmithCareer = findVillagerCareer(IEVillagerHandler.PROF_ENGINEER, "immersiveengineering.gunsmith"))==null) + { + IILogger.error("Could not modify Gunsmith villager to sell II iron gun barrels."); + return; + } + + List trades = gunsmithCareer.getTrades(1); + if(trades!=null) + { + gunsmithCareer.addTrade(1, + new ItemstackForEmerald(IIContent.itemMaterial.getStack(Materials.IRON_GUN_BARREL), new PriceInfo(2, 4)) + ); + gunsmithCareer.addTrade(2, + new ItemstackForEmerald(IIContent.itemAmmoCasing.getStack(Casing.SMG_1BCAL, 12), new PriceInfo(1, 5)), + new ItemstackForEmerald(IIContent.itemAmmoCasing.getStack(Casing.MG_2BCAL, 12), new PriceInfo(2, 6)) + ); + gunsmithCareer.addTrade(3, + new ItemstackForEmerald(IIContent.itemAmmoCasing.getStack(Casing.STG_1BCAL, 8), new PriceInfo(6, 12)) + ); + gunsmithCareer.addTrade(4, + new ItemstackForEmerald(IIContent.itemMaterial.getStack(Materials.TUNGSTEN_GUN_BARREL), new PriceInfo(6, 12)) + ); + } + } + + //Import IE's recipe lists into II's registry, so that they can be used inside manual pages + importIERecipes(); + } + + @Override + public void loadComplete() + { + super.loadComplete(); + //Override IE's Event Handler + IIReflectionUtils.overrideEventHandler(blusunrize.immersiveengineering.common.EventHandler.class, new IEOverrideEventHandler()); + } + + private void importIERecipes() + { + int imported = 0; + imported += adaptRecipes(ArcFurnaceRecipe.recipeList, + ArcFurnaceRecipeAdapter::new); + imported += adaptRecipes(MetalPressRecipe.recipeList.values(), MetalPressRecipe::listInJEI, + MetalPressRecipeAdapter::new); + imported += adaptRecipes(SqueezerRecipe.recipeList, + SqueezerRecipeAdapter::new); + imported += adaptRecipes(FermenterRecipe.recipeList, + FermenterRecipeAdapter::new); + imported += adaptRecipes(RefineryRecipe.recipeList, + RefineryRecipeAdapter::new); + imported += adaptRecipes(MixerRecipe.recipeList, + MixerRecipeAdapter::new); + imported += adaptRecipes(CrusherRecipe.recipeList, + CrusherRecipeAdapter::new); + imported += adaptRecipes(CokeOvenRecipe.recipeList, + CokeOvenRecipeAdapter::new); + imported += adaptRecipes(BlastFurnaceRecipe.recipeList, + BlastFurnaceRecipeAdapter::new); + imported += adaptRecipes(AlloyRecipe.recipeList, + AlloyingFurnaceRecipeAdapter::new); + + IILogger.info("Imported "+imported+" Immersive Engineering recipes for II manual layouts"); + } + + private int adaptRecipes(Iterable recipes, Consumer adapterFactory) + { + return adaptRecipes(recipes, recipe -> true, adapterFactory); + } + + private int adaptRecipes(Iterable recipes, Predicate filter, Consumer adapterFactory) + { + int count = 0; + for(T recipe : recipes) + if(recipe!=null&&filter.test(recipe)) + { + adapterFactory.accept(recipe); + count++; + } + return count; + } + + @SubscribeEvent + @SideOnly(Side.CLIENT) + public void onGuiOpen(GuiOpenEvent event) + { + if(!Overrides.enableDecoOverride) + return; + GuiScreen gui = event.getGui(); + if(gui instanceof GuiCrate) + event.setGui(new GuiIECrateOverride(ClientUtils.mc().player, getTileFromIEGUI(gui))); + } + + private T getTileFromIEGUI(Gui gui) + { + //noinspection unchecked + return ((ContainerIEBase)((GuiIEContainerBase)gui).inventorySlots).tile; + } + + private VillagerCareer findVillagerCareer(VillagerProfession profession, String careerName) + { + VillagerCareer career = profession.getCareer(0), careerZero = career; + int iterator = 1; + do + { + if(career.getName().equals(careerName)) + return career; + career = profession.getCareer(iterator++); + } + while(career!=careerZero); + return career; + } + + @SideOnly(Side.CLIENT) + @Override + public void clientPreInit() + { + if(Railgun.enableRailgunOverride) + { + //Railgun overwrite Sunlight Railgun Overdrive! + ImmersiveModelRegistry.instance.registerCustomItemModel(new ItemStack(IEContent.itemRailgun, 1, 0), new ItemModelReplacement_OBJ("immersiveengineering:models/item/railgun.obj", true) + .setTransformations(TransformType.FIRST_PERSON_RIGHT_HAND, new Matrix4().scale(.125, .125, .125).translate(-.1875f, 2.5f, .25f).rotate(Math.PI*.46875, 0, 1, 0).translate(0.5, 0.25, -0.75f) + .rotate(Math.PI*.0225, 0, 0, 1).scale(1.125, 1.125, 1.125)) + .setTransformations(TransformType.FIRST_PERSON_LEFT_HAND, new Matrix4().scale(.125, .125, .125).translate(-1.75, 1.625, .875).rotate(-Math.PI*.46875, 0, 1, 0)) + .setTransformations(TransformType.THIRD_PERSON_RIGHT_HAND, new Matrix4().scale(.1875, .1875, .1875).translate(0.5, 0.5f, -3.5).rotate(Math.PI*.40125, 0, 1, 0)) + .setTransformations(TransformType.THIRD_PERSON_LEFT_HAND, new Matrix4().translate(-.1875, .5, -.3125).scale(.1875, .1875, .1875).rotate(-Math.PI*.46875, 0, 1, 0).rotate(-Math.PI*.25, 0, 0, 1)) + .setTransformations(TransformType.FIXED, new Matrix4().translate(.1875, .0625, .0625).scale(.125, .125, .125).rotate(-Math.PI*.25, 0, 0, 1)) + .setTransformations(TransformType.GUI, new Matrix4().translate(-.1875, 0, 0).scale(.1875, .1875, .1875).rotate(-Math.PI*.6875, 0, 1, 0).rotate(-Math.PI*.1875, 0, 0, 1)) + .setTransformations(TransformType.GROUND, new Matrix4().translate(.125, .125, .0625).scale(.125, .125, .125))); + IEContent.itemRailgun.setTileEntityItemStackRenderer(ItemRendererIEOBJ.INSTANCE); + } + + //Enable overriding IE GUIs with II's Deco based ones + if(Overrides.enableDecoOverride) + MinecraftForge.EVENT_BUS.register(this); + + } + + @SideOnly(Side.CLIENT) + @Override + public void clientInit() + { + + } + + @SideOnly(Side.CLIENT) + @Override + public void clientPostInit() + { + + } + + private static class ItemstackForEmerald implements ITradeList + { + public ItemStack sellingItem; + public PriceInfo priceInfo; + + public ItemstackForEmerald(ItemStack stack, PriceInfo priceInfo) + { + this.sellingItem = stack; + this.priceInfo = priceInfo; + } + + @Override + public void addMerchantRecipe(IMerchant merchant, MerchantRecipeList recipeList, Random random) + { + int i = 1; + if(this.priceInfo!=null) + i = this.priceInfo.getPrice(random); + ItemStack itemstack; + ItemStack itemstack1; + if(i < 0) + { + itemstack = new ItemStack(Items.EMERALD); + itemstack1 = Utils.copyStackWithAmount(sellingItem, -i); + } + else + { + itemstack = new ItemStack(Items.EMERALD, i, 0); + itemstack1 = Utils.copyStackWithAmount(sellingItem, 1); + } + recipeList.add(new MerchantRecipe(itemstack, itemstack1)); + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/AlloyingFurnaceRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/AlloyingFurnaceRecipeAdapter.java new file mode 100644 index 000000000..58b08de16 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/AlloyingFurnaceRecipeAdapter.java @@ -0,0 +1,36 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.AlloyRecipe; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Alloy Kiln recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class AlloyingFurnaceRecipeAdapter extends IERecipeAdapterBase +{ + public AlloyingFurnaceRecipeAdapter(AlloyRecipe recipe) + { + super(recipe, recipe.time, recipe.input0, recipe.input1); + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(158, 60, true) + .withInputSlot(2, 12, recipe.input0) + .withInputSlot(22, 12, recipe.input1) + .withMultiblockModel(48, -12, 68, 68, "") + .withTimeInfo(); + if(!recipe.output.isEmpty()) + builder.withOutputSlot(138, 12, recipe.output); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/ArcFurnaceRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/ArcFurnaceRecipeAdapter.java new file mode 100644 index 000000000..a4fe41654 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/ArcFurnaceRecipeAdapter.java @@ -0,0 +1,58 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.ArcFurnaceRecipe; +import net.minecraft.item.ItemStack; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout.IOType; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +/** + * Read-only manual adapter for IE Arc Furnace recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class ArcFurnaceRecipeAdapter extends IEMultiblockRecipeAdapter +{ + public ArcFurnaceRecipeAdapter(ArcFurnaceRecipe recipe) + { + super(recipe, recipe.input, recipe.additives); + } + + private List getOutputs() + { + List outputs = recipe.getItemOutputs(); + if(outputs==null||outputs.isEmpty()) + return recipe.output==null||recipe.output.isEmpty()?Collections.emptyList(): Collections.singletonList(recipe.output); + return outputs; + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + List outputs = getOutputs(); + int additiveRows = Math.max(1, (recipe.additives.length+1)/2); + int outputRows = Math.max(1, (outputs.size()+1)/2); + int height = Math.max(72, 28+Math.max(additiveRows, outputRows)*20); + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(188, height) + .withSlot(2, 22, recipe.input, IOType.INPUT, "frame") + .withMultiblockModel(58, -4, 64, 64, "") + .withTimeInfo() + .withPowerInfo(); + + for(int i = 0; i < recipe.additives.length; i++) + builder.withSlot(26+(i%2)*20, 12+(i/2)*20, recipe.additives[i], IOType.INPUT, "frame"); + for(int i = 0; i < outputs.size(); i++) + if(!outputs.get(i).isEmpty()) + builder.withSlot(126+(i%2)*20, 12+(i/2)*20, outputs.get(i), IOType.OUTPUT, "frame"); + if(!recipe.slag.isEmpty()) + builder.withSlot(168, 22, recipe.slag, IOType.OUTPUT, "frame_red"); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/BlastFurnaceRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/BlastFurnaceRecipeAdapter.java new file mode 100644 index 000000000..66946e160 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/BlastFurnaceRecipeAdapter.java @@ -0,0 +1,38 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.ApiUtils; +import blusunrize.immersiveengineering.api.crafting.BlastFurnaceRecipe; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Blast Furnace recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class BlastFurnaceRecipeAdapter extends IERecipeAdapterBase +{ + public BlastFurnaceRecipeAdapter(BlastFurnaceRecipe recipe) + { + super(recipe, recipe.time, recipe.input); + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(158, 60, true) + .withInputSlot(2, 12, ApiUtils.createIngredientStack(recipe.input)) + .withMultiblockModel(38, -12, 70, 68, "") + .withTimeInfo(); + if(!recipe.output.isEmpty()) + builder.withOutputSlot(116, 12, recipe.output); + if(!recipe.slag.isEmpty()) + builder.withSlot(138, 12, recipe.slag, IIRecipeLayout.IOType.OUTPUT, "frame_red"); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/CokeOvenRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/CokeOvenRecipeAdapter.java new file mode 100644 index 000000000..a8f62ab4b --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/CokeOvenRecipeAdapter.java @@ -0,0 +1,47 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.ApiUtils; +import blusunrize.immersiveengineering.api.crafting.CokeOvenRecipe; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Coke Oven recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class CokeOvenRecipeAdapter extends IERecipeAdapterBase +{ + public CokeOvenRecipeAdapter(CokeOvenRecipe recipe) + { + super(recipe, recipe.time, recipe.input); + } + + @Nullable + private FluidStack getCreosoteOutput() + { + return recipe.creosoteOutput > 0?FluidRegistry.getFluidStack("creosote", recipe.creosoteOutput): null; + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + FluidStack creosoteOutput = getCreosoteOutput(); + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(164, 64, true) + .withInputSlot(2, 16, ApiUtils.createIngredientStack(recipe.input)) + .withMultiblockModel(38, -8, 68, 68, "") + .withTimeInfo(); + if(!recipe.output.isEmpty()) + builder.withOutputSlot(116, 16, recipe.output); + if(creosoteOutput!=null) + builder.withOutputFluidTank(140, 3, creosoteOutput); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/CrusherRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/CrusherRecipeAdapter.java new file mode 100644 index 000000000..aeb4af852 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/CrusherRecipeAdapter.java @@ -0,0 +1,50 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.CrusherRecipe; +import net.minecraft.item.ItemStack; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout.IOType; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Crusher recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class CrusherRecipeAdapter extends IEMultiblockRecipeAdapter +{ + public CrusherRecipeAdapter(CrusherRecipe recipe) + { + super(recipe, recipe.input); + } + + private ItemStack[] getSecondaryOutputs() + { + return recipe.secondaryOutput==null?new ItemStack[0]: recipe.secondaryOutput; + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + ItemStack[] secondaryOutputs = getSecondaryOutputs(); + int rows = Math.max(1, (secondaryOutputs.length+1)/2); + int height = Math.max(64, 26+rows*20); + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(178, height) + .withInputSlot(2, 16, recipe.input) + .withMultiblockModel(38, -8, 72, 68, "") + .withTimeInfo() + .withPowerInfo(); + + if(!recipe.output.isEmpty()) + builder.withOutputSlot(116, 16, recipe.output); + for(int i = 0; i < secondaryOutputs.length; i++) + if(!secondaryOutputs[i].isEmpty()) + builder.withSlot(138+(i%2)*20, 6+(i/2)*20, secondaryOutputs[i], IOType.OUTPUT, "frame_red"); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/FermenterRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/FermenterRecipeAdapter.java new file mode 100644 index 000000000..13995c739 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/FermenterRecipeAdapter.java @@ -0,0 +1,38 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.FermenterRecipe; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Fermenter recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class FermenterRecipeAdapter extends IEMultiblockRecipeAdapter +{ + public FermenterRecipeAdapter(FermenterRecipe recipe) + { + super(recipe, recipe.input); + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(158, 64) + .withInputSlot(2, 16, recipe.input) + .withMultiblockModel(38, -10, 68, 68, "") + .withTimeInfo() + .withPowerInfo(); + if(!recipe.itemOutput.isEmpty()) + builder.withOutputSlot(112, 16, recipe.itemOutput); + if(recipe.fluidOutput!=null) + builder.withOutputFluidTank(136, 3, recipe.fluidOutput); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/IEMultiblockRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/IEMultiblockRecipeAdapter.java new file mode 100644 index 000000000..41a1b1d52 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/IEMultiblockRecipeAdapter.java @@ -0,0 +1,17 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.MultiblockRecipe; + +/** + * Read-only adapter base for recipes imported from Immersive Engineering. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @since 13.07.2026 + */ +public abstract class IEMultiblockRecipeAdapter extends IERecipeAdapterBase +{ + protected IEMultiblockRecipeAdapter(T recipe, Object... nameSources) + { + super(recipe, recipe.getTotalProcessTime(), recipe.getTotalProcessEnergy(), nameSources); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/IERecipeAdapterBase.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/IERecipeAdapterBase.java new file mode 100644 index 000000000..c85d4c136 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/IERecipeAdapterBase.java @@ -0,0 +1,63 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.IngredientStack; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIMultiblockRecipe; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +abstract class IERecipeAdapterBase extends IIMultiblockRecipe +{ + protected final T recipe; + + protected IERecipeAdapterBase(T recipe, int totalProcessTime, int totalProcessEnergy, Object... nameSources) + { + super(createRecipeName(nameSources)); + this.recipe = recipe; + setTimeAndEnergy(totalProcessTime, totalProcessEnergy); + } + + protected IERecipeAdapterBase(T recipe, int totalProcessTime, Object... nameSources) + { + this(recipe, totalProcessTime, 0, nameSources); + } + + private static String createRecipeName(Object... nameSources) + { + List flattenedSources = new ArrayList<>(); + for(Object source : nameSources) + appendNameSource(flattenedSources, source); + + if(flattenedSources.isEmpty()) + return "unnamed"; + + Object first = flattenedSources.remove(0); + return generateRecipeName(first, flattenedSources.toArray()).toLowerCase(Locale.ROOT); + } + + private static void appendNameSource(List destination, @Nullable Object source) + { + if(source==null) + return; + if(source instanceof Object[]) + { + for(Object nested : (Object[])source) + appendNameSource(destination, nested); + return; + } + if(source instanceof ItemStack) + source = new IngredientStack((ItemStack)source); + else if(source instanceof FluidStack) + source = new IngredientStack((FluidStack)source); + destination.add(source); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/MetalPressRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/MetalPressRecipeAdapter.java new file mode 100644 index 000000000..cc8b8f53e --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/MetalPressRecipeAdapter.java @@ -0,0 +1,48 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.ComparableItemStack; +import blusunrize.immersiveengineering.api.crafting.IngredientStack; +import blusunrize.immersiveengineering.api.crafting.MetalPressRecipe; +import net.minecraftforge.oredict.OreDictionary; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout.IOType; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Metal Press recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class MetalPressRecipeAdapter extends IEMultiblockRecipeAdapter +{ + public MetalPressRecipeAdapter(MetalPressRecipe recipe) + { + super(recipe, recipe.input, getMold(recipe.mold)); + } + + private static IngredientStack getMold(ComparableItemStack mold) + { + if(mold.oreID >= 0) + return new IngredientStack(OreDictionary.getOreName(mold.oreID)); + return new IngredientStack(mold.stack); + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(156, 60) + .withInputSlot(2, 12, recipe.input) + .withSlot(28, 12, getMold(recipe.mold), IOType.NEUTRAL, "frame") + .withMultiblockModel(50, -14, 64, 64, "") + .withTimeInfo() + .withPowerInfo(); + if(!recipe.output.isEmpty()) + builder.withOutputSlot(136, 12, recipe.output); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/MixerRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/MixerRecipeAdapter.java new file mode 100644 index 000000000..a5578247d --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/MixerRecipeAdapter.java @@ -0,0 +1,43 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.MixerRecipe; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Mixer recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class MixerRecipeAdapter extends IEMultiblockRecipeAdapter +{ + public MixerRecipeAdapter(MixerRecipe recipe) + { + super(recipe, recipe.fluidInput, recipe.itemInputs); + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + int columns = 4; + int rows = Math.max(1, (recipe.itemInputs.length+columns-1)/columns); + int height = Math.max(66, 20+rows*20); + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(178, height) + .withProgressArrow(116, Math.max(14, (height-29)/2)) + .withTimeInfo() + .withPowerInfo(); + + if(recipe.fluidInput!=null) + builder.withInputFluidTank(2, 3, recipe.fluidInput); + for(int i = 0; i < recipe.itemInputs.length; i++) + builder.withInputSlot(28+(i%columns)*20, 3+(i/columns)*20, recipe.itemInputs[i]); + if(recipe.fluidOutput!=null) + builder.withOutputFluidTank(158, 3, recipe.fluidOutput); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/RefineryRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/RefineryRecipeAdapter.java new file mode 100644 index 000000000..c5bd5e8b5 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/RefineryRecipeAdapter.java @@ -0,0 +1,39 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.RefineryRecipe; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Refinery recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class RefineryRecipeAdapter extends IEMultiblockRecipeAdapter +{ + public RefineryRecipeAdapter(RefineryRecipe recipe) + { + super(recipe, recipe.input0, recipe.input1); + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(178, 66) + .withMultiblockModel(48, -8, 76, 68, "") + .withTimeInfo() + .withPowerInfo(); + if(recipe.input0!=null) + builder.withInputFluidTank(2, 3, recipe.input0); + if(recipe.input1!=null) + builder.withInputFluidTank(24, 3, recipe.input1); + if(recipe.output!=null) + builder.withOutputFluidTank(158, 3, recipe.output); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/SqueezerRecipeAdapter.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/SqueezerRecipeAdapter.java new file mode 100644 index 000000000..ba36e91f6 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/ie/recipe/SqueezerRecipeAdapter.java @@ -0,0 +1,38 @@ +package pl.pabilo8.immersiveintelligence.common.compat.ie.recipe; + +import blusunrize.immersiveengineering.api.crafting.SqueezerRecipe; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayout; +import pl.pabilo8.immersiveintelligence.api.crafting.recipe.IIRecipeLayoutBuilder; + +import javax.annotation.Nullable; + +/** + * Read-only manual adapter for IE Squeezer recipes. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 15.07.2026 + */ +public class SqueezerRecipeAdapter extends IEMultiblockRecipeAdapter +{ + public SqueezerRecipeAdapter(SqueezerRecipe recipe) + { + super(recipe, recipe.input); + } + + @Nullable + @Override + protected IIRecipeLayout initRecipeLayout() + { + IIRecipeLayoutBuilder builder = new IIRecipeLayoutBuilder(158, 64) + .withInputSlot(2, 16, recipe.input) + .withMultiblockModel(38, -10, 68, 68, "") + .withTimeInfo() + .withPowerInfo(); + if(!recipe.itemOutput.isEmpty()) + builder.withOutputSlot(112, 16, recipe.itemOutput); + if(recipe.fluidOutput!=null) + builder.withOutputFluidTank(136, 3, recipe.fluidOutput); + return builder.build(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/DecoGuiJEIHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/DecoGuiJEIHandler.java index be90ad04a..1eb03322c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/DecoGuiJEIHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/DecoGuiJEIHandler.java @@ -1,12 +1,11 @@ package pl.pabilo8.immersiveintelligence.common.compat.jei; -import blusunrize.immersiveengineering.common.blocks.TileEntityIEBase; -import blusunrize.immersiveengineering.common.util.inventory.IIEInventory; import mezz.jei.api.gui.IAdvancedGuiHandler; +import net.minecraft.inventory.Container; import pl.pabilo8.immersiveintelligence.client.gui.deco.DecoGui; import pl.pabilo8.immersiveintelligence.common.IIGUI; -import pl.pabilo8.immersiveintelligence.common.util.gui.ContainerIITileBase; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.awt.*; import java.util.List; @@ -15,12 +14,13 @@ * @author Pabilo8 (pabilo@iiteam.net) * @since 24.03.2021 */ -public class DecoGuiJEIHandler, T extends TileEntityIEBase & IIEInventory, C extends ContainerIITileBase> implements IAdvancedGuiHandler +public class DecoGuiJEIHandler, T, C extends Container> implements IAdvancedGuiHandler { - Class wrappedClass; + private final Class wrappedClass; public DecoGuiJEIHandler(IIGUI gui) { + //noinspection unchecked this.wrappedClass = (Class)gui.guiClass; } @@ -38,6 +38,7 @@ public Object getIngredientUnderMouse(GUI guiContainer, int mouseX, int mouseY) return guiContainer.getIngredientUnderMouse(); } + @Nonnull @Override public Class getGuiContainerClass() { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/JEIHelper.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/JEIHelper.java index db1fc160e..12d56f03a 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/JEIHelper.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/compat/jei/JEIHelper.java @@ -142,6 +142,8 @@ public void register(@Nonnull IModRegistry registryIn) jeiHelpers.getIngredientBlacklist().addIngredientToBlacklist(new ItemStack(IIContent.itemAmmoRevolver, 1, RevolverAmmoPart.UNUSED.ordinal())); + jeiHelpers.getIngredientBlacklist().addIngredientToBlacklist(new ItemStack(IIContent.itemPlaceholderIcon, 1, OreDictionary.WILDCARD_VALUE)); + for(IAmmoTypeItem bullet : AmmoRegistry.getAllAmmoItems()) { ItemStack stack = bullet.getAmmoStack(AmmoRegistry.MISSING_CORE, CoreType.SOFTPOINT, FuseType.CONTACT); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/IIRecipes.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/IIRecipes.java index 8b8223ca5..b9e229696 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/IIRecipes.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/IIRecipes.java @@ -52,6 +52,8 @@ import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.PrintingPress; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines.Sawmill; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Overrides; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Overrides.Railgun; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.block.metal_device.BlockIIMetalDevice.IIBlockTypes_MetalDevice; import pl.pabilo8.immersiveintelligence.common.block.mines.BlockIIMine.IIBlockTypes_Mine; @@ -127,12 +129,12 @@ public static void doRecipes(IForgeRegistryModifiable recipeRegistry) ); //--- Replace Recipes ---// - replaceRecipe(recipeRegistry, IIConfig.changeRevolverProduction, "material/gunpart_drum"); - replaceRecipe(recipeRegistry, IIConfig.changeRevolverProduction, "material/gunpart_hammer"); - replaceRecipe(recipeRegistry, IIConfig.changeRevolverProduction, "toolupgrades/railgun_scope", "materials/gunparts/precision_scope"); - replaceRecipe(recipeRegistry, IIConfig.changeRevolverProduction, "tool/revolver"); - replaceRecipe(recipeRegistry, IIConfig.changeRailgunProduction, "tool/railgun"); - replaceRecipe(recipeRegistry, IIConfig.changeChemthrowerProduction, "tool/chemthrower"); + replaceRecipe(recipeRegistry, Overrides.changeRevolverProduction, "material/gunpart_drum"); + replaceRecipe(recipeRegistry, Overrides.changeRevolverProduction, "material/gunpart_hammer"); + replaceRecipe(recipeRegistry, Overrides.changeRevolverProduction, "toolupgrades/railgun_scope", "materials/gunparts/precision_scope"); + replaceRecipe(recipeRegistry, Overrides.changeRevolverProduction, "tool/revolver"); + replaceRecipe(recipeRegistry, Railgun.changeRailgunProduction, "tool/railgun"); + replaceRecipe(recipeRegistry, Overrides.changeChemthrowerProduction, "tool/chemthrower"); //--- Add Recipes ---// addMinecartRecipes(recipeRegistry); @@ -311,16 +313,17 @@ public int[] getInkTypesRequired(DataPacket data) @Override public ItemStack apply(ItemStack input, DataPacket data) { - return IIContent.itemPrintedPage.getStack(PageType.TEXT, - nbt -> nbt.withString("text", data.get('t').toString()) - ); + ItemStack stack = IIContent.itemPunchtape.getStack(1); + DataPacket cloned = data.clone(); + cloned.remove('a', 'm', 't'); + IIContent.itemPunchtape.writeDataToItem(stack, cloned); + return stack; } @Override public int[] getInkTypesRequired(DataPacket data) { - String text = data.get('t').toString(); - return new int[]{0, 0, 0, text.length()*PrintingPress.printInkUsage}; + return new int[]{0, 0, 0, 0}; } @Nullable @@ -398,7 +401,7 @@ public static void addCircuitRecipes() { //Allow me to introduce you to Immersive Gregineering™ - if(IIConfig.changeCircuitProduction) + if(Overrides.changeCircuitProduction) { BlueprintCraftingRecipe.recipeList.get("components").removeIf(blueprintCraftingRecipe -> blueprintCraftingRecipe.output.isItemEqual(BASIC_CIRCUIT)); @@ -725,7 +728,7 @@ public static void addSpringRecipes() public static void addHandWeaponRecipes(IForgeRegistry recipeRegistry) { //IE Revolver Tweaks - if(IIConfig.changeRevolverProduction) + if(Overrides.changeRevolverProduction) { //TODO: 15.10.2023 change revolver to use iron instead of steel } @@ -778,7 +781,8 @@ public static void addMiscIERecipes() 3200, 120, false ); - MetalPressRecipe.addRecipe(new ItemStack(IIContent.itemPrintedPage, 1, 0), new IngredientStack("paper"), new ItemStack(IEContent.itemMold, 1, 0), 600); + MetalPressRecipe.addRecipe(new ItemStack(IIContent.itemPrintedPage, 1, 0), + new IngredientStack("paper"), new ItemStack(IEContent.itemMold, 1, 0), 600); ArcFurnaceRecipe.addRecipe( IIContent.itemMaterial.getStack(Materials.WHITE_PHOSPHORUS, 4), diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/RecipeSkinCraftingHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/RecipeSkinCraftingHandler.java index 4e95910a0..2effe61d7 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/RecipeSkinCraftingHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/crafting/RecipeSkinCraftingHandler.java @@ -2,7 +2,6 @@ import blusunrize.immersiveengineering.common.IEContent; import blusunrize.immersiveengineering.common.util.ItemNBTHelper; -import net.minecraft.client.Minecraft; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; @@ -15,11 +14,12 @@ /** * @author Pabilo8 (pabilo@iiteam.net) + * @updated 18.07.2026 + * @ii-approved 0.3.1 * @since 07.08.2021 */ public class RecipeSkinCraftingHandler extends Impl implements IRecipe { - @Override public boolean matches(InventoryCrafting inv, World worldIn) { @@ -91,9 +91,7 @@ private boolean process(InventoryCrafting inv) { ItemStack stack = inv.getStackInSlot(i); if(!stack.isEmpty()) - { if(stack.getItem()==IEContent.itemTool&&stack.getItemDamage()==3) - { if(manual.isEmpty()&&ItemNBTHelper.hasKey(stack, "lastSkin")) { manual = stack; @@ -101,7 +99,6 @@ private boolean process(InventoryCrafting inv) } else return false; - } else if(stack.getItem() instanceof ISkinnable) { if(item.isEmpty()) @@ -112,34 +109,20 @@ else if(stack.getItem() instanceof ISkinnable) } else return false; - } } boolean result = !manual.isEmpty()&&skinnable!=null; if(result) { - String sessionID = Minecraft.getMinecraft().getSession().getSessionID(); // Result: token:FML:X where X is the UUID - String uuid = sessionID.substring(sessionID.lastIndexOf(':')+1), skinName = ItemNBTHelper.getString(manual, "lastSkin"); - if(IISkinHandler.isValidSkin(skinName)) + String[] info = ItemNBTHelper.getString(manual, "lastSkin").split(":"); + if(info.length==2&&IISkinHandler.isValidSkin(info[1])) { - IISpecialSkin skin = IISkinHandler.getSkin(skinName); - boolean eligible = false, doesApply = skin.doesApply(skinnable.getSkinnableName()); - - for(String id : skin.uuid) - { - if(id.replace("-", "").equals(uuid)) - { - eligible = true; - break; - } - } - - if(!eligible||!doesApply) return false; + IISpecialSkin skin = IISkinHandler.getSkin(info[1]); + assert skin!=null; + return skin.appliesToPlayer(info[0])&&skin.doesApply(skinnable.getSkinnableName()); } else - { return false; - } } return result; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityAtomicBoom.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityAtomicBoom.java index 46b2e79b7..cbf6c7fc9 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityAtomicBoom.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityAtomicBoom.java @@ -9,7 +9,6 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; @@ -22,6 +21,8 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import pl.pabilo8.immersiveintelligence.api.api.protection.RadiationHandler; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.IRadiationEmitter; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIPotions; @@ -30,10 +31,11 @@ * @since 19.12.2020 */ @Interface(iface = "com.elytradev.mirage.lighting.IEntityLightEventConsumer", modid = "mirage") -public class EntityAtomicBoom extends Entity implements IEntityAdditionalSpawnData, IEntityLightEventConsumer +public class EntityAtomicBoom extends Entity implements IEntityAdditionalSpawnData, IEntityLightEventConsumer, IRadiationEmitter { - public float size; + public float size = 0; public int progress = 0; + private boolean falloutRegistered = false; public EntityAtomicBoom(World worldIn) { @@ -45,7 +47,6 @@ public EntityAtomicBoom(World worldIn, float size) this(worldIn); this.size = size; this.ignoreFrustumCheck = true; - setRenderDistanceWeight(32.0); } @Override @@ -53,6 +54,11 @@ public void onUpdate() { super.onUpdate(); progress++; + if(!world.isRemote&&!falloutRegistered) + { + RadiationHandler.INSTANCE.addOrIncreaseRadiationCenter(world, getPosition(), 60*size, Math.max(1f, size)); + falloutRegistered = true; + } if(world.isRemote&&world.getTotalWorldTime()%4==0) { Vec3d pos = getPositionVector(); @@ -137,24 +143,12 @@ else if(!world.isRemote&&progress > 20&&progress < 60) return; } - //apply half a second - if(world.getTotalWorldTime()%10==0) + //Apply nuclear heat server-side; radiation exposure is handled centrally. + if(!world.isRemote&&world.getTotalWorldTime()%10==0) { AxisAlignedBB aabb = new AxisAlignedBB(getPosition()).grow(40*size); - EntityLivingBase[] entities = world.getEntitiesWithinAABB(EntityLivingBase.class, aabb).toArray(new EntityLivingBase[0]); - for(EntityLivingBase e : entities) - { - //if(e instanceof EntityPlayer&&((EntityPlayer)e).isCreative()) - // continue; - e.addPotionEffect(new PotionEffect(IIPotions.nuclearHeat, 400, 0, false, false)); - } - entities = world.getEntitiesWithinAABB(EntityLivingBase.class, aabb.grow(20*size)).toArray(new EntityLivingBase[0]); - for(EntityLivingBase e : entities) - { - if(e instanceof EntityPlayer&&((EntityPlayer)e).isCreative()) - continue; - e.addPotionEffect(new PotionEffect(IIPotions.radiation, 2000, 0, false, false)); - } + for(EntityLivingBase entity : world.getEntitiesWithinAABB(EntityLivingBase.class, aabb)) + entity.addPotionEffect(new PotionEffect(IIPotions.nuclearHeat, 400, 0, false, false)); } } @@ -225,12 +219,16 @@ protected void entityInit() protected void readEntityFromNBT(NBTTagCompound compound) { size = compound.getFloat("size"); + progress = compound.getInteger("progress"); + falloutRegistered = compound.getBoolean("falloutRegistered"); } @Override protected void writeEntityToNBT(NBTTagCompound compound) { compound.setFloat("size", size); + compound.setInteger("progress", progress); + compound.setBoolean("falloutRegistered", falloutRegistered); } @SideOnly(Side.CLIENT) @@ -240,6 +238,26 @@ public boolean isInRangeToRenderDist(double distance) return true; } + //--- IRadiationEmitter ---// + + @Override + public float getRadiationRadius() + { + return 60*size; + } + + @Override + public float getRadiationStrength() + { + return Math.max(1f, size*4f); + } + + @Override + public boolean isRadiationActive() + { + return !isDead&&progress <= 400; + } + @Override public void writeSpawnData(ByteBuf buffer) { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityGasCloud.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityGasCloud.java index 1c23a739b..7a49f5ab7 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityGasCloud.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/component/EntityGasCloud.java @@ -6,7 +6,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.MoverType; -import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; @@ -15,7 +14,7 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.fluids.FluidStack; -import pl.pabilo8.immersiveintelligence.api.utils.armor.IGasmask; +import pl.pabilo8.immersiveintelligence.api.api.protection.ProtectionHandler; import pl.pabilo8.immersiveintelligence.client.fx.utils.ParticleRegistry; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; @@ -115,18 +114,8 @@ public void onUpdate() ChemthrowerEffect effect = ChemthrowerHandler.getEffect(fluidStack.getFluid()); if(effect!=null) for(EntityLivingBase entity : entities) - { - boolean isProtected = false; - for(EntityEquipmentSlot slot : EntityEquipmentSlot.values()) - { - ItemStack stack = entity.getItemStackFromSlot(slot); - if(!stack.isEmpty()&&stack.getItem() instanceof IGasmask) - if(((IGasmask)stack.getItem()).protectsFromGasses(stack)) - isProtected = true; - } - if(!isProtected) + if(!ProtectionHandler.isProtectedFromGas(entity)) effect.applyToEntity(entity, null, ItemStack.EMPTY, fluidStack); - } //Expansion logic if(currentRadius < maxRadius) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/types/EntityAmmoProjectile.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/types/EntityAmmoProjectile.java index e6b5276c7..0129ea24b 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/types/EntityAmmoProjectile.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/ammo/types/EntityAmmoProjectile.java @@ -308,6 +308,7 @@ protected void finallyDetonate() for(Tuple component : components) component.getFirst().onEffect(world, pos, dir, coreType.getEffectShape(), component.getSecond(), ammoType.getComponentSize(), multiplier, owner); + markedForDetonation = false; setDead(); } } @@ -414,9 +415,9 @@ protected boolean handleEntityDamage(RayTraceResult hit) if(living!=null) { float armor = MathHelper.floor(living.getEntityAttribute(SharedMonsterAttributes.ARMOR).getAttributeValue())*ARMOR_FACTOR; - //Damage the other entity armour whether penetrated or not + //Damage the other entity armor whether penetrated or not if(armor > 0) - IIAmmoUtils.breakArmour(other, (int)getDamage()); + IIAmmoUtils.breakArmor(other, (int)getDamage()); } //Ricochet off the entity if(penHandler.getPenetrationHardness().compareTo(penetrationHardness) > 0) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/vehicle/EntityDrone.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/vehicle/EntityDrone.java index bbbcf56eb..fd56a5194 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/vehicle/EntityDrone.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/entity/vehicle/EntityDrone.java @@ -227,6 +227,7 @@ public void onEntityUpdate() //Create and sustain engine noise if(engineNoise==null) this.engineNoise = new ConditionCompoundSound<>(IISounds.dronePropellerLoop, this.getPositionVector(), this, drone -> !drone.isDead); + this.engineNoise.setMaxRange(48); this.engineNoise.setPosition(getPositionVector()); this.engineNoise.setVolume(1f); this.engineNoise.setPitch((float)IIEntityUtils.getEntityMotion(this).lengthSquared()*0.1f+0.95f); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/event/IIBaseEventHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/event/IIBaseEventHandler.java deleted file mode 100644 index 717b5e683..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/event/IIBaseEventHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -package pl.pabilo8.immersiveintelligence.common.event; - -import net.minecraftforge.common.MinecraftForge; -import pl.pabilo8.immersiveintelligence.common.IILogger; - -/** - * @author GabrielV (gabriel@iiteam.net) - * @since 21.04.2024 - */ -public class IIBaseEventHandler -{ - public void registerEventHandler() - { - IILogger.info("Registering event handler: "+this); - MinecraftForge.EVENT_BUS.register(this); - } -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/event/LightEngineerEventHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/event/LightEngineerEventHandler.java deleted file mode 100644 index 30a96223b..000000000 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/event/LightEngineerEventHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -package pl.pabilo8.immersiveintelligence.common.event; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraftforge.event.entity.living.LivingFallEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import pl.pabilo8.immersiveintelligence.common.item.armor.ItemIILightEngineerBoots; - -/** - * @author GabrielV (gabriel@iiteam.net) - * @since 21.04.2024 - */ -public class LightEngineerEventHandler extends IIBaseEventHandler -{ - @SubscribeEvent - public void onLivingFallEvent(LivingFallEvent event) - { - if(!(event.getEntityLiving() instanceof EntityPlayer)) return; - - EntityPlayer player = (EntityPlayer)event.getEntityLiving(); - Iterable armor = player.getArmorInventoryList(); - - for(ItemStack piece : armor) - { - if(!(piece.getItem() instanceof ItemIILightEngineerBoots)) continue; - ItemIILightEngineerBoots boots = (ItemIILightEngineerBoots)piece.getItem(); - if(boots.hasUpgrade(piece, "internal_springs")) - { - event.setDistance(0); - } - } - } -} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/gui/ContainerFlagpole.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/gui/ContainerFlagpole.java index 5bf9e94c3..0cbc1031a 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/gui/ContainerFlagpole.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/gui/ContainerFlagpole.java @@ -15,7 +15,7 @@ public class ContainerFlagpole extends ContainerIITileBase private ContainerFlagpole(EntityPlayer player, TileEntityFlagpole tile, boolean faction) { super(player, tile); - addPlayerInventory(player.inventory, faction?56: 40, faction?(166+32): 166); + addPlayerInventory(player.inventory, 40, 166); } public static Container getContainerForFlagpolePage(EntityPlayer player, TileEntityFlagpole tile) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/gui/ContainerPlayerGui.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/gui/ContainerPlayerGui.java new file mode 100644 index 000000000..758f2cfcf --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/gui/ContainerPlayerGui.java @@ -0,0 +1,29 @@ +package pl.pabilo8.immersiveintelligence.common.gui; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; + +import javax.annotation.Nonnull; + +/** + * Lightweight container for GUIs whose only authoritative context is the player. + * It deliberately exposes no slots; the GUI is not an alternative inventory view. + * + * @author Pabilo8 + * @since 22.07.2026 + */ +public class ContainerPlayerGui extends Container +{ + private final EntityPlayer player; + + public ContainerPlayerGui(@Nonnull EntityPlayer player) + { + this.player = player; + } + + @Override + public boolean canInteractWith(@Nonnull EntityPlayer playerIn) + { + return playerIn==player&&!player.isDead; + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ItemIIPlaceholderIcon.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ItemIIPlaceholderIcon.java new file mode 100644 index 000000000..c9dbe9fb8 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ItemIIPlaceholderIcon.java @@ -0,0 +1,65 @@ +package pl.pabilo8.immersiveintelligence.common.item; + +import net.minecraft.item.ItemStack; +import pl.pabilo8.immersiveintelligence.common.item.ItemIIPlaceholderIcon.Icons; +import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; +import pl.pabilo8.immersiveintelligence.common.util.item.IIItemEnum; +import pl.pabilo8.immersiveintelligence.common.util.item.IIItemEnum.IIItemProperties; +import pl.pabilo8.immersiveintelligence.common.util.item.ItemIISubItemsBase; +import pl.pabilo8.modworks.annotations.item.GeneratedItemModels; +import pl.pabilo8.modworks.annotations.item.GeneratedSubItemModel; + +import javax.annotation.Nonnull; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @since 11.05.2019 + */ +@IIItemProperties(category = IICategory.NULL, hidden = true, stackSize = 1) +public class ItemIIPlaceholderIcon extends ItemIISubItemsBase +{ + public ItemIIPlaceholderIcon() + { + super("placeholder_icon", 1, Icons.values()); + } + + @Nonnull + @Override + public String getUnlocalizedName(ItemStack stack) + { + return this.getUnlocalizedName(); + } + + @GeneratedItemModels(itemName = "placeholder_icon") + public enum Icons implements IIItemEnum + { + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/autocannon") + EMPLACEMENT_AUTOCANNON, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/cpds") + EMPLACEMENT_CPDS, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/guided_missile_launcher") + EMPLACEMENT_GUIDED_MISSILE_LAUNCHER, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/heavy_chemthrower") + EMPLACEMENT_HEAVY_CHEMTHROWER, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/heavy_railgun") + EMPLACEMENT_HEAVY_RAILGUN, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/infrared_observer") + EMPLACEMENT_INFRARED_OBSERVER, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/light_howitzer") + EMPLACEMENT_LIGHT_HOWITZER, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/machinegun") + EMPLACEMENT_MACHINEGUN, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/mortar") + EMPLACEMENT_MORTAR, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/rocket_launcher") + EMPLACEMENT_ROCKET_LAUNCHER, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/searchlight") + EMPLACEMENT_SEARCHLIGHT, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/spotlight_tower") + EMPLACEMENT_SPOTLIGHT_TOWER, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/emplacement/tesla") + EMPLACEMENT_TESLA, + @GeneratedSubItemModel(customTexturePath = "immersiveintelligence:gui/upgrade/flagpole/unit_post") + FLAGPOLE_UNIT_POST, + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryHeavy.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryHeavy.java index 6dccad2fe..9c7164fa3 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryHeavy.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryHeavy.java @@ -9,6 +9,7 @@ import pl.pabilo8.immersiveintelligence.api.ammo.parts.IAmmoTypeItem.IIAmmoProjectile; import pl.pabilo8.immersiveintelligence.client.model.builtin.IAmmoModel; import pl.pabilo8.immersiveintelligence.client.model.builtin.ModelAmmoProjectile; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Ammunition; import pl.pabilo8.immersiveintelligence.common.entity.ammo.types.EntityAmmoArtilleryProjectile; import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIAmmoBase; import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIAmmoBase.AmmoParts; @@ -38,7 +39,7 @@ public ItemIIAmmoArtilleryHeavy() @Override public float getComponentSize() { - return 1f; + return 1.385f; } @Override @@ -74,8 +75,7 @@ public float getCasingMass() @Override public float getVelocity() { - return 6; -// return Ammunition.artilleryHowiVelocity; + return Ammunition.artilleryHowiVelocity; } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryMedium.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryMedium.java index 9a94341b4..d449b5b91 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryMedium.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/ammo/artillery/ItemIIAmmoArtilleryMedium.java @@ -40,7 +40,7 @@ public ItemIIAmmoArtilleryMedium() @Override public float getComponentSize() { - return 0.65f; + return 1f; } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerArmorBase.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerArmorBase.java index 6aa23dc1e..a1a74ed7e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerArmorBase.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerArmorBase.java @@ -3,42 +3,68 @@ import blusunrize.immersiveengineering.common.util.EnergyHelper.IIEEnergyItem; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.capabilities.ICapabilityProvider; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; -import pl.pabilo8.immersiveintelligence.api.CorrosionHandler.IAcidProtectionEquipment; -import pl.pabilo8.immersiveintelligence.api.CorrosionHandler.ICorrosionProtectionEquipment; -import pl.pabilo8.immersiveintelligence.api.utils.armor.IRadiationProtectionEquipment; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.ProtectionCapabilities; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.ProtectionCapabilityProvider; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.util.item.ItemIIUpgradeableArmor; +import javax.annotation.Nullable; + /** * @author Pabilo8 (pabilo@iiteam.net) * @since 08.01.2022 */ -public abstract class ItemIILightEngineerArmorBase extends ItemIIUpgradeableArmor implements ICorrosionProtectionEquipment, IRadiationProtectionEquipment, IAcidProtectionEquipment, IIEEnergyItem +public abstract class ItemIILightEngineerArmorBase extends ItemIIUpgradeableArmor implements IIEEnergyItem { public ItemIILightEngineerArmorBase(EntityEquipmentSlot slot, String upgradeType) { super(IIContent.ARMOR_MATERIAL_LIGHT_ENGINEER, slot, upgradeType); } + @Nullable @Override - public boolean canCorrode(ItemStack stack) + public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable NBTTagCompound nbt) { - return !hasUpgrade(stack, "hazmat"); + ICapabilityProvider parent = super.initCapabilities(stack, nbt); + if(stack.isEmpty()||parent==null) + return parent; + + return new ProtectionCapabilityProvider(parent) + .with(ProtectionCapabilities.CORROSION_PROTECTION, () -> protectsFromCorrosion(stack)) + .with(ProtectionCapabilities.RADIATION_PROTECTION, () -> protectsFromRadiation(stack)) + .with(ProtectionCapabilities.ACID_PROTECTION, () -> protectsFromAcid(stack)) + .with(ProtectionCapabilities.GAS_PROTECTION, () -> protectsFromGases(stack)) + .with(ProtectionCapabilities.INFRARED_PROTECTION, () -> isInvisibleToInfrared(stack)); } - @Override - public boolean protectsFromRadiation(ItemStack stack) + protected boolean protectsFromCorrosion(ItemStack stack) { return hasUpgrade(stack, "hazmat"); } - @Override - public boolean protectsFromAcid(ItemStack stack) + protected boolean protectsFromRadiation(ItemStack stack) + { + return hasUpgrade(stack, "hazmat"); + } + + protected boolean protectsFromAcid(ItemStack stack) { return hasUpgrade(stack, "hazmat"); } + protected boolean protectsFromGases(ItemStack stack) + { + return false; + } + + protected boolean isInvisibleToInfrared(ItemStack stack) + { + return false; + } + public boolean protectsFromHeat(ItemStack stack) { return hasUpgrade(stack, "heatcoat"); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerChestplate.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerChestplate.java index afbebf72a..5884b1947 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerChestplate.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerChestplate.java @@ -20,9 +20,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumFacing; import net.minecraft.world.World; -import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidUtil; @@ -30,12 +28,11 @@ import net.minecraftforge.fluids.capability.IFluidHandlerItem; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -import pl.pabilo8.immersiveintelligence.api.utils.armor.IInfraredProtectionEquipment; +import pl.pabilo8.immersiveintelligence.api.api.protection.capability.ProtectionCapabilityProvider; import pl.pabilo8.immersiveintelligence.client.model.armor.ModelLightEngineerArmor; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Weapons.LightEngineerArmor; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.IIPotions; -import pl.pabilo8.immersiveintelligence.common.util.item.IIArmorItemStackHandler; import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; import pl.pabilo8.immersiveintelligence.common.util.item.IIItemEnum.IIItemProperties; @@ -49,7 +46,7 @@ * @since 13.09.2020 */ @IIItemProperties(category = IICategory.WARFARE) -public class ItemIILightEngineerChestplate extends ItemIILightEngineerArmorBase implements IElectricEquipment, IInfraredProtectionEquipment, IAdvancedFluidItem +public class ItemIILightEngineerChestplate extends ItemIILightEngineerArmorBase implements IElectricEquipment, IAdvancedFluidItem { public ItemIILightEngineerChestplate() { @@ -60,27 +57,12 @@ public ItemIILightEngineerChestplate() @Override public ICapabilityProvider initCapabilities(ItemStack stack, NBTTagCompound nbt) { - if(!stack.isEmpty()) - return new IIArmorItemStackHandler(stack) - { - final IEItemFluidHandler fluids = new IEItemFluidHandler(stack, 0); - - @Override - public boolean hasCapability(Capability capability, EnumFacing facing) - { - return capability==CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY|| - super.hasCapability(capability, facing); - } - - @Override - public T getCapability(Capability capability, EnumFacing facing) - { - if(capability==CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY) - return (T)fluids; - return super.getCapability(capability, facing); - } - }; - return null; + ICapabilityProvider parent = super.initCapabilities(stack, nbt); + if(stack.isEmpty()||parent==null) + return parent; + + return new ProtectionCapabilityProvider(parent) + .with(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, new IEItemFluidHandler(stack, 0)); } @Override @@ -251,7 +233,7 @@ public int getSlotCount() } @Override - public boolean invisibleToInfrared(ItemStack stack) + protected boolean isInvisibleToInfrared(ItemStack stack) { return hasUpgrade(stack, "ir_mesh"); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerHelmet.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerHelmet.java index 838cada96..066ca3746 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerHelmet.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/armor/ItemIILightEngineerHelmet.java @@ -20,7 +20,6 @@ import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -import pl.pabilo8.immersiveintelligence.api.utils.armor.IGasmask; import pl.pabilo8.immersiveintelligence.client.ClientProxy; import pl.pabilo8.immersiveintelligence.client.model.armor.ModelLightEngineerArmor; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Weapons.LightEngineerArmor; @@ -39,7 +38,7 @@ * @since 13.09.2020 */ @IIItemProperties(category = IICategory.WARFARE) -public class ItemIILightEngineerHelmet extends ItemIILightEngineerArmorBase implements IElectricEquipment, IGasmask +public class ItemIILightEngineerHelmet extends ItemIILightEngineerArmorBase implements IElectricEquipment { public ItemIILightEngineerHelmet() { @@ -139,7 +138,7 @@ public int getSlotCount() } @Override - public boolean protectsFromGasses(ItemStack stack) + protected boolean protectsFromGases(ItemStack stack) { return getUpgrades(stack).hasKey("gasmask"); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/crafting/ItemIIAssemblyScheme.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/crafting/ItemIIAssemblyScheme.java index 74fac6286..26f86360d 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/crafting/ItemIIAssemblyScheme.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/crafting/ItemIIAssemblyScheme.java @@ -135,7 +135,7 @@ public PrecisionAssemblerRecipe getSchemeRecipe(ItemStack stack) ItemStack recipeResult = new ItemStack(tag); return IIMultiblockRecipe.streamRecipes(PrecisionAssemblerRecipe.class) - .filter(recipe -> recipe.output.isItemEqual(recipeResult)) + .filter(recipe -> ItemStack.areItemStacksEqual(recipe.output, recipeResult)) .findFirst().orElse(null); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/data/ItemIIFunctionalCircuit.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/data/ItemIIFunctionalCircuit.java index d86d0c7a2..cecd50ca9 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/data/ItemIIFunctionalCircuit.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/data/ItemIIFunctionalCircuit.java @@ -205,11 +205,6 @@ public List getOperationsList(ItemStack stack) return Collections.emptyList(); } - public String getTESRRenderTexture(ItemStack stack) - { - return stackToSub(stack).tier.texture; - } - @GeneratedItemModels(itemName = "circuit_functional") public enum Circuits implements IIItemEnum @@ -352,16 +347,15 @@ public String[] getFunctions() public enum CircuitTypes implements ISerializableEnum { - BASIC("basic_circuits", "circuitBasic"), - ADVANCED("advanced_circuits", "circuitAdvanced"), - CRYPTOGRAPHIC("cryptography_circuits", "circuitCryptographic"), - PROCESSOR("processor_circuits", "circuitProcessor"); + BASIC("circuitBasic"), + ADVANCED("circuitAdvanced"), + CRYPTOGRAPHIC("circuitCryptographic"), + PROCESSOR("circuitProcessor"); - public final String texture, material; + public final String material; - CircuitTypes(String texture, String material) + CircuitTypes(String material) { - this.texture = texture; this.material = material; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIElectricWrench.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIElectricWrench.java index aebe16622..58bbbe8ed 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIElectricWrench.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIElectricWrench.java @@ -14,11 +14,13 @@ import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import pl.pabilo8.immersiveintelligence.api.upgrade.IUpgradableDevice; +import pl.pabilo8.immersiveintelligence.api.upgrade.Upgrade; import pl.pabilo8.immersiveintelligence.api.upgrade.UpgradeUtils; import pl.pabilo8.immersiveintelligence.api.utils.tools.IWrench; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Tools; import pl.pabilo8.immersiveintelligence.common.IISounds; import pl.pabilo8.immersiveintelligence.common.util.IIReference; +import pl.pabilo8.immersiveintelligence.common.util.advancements.UpgradeTrigger; import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; import pl.pabilo8.immersiveintelligence.common.util.item.IIItemEnum.IIItemProperties; import pl.pabilo8.immersiveintelligence.common.util.item.IIItemUtils; @@ -49,6 +51,7 @@ public EnumActionResult onItemUseFirst(EntityPlayer player, World world, BlockPo if(te==null||te.getCurrentUpgrade()==null) return EnumActionResult.PASS; + Upgrade installed = te.getCurrentUpgrade(); ItemStack heldItem = player.getHeldItem(hand); //check if the powered wrench has charge if(!IIItemUtils.canUpgradeFreeOfCharge(player)&&!hasEnoughEnergy(heldItem)) @@ -57,6 +60,8 @@ public EnumActionResult onItemUseFirst(EntityPlayer player, World world, BlockPo if(te.addUpgradeInstallProgress(IIItemUtils.canUpgradeFreeOfCharge(player)?999999: Tools.electricWrenchUpgradeProgress)) { world.playSound(null, pos, IISounds.constructionElectricWrench, SoundCategory.PLAYERS, 0.5f, 1); + if(te.getCurrentUpgrade()==null) + UpgradeTrigger.trigger(installed, player, heldItem); damageWrench(heldItem, player); } return EnumActionResult.SUCCESS; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIWrench.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIWrench.java index 68baa17ab..4c6149fd6 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIWrench.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/tools/ItemIIWrench.java @@ -24,12 +24,14 @@ import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import pl.pabilo8.immersiveintelligence.api.upgrade.IUpgradableDevice; +import pl.pabilo8.immersiveintelligence.api.upgrade.Upgrade; import pl.pabilo8.immersiveintelligence.api.upgrade.UpgradeUtils; import pl.pabilo8.immersiveintelligence.api.utils.tools.IWrench; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Tools; import pl.pabilo8.immersiveintelligence.common.IISounds; import pl.pabilo8.immersiveintelligence.common.util.IIReference; import pl.pabilo8.immersiveintelligence.common.util.IIStringUtil; +import pl.pabilo8.immersiveintelligence.common.util.advancements.UpgradeTrigger; import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; import pl.pabilo8.immersiveintelligence.common.util.item.IIItemEnum.IIItemProperties; import pl.pabilo8.immersiveintelligence.common.util.item.ItemIIBase; @@ -179,10 +181,14 @@ public EnumActionResult onItemUseFirst(EntityPlayer player, World world, BlockPo if(te==null||te.getCurrentUpgrade()==null) return EnumActionResult.PASS; + Upgrade installed = te.getCurrentUpgrade(); + ItemStack heldItem = player.getHeldItem(hand); if(te.addUpgradeInstallProgress(player.isCreative()?999999: Tools.electricWrenchUpgradeProgress)) { world.playSound(null, pos, IISounds.constructionElectricWrench, SoundCategory.PLAYERS, 0.5f, 1); - damageWrench(player.getHeldItem(hand), player); + if(te.getCurrentUpgrade()==null) + UpgradeTrigger.trigger(installed, player, heldItem); + damageWrench(heldItem, player); } return EnumActionResult.SUCCESS; } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/weapons/ItemIIRailgunOverride.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/weapons/ItemIIRailgunOverride.java index 56f5a0f65..505c2b360 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/item/weapons/ItemIIRailgunOverride.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/item/weapons/ItemIIRailgunOverride.java @@ -22,7 +22,7 @@ import net.minecraftforge.items.IItemHandler; import org.apache.commons.lang3.tuple.Triple; import pl.pabilo8.immersiveintelligence.api.ammo.utils.AmmoFactory; -import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Weapons.Railgun; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Overrides; import pl.pabilo8.immersiveintelligence.common.item.ammo.ItemIIAmmoRailgunGrenade; import pl.pabilo8.immersiveintelligence.common.util.item.IICategory; import pl.pabilo8.immersiveintelligence.common.util.item.IIItemEnum.IIItemProperties; @@ -80,7 +80,7 @@ public ActionResult onItemRightClick(World world, EntityPlayer player { ItemStack stack = player.getHeldItem(hand); - if(Railgun.disableRailgunOffhand&&hand==EnumHand.OFF_HAND) + if(Overrides.Railgun.disableRailgunOffhand&&hand==EnumHand.OFF_HAND) return new ActionResult<>(EnumActionResult.PASS, stack); int energy = IEConfig.Tools.railgun_consumption; @@ -141,7 +141,7 @@ public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase ammo.shrink(1); - if(Railgun.railgunRecoil) + if(Overrides.Railgun.railgunRecoil) user.move(MoverType.PISTON, -vec.x*mass*0.25f, 0, -vec.z*mass*0.25f); Triple shader = ShaderRegistry.getStoredShaderAndCase(stack); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageDiplomacyAction.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageDiplomacyAction.java index f08ac10e9..407b9865c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageDiplomacyAction.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageDiplomacyAction.java @@ -91,6 +91,43 @@ public static MessageDiplomacyAction removeMember(@Nonnull UUID playerUUID) return msg; } + @SideOnly(Side.CLIENT) + public static MessageDiplomacyAction cancelInvitation(@Nonnull UUID playerUUID) + { + MessageDiplomacyAction msg = new MessageDiplomacyAction(); + msg.action = DiplomaticAction.CANCEL_INVITATION; + msg.targetPlayer = playerUUID; + return msg; + } + + @SideOnly(Side.CLIENT) + public static MessageDiplomacyAction changeMemberRole(@Nonnull UUID playerUUID, @Nonnull PermissionRole role) + { + MessageDiplomacyAction msg = new MessageDiplomacyAction(); + msg.action = DiplomaticAction.CHANGE_MEMBER_ROLE; + msg.targetPlayer = playerUUID; + msg.targetRole = role.getId(); + return msg; + } + + @SideOnly(Side.CLIENT) + public static MessageDiplomacyAction acceptInvitation(@Nonnull UUID factionUUID) + { + MessageDiplomacyAction msg = new MessageDiplomacyAction(); + msg.action = DiplomaticAction.ACCEPT_INVITATION; + msg.targetFaction = factionUUID; + return msg; + } + + @SideOnly(Side.CLIENT) + public static MessageDiplomacyAction denyInvitation(@Nonnull UUID factionUUID) + { + MessageDiplomacyAction msg = new MessageDiplomacyAction(); + msg.action = DiplomaticAction.DENY_INVITATION; + msg.targetFaction = factionUUID; + return msg; + } + @SideOnly(Side.CLIENT) public static MessageDiplomacyAction merge(@Nonnull UUID targetFactionName) { @@ -181,11 +218,25 @@ protected void onServerReceive(WorldServer world, NetHandlerPlayServer handler) { EntityPlayerMP sender = handler.player; DiplomacyHandler diplomacy = DiplomacyHandler.getInstance(false); - OwnerIdentity identity = diplomacy.getOwnerIdentityForEntity(sender); - if(identity.isInvalid()) + + //Invitation replies are player-scoped actions, they don't require permission checks + if(action==DiplomaticAction.ACCEPT_INVITATION||action==DiplomaticAction.DENY_INVITATION) + { + OwnerIdentity invitedIdentity = diplomacy.getIdentityByUUID(targetFaction); + if(invitedIdentity.getUUID()==DiplomacyHandler.NEUTRAL_UUID||invitedIdentity.getUUID()==DiplomacyHandler.GLOBAL_ENEMY_UUID) + return; + if(invitedIdentity.isInvalid()) + return; + if(action==DiplomaticAction.ACCEPT_INVITATION) + diplomacy.acceptInvitation(invitedIdentity, sender.getUniqueID()); + else + diplomacy.denyInvitation(invitedIdentity, sender.getUniqueID()); return; - if(!identity.isPermitted(sender, action.getRequiredPermission())) + } + + OwnerIdentity identity = diplomacy.getOwnerIdentityForEntity(sender); + if(identity.isInvalid()||!identity.isPermitted(sender, action.getRequiredPermission())) return; switch(action) @@ -198,33 +249,63 @@ protected void onServerReceive(WorldServer world, NetHandlerPlayServer handler) break; } case START_SEIZING: + break; + case ADD_MEMBER: { + if(targetPlayer!=null&&!identity.isMember(targetPlayer)&&!identity.isInvited(targetPlayer)) + { + IILogger.info("Invited player "+targetPlayer+" to faction "+identity.getDisplayName()); + identity.invitePlayer(targetPlayer); + diplomacy.saveAndSyncIdentity(identity); + } break; } - case ADD_MEMBER: + case CANCEL_INVITATION: { - IILogger.info("Invited player "+targetPlayer+" to faction "+identity.getDisplayName()); - identity.invitePlayer(targetPlayer); + if(targetPlayer!=null&&identity.isInvited(targetPlayer)) + { + identity.removeInvitation(targetPlayer); + diplomacy.saveAndSyncIdentity(identity); + } break; } case REMOVE_MEMBER: { if(identity.isMember(targetPlayer)) - identity.removeMember(targetPlayer); //sync = true + { + PermissionRole target = targetPlayer==null?null: identity.getRoleOf(targetPlayer); + if(target!=null&&!target.isOwner()&&!targetPlayer.equals(sender.getUniqueID())) + { + identity.removeMember(targetPlayer); + //sync = true + diplomacy.saveAndSyncIdentity(identity); + } + } + break; + } + case CHANGE_MEMBER_ROLE: + { + PermissionRole actingRole = identity.getRoleOf(sender.getUniqueID()); + PermissionRole currentRole = targetPlayer==null?null: identity.getRoleOf(targetPlayer); + PermissionRole changedRole = identity.getAvailableRoles().get(targetRole); + if(actingRole!=null&&actingRole.isOwner()&¤tRole!=null&&!currentRole.isOwner() + &&changedRole!=null&&!changedRole.isOwner()) + { + identity.withMember(targetPlayer, changedRole.getId()); + diplomacy.saveAndSyncIdentity(identity); + } break; } case MERGE: { OwnerIdentity targetIdentity = diplomacy.getIdentityByUUID(targetFaction); - if(targetIdentity!=DiplomacyHandler.NEUTRAL&&!targetIdentity.equals(identity)) + if((targetIdentity.getUUID()==DiplomacyHandler.NEUTRAL_UUID||targetIdentity.getUUID()==DiplomacyHandler.GLOBAL_ENEMY_UUID) + &&!targetIdentity.isInvalid()&&!targetIdentity.equals(identity)) diplomacy.merge(identity, targetIdentity); break; } case DISBAND: - { - //DiplomacyUtils.removeIdentity(identity); break; - } case RENAME: { identity.withDisplayName(newDisplayName); @@ -260,7 +341,8 @@ protected void onServerReceive(WorldServer world, NetHandlerPlayServer handler) } OwnerIdentity targetIdentity = diplomacy.getIdentityByUUID(targetFaction); - if(targetIdentity!=DiplomacyHandler.NEUTRAL) + if(targetIdentity.getUUID()!=DiplomacyHandler.NEUTRAL_UUID&&targetIdentity.getUUID()!=DiplomacyHandler.GLOBAL_ENEMY_UUID + &&!targetIdentity.isInvalid()) diplomacy.proposeAgreement(identity, targetIdentity, proposal); break; } @@ -268,8 +350,7 @@ protected void onServerReceive(WorldServer world, NetHandlerPlayServer handler) { PermissionRole role = identity.getRoleOf(sender.getUniqueID()); PermissionRole changed = identity.getAvailableRoles().get(targetRole); - //Only the owner can change permissions - if(changed!=null&&role!=null&&role.isOwner()) + if(changed!=null&&!changed.isOwner()&&role!=null&&role.isOwner()) { changed.withPermission(permissionCategory, permissionSetting); diplomacy.saveAndSyncIdentity(identity); @@ -301,9 +382,16 @@ public void fromBytes(ByteBuf buf) break; case ADD_MEMBER: case REMOVE_MEMBER: + case CANCEL_INVITATION: + this.targetPlayer = UUID.fromString(readString(buf)); + break; + case CHANGE_MEMBER_ROLE: this.targetPlayer = UUID.fromString(readString(buf)); + this.targetRole = readString(buf); break; case MERGE: + case ACCEPT_INVITATION: + case DENY_INVITATION: this.targetFaction = readUUID(buf); break; case DISBAND: @@ -348,9 +436,16 @@ public void toBytes(ByteBuf buf) break; case ADD_MEMBER: case REMOVE_MEMBER: + case CANCEL_INVITATION: writeString(buf, targetPlayer.toString()); break; + case CHANGE_MEMBER_ROLE: + writeString(buf, targetPlayer.toString()); + writeString(buf, targetRole); + break; case MERGE: + case ACCEPT_INVITATION: + case DENY_INVITATION: writeUUID(buf, targetFaction); break; case DISBAND: diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageExplosion.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageExplosion.java index d63e9d01f..a20ab7523 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageExplosion.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageExplosion.java @@ -4,6 +4,7 @@ import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; @@ -15,7 +16,10 @@ import pl.pabilo8.immersiveintelligence.client.fx.utils.ParticleRegistry; import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Graphics; import pl.pabilo8.immersiveintelligence.common.network.IIMessage; -import pl.pabilo8.immersiveintelligence.common.util.IIExplosion; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class MessageExplosion extends IIMessage implements IPositionBoundMessage { @@ -24,8 +28,15 @@ public class MessageExplosion extends IIMessage implements IPositionBoundMessage private float radius, strength; private Vec3d pos, direction; private ComponentEffectShape shape; + private List particleBlocks = Collections.emptyList(); public MessageExplosion(World world, boolean flaming, boolean damagesTerrain, float radius, float strength, Vec3d pos, Vec3d direction, ComponentEffectShape shape) + { + this(world, flaming, damagesTerrain, radius, strength, pos, direction, shape, Collections.emptyList()); + } + + public MessageExplosion(World world, boolean flaming, boolean damagesTerrain, float radius, float strength, + Vec3d pos, Vec3d direction, ComponentEffectShape shape, List particleBlocks) { this.world = world; this.flaming = flaming; @@ -35,6 +46,7 @@ public MessageExplosion(World world, boolean flaming, boolean damagesTerrain, fl this.pos = pos; this.direction = direction; this.shape = shape; + this.particleBlocks = particleBlocks==null?Collections.emptyList(): particleBlocks; } public MessageExplosion() @@ -52,8 +64,7 @@ protected void onServerReceive(WorldServer world, NetHandlerPlayServer handler) protected void onClientReceive(WorldClient world, NetHandlerPlayClient handler) { ClientEventHandler.addScreenshakeSource(pos, MathHelper.clamp(strength/4f, 0.25f, 3f), 4, 2); - ParticleRegistry.spawnExplosionBoomFX(world, pos, direction, - new IIExplosion(world, null, pos, direction, radius, strength, shape, flaming, damagesTerrain, false)); + ParticleRegistry.spawnExplosionBoomFX(world, pos, direction, radius, strength, shape, particleBlocks); } @Override @@ -69,6 +80,14 @@ public void fromBytes(ByteBuf buf) this.direction = readVec3(buf); this.shape = readEnum(buf, ComponentEffectShape.class); + + if(buf.readableBytes() >= 2) + { + int particleBlockCount = buf.readUnsignedShort(); + this.particleBlocks = new ArrayList<>(particleBlockCount); + for(int i = 0; i < particleBlockCount&&buf.readableBytes() >= 8; i++) + this.particleBlocks.add(BlockPos.fromLong(buf.readLong())); + } } @Override @@ -84,6 +103,11 @@ public void toBytes(ByteBuf buf) writeVec3(buf, direction); writeEnum(buf, shape); + + int particleBlockCount = Math.min(0xFFFF, particleBlocks.size()); + buf.writeShort(particleBlockCount); + for(int i = 0; i < particleBlockCount; i++) + buf.writeLong(particleBlocks.get(i).toLong()); } @Override @@ -103,4 +127,4 @@ public int getPacketDistance() { return Graphics.explosionMessageDistance; } -} \ No newline at end of file +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageIIRequestChunkClaimData.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageIIRequestChunkClaimData.java index 2f025ae6a..8f51347af 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageIIRequestChunkClaimData.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageIIRequestChunkClaimData.java @@ -39,25 +39,27 @@ public MessageIIRequestChunkClaimData(Chunk chunk) @Override protected void onServerReceive(WorldServer world, NetHandlerPlayServer handler) { - Chunk chunk = world.getChunkProvider().getLoadedChunk(chunkX, chunkZ); - if(chunk==null) - { - IILogger.error("MessageIIRequestChunkClaimData: Chunk at "+new ChunkPos(chunkX, chunkZ)+" is not loaded!"); - return; - } + world.getChunkProvider().loadChunk(chunkX, chunkZ, () -> { + Chunk chunk = world.getChunkProvider().getLoadedChunk(chunkX, chunkZ); + if(chunk==null) + { + IILogger.error("MessageIIRequestChunkClaimData: Chunk at "+new ChunkPos(chunkX, chunkZ)+" is not loaded!"); + return; + } - if(!chunk.hasCapability(CapabilityChunkOwnership.CHUNK_OWNERSHIP_CAP, null)) - { - IILogger.error("MessageIIRequestChunkClaimData: Chunk at "+new ChunkPos(chunkX, chunkZ)+" has no ChunkOwnership capability!"); - return; - } + if(!chunk.hasCapability(CapabilityChunkOwnership.CHUNK_OWNERSHIP_CAP, null)) + { + IILogger.error("MessageIIRequestChunkClaimData: Chunk at "+new ChunkPos(chunkX, chunkZ)+" has no ChunkOwnership capability!"); + return; + } - IChunkOwnership cap = chunk.getCapability(CapabilityChunkOwnership.CHUNK_OWNERSHIP_CAP, null); - assert cap!=null; + IChunkOwnership cap = chunk.getCapability(CapabilityChunkOwnership.CHUNK_OWNERSHIP_CAP, null); + assert cap!=null; - //Send a reply - IIPacketHandler.sendToClient(handler.player, new MessageIIChunkClaimData(world, chunk.getPos().getBlock(8, 8, 8), - cap.getOwner(), cap.getClaimData())); + //Send a reply + IIPacketHandler.sendToClient(handler.player, new MessageIIChunkClaimData(world, chunk.getPos().getBlock(8, 8, 8), + cap.getOwner(), cap.getClaimData())); + }); } @SideOnly(Side.CLIENT) @@ -71,13 +73,13 @@ protected void onClientReceive(WorldClient world, NetHandlerPlayClient handler) public void fromBytes(ByteBuf buf) { this.chunkX = buf.readInt(); - this.chunkZ = buf.readByte(); + this.chunkZ = buf.readInt(); } @Override public void toBytes(ByteBuf buf) { buf.writeInt(this.chunkX); - buf.writeByte(this.chunkZ); + buf.writeInt(this.chunkZ); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageManualClose.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageManualClose.java index c14675cda..512be8406 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageManualClose.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/network/messages/MessageManualClose.java @@ -10,6 +10,8 @@ import net.minecraft.world.WorldServer; import pl.pabilo8.immersiveintelligence.common.network.IIMessage; +import java.util.UUID; + /** * @author Pabilo8 (pabilo@iiteam.net) * @since 20.07.2021 @@ -42,7 +44,10 @@ protected void onServerReceive(WorldServer world, NetHandlerPlayServer handler) if((skin==null||skin.isEmpty())&&ItemNBTHelper.hasKey(target, "lastSkin")) ItemNBTHelper.remove(target, "lastSkin"); else if(skin!=null) - ItemNBTHelper.setString(target, "lastSkin", skin); + { + UUID uniqueID = handler.player.getUniqueID(); + ItemNBTHelper.setString(target, "lastSkin", uniqueID+":"+skin); + } } private boolean isManual(ItemStack stack) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/CommandIIBase.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/CommandIIBase.java index 8d0889c62..060f16f9e 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/CommandIIBase.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/CommandIIBase.java @@ -8,7 +8,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; import java.util.Optional; +import java.util.function.Predicate; /** * @author Pabilo8 (pabilo@iiteam.net) @@ -49,4 +52,19 @@ public final String getUsage(@Nullable ICommandSender sender) @Override public abstract void execute(@Nonnull MinecraftServer server, @Nullable ICommandSender sender, @Nonnull String[] args) throws CommandException; + + //--- Utils ---// + + protected static & ISerializableEnum> List getTabCompletionsEnum(String[] args, Class enumType) + { + return getTabCompletionsEnum(args, enumType, e -> true); + } + + protected static & ISerializableEnum> List getTabCompletionsEnum(String[] args, Class enumType, Predicate filter) + { + return getListOfStringsMatchingLastWord(args, Arrays.stream(enumType.getEnumConstants()) + .filter(filter) + .map(E::getName) + .toArray(String[]::new)); + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIExplosion.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIExplosion.java index 08ab91606..30ef1f63f 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIExplosion.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIExplosion.java @@ -1,7 +1,6 @@ package pl.pabilo8.immersiveintelligence.common.util; import blusunrize.immersiveengineering.common.util.Utils; -import com.google.common.collect.Sets; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; @@ -13,7 +12,6 @@ import net.minecraft.util.DamageSource; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundCategory; -import net.minecraft.util.Tuple; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; @@ -29,10 +27,8 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.vecmath.AxisAngle4f; -import javax.vecmath.Matrix4f; -import javax.vecmath.Vector4f; import java.util.*; +import java.util.stream.IntStream; /** * @author Pabilo8 (pabilo@iiteam.net) @@ -46,6 +42,20 @@ public class IIExplosion extends Explosion * The loss of energy for a explosion line trace */ private static final float LOSS = 0.3F*0.75F*5; + private static final float TRACE_STEP = 0.5F; + private static final double TRACE_STEP_D = TRACE_STEP; + private static final double ZERO_DIRECTION_EPSILON = 1.0E-7D; + private static final float PARALLEL_TRACE_SIZE_THRESHOLD = 56f; + private static final int PARALLEL_TRACE_RAY_THRESHOLD = 8192; + private static final float PARTICLE_SURFACE_SAMPLE_SIZE_THRESHOLD = 8f; + private static final int MAX_PARTICLE_SURFACE_SAMPLES = 256; + + private static final long POS_X_MASK = 0x3FFFFFFL; + private static final long POS_Y_MASK = 0xFFFL; + private static final long POS_Z_MASK = 0x3FFFFFFL; + private static final int POS_Y_SHIFT = 26; + private static final int POS_X_SHIFT = 38; + @Nonnull private final Vec3d center, direction; private final ComponentEffectShape shape; @@ -54,9 +64,9 @@ public class IIExplosion extends Explosion private int delay; public IIExplosion(World world, @Nonnull Entity exploder, - Vec3d position, @Nullable Vec3d direction, - float size, float power, ComponentEffectShape shape, - boolean flaming, boolean damagesTerrain, boolean doDrops + Vec3d position, @Nullable Vec3d direction, + float size, float power, ComponentEffectShape shape, + boolean flaming, boolean damagesTerrain, boolean doDrops ) { super(world, exploder, position.x, position.y, position.z, size, flaming, damagesTerrain); @@ -77,25 +87,24 @@ public static Set getTopBlocks(Set positions, EnumFacing fac { EnumFacing.Axis axis = facing.getAxis(); EnumFacing.AxisDirection direction = facing.getAxisDirection(); - Set topBlocks = Sets.newHashSet(); - // Group positions by the other two axes - Map, BlockPos> groupedPositions = new HashMap<>(); + //Group positions by the other two axes, using a primitive long key to avoid Tuple churn. + Map groupedPositions = new HashMap<>(positions.size()); for(BlockPos pos : positions) { - int primary = axis==EnumFacing.Axis.X?pos.getX(): (axis==EnumFacing.Axis.Y?pos.getY(): pos.getZ()); + int primary = getAxisValue(pos, axis); int secondary1 = axis==EnumFacing.Axis.X?pos.getY(): pos.getX(); int secondary2 = axis==EnumFacing.Axis.Z?pos.getY(): pos.getZ(); + long key = packTwoInts(secondary1, secondary2); - Tuple key = new Tuple<>(secondary1, secondary2); - if(!groupedPositions.containsKey(key)|| - (direction==EnumFacing.AxisDirection.POSITIVE&&primary > (axis==EnumFacing.Axis.X?groupedPositions.get(key).getX(): (axis==EnumFacing.Axis.Y?groupedPositions.get(key).getY(): groupedPositions.get(key).getZ())))|| - (direction==EnumFacing.AxisDirection.NEGATIVE&&primary < (axis==EnumFacing.Axis.X?groupedPositions.get(key).getX(): (axis==EnumFacing.Axis.Y?groupedPositions.get(key).getY(): groupedPositions.get(key).getZ())))) + BlockPos previous = groupedPositions.get(key); + if(previous==null|| + (direction==EnumFacing.AxisDirection.POSITIVE&&primary > getAxisValue(previous, axis))|| + (direction==EnumFacing.AxisDirection.NEGATIVE&&primary < getAxisValue(previous, axis))) groupedPositions.put(key, pos); } - topBlocks.addAll(groupedPositions.values()); - return topBlocks; + return new HashSet<>(groupedPositions.values()); } @Override @@ -104,6 +113,7 @@ public void doExplosionA() this.affectedBlockPositions.addAll(generateAffectedBlockPositions()); float diameter = this.size*2.0F; + double diameterSq = diameter*diameter; int k1 = MathHelper.floor(this.x-(double)diameter-1.0D); int l1 = MathHelper.floor(this.x+(double)diameter+1.0D); int i2 = MathHelper.floor(this.y-(double)diameter-1.0D); @@ -114,44 +124,45 @@ public void doExplosionA() List list = this.world.getEntitiesWithinAABBExcludingEntity(this.exploder, new AxisAlignedBB(k1, i2, j2, l1, i1, j1)); ForgeEventFactory.onExplosionDetonate(this.world, this, list, diameter); Vec3d vec3d = new Vec3d(this.x, this.y, this.z); + DamageSource explosionDamage = DamageSource.causeExplosionDamage(this); for(Entity entity : list) if(!entity.isDead&&!entity.isImmuneToExplosions()) { - double fragment = entity.getDistance(this.x, this.y, this.z)/(double)diameter; + double distanceSq = entity.getDistanceSq(this.x, this.y, this.z); + if(distanceSq > diameterSq) + continue; - if(fragment <= 1.0D) - { - double xDiff = entity.posX-this.x; - double yDiff = entity.posY+(double)entity.getEyeHeight()-this.y; - double zDiff = entity.posZ-this.z; - double dist = MathHelper.sqrt(xDiff*xDiff+yDiff*yDiff+zDiff*zDiff); + double fragment = MathHelper.sqrt(distanceSq)/(double)diameter; + double xDiff = entity.posX-this.x; + double yDiff = entity.posY+(double)entity.getEyeHeight()-this.y; + double zDiff = entity.posZ-this.z; + double dist = MathHelper.sqrt(xDiff*xDiff+yDiff*yDiff+zDiff*zDiff); - if(dist!=0.0D) + if(dist!=0.0D) + { + xDiff = xDiff/dist; + yDiff = yDiff/dist; + zDiff = zDiff/dist; + double blockDensity = this.world.getBlockDensity(vec3d, entity.getEntityBoundingBox()); + double reversed = (1.0D-fragment)*blockDensity; + entity.attackEntityFrom(explosionDamage, + (float)((int)((reversed*reversed+reversed)/2.0D*15.0D*power/2f*(double)diameter+1.0D))); + double reversedTmp = reversed; + + if(entity instanceof EntityLivingBase) + reversedTmp = EnchantmentProtection.getBlastDamageReduction((EntityLivingBase)entity, reversed); + + entity.motionX += xDiff*reversedTmp; + entity.motionY += yDiff*reversedTmp; + entity.motionZ += zDiff*reversedTmp; + + if(entity instanceof EntityPlayer) { - xDiff = xDiff/dist; - yDiff = yDiff/dist; - zDiff = zDiff/dist; - double blockDensity = this.world.getBlockDensity(vec3d, entity.getEntityBoundingBox()); - double reversed = (1.0D-fragment)*blockDensity; - entity.attackEntityFrom(DamageSource.causeExplosionDamage(this), - (float)((int)((reversed*reversed+reversed)/2.0D*7.0D*power/2f*(double)diameter+1.0D))); - double reversedTmp = reversed; - - if(entity instanceof EntityLivingBase) - reversedTmp = EnchantmentProtection.getBlastDamageReduction((EntityLivingBase)entity, reversed); - - entity.motionX += xDiff*reversedTmp; - entity.motionY += yDiff*reversedTmp; - entity.motionZ += zDiff*reversedTmp; - - if(entity instanceof EntityPlayer) - { - EntityPlayer entityplayer = (EntityPlayer)entity; - - if(!entityplayer.isSpectator()&&(!entityplayer.isCreative()||!entityplayer.capabilities.isFlying)) - this.playerKnockbackMap.put(entityplayer, new Vec3d(xDiff*reversed, yDiff*reversed, zDiff*reversed)); - } + EntityPlayer entityplayer = (EntityPlayer)entity; + + if(!entityplayer.isSpectator()&&(!entityplayer.isCreative()||!entityplayer.capabilities.isFlying)) + this.playerKnockbackMap.put(entityplayer, new Vec3d(xDiff*reversed, yDiff*reversed, zDiff*reversed)); } } } @@ -162,25 +173,18 @@ public void doExplosionA() */ public Set generateAffectedBlockPositions() { - Set set; - //Generate block positions based on shape switch(shape) { case LINE: - set = generateLineBlockPos(); - break; + return generateLineBlockPos(); case CONE: - set = generateConeBlockPos(1, 1f); - break; + return generateConeBlockPos(1, 1f); case ORB: - set = generateOrbBlockPos(1, 1f); - break; + return generateOrbBlockPos(1, 1f); default: case STAR: - set = generateOrbBlockPos(0.35f, 0.9f); - break; + return generateOrbBlockPos(0.35f, 0.9f); } - return set; } //--- Explosion Shapes ---// @@ -193,55 +197,42 @@ public Set generateAffectedBlockPositions() */ private Set generateOrbBlockPos(float densityScale, float powerMultiplier) { - ArrayList set = new ArrayList<>(); - //Steps per rotation - final int steps = MathHelper.ceil(Math.PI*size*densityScale); - float power, pitch, yaw; - Vec3d current, direction; + final int steps = Math.max(1, MathHelper.ceil((float)Math.PI*size*densityScale)); + final int maxTraceSteps = MathHelper.floor(size/TRACE_STEP); + final float basePower = this.power*powerMultiplier; + final float yawStep = (float)Math.PI/steps; + final float pitchStep = (float)Math.PI/steps; + + final int yawCount = 2*steps; + final int rayCount = yawCount*steps; + + if(shouldUseParallelTracing(rayCount)) + return generateOrbBlockPosParallel(steps, maxTraceSteps, basePower, yawStep, pitchStep); + + BlockTraceCollector collector = new BlockTraceCollector(estimateCollectorCapacity()); + + for(int yawSlice = 0; yawSlice < yawCount; yawSlice++) + { + float yaw = yawStep*yawSlice; + float cosYaw = MathHelper.cos(yaw); + float sinYaw = MathHelper.sin(yaw); - for(int yawSlices = 0; yawSlices < 2*steps; yawSlices++) for(int pitchSlice = 0; pitchSlice < steps; pitchSlice++) { - //Calculate power - power = this.power*powerMultiplier-(this.size*world.rand.nextFloat()/2); - //Get angles for rotation steps - yaw = (float)((Math.PI/steps)*yawSlices); - pitch = (float)((Math.PI/steps)*pitchSlice); - - //Figure out vector to move for trace (cut in half to improve trace skipping blocks) - direction = new Vec3d( - MathHelper.sin(pitch)*MathHelper.cos(yaw)*0.5, - MathHelper.cos(pitch)*0.5, - MathHelper.sin(pitch)*MathHelper.sin(yaw)*0.5 - ); - - //Revert position to explosion center - current = center; - - //Trace from start to end - while(center.distanceTo(current) <= size&&power > 0) - { - //Consume power per loop - power -= LOSS; + float pitch = pitchStep*pitchSlice; + float sinPitch = MathHelper.sin(pitch); - //Convert double position to int position as block pos - final BlockPos pos = new BlockPos(MathHelper.floor(current.x), MathHelper.floor(current.y), MathHelper.floor(current.z)); - - //Stops from scanning the same position twice - if(!set.contains(pos)) - { - //Cannot destroy unloaded blocks - if(!world.isBlockLoaded(pos)) - continue; - if(canDestroyBlock(pos, power)) - set.add(pos); - } + //Figure out vector to move for trace. The 0.5 step keeps the old ray density. + double stepX = sinPitch*cosYaw*TRACE_STEP_D; + double stepY = MathHelper.cos(pitch)*TRACE_STEP_D; + double stepZ = sinPitch*sinYaw*TRACE_STEP_D; + float rayPower = basePower-(this.size*world.rand.nextFloat()*0.5F); - //Move forward - current = current.add(direction); - } + traceRay(collector, stepX, stepY, stepZ, maxTraceSteps, rayPower); } - return Sets.newHashSet(set); + } + + return collector.toBlockPosSet(); } /** @@ -249,22 +240,30 @@ private Set generateOrbBlockPos(float densityScale, float powerMultipl */ private Set generateLineBlockPos() { - ArrayList set = new ArrayList<>(); - float power = this.power; - for(float i = 0; i < size*1.25f; i += 0.5f, power -= LOSS) + BlockTraceCollector collector = new BlockTraceCollector(Math.max(16, MathHelper.ceil(size*1.25f/TRACE_STEP))); + Vec3d dir = getSafeDirection(); + double stepX = dir.x*TRACE_STEP_D; + double stepY = dir.y*TRACE_STEP_D; + double stepZ = dir.z*TRACE_STEP_D; + int maxTraceSteps = MathHelper.floor(size*1.25f/TRACE_STEP); + + double currentX = center.x; + double currentY = center.y; + double currentZ = center.z; + float tracePower = this.power; + + for(int i = 0; i <= maxTraceSteps&&tracePower > 0; i++, tracePower -= LOSS) { - BlockPos pos = new BlockPos(center.add(direction.scale(i))); - if(!world.isBlockLoaded(pos)) - continue; - if(canDestroyBlock(pos, power)) - { - if(!set.contains(pos)) - set.add(pos); - } - else + ExposionTraceResult result = collector.tryAdd(MathHelper.floor(currentX), MathHelper.floor(currentY), MathHelper.floor(currentZ), tracePower); + if(result==ExposionTraceResult.BLOCKED) break; + + currentX += stepX; + currentY += stepY; + currentZ += stepZ; } - return Sets.newHashSet(set); + + return collector.toBlockPosSet(); } /** @@ -272,62 +271,134 @@ private Set generateLineBlockPos() */ private Set generateConeBlockPos(float densityScale, float powerMultiplier) { - ArrayList set = new ArrayList<>(); - - //Steps per rotation - final int steps = MathHelper.ceil(0.5f*size*densityScale); + final int steps = Math.max(1, MathHelper.ceil(0.5f*size*densityScale)); + final int maxTraceSteps = MathHelper.floor(size); final float step = 0.5f/steps; - float power; + final float basePower = this.power*powerMultiplier; + final Vec3d dir = getSafeDirection(); + + final int rayCount = steps*steps; + if(shouldUseParallelTracing(rayCount)) + return generateConeBlockPosParallel(steps, maxTraceSteps, step, basePower, dir); + + BlockTraceCollector collector = new BlockTraceCollector(estimateCollectorCapacity()/2); for(float pitch = -0.25f; pitch < 0.25f; pitch += step) for(float yaw = -0.25f; yaw < 0.25f; yaw += step) { - Vec3d initial = rotateVector(direction, (float)(pitch*Math.PI), (float)(yaw*Math.PI)); - Vec3d current = this.center; - power = this.power*powerMultiplier; + Vec3d initial = rotateVector(dir, (float)(pitch*Math.PI), (float)(yaw*Math.PI)); + traceRay(collector, initial.x, initial.y, initial.z, maxTraceSteps, basePower); + } - while(this.center.distanceTo(current) <= size&&power > 0) - { - //Consume power per loop - power -= LOSS; + return collector.toBlockPosSet(); + } - //Convert double position to int position as block pos - final BlockPos pos = new BlockPos(MathHelper.floor(current.x), MathHelper.floor(current.y), MathHelper.floor(current.z)); + private Set generateOrbBlockPosParallel(final int steps, final int maxTraceSteps, final float basePower, + final float yawStep, final float pitchStep) + { + final int pitchCount = steps; + final int rayCount = 2*steps*pitchCount; + final float[] randomLosses = createOrbRandomLosses(rayCount); + + BlockPowerCollector collector = IntStream.range(0, rayCount).parallel().collect( + this::createParallelCollector, + (localCollector, rayIndex) -> { + int yawSlice = rayIndex/pitchCount; + int pitchSlice = rayIndex-yawSlice*pitchCount; + + float yaw = yawStep*yawSlice; + float pitch = pitchStep*pitchSlice; + float sinPitch = MathHelper.sin(pitch); + + double stepX = sinPitch*MathHelper.cos(yaw)*TRACE_STEP_D; + double stepY = MathHelper.cos(pitch)*TRACE_STEP_D; + double stepZ = sinPitch*MathHelper.sin(yaw)*TRACE_STEP_D; + float rayPower = basePower-randomLosses[rayIndex]; + + traceRayCandidates(localCollector, stepX, stepY, stepZ, maxTraceSteps, rayPower); + }, + BlockPowerCollector::mergeFrom + ); + + return validateCandidateBlocks(collector); + } - //Stops from scanning the same position twice - if(!set.contains(pos)) - { - //Cannot destroy unloaded blocks - if(!world.isBlockLoaded(pos)) - continue; - if(canDestroyBlock(pos, power)) - set.add(pos); - } + private Set generateConeBlockPosParallel(final int steps, final int maxTraceSteps, final float step, + final float basePower, final Vec3d dir) + { + final int rayCount = steps*steps; - //Move forward - current = current.add(initial); - } - } - return Sets.newHashSet(set); + BlockPowerCollector collector = IntStream.range(0, rayCount).parallel().collect( + this::createParallelCollector, + (localCollector, rayIndex) -> { + int pitchIndex = rayIndex/steps; + int yawIndex = rayIndex-pitchIndex*steps; + + float pitch = -0.25f+step*pitchIndex; + float yaw = -0.25f+step*yawIndex; + Vec3d initial = rotateVector(dir, (float)(pitch*Math.PI), (float)(yaw*Math.PI)); + + traceRayCandidates(localCollector, initial.x, initial.y, initial.z, maxTraceSteps, basePower); + }, + BlockPowerCollector::mergeFrom + ); + + return validateCandidateBlocks(collector); } - public Vec3d rotateVector(Vec3d vec, float pitch, float yaw) + private Vec3d rotateVector(Vec3d vec, float pitch, float yaw) { - // Create rotation matrices for pitch and yaw - Matrix4f pitchRotation = new Matrix4f(); - pitchRotation.setIdentity(); - pitchRotation.setRotation(new AxisAngle4f(1, 0, 0, pitch)); - Matrix4f yawRotation = new Matrix4f(); - yawRotation.setIdentity(); - yawRotation.setRotation(new AxisAngle4f(0, 1, 0, yaw)); + float cosPitch = MathHelper.cos(pitch); + float sinPitch = MathHelper.sin(pitch); + float cosYaw = MathHelper.cos(yaw); + float sinYaw = MathHelper.sin(yaw); - // Apply pitch and yaw rotations - Vector4f rotatedVector = new Vector4f((float)vec.x, (float)vec.y, (float)vec.z, 1); - pitchRotation.transform(rotatedVector); - yawRotation.transform(rotatedVector); + double xPitch = vec.x; + double yPitch = vec.y*cosPitch-vec.z*sinPitch; + double zPitch = vec.y*sinPitch+vec.z*cosPitch; - // Return the rotated vector - return new Vec3d(rotatedVector.x, rotatedVector.y, rotatedVector.z).normalize(); + double xYaw = xPitch*cosYaw+zPitch*sinYaw; + double zYaw = -xPitch*sinYaw+zPitch*cosYaw; + + return normaliseOrZero(xYaw, yPitch, zYaw); + } + + private void traceRay(BlockTraceCollector collector, double stepX, double stepY, double stepZ, int maxTraceSteps, float initialPower) + { + double currentX = center.x; + double currentY = center.y; + double currentZ = center.z; + float tracePower = initialPower; + + for(int i = 0; i <= maxTraceSteps&&tracePower > 0; i++) + { + //Keep the old order: consume power before testing the block at the current step. + tracePower -= LOSS; + collector.tryAdd(MathHelper.floor(currentX), MathHelper.floor(currentY), MathHelper.floor(currentZ), tracePower); + + currentX += stepX; + currentY += stepY; + currentZ += stepZ; + } + } + + private void traceRayCandidates(BlockPowerCollector collector, double stepX, double stepY, double stepZ, int maxTraceSteps, float initialPower) + { + double currentX = center.x; + double currentY = center.y; + double currentZ = center.z; + float tracePower = initialPower; + + for(int i = 0; i <= maxTraceSteps&&tracePower > 0; i++) + { + tracePower -= LOSS; + if(tracePower > 0.0F) + collector.record(MathHelper.floor(currentX), MathHelper.floor(currentY), MathHelper.floor(currentZ), tracePower); + + currentX += stepX; + currentY += stepY; + currentZ += stepZ; + } } @Override @@ -340,7 +411,9 @@ public void doExplosionB(boolean spawnParticles) SoundCategory.NEUTRAL, (int)(72*size), 1f, pitch); if(spawnParticles) - IIPacketHandler.sendToClient(new MessageExplosion(this.world, this.causesFire, this.damagesTerrain, this.size, this.power, center, direction, shape)); + IIPacketHandler.sendToClient(new MessageExplosion(this.world, this.causesFire, this.damagesTerrain, this.size, this.power, center, direction, shape, + this.size > PARTICLE_SURFACE_SAMPLE_SIZE_THRESHOLD? + getParticleEffectBlocks(MAX_PARTICLE_SURFACE_SAMPLES): Collections.emptyList())); EventHandler.pendingExplosions.add(this); } @@ -359,16 +432,27 @@ public boolean explodeBlocks() if(iblockstate.getMaterial()!=Material.AIR) { if(doDrops&&block.canDropFromExplosion(this)) - block.dropBlockAsItemWithChance(this.world, pos, this.world.getBlockState(pos), 1.0F/this.size, 0); + block.dropBlockAsItemWithChance(this.world, pos, iblockstate, 1.0F/this.size, 0); block.onBlockExploded(this.world, pos, this); } } if(this.causesFire) + { + BlockPos.MutableBlockPos below = new BlockPos.MutableBlockPos(); + for(BlockPos blockpos1 : this.affectedBlockPositions) - if(this.world.getBlockState(blockpos1).getMaterial()==Material.AIR&&this.world.getBlockState(blockpos1.down()).isFullBlock()&&this.random.nextInt(3)==0) + { + IBlockState state = this.world.getBlockState(blockpos1); + if(state.getMaterial()!=Material.AIR) + continue; + + below.setPos(blockpos1.getX(), blockpos1.getY()-1, blockpos1.getZ()); + if(this.world.getBlockState(below).isFullBlock()&&this.random.nextInt(3)==0) this.world.setBlockState(blockpos1, Blocks.FIRE.getDefaultState()); + } + } return true; } @@ -392,14 +476,111 @@ public IIExplosion doExplosion(boolean spawnParticles) private boolean canDestroyBlock(BlockPos pos, float power) { - //Get block state from position + if(power <= 0.0F) + return false; + IBlockState state = world.getBlockState(pos); + Block block = state.getBlock(); //Ignore air blocks && Only break block that can be broken - if(!state.getBlock().isAir(state, world, pos)&&power >= state.getBlock().getExplosionResistance(world, pos, exploder, this)) - return power > 0.0F&&(this.exploder==null||this.exploder.canExplosionDestroyBlock(this, this.world, pos, state, power)); - //Block cannot be destroyed - return false; + return !block.isAir(state, world, pos)&& + power >= block.getExplosionResistance(world, pos, exploder, this)&& + (this.exploder==null||this.exploder.canExplosionDestroyBlock(this, this.world, pos, state, power)); + } + + private Vec3d getSafeDirection() + { + return normaliseOrZero(direction.x, direction.y, direction.z); + } + + private static Vec3d normaliseOrZero(double x, double y, double z) + { + double lengthSq = x*x+y*y+z*z; + if(lengthSq <= ZERO_DIRECTION_EPSILON) + return Vec3d.ZERO; + + double invLength = MathHelper.fastInvSqrt(lengthSq); + return new Vec3d(x*invLength, y*invLength, z*invLength); + } + + private int estimateCollectorCapacity() + { + //A rough volume-based starting capacity. It is intentionally conservative, because HashMap resizing hurts less than allocating far too much for small explosions. + return Math.max(64, MathHelper.floor(size*size*size*0.35F)); + } + + private boolean shouldUseParallelTracing(int rayCount) + { + return size > PARALLEL_TRACE_SIZE_THRESHOLD&& + rayCount >= PARALLEL_TRACE_RAY_THRESHOLD&& + Runtime.getRuntime().availableProcessors() > 1; + } + + private BlockPowerCollector createParallelCollector() + { + int processors = Math.max(1, Runtime.getRuntime().availableProcessors()); + int capacity = estimateCollectorCapacity()/Math.max(4, processors*4); + return new BlockPowerCollector(Math.max(256, Math.min(16384, capacity))); + } + + private float[] createOrbRandomLosses(int rayCount) + { + float[] losses = new float[rayCount]; + for(int i = 0; i < rayCount; i++) + losses[i] = this.size*world.rand.nextFloat()*0.5F; + return losses; + } + + private Set validateCandidateBlocks(BlockPowerCollector collector) + { + Set positions = new LinkedHashSet<>(collector.size()); + BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); + + for(Map.Entry entry : collector.strongestPower.entrySet()) + { + long packed = entry.getKey(); + mutablePos.setPos(unpackX(packed), unpackY(packed), unpackZ(packed)); + + if(!world.isBlockLoaded(mutablePos)) + continue; + if(canDestroyBlock(mutablePos, entry.getValue())) + positions.add(new BlockPos(mutablePos.getX(), mutablePos.getY(), mutablePos.getZ())); + } + + return positions; + } + + private static int getAxisValue(BlockPos pos, EnumFacing.Axis axis) + { + return axis==EnumFacing.Axis.X?pos.getX(): (axis==EnumFacing.Axis.Y?pos.getY(): pos.getZ()); + } + + private static long packTwoInts(int a, int b) + { + return ((long)a<<32)^(b&0xFFFFFFFFL); + } + + private static long packPos(int x, int y, int z) + { + return ((long)x&POS_X_MASK)<>POS_X_SHIFT); + } + + private static int unpackY(long packed) + { + int y = (int)((packed>>POS_Y_SHIFT)&POS_Y_MASK); + return y >= 2048?y-4096: y; + } + + private static int unpackZ(long packed) + { + return (int)(packed<<38>>38); } private BlockPos getPos() @@ -407,6 +588,74 @@ private BlockPos getPos() return new BlockPos(this.x, this.y, this.z); } + /** + * Creates a bounded, evenly distributed sample of the actual affected surface for client particles. + * The positions come from {@link #affectedBlockPositions}, so dust and debris cannot be placed + * beyond the blocks selected by the server-side explosion calculation. + */ + public List getParticleEffectBlocks(int maxSamples) + { + if(maxSamples <= 0||this.affectedBlockPositions.isEmpty()) + return Collections.emptyList(); + + double directionLengthSq = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z; + Vec3d visualDirection = directionLengthSq <= ZERO_DIRECTION_EPSILON? + new Vec3d(0, 1, 0): direction.scale(-1).normalize(); + EnumFacing facing = EnumFacing.getFacingFromVector( + (float)visualDirection.x, + (float)visualDirection.y, + (float)visualDirection.z + ); + EnumFacing.Axis axis = facing.getAxis(); + EnumFacing.AxisDirection axisDirection = facing.getAxisDirection(); + + int minSecondary1 = Integer.MAX_VALUE; + int minSecondary2 = Integer.MAX_VALUE; + int maxSecondary1 = Integer.MIN_VALUE; + int maxSecondary2 = Integer.MIN_VALUE; + + for(BlockPos blockPos : this.affectedBlockPositions) + { + int secondary1 = axis==EnumFacing.Axis.X?blockPos.getY(): blockPos.getX(); + int secondary2 = axis==EnumFacing.Axis.Z?blockPos.getY(): blockPos.getZ(); + minSecondary1 = Math.min(minSecondary1, secondary1); + minSecondary2 = Math.min(minSecondary2, secondary2); + maxSecondary1 = Math.max(maxSecondary1, secondary1); + maxSecondary2 = Math.max(maxSecondary2, secondary2); + } + + long projectedArea = (long)(maxSecondary1-minSecondary1+1)*(maxSecondary2-minSecondary2+1); + int bucketSize = Math.max(1, MathHelper.ceil((float)Math.sqrt(projectedArea/(double)maxSamples))); + Map surfaceBuckets = new LinkedHashMap<>(maxSamples); + + for(BlockPos blockPos : this.affectedBlockPositions) + { + int primary = getAxisValue(blockPos, axis); + int secondary1 = axis==EnumFacing.Axis.X?blockPos.getY(): blockPos.getX(); + int secondary2 = axis==EnumFacing.Axis.Z?blockPos.getY(): blockPos.getZ(); + long bucketKey = packTwoInts( + Math.floorDiv(secondary1, bucketSize), + Math.floorDiv(secondary2, bucketSize) + ); + + BlockPos previous = surfaceBuckets.get(bucketKey); + if(previous==null|| + (axisDirection==EnumFacing.AxisDirection.POSITIVE&&primary > getAxisValue(previous, axis))|| + (axisDirection==EnumFacing.AxisDirection.NEGATIVE&&primary < getAxisValue(previous, axis))) + surfaceBuckets.put(bucketKey, blockPos); + } + + List surface = new ArrayList<>(surfaceBuckets.values()); + if(surface.size() <= maxSamples) + return surface; + + List reduced = new ArrayList<>(maxSamples); + float stride = surface.size()/(float)maxSamples; + for(int i = 0; i < maxSamples; i++) + reduced.add(surface.get(Math.min(surface.size()-1, MathHelper.floor(i*stride)))); + return reduced; + } + public double getPower() { return power; @@ -416,4 +665,102 @@ public double getSize() { return size; } + + public ComponentEffectShape getShape() + { + return shape; + } + + //--- Helper classes ---// + + private static class BlockPowerCollector + { + private final Map strongestPower; + + private BlockPowerCollector(int expectedSize) + { + this.strongestPower = new HashMap<>(Math.max(16, expectedSize)); + } + + private void record(int x, int y, int z, float power) + { + long packed = packPos(x, y, z); + Float previousPower = strongestPower.get(packed); + if(previousPower==null||previousPower < power) + strongestPower.put(packed, power); + } + + private void mergeFrom(BlockPowerCollector other) + { + for(Map.Entry entry : other.strongestPower.entrySet()) + { + Float previousPower = strongestPower.get(entry.getKey()); + if(previousPower==null||previousPower < entry.getValue()) + strongestPower.put(entry.getKey(), entry.getValue()); + } + } + + private int size() + { + return strongestPower.size(); + } + } + + private class BlockTraceCollector + { + private final Set affectedBlocks; + private final Map strongestTestedPower; + private final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); + + private BlockTraceCollector(int expectedSize) + { + int capacity = Math.max(16, expectedSize); + this.affectedBlocks = new LinkedHashSet<>(capacity); + this.strongestTestedPower = new HashMap<>(capacity); + } + + private ExposionTraceResult tryAdd(int x, int y, int z, float power) + { + if(power <= 0.0F) + return ExposionTraceResult.KNOWN; + + long packed = packPos(x, y, z); + if(affectedBlocks.contains(packed)) + return ExposionTraceResult.KNOWN; + + Float previousPower = strongestTestedPower.get(packed); + if(previousPower!=null&&previousPower >= power) + return ExposionTraceResult.KNOWN; + + strongestTestedPower.put(packed, power); + mutablePos.setPos(x, y, z); + + if(!world.isBlockLoaded(mutablePos)) + return ExposionTraceResult.UNLOADED; + + if(canDestroyBlock(mutablePos, power)) + { + affectedBlocks.add(packed); + return ExposionTraceResult.ADDED; + } + + return ExposionTraceResult.BLOCKED; + } + + private Set toBlockPosSet() + { + Set positions = new LinkedHashSet<>(affectedBlocks.size()); + for(Long packed : affectedBlocks) + positions.add(BlockPos.fromLong(packed)); + return positions; + } + } + + private enum ExposionTraceResult + { + KNOWN, + ADDED, + UNLOADED, + BLOCKED + } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIReflectionUtils.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIReflectionUtils.java index 0b59d3d47..aca87b815 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIReflectionUtils.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IIReflectionUtils.java @@ -77,9 +77,12 @@ public static void overrideEventHandler(Class origEvent, Object overrideEvent for(Map.Entry> o : listeners.entrySet()) { Object c1 = o.getKey(); - if(!c1.getClass().getName().equals(origEvent.getName())) continue; + if(!c1.getClass().getName().equals(origEvent.getName())) + continue; MinecraftForge.EVENT_BUS.unregister(c1); MinecraftForge.EVENT_BUS.register(overrideEvent); + IILogger.info("[Reflector] Replaced event handler for "+origEvent.getName()+" with "+overrideEvent.getClass().getName()); + return; } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IISkinHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IISkinHandler.java index 4d78d2611..246294704 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IISkinHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/IISkinHandler.java @@ -1,6 +1,7 @@ package pl.pabilo8.immersiveintelligence.common.util; import blusunrize.immersiveengineering.api.ManualHelper; +import blusunrize.immersiveengineering.client.ClientUtils; import blusunrize.immersiveengineering.common.util.ItemNBTHelper; import blusunrize.lib.manual.ManualInstance.ManualEntry; import blusunrize.lib.manual.ManualPages; @@ -8,6 +9,8 @@ import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.item.EnumRarity; import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import pl.pabilo8.immersiveintelligence.client.manual.pages.IIManualPageContributorSkin; import pl.pabilo8.immersiveintelligence.client.util.amt.AMTLoader; import pl.pabilo8.immersiveintelligence.common.IILogger; @@ -91,6 +94,7 @@ public static String getCurrentSkin(ItemStack item) return ItemNBTHelper.getString(item, NBT_ENTRY); } + @SideOnly(Side.CLIENT) public static void getManualPages() { ManualHelper.getManual().manualContents.removeAll("Contributor Skins"); @@ -100,8 +104,11 @@ public static void getManualPages() "donations through Patreon or simply contributing to II community, can not be overlooked and have to be rewarded.\n"+ "For that, a collection of skins has been added to the game, these are applicable to various weapons, ranging from machineguns to howitzers. "+ "Huge thanks to all of you, without you this project would take much longer than Soon(TM).")); + UUID id = ClientUtils.mc().player.getGameProfile().getId(); + String uuid = (id==null?ClientUtils.mc().player.getUniqueID(): id).toString(); for(IISpecialSkin skin : IISkinHandler.specialSkins.values()) - skin_pages.add(new IIManualPageContributorSkin(ManualHelper.getManual(), skin)); + if(skin.appliesToPlayer(uuid)) + skin_pages.add(new IIManualPageContributorSkin(ManualHelper.getManual(), skin)); ManualEntry contributor_skins = ManualHelper.getManual().getEntry("Contributor Skins"); if(contributor_skins==null) @@ -123,7 +130,6 @@ public static class IISpecialSkin public final String[] appliesTo; public final List mods; public int textColor = 0xffffff; - public boolean hasCape = false; public EnumRarity rarity = EnumRarity.UNCOMMON; public IISpecialSkin(String name, String[] uuid, String[] appliesTo, List mods) @@ -145,11 +151,18 @@ public boolean doesApply(String skinnableName) return !Arrays.asList(appliesTo).isEmpty()&&Arrays.asList(appliesTo).contains(skinnableName); } + /** + * @param uuid UUID of the player to check + * @return If skin applies to specific user + */ + public boolean appliesToPlayer(String uuid) + { + return !Arrays.asList(this.uuid).isEmpty()&&Arrays.asList(this.uuid).contains(uuid); + } + //Couldn't do it in the constructor, because it spitted an error void parseAdditionals() { - hasCape = mods.contains("cape"); - //lambdas are love, lambdas are life Optional optional = mods.stream().filter(s -> s.contains("text_color=")).findFirst(); optional.ifPresent(s -> this.textColor = Integer.parseInt(s.substring(11), 16)); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/advancements/UpgradeTrigger.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/advancements/UpgradeTrigger.java new file mode 100644 index 000000000..6289c2755 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/advancements/UpgradeTrigger.java @@ -0,0 +1,167 @@ +package pl.pabilo8.immersiveintelligence.common.util.advancements; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import net.minecraft.advancements.CriteriaTriggers; +import net.minecraft.advancements.ICriterionTrigger; +import net.minecraft.advancements.PlayerAdvancements; +import net.minecraft.advancements.critereon.AbstractCriterionInstance; +import net.minecraft.advancements.critereon.ItemPredicate; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.util.JsonUtils; +import net.minecraft.util.ResourceLocation; +import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; +import pl.pabilo8.immersiveintelligence.api.upgrade.Upgrade; +import pl.pabilo8.immersiveintelligence.common.util.advancements.UpgradeTrigger.UpgradeCriterionInstance; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Advancement criterion fired after a machine upgrade finishes installing. + * + * @author Pabilo8 (pabilo@iiteam.net) + * @since 12.07.2026 + */ +public class UpgradeTrigger implements ICriterionTrigger +{ + private static final ResourceLocation ID = new ResourceLocation(ImmersiveIntelligence.MODID, "upgrade_installed"); + public static final UpgradeTrigger INSTANCE = CriteriaTriggers.register(new UpgradeTrigger()); + + private final Map listeners = Maps.newHashMap(); + + private UpgradeTrigger() + { + + } + + @Nonnull + @Override + public ResourceLocation getId() + { + return ID; + } + + @Override + public void addListener(@Nonnull PlayerAdvancements playerAdvancements, @Nonnull ICriterionTrigger.Listener listener) + { + UpgradeListeners listeners = this.listeners.get(playerAdvancements); + if(listeners==null) + { + listeners = new UpgradeListeners(playerAdvancements); + this.listeners.put(playerAdvancements, listeners); + } + listeners.add(listener); + } + + @Override + public void removeListener(@Nonnull PlayerAdvancements playerAdvancements, @Nonnull ICriterionTrigger.Listener listener) + { + UpgradeListeners listeners = this.listeners.get(playerAdvancements); + if(listeners!=null) + { + listeners.remove(listener); + if(listeners.isEmpty()) + this.listeners.remove(playerAdvancements); + } + } + + @Override + public void removeAllListeners(@Nonnull PlayerAdvancements playerAdvancements) + { + this.listeners.remove(playerAdvancements); + } + + @Nonnull + @Override + public UpgradeCriterionInstance deserializeInstance(@Nonnull JsonObject json, @Nonnull JsonDeserializationContext context) + { + ResourceLocation upgrade = new ResourceLocation(JsonUtils.getString(json, "upgrade")); + ItemPredicate item = ItemPredicate.deserialize(json.get("item")); + return new UpgradeCriterionInstance(upgrade, item); + } + + /** + * Fires the criterion for a completed upgrade installation. + * + * @param upgrade installed upgrade + * @param player player who completed the installation + * @param wrench wrench used for the final installation action + */ + public static void trigger(Upgrade upgrade, EntityPlayer player, ItemStack wrench) + { + if(upgrade==null||!(player instanceof EntityPlayerMP)) + return; + + UpgradeListeners listeners = INSTANCE.listeners.get(((EntityPlayerMP)player).getAdvancements()); + if(listeners!=null) + listeners.trigger(upgrade, wrench); + } + + public static class UpgradeCriterionInstance extends AbstractCriterionInstance + { + private final ResourceLocation upgrade; + private final ItemPredicate item; + + public UpgradeCriterionInstance(ResourceLocation upgrade, ItemPredicate item) + { + super(ID); + this.upgrade = upgrade; + this.item = item; + } + + public boolean test(Upgrade upgrade, ItemStack wrench) + { + return this.upgrade.equals(upgrade.getId())&&this.item.test(wrench); + } + } + + static class UpgradeListeners + { + private final PlayerAdvancements playerAdvancements; + private final Set> listeners = Sets.newHashSet(); + + UpgradeListeners(PlayerAdvancements playerAdvancements) + { + this.playerAdvancements = playerAdvancements; + } + + boolean isEmpty() + { + return listeners.isEmpty(); + } + + void add(ICriterionTrigger.Listener listener) + { + listeners.add(listener); + } + + void remove(ICriterionTrigger.Listener listener) + { + listeners.remove(listener); + } + + void trigger(Upgrade upgrade, ItemStack wrench) + { + List> matched = null; + for(ICriterionTrigger.Listener listener : listeners) + if(listener.getCriterionInstance().test(upgrade, wrench)) + { + if(matched==null) + matched = Lists.newArrayList(); + matched.add(listener); + } + + if(matched!=null) + for(ICriterionTrigger.Listener listener : matched) + listener.grantCriterion(playerAdvancements); + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIIFluid.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIIFluid.java index 05b7e611f..72450a972 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIIFluid.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIIFluid.java @@ -19,7 +19,7 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidUtil; import pl.pabilo8.immersiveintelligence.ImmersiveIntelligence; -import pl.pabilo8.immersiveintelligence.api.CorrosionHandler.IAcidProtectionEquipment; +import pl.pabilo8.immersiveintelligence.api.api.protection.ProtectionHandler; import pl.pabilo8.immersiveintelligence.common.IIContent; import pl.pabilo8.immersiveintelligence.common.entity.ammo.component.EntityGasCloud; @@ -103,20 +103,9 @@ public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState sta if(effect!=null) ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(effect)); - if(isAcid) - { - for(ItemStack stack1 : entity.getArmorInventoryList()) - { - if(!(stack1.getItem() instanceof IAcidProtectionEquipment)|| - !((IAcidProtectionEquipment)stack1.getItem()).protectsFromAcid(stack1)) - { - entity.attackEntityFrom(IEDamageSources.acid, 2); - break; - } - } - } + if(isAcid&&!ProtectionHandler.isProtectedFromAcid((EntityLivingBase)entity)) + entity.attackEntityFrom(IEDamageSources.acid, 2); } - } public void addToChemthrower() @@ -138,15 +127,8 @@ public ChemthrowerEffect_Acid(PotionEffect... effects) @Override public void applyToEntity(EntityLivingBase target, @Nullable EntityPlayer shooter, ItemStack thrower, Fluid fluid) { - for(ItemStack stack1 : target.getArmorInventoryList()) - { - if(!(stack1.getItem() instanceof IAcidProtectionEquipment)|| - !((IAcidProtectionEquipment)stack1.getItem()).protectsFromAcid(stack1)) - { - super.applyToEntity(target, shooter, thrower, fluid); - return; - } - } + if(!ProtectionHandler.isProtectedFromAcid(target)) + super.applyToEntity(target, shooter, thrower, fluid); } } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIITileProvider.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIITileProvider.java index 7e9fbbbad..4fa623641 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIITileProvider.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/block/BlockIITileProvider.java @@ -58,11 +58,13 @@ import pl.pabilo8.immersiveintelligence.common.IIGUI; import pl.pabilo8.immersiveintelligence.common.IIUtils; import pl.pabilo8.immersiveintelligence.common.util.block.IIBlockInterfaces.IITileProviderEnum; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; import pl.pabilo8.immersiveintelligence.common.util.item.IIItemUtils; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IConstructionRequiringDevice; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IDamageResistantMultiblock; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.IExplosionResistantMultiblock; import pl.pabilo8.immersiveintelligence.common.util.multiblock.IIMultiblockInterfaces.ILadderMultiblock; +import pl.pabilo8.immersiveintelligence.common.util.multiblock.TileEntityMultiblockIIBase; import javax.annotation.Nullable; import java.util.*; @@ -79,8 +81,8 @@ public abstract class BlockIITileProvider & IITileProviderEnum private boolean hasConnections = false; public BlockIITileProvider(String name, Material material, PropertyEnum mainProperty, - Function, ItemBlockIIBase> itemBlock, - Object... additionalProperties) + Function, ItemBlockIIBase> itemBlock, + Object... additionalProperties) { super(name, mainProperty, material, itemBlock, additionalProperties); @@ -196,7 +198,7 @@ public TileEntity createBasicTE(E type) { if(tiles[type.ordinal()]!=null) try {return tiles[type.ordinal()].newInstance();} catch(InstantiationException| - IllegalAccessException ignored) {} + IllegalAccessException ignored) {} return null; } @@ -292,10 +294,13 @@ public float getExplosionResistance(World world, BlockPos pos, Entity exploder, assert mb!=null; //float damageDealt = explosion instanceof IIExplosion?(float)(((IIExplosion)explosion).getPower()/3f): explosion.size; boolean dead = mb.damageHealth(Math.max(explosion.size-mb.getExplosionResistance(), 0)); + if(!dead&&mb instanceof TileEntityMultiblockIIBase) + ((TileEntityMultiblockIIBase)mb).updateTileForEvent(SyncEvents.TILE_DAMAGED); + return dead?0: Float.MAX_VALUE; } - if(te instanceof IExplosionResistantMultiblock) + else if(te instanceof IExplosionResistantMultiblock) { float v = ((IExplosionResistantMultiblock)te).getExplosionResistance(); if(v!=-1) diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/DiplomacyHandler.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/DiplomacyHandler.java index 035c66714..c9c417e65 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/DiplomacyHandler.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/DiplomacyHandler.java @@ -11,6 +11,7 @@ import net.minecraft.item.ItemBanner; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; +import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; @@ -62,6 +63,7 @@ * * @author Pabilo8 (pabilo@iiteam.net) * @ii-approved 0.3.1 + * @updated 22.07.2026 * @since 03.09.2025 */ public class DiplomacyHandler @@ -71,10 +73,13 @@ public class DiplomacyHandler public static OwnerIdentity NEUTRAL, GLOBAL_ENEMY; public static PlayerInfo DEFAULT_PLAYER_INFO = new PlayerInfo(UUID.fromString("00000000-0000-0000-0000-000000000000"), "Unknown"); - private static final String KEY_IDENTITIES = "identities", KEY_CHUNKLOADERS = "chunkloaders", KEY_PLAYERS = "players"; + private static final String KEY_IDENTITIES = "identities", KEY_PLAYERS = "players"; private static final DiplomacyHandler INSTANCE_SERVER = new DiplomacyHandler(false); private static final DiplomacyHandler INSTANCE_CLIENT = new DiplomacyHandler(true); + private static final Comparator IDENTITY_AGE_COMPARATOR = Comparator + .comparingLong(OwnerIdentity::getFoundingDate) + .thenComparing(identity -> identity.getUUID().toString()); private final HashMap ownerIdentities = new HashMap<>(); private final HashMap properties = new HashMap<>(); @@ -147,25 +152,75 @@ public void loadAllFromNBT(EasyNBT nbt) return identity; })); - //Remove identities that are invalid + //Remove placeholder identities ownerIdentities.values().removeIf(OwnerIdentity::isInvalid); - //Fix loaded properties + //Load player infos before reporting integrity repairs. + playerInfos.clear(); + nbt.streamList(NBTTagCompound.class, KEY_PLAYERS) + .map(PlayerInfo::new) + .forEach(playerInfo -> playerInfos.put(playerInfo.uuid, playerInfo)); + + + //Ensure that player belongs to only one identity, move them and invalidate identities without players left + boolean repaired = validateIdentityIntegrity(); + ownerIdentities.entrySet().removeIf(entry -> { + OwnerIdentity identity = entry.getValue(); + return identity.isInvalid()&&identity.getUUID()!=NEUTRAL_UUID&&identity.getUUID()!=GLOBAL_ENEMY_UUID; + }); + + //Fix loaded properties and canonicalize their identity references. properties.values().stream() .map(IOwnableProperty::master) .forEach(property -> { OwnerIdentity identity = property.getOwnerIdentity(); - property.setOwnerIdentity(identity.isInvalid()?NEUTRAL: identity); + OwnerIdentity canonical = identity==null?null: ownerIdentities.get(identity.getUUID()); + property.setOwnerIdentity(canonical==null||canonical.isInvalid()?NEUTRAL: canonical); }); - //Load player infos from NBT - nbt.streamList(NBTTagCompound.class, KEY_PLAYERS) - .map(PlayerInfo::new) - .forEach(playerInfo -> playerInfos.put(playerInfo.uuid, playerInfo)); + diplomacyInitialized = true; if(!isRemote) + { for(MessageDiplomacySync message : MessageDiplomacySync.updateAllMessage()) IIPacketHandler.sendToAllClients(message); + if(repaired) + IISaveData.setDirty(); + } + } + + private boolean validateIdentityIntegrity() + { + Map memberships = new HashMap<>(); + List identities = ownerIdentities.values().stream() + .filter(identity -> identity.getUUID()!=NEUTRAL_UUID) + .filter(identity -> identity.getUUID()!=GLOBAL_ENEMY_UUID) + .filter(identity -> !identity.isInvalid()) + .sorted(IDENTITY_AGE_COMPARATOR) + .collect(Collectors.toList()); + boolean repaired = false; + + for(OwnerIdentity identity : identities) + for(UUID member : new ArrayList<>(identity.getMembers())) + { + OwnerIdentity oldestIdentity = memberships.putIfAbsent(member, identity); + if(oldestIdentity!=null&&identity.removeMemberForIntegrityCheck(member)) + { + repaired = true; + IILogger.warn("Removed duplicate member "+member+" from identity "+identity.getUUID() + +"; keeping oldest identity "+oldestIdentity.getUUID()+"."); + } + } + + for(OwnerIdentity identity : identities) + if(!identity.hasAnyPlayers()) + { + identity.invalidateForIntegrityCheck(); + repaired = true; + IILogger.warn("Invalidated empty owner identity "+identity.getUUID()+"."); + } + + return repaired; } public EasyNBT saveAllToNBT() @@ -187,7 +242,6 @@ public void cleanup() propertyTickets.clear(); pendingTickets.clear(); pendingTicketCheckTimer = 0; - //NEUTRAL = GLOBAL_ENEMY = null; } //--- Update Loop ---// @@ -258,7 +312,7 @@ public void validateProperty(IOwnableProperty property) private void claimChunks(IOwnableProperty property) { - if(!diplomacyInitialized||property.getOwnerIdentity()==null) + if(!diplomacyInitialized||property==null||property.getOwnerIdentity()==null) return; IILogger.debug("Claiming chunks for property: "+property.getUUID()); World world = property.getIIWorld(); @@ -293,7 +347,8 @@ private void claimChunks(IOwnableProperty property) ownership.setOwner(property.getOwnerIdentity()); ownership.setClaimData(chunkClaimData); if(!world.isRemote) - IIPacketHandler.sendToClient(new MessageIIChunkClaimData(world, pos, property.getOwnerIdentity(), chunkClaimData)); + IIPacketHandler.INSTANCE.sendToDimension(new MessageIIChunkClaimData(world, pos, property.getOwnerIdentity(), chunkClaimData), + world.provider.getDimension()); } } else @@ -302,7 +357,8 @@ private void claimChunks(IOwnableProperty property) ownership.setOwner(property.getOwnerIdentity()); ownership.setClaimData(chunkClaimData); if(!world.isRemote) - IIPacketHandler.sendToClient(new MessageIIChunkClaimData(world, pos, property.getOwnerIdentity(), chunkClaimData)); + IIPacketHandler.INSTANCE.sendToDimension(new MessageIIChunkClaimData(world, pos, property.getOwnerIdentity(), chunkClaimData), + world.provider.getDimension()); } } } @@ -417,11 +473,6 @@ public static OwnerIdentity getLocalPlayerIdentity() return getInstance(true).getOwnerIdentityForEntity(ClientUtils.mc().player); } - /*public static OwnerIdentity getIdentityByUUID(String uuid) - { - - }*/ - public OwnerIdentity getIdentityByUUID(String uuid) { try @@ -461,12 +512,18 @@ public OwnerIdentity getOwnerIdentityForEntity(EntityLivingBase player) if(!isRemote&&player instanceof EntityPlayer&&!playerInfos.containsKey(player.getUniqueID())) updatePlayerInfo(new PlayerInfo(player)); - //Try to get an existing identity - for(OwnerIdentity identity : ownerIdentities.values()) - if(identity.isMember(player)) - return identity; - - //Only players should be able to create a new indentity + //Try to get an existing identity. The age ordering keeps this deterministic even + //if invalid runtime state is introduced before the next save/load integrity pass. + Optional existing = ownerIdentities.values().stream() + .filter(oi -> oi.getUUID()!=NEUTRAL_UUID) + .filter(oi -> oi.getUUID()!=GLOBAL_ENEMY_UUID) + .filter(identity -> !identity.isInvalid()) + .filter(identity -> identity.isMember(player)) + .min(IDENTITY_AGE_COMPARATOR); + if(existing.isPresent()) + return existing.get(); + + //Only players should be able to create a new ideentity if(!player.world.isRemote&&player instanceof EntityPlayer) { //Create new identity @@ -496,7 +553,16 @@ public IChunkOwnership getChunkOwnership(Chunk chunk) public void setChunkOwnership(Chunk chunk, IChunkOwnership ownership) { - + if(chunk.hasCapability(CapabilityChunkOwnership.CHUNK_OWNERSHIP_CAP, null)) + { + IChunkOwnership cap = chunk.getCapability(CapabilityChunkOwnership.CHUNK_OWNERSHIP_CAP, null); + if(cap!=null) + { + cap.setOwner(ownership.getOwner()); + return; + } + } + IILogger.error("Could not set chunk ownership for chunk at "+chunk.getPos().x+", "+chunk.getPos().z+"; missing or invalid IChunkOwnership capability!"); } public IChunkOwnership getPositionOwnership(World world, BlockPos pos) @@ -527,7 +593,8 @@ public void proposeAgreement(OwnerIdentity from, OwnerIdentity to, DiplomaticAgr //In accept/deny of an agreement (when the target faction accepts/denies a proposal): public void acceptAgreement(OwnerIdentity acceptingFaction, DiplomaticAgreement proposal) { - if(!proposal.isPending()) return; + if(!proposal.isPending()) + return; proposal.accept(); //Remove from pending lists acceptingFaction.removeAgreement(proposal); @@ -554,7 +621,9 @@ public void acceptAgreement(OwnerIdentity acceptingFaction, DiplomaticAgreement public OwnerIdentity merge(OwnerIdentity a, OwnerIdentity b) { //Merge two identities - OwnerIdentity merged = new OwnerIdentity(a, b); + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + long foundingDate = server==null?Math.max(a.getFoundingDate(), b.getFoundingDate()): server.getEntityWorld().getTotalWorldTime(); + OwnerIdentity merged = new OwnerIdentity(a, b, foundingDate); ownerIdentities.remove(a.getUUID()); ownerIdentities.remove(b.getUUID()); ownerIdentities.put(merged.getUUID(), merged); @@ -576,12 +645,18 @@ public OwnerIdentity[] split(OwnerIdentity identity, EntityLivingBase... between //--- Player Invitation ---// - public Set getPendingInvitationsForPlayer(UUID playerUUID) + public List getPendingInvitationIdentitiesForPlayer(UUID playerUUID) { return ownerIdentities.values().stream() - .filter(oi -> oi.isInvited(playerUUID)) - .map(OwnerIdentity::getDisplayName) - .collect(Collectors.toSet()); + .filter(oi -> !oi.isInvalid()) + .filter(oi -> oi.getUUID()!=NEUTRAL_UUID) + .filter(oi -> oi.getUUID()!=GLOBAL_ENEMY_UUID) + .filter(identity -> !identity.isInvalid()) + .filter(identity -> identity.isInvited(playerUUID)) + .sorted(Comparator + .comparing(OwnerIdentity::getDisplayName, String.CASE_INSENSITIVE_ORDER) + .thenComparing(identity -> identity.getUUID().toString())) + .collect(Collectors.toList()); } public Set getPendingInvitationsForFaction(UUID factionUUID) @@ -592,36 +667,73 @@ public Set getPendingInvitationsForFaction(UUID factionUUID) public boolean acceptInvitation(OwnerIdentity identity, UUID playerUUID) { - if(identity.isInvited(playerUUID)) + if(identity.getUUID()!=NEUTRAL_UUID&&identity.getUUID()!=GLOBAL_ENEMY_UUID&&identity.isInvalid()) + return false; + if(!identity.isInvited(playerUUID)) + return false; + + //A stale invitation for an existing member must never overwrite their current role. + if(identity.isMember(playerUUID)) { - //Add to new identity identity.removeInvitation(playerUUID); - identity.withMember(playerUUID, identity.getStartingMemberRole()); + saveAndSyncIdentity(identity); + return true; + } - //Remove from old identity - ownerIdentities.values().stream() - .filter(oi -> oi!=identity) - .filter(oi -> oi.isMember(playerUUID)) - .forEach(faction -> { - //Remove player from old identity - faction.removeMember(playerUUID); - }); + //Remove from old identity + Optional first = ownerIdentities.values().stream() + .filter(oi -> oi!=identity) + .filter(oi -> oi.isMember(playerUUID)) + .findFirst(); + if(first.isPresent()) + { + //Check if the player is the last remaining player in the faction + OwnerIdentity previousIdentity = first.get(); + boolean isLastOwner = previousIdentity.isOwner(playerUUID)&&previousIdentity.getMembers().size()==1; + previousIdentity.removeMember(playerUUID); + //Pass all the properties owned by the previous identity to the new identity + if(isLastOwner) + { + IILogger.info("Passed all properties of faction %s to new owner %s after player %s accepted invitation.", + previousIdentity.getDisplayName(), identity.getDisplayName(), playerUUID); + for(IOwnableProperty value : properties.values()) + { + IOwnableProperty master = value.master(); + try + { + assert master!=null; + master.setOwnerIdentity(identity); + } catch(Exception e) + { + IILogger.error("Failed to transfer property %s from %s to %s after player %s accepted invitation.", + value.getUUID(), previousIdentity.getUUID(), identity.getUUID(), playerUUID); + } + } - return true; + //Reclaim chunks + for(IOwnableProperty value : properties.values()) + claimChunks(value.master()); + } } - return false; + + //Add first so a damaged role table cannot strand the player between identities. + identity.withMember(playerUUID, identity.getStartingMemberRole()); + if(!identity.isMember(playerUUID)) + return false; + identity.removeInvitation(playerUUID); + saveAndSyncIdentity(identity); + return true; } public boolean denyInvitation(OwnerIdentity identity, UUID playerUUID) { - if(identity.isInvited(playerUUID)) - { - identity.removeInvitation(playerUUID); - saveAndSyncIdentity(identity); - return true; - } - return false; + if(identity.getUUID()==NEUTRAL_UUID||identity.getUUID()==GLOBAL_ENEMY_UUID + ||identity.isInvalid()||!identity.isInvited(playerUUID)) + return false; + identity.removeInvitation(playerUUID); + saveAndSyncIdentity(identity); + return true; } //--- Server Sync Methods ---// @@ -638,6 +750,8 @@ public void saveAndSyncIdentity(OwnerIdentity identity) public void removeIdentity(UUID uuid) { + if(uuid==NEUTRAL_UUID||uuid==GLOBAL_ENEMY_UUID) + return; OwnerIdentity removed = ownerIdentities.remove(uuid); if(removed!=null) { @@ -795,6 +909,9 @@ public void onPlayerLoggedIn(PlayerLoggedInEvent event) PlayerInfo playerInfo = new PlayerInfo(event.player); updatePlayerInfo(new PlayerInfo(event.player)); IIPacketHandler.sendToAllClients(MessageDiplomacySync.syncPlayerInfo(playerInfo)); + + //If player has no identity created yet, generate and sync it + getOwnerIdentityForEntity(event.player); } @SubscribeEvent diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/OwnerIdentity.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/OwnerIdentity.java index 8a6efce24..672ee6644 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/OwnerIdentity.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/OwnerIdentity.java @@ -30,7 +30,7 @@ * * @author Pabilo8 (pabilo@iiteam.net) * @ii-approved 0.3.1 - * @updated 24.04.2026 + * @updated 22.07.2026 * @since 03.09.2025 */ public class OwnerIdentity implements INBTSerializable @@ -44,6 +44,10 @@ public class OwnerIdentity implements INBTSerializable private LawForm lawForm = LawForm.DEFAULT; private IIColor color = IIColor.ALPHA; private ItemStack banner = new ItemStack(Items.BANNER); + /** + * Total world time at which this identity was founded; legacy identities use tick zero. + */ + private long foundingDate; private boolean invalid; //--- Diplomacy (agreements) ---// @@ -58,23 +62,26 @@ public class OwnerIdentity implements INBTSerializable //--- Constructors ---// protected OwnerIdentity(@Nonnull UUID uuid) { - this.uuid = uuid; - this.displayName = "placeholder"; - this.invalid = true; - initAvailableRoles(); + this(uuid, "placeholder", 0); } protected OwnerIdentity(@Nonnull UUID uuid, @Nonnull String displayName) + { + this(uuid, displayName, 0); + } + + protected OwnerIdentity(@Nonnull UUID uuid, @Nonnull String displayName, long foundingDate) { this.uuid = uuid; this.displayName = displayName; + this.foundingDate = Math.max(0, foundingDate); this.invalid = true; initAvailableRoles(); } public OwnerIdentity(EntityLivingBase player) { - this(UUID.randomUUID(), player.getName()); + this(UUID.randomUUID(), player.getName(), player.world.getTotalWorldTime()); this.withMember(player.getUniqueID(), LawForm.DEFAULT.getOwnerRole()); this.color = IIColor.fromHSV(player.getRNG().nextFloat(), 0.35f, 0.85f); this.banner = new ItemStack(Items.BANNER, 1, color.getDyeColor().getMetadata()); @@ -82,7 +89,12 @@ public OwnerIdentity(EntityLivingBase player) public OwnerIdentity(OwnerIdentity a, OwnerIdentity b) { - this(UUID.randomUUID(), a.getDisplayName()+"-"+b.getDisplayName()); + this(a, b, Math.max(a.foundingDate, b.foundingDate)); + } + + public OwnerIdentity(OwnerIdentity a, OwnerIdentity b, long foundingDate) + { + this(UUID.randomUUID(), a.getDisplayName()+"-"+b.getDisplayName(), foundingDate); this.invalid = false; //Mix colors and make a banner this.color = a.color.mixedWith(b.color, 0.5f); @@ -100,15 +112,17 @@ public OwnerIdentity(OwnerIdentity a, OwnerIdentity b) public OwnerIdentity(EasyNBT tag) { - if(this.invalid = tag.hasKey("invalid")||!tag.hasKey("uuid")) + UUID loadedUUID = tag.getUUID("uuid"); + if(this.invalid = tag.getBoolean("invalid")||loadedUUID==null) { - this.uuid = UUID.randomUUID(); + this.uuid = loadedUUID==null?UUID.randomUUID(): loadedUUID; this.displayName = "invalid"; + this.foundingDate = Math.max(0, tag.getLong("foundingDate")); initAvailableRoles(); } else { - this.uuid = tag.getUUID("uuid"); + this.uuid = loadedUUID; this.displayName = tag.getString("displayName"); deserializeNBT(tag.unwrap()); } @@ -144,9 +158,11 @@ public OwnerIdentity withStartingMemberRole(String startingMemberRole) */ public OwnerIdentity withMember(UUID uuid, String roleId) { - invalid = false; if(availableRoles.containsKey(roleId)) + { memberRoles.put(uuid, roleId); + invalid = false; + } return this; } @@ -160,6 +176,11 @@ public OwnerIdentity removeMember(UUID uuid) } private void disband() + { + disband(true); + } + + private void disband(boolean sync) { this.invalid = true; this.displayName = "invalid"; @@ -173,7 +194,18 @@ private void disband() this.pendingIncomingProposals.clear(); this.invitedPlayers.clear(); - IIPacketHandler.sendToAllClients(MessageDiplomacySync.removeIdentityMessage(this)); + if(sync) + IIPacketHandler.sendToAllClients(MessageDiplomacySync.removeIdentityMessage(this)); + } + + boolean removeMemberForIntegrityCheck(UUID uuid) + { + return memberRoles.remove(uuid)!=null; + } + + void invalidateForIntegrityCheck() + { + disband(false); } public OwnerIdentity withLawForm(LawForm lawForm) @@ -213,6 +245,11 @@ public String getDisplayName() return displayName; } + public long getFoundingDate() + { + return foundingDate; + } + public Map getMemberRolesMap() { return Collections.unmodifiableMap(memberRoles); @@ -416,6 +453,7 @@ public NBTTagCompound serializeNBT() return EasyNBT.newNBT() .withUUID("uuid", uuid) .withString("displayName", displayName) + .withLong("foundingDate", foundingDate) .withBoolean("invalid", true) .unwrap(); @@ -433,12 +471,10 @@ public NBTTagCompound serializeNBT() EasyNBT outTag = saveAgreementMap(pendingOutgoingProposals); EasyNBT inTag = saveAgreementMap(pendingIncomingProposals); - List invitedList = new ArrayList<>(); - invitedPlayers.forEach(uuid -> invitedList.add(uuid.toString())); - return EasyNBT.newNBT() .withUUID("uuid", uuid) .withString("displayName", displayName) + .withLong("foundingDate", foundingDate) .withTag("memberRoles", rolesTag) .withTag("availableRoles", availRolesTag) .withEnum("lawForm", lawForm) @@ -449,7 +485,7 @@ public NBTTagCompound serializeNBT() .withTag("activeTargetAgreements", targetTag) .withTag("pendingOutgoing", outTag) .withTag("pendingIncoming", inTag) - .withList("invitedPlayers", invitedList) + .withList("invitedPlayers", invitedPlayers.toArray()) .unwrap(); } @@ -465,6 +501,7 @@ public void deserializeNBT(NBTTagCompound nbt) pendingOutgoingProposals.clear(); pendingIncomingProposals.clear(); invitedPlayers.clear(); + foundingDate = Math.max(0, enbt.getLong("foundingDate")); if(enbt.getBoolean("invalid")) { diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/permission/DiplomaticAction.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/permission/DiplomaticAction.java index f5821b20f..8e66fdfd5 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/permission/DiplomaticAction.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/diplomacy/permission/DiplomaticAction.java @@ -66,7 +66,23 @@ public enum DiplomaticAction implements ILocalizedEnum /** * Send a diplomatic ultimatum to another identity, which may be accepted or rejected. May result in a diplomatic incident if the ultimatum is rejected. */ - SEND_ULTIMATUM(PermissionCategory.FOREIGN_AFFAIRS); + SEND_ULTIMATUM(PermissionCategory.FOREIGN_AFFAIRS), + /** + * Cancel a pending invitation sent by the identity. + */ + CANCEL_INVITATION(PermissionCategory.INVITE_MEMBERS), + /** + * Change a member's non-owner role. + */ + CHANGE_MEMBER_ROLE, + /** + * Accept an invitation addressed to the acting player. + */ + ACCEPT_INVITATION, + /** + * Reject an invitation addressed to the acting player. + */ + DENY_INVITATION; @Nullable private PermissionCategory requiredPermission; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/EasyNBT.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/EasyNBT.java index cf75259ae..90cfbdc8c 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/EasyNBT.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/EasyNBT.java @@ -146,6 +146,10 @@ else if(element instanceof Vec3d) Vec3d pos = (Vec3d)element; list.appendTag(listOf(pos.x, pos.y, pos.z)); } + else if(element instanceof UUID) + { + list.appendTag(new NBTTagString(element.toString())); + } else if(element instanceof ItemStack) list.appendTag(((ItemStack)element).serializeNBT()); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/NBTSerialisation.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/NBTSerialisation.java index 255205229..a003b09eb 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/NBTSerialisation.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/NBTSerialisation.java @@ -1,6 +1,7 @@ package pl.pabilo8.immersiveintelligence.common.util.easynbt; import blusunrize.immersiveengineering.api.energy.immersiveflux.FluxStorage; +import blusunrize.immersiveengineering.api.energy.wires.WireType; import blusunrize.immersiveengineering.common.util.inventory.MultiFluidTank; import net.minecraft.item.ItemStack; import net.minecraft.nbt.*; @@ -225,6 +226,14 @@ public static void postInit() } ); + registerSerializer(WireType.class, NBTTagString.class, + wireType -> new NBTTagString(wireType==null?"": wireType.getUniqueName()), + nbt -> { + String string = nbt.getString(); + return string.isEmpty()?null: WireType.getValue(string); + } + ); + //Inserter tasks NBTSerialisation.registerPolimorphicTypeClass(InserterTaskItem.class); NBTSerialisation.registerPolimorphicTypeClass(InserterTaskPlaceBlock.class); diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/SyncNBT.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/SyncNBT.java index bb0c9f934..e48850f59 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/SyncNBT.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/SyncNBT.java @@ -43,6 +43,7 @@ enum SyncEvents TILE_CLIENT_MESSAGE, TILE_OWNERSHIP_MODIFIED, TILE_DAMAGED, + TILE_ENERGY_CHANGED, TILE_CUSTOM1, TILE_CUSTOM2, diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/TargetCoordinateReference.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/TargetCoordinateReference.java index 7be9af58b..43fcfbf91 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/TargetCoordinateReference.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/easynbt/TargetCoordinateReference.java @@ -100,9 +100,10 @@ public boolean shouldBeExecuted(@Nullable World world) if(position!=null) { //Unloaded chunks are inconclusive, not a completed mission. - if(world==null||!world.isBlockLoaded(position)) + if(world==null||(!world.isRemote&&!(world.isBlockLoaded(position)))) return true; - return !world.isAirBlock(position); + //Either the mission requires destroying a block or firing an amount of shots + return !world.isAirBlock(position)^shotsAreFinite; } return false; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/TileEntityMultiblockIIGeneric.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/TileEntityMultiblockIIGeneric.java index 7752e6af5..e3a037524 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/TileEntityMultiblockIIGeneric.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/TileEntityMultiblockIIGeneric.java @@ -51,7 +51,7 @@ public abstract class TileEntityMultiblockIIGeneric inventory; - @SyncNBT(name = "ifluxEnergy") + @SyncNBT(name = "ifluxEnergy", events = {SyncEvents.TILE_GUI_OPENED, SyncEvents.TILE_RECIPE_CHANGED, SyncEvents.TILE_ENERGY_CHANGED}) public FluxStorageAdvanced energyStorage; @SyncNBT(name = "redstone_control") public boolean redstoneControlInverted = false; diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionMulti.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionMulti.java index 133e39b31..6fbbbd0f0 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionMulti.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionMulti.java @@ -1,6 +1,7 @@ package pl.pabilo8.immersiveintelligence.common.util.multiblock.production; import blusunrize.immersiveengineering.common.blocks.metal.TileEntityMultiblockMetal; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; import pl.pabilo8.immersiveintelligence.common.util.multiblock.MultiblockStuctureBase; @@ -44,6 +45,7 @@ protected void dummyCleanup() protected void onUpdate() { //Iterate existing processes and try to progress them + boolean updateQueue = false; if(!processQueue.isEmpty()) { Iterator> iterator = processQueue.iterator(); @@ -59,6 +61,7 @@ protected void onUpdate() //Remove the process from the queue onProductionFinish(process); iterator.remove(); + updateQueue = true; } break; } @@ -66,7 +69,13 @@ protected void onUpdate() //Else, try to progress this process float progress = getProductionStep(process, false); if(progress > 0) + { process.ticks += progress; + //Sync the client each 100 ticks + if(Machines.recipeUpdateInterval > 0&&process.ticks%Machines.recipeUpdateInterval==0) + updateQueue = true; + + } } } @@ -88,10 +97,13 @@ protected void onUpdate() if(process!=null) { processQueue.add(process); - updateTileForEvent(SyncEvents.TILE_RECIPE_CHANGED); + updateQueue = true; } } } + + if(!world.isRemote&&updateQueue) + updateTileForEvent(SyncEvents.TILE_RECIPE_CHANGED); } @Override diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionSingle.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionSingle.java index 81db17ba1..4031410ab 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionSingle.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/multiblock/production/TileEntityMultiblockProductionSingle.java @@ -2,6 +2,7 @@ import blusunrize.immersiveengineering.common.blocks.metal.TileEntityMultiblockMetal; import net.minecraft.nbt.NBTTagCompound; +import pl.pabilo8.immersiveintelligence.common.IIConfigHandler.IIConfig.Machines; import pl.pabilo8.immersiveintelligence.common.util.easynbt.EasyNullableSyncMechanism; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT.SyncEvents; @@ -52,7 +53,7 @@ protected void dummyCleanup() protected void onUpdate() { IIMultiblockProcess existingProcess = currentProcess; - + boolean updateProcess = false; if(currentProcess!=null) { //Do process output @@ -61,27 +62,34 @@ protected void onUpdate() if(attemptProductionOutput(currentProcess)) { onProductionFinish(currentProcess); - currentProcess = null; + currentProcess = existingProcess = null; + updateProcess = true; } - else - return; } else { float progress = getProductionStep(currentProcess, false); if(progress > 0) + { currentProcess.ticks += progress; - return; + if(Machines.recipeUpdateInterval > 0&¤tProcess.ticks%Machines.recipeUpdateInterval==0) + updateProcess = true; + } } } if(world.isRemote) return; //Add new process to the queue (no matter whether it's null) - this.currentProcess = findNewProductionProcess(); + if(this.currentProcess==null) + { + this.currentProcess = findNewProductionProcess(); + if(this.currentProcess!=existingProcess) + updateProcess = true; + } - //Send block update on changes - if(this.currentProcess!=existingProcess) + //Send nbt update on changes + if(updateProcess) updateTileForEvent(SyncEvents.TILE_RECIPE_CHANGED); } diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIBase.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIBase.java index dadc9369e..cb0e0e4c1 100644 --- a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIBase.java +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIBase.java @@ -43,12 +43,18 @@ public void receiveMessageFromServer(@Nonnull NBTTagCompound message) public void receiveMessageFromClient(@Nonnull NBTTagCompound message) { NBTSerialisation.synchroniseFor(this, (tag, entity) -> tag.deserializeAll(this, message, true)); + + if(!message.hasNoTags()) + { + NBTSerialisation.synchroniseFor(this, (tag, tile) -> tag.deserializeAll(tile, message, true)); + IIPacketHandler.sendToClient(new MessageIITileSync(this, message)); + } } //--- Additional SyncNBT methods ---// - public void updateEntityForTime(int time) + public void updateTileForTime(int time) { NBTTagCompound nbt = new NBTTagCompound(); NBTSerialisation.synchroniseFor(this, (tag, entity) -> tag.serializeForTime(entity, nbt, time)); @@ -56,18 +62,18 @@ public void updateEntityForTime(int time) } @SuppressWarnings({"unchecked"}) - public void updateEntityForEvent(SyncNBT.SyncEvents event) + public void updateTileForEvent(SyncNBT.SyncEvents event) { NBTTagCompound nbt = new NBTTagCompound(); NBTSerialisation.synchroniseFor(this, (tag, entity) -> tag.serializeForEvent(entity, nbt, event)); IIPacketHandler.sendToClient(new MessageIITileSync(this, nbt)); } - public void sendServerUpdateForEvent(SyncNBT.SyncEvents event) + public void updateTileForAll() { NBTTagCompound nbt = new NBTTagCompound(); - NBTSerialisation.synchroniseFor(this, (tag, entity) -> tag.serializeForEvent(entity, nbt, event)); - IIPacketHandler.sendToServer(new MessageIITileSync(this, nbt)); + NBTSerialisation.synchroniseFor(this, (tag, entity) -> tag.serializeAll(entity, nbt)); + IIPacketHandler.sendToClient(new MessageIITileSync(this, nbt)); } //--- Built-In ---// diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIConnectable.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIConnectable.java new file mode 100644 index 000000000..f72681a16 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIConnectable.java @@ -0,0 +1,340 @@ +package pl.pabilo8.immersiveintelligence.common.util.tile; + +import blusunrize.immersiveengineering.ImmersiveEngineering; +import blusunrize.immersiveengineering.api.ApiUtils; +import blusunrize.immersiveengineering.api.TargetingInfo; +import blusunrize.immersiveengineering.api.energy.wires.*; +import blusunrize.immersiveengineering.api.energy.wires.ImmersiveNetHandler.Connection; +import blusunrize.immersiveengineering.common.util.Utils; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.World; +import net.minecraftforge.common.property.IExtendedBlockState; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import pl.pabilo8.immersiveintelligence.common.IILogger; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 16.07.2026 + */ +public abstract class TileEntityIIConnectable extends TileEntityIIBase implements IImmersiveConnectable +{ + @SyncNBT(nullable = true) + public WireType limitType = null; + private List>> sources = new ArrayList<>(); + private long lastSourceUpdate = 0; + + //--- Abstract ---// + + public abstract boolean acceptsWireType(WireType category); + + public abstract boolean isRelay(); + + //--- IRedstoneConnector ---// + + public World getConnectorWorld() + { + return getWorld(); + } + + //--- NBT ---// + + @Override + public boolean receiveClientEvent(int id, int arg) + { + if(id==-1||id==255) + { + IBlockState state = world.getBlockState(pos); + world.notifyBlockUpdate(pos, state, state, 3); + return true; + } + else if(id==254) + { + IBlockState state = world.getBlockState(pos); + if(state instanceof IExtendedBlockState) + { + state = state.getActualState(world, getPos()); + state = state.getBlock().getExtendedState(state, world, getPos()); + ImmersiveEngineering.proxy.removeStateFromSmartModelCache((IExtendedBlockState)state); + ImmersiveEngineering.proxy.removeStateFromConnectionModelCache((IExtendedBlockState)state); + } + world.notifyBlockUpdate(pos, state, state, 3); + return true; + } + return super.receiveClientEvent(id, arg); + } + + @Override + public void readCustomNBT(@Nonnull NBTTagCompound nbt, boolean descPacket) + { + super.readCustomNBT(nbt, descPacket); + if(nbt.hasKey("connectionList")) + try + { + loadConnsFromNBT(nbt); + } catch(Exception e) + { + IILogger.error("TileEntityIIConnectable encountered an error reading connection NBT."); + IILogger.error(e.getStackTrace()); + } + } + + @Override + public void writeCustomNBT(@Nonnull NBTTagCompound nbt, boolean descPacket) + { + super.writeCustomNBT(nbt, descPacket); + if(descPacket) + try + { + writeConnsToNBT(nbt); + } catch(Exception e) + { + IILogger.error("TileEntityIIConnectable encountered an error writing connection NBT."); + IILogger.error(e.getStackTrace()); + } + } + + private void loadConnsFromNBT(NBTTagCompound nbt) + { + if(world!=null&&world.isRemote&&!Minecraft.getMinecraft().isSingleplayer()&&nbt!=null) + { + NBTTagList connectionList = nbt.getTagList("connectionList", 10); + ImmersiveNetHandler.INSTANCE.clearConnectionsOriginatingFrom(Utils.toCC(this), world); + for(int i = 0; i < connectionList.tagCount(); i++) + { + NBTTagCompound conTag = connectionList.getCompoundTagAt(i); + Connection con = Connection.readFromNBT(conTag); + if(con!=null) + ImmersiveNetHandler.INSTANCE.addConnection(world, Utils.toCC(this), con); + else + IILogger.error("Client read connection as null from {}", nbt); + } + } + } + + private void writeConnsToNBT(NBTTagCompound nbt) + { + if(world!=null&&!world.isRemote&&nbt!=null) + { + NBTTagList connectionList = new NBTTagList(); + Set conL = ImmersiveNetHandler.INSTANCE.getConnections(world, Utils.toCC(this)); + if(conL!=null) + for(Connection con : conL) + connectionList.appendTag(con.writeToNBT()); + nbt.setTag("connectionList", connectionList); + } + } + + //--- Validation ---// + + @Override + public void onChunkUnload() + { + super.onChunkUnload(); + if(!world.isRemote) + ImmersiveNetHandler.INSTANCE.addProxy(new IICProxy(this)); + } + + /** + * validates a tile entity + */ + @Override + public void validate() + { + super.validate(); + if(!world.isRemote) + ApiUtils.addFutureServerTask(world, () -> ImmersiveNetHandler.INSTANCE.onTEValidated(this)); + } + + /** + * invalidates a tile entity + */ + @Override + public void invalidate() + { + super.invalidate(); + if(world.isRemote&&!Minecraft.getMinecraft().isSingleplayer()) + ImmersiveNetHandler.INSTANCE.clearAllConnectionsFor(pos, world, this, false); + } + + //--- Wire System Implementation ---// + + @Override + public boolean moveConnectionTo(Connection c, BlockPos newEnd) + { + return true; + } + + @Override + public void onEnergyPassthrough(int amount) + { + + } + + @Override + public boolean allowEnergyToPass(Connection con) + { + return true; + } + + @Override + public boolean canConnect() + { + return true; + } + + @Override + public boolean isEnergyOutput() + { + return false; + } + + @Override + public int outputEnergy(int amount, boolean simulate, int energyType) + { + return 0; + } + + @Override + public BlockPos getConnectionMaster(WireType cableType, TargetingInfo target) + { + return getPos(); + } + + @Override + public boolean canConnectCable(WireType cableType, TargetingInfo target, Vec3i offset) + { + if(!acceptsWireType(cableType)) + return false; + return limitType==null||(this.isRelay()&&WireApi.canMix(limitType, cableType)); + } + + @Override + public void connectCable(WireType cableType, TargetingInfo target, IImmersiveConnectable other) + { + this.limitType = cableType; + } + + @Override + public WireType getCableLimiter(TargetingInfo target) + { + return this.limitType; + } + + @Override + public void removeCable(Connection connection) + { + WireType type = connection!=null?connection.cableType: null; + Set outputs = ImmersiveNetHandler.INSTANCE.getConnections(world, Utils.toCC(this)); + if(outputs==null||outputs.isEmpty()) + if(type==limitType||type==null) + this.limitType = null; + this.markDirty(); + if(world!=null) + { + IBlockState state = world.getBlockState(pos); + world.notifyBlockUpdate(pos, state, state, 3); + } + } + + @Override + public void addAvailableEnergy(float amount, Consumer consume) + { + long currentTime = world.getTotalWorldTime(); + if(lastSourceUpdate!=currentTime) + { + sources.clear(); + Pair> own = getOwnEnergy(); + if(own!=null) + sources.add(own); + lastSourceUpdate = currentTime; + } + if(amount > 0&&consume!=null) + sources.add(new ImmutablePair<>(amount, consume)); + } + + @Nullable + protected Pair> getOwnEnergy() + { + return null; + } + + @Override + public float getDamageAmount(Entity e, Connection c) + { + float baseDmg = getBaseDamage(c); + float max = (float)c.cableType.getTransferRate()/8*getBaseDamage(c); + if(baseDmg==0||world.getTotalWorldTime()-lastSourceUpdate > 1) + return 0; + float damage = 0; + for(int i = 0; i < sources.size()&&damage < max; i++) + { + int consume = (int)Math.min(sources.get(i).getLeft(), (max-damage)/baseDmg); + damage += baseDmg*consume; + } + return damage; + } + + @Override + public void processDamage(Entity e, float amount, Connection c) + { + float baseDmg = getBaseDamage(c); + float damage = 0; + for(int i = 0; i < sources.size()&&damage < amount; i++) + { + float consume = Math.min(sources.get(i).getLeft(), (amount-damage)/baseDmg); + sources.get(i).getRight().accept(consume); + damage += baseDmg*consume; + if(consume==sources.get(i).getLeft()) + { + sources.remove(i); + i--; + } + } + } + + protected float getBaseDamage(Connection c) + { + if(c.cableType.isEnergyWire()&&c.cableType.canCauseDamage()) + { + if(c.cableType.getElectricSource().level < 0) + return 0; + //Rough approximation of the original, but it's universal for any new cables + return 8*(7f*c.cableType.getElectricSource().level-1.5f)/c.cableType.getTransferRate(); + } + return 0; + } + + //--- Render Range ---// + + @SideOnly(Side.CLIENT) + @Override + public AxisAlignedBB getRenderBoundingBox() + { + int inc = getRenderRadiusIncrease(); + return new AxisAlignedBB(this.pos.getX()-inc, this.pos.getY()-inc, this.pos.getZ()-inc, this.pos.getX()+inc+1, this.pos.getY()+inc+1, this.pos.getZ()+inc+1); + } + + int getRenderRadiusIncrease() + { + return WireType.COPPER.getMaxLength(); + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIDirectional.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIDirectional.java new file mode 100644 index 000000000..27b904972 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIDirectional.java @@ -0,0 +1,111 @@ +package pl.pabilo8.immersiveintelligence.common.util.tile; + +import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.EnumFacing; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; + +import javax.annotation.Nonnull; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 16.07.2026 + */ +public abstract class TileEntityIIDirectional extends TileEntityIIBase implements IDirectionalTile +{ + @SyncNBT + public EnumFacing facing = EnumFacing.NORTH; + + @Nonnull + @Override + public final EnumFacing getFacing() + { + return facing; + } + + @Override + public final void setFacing(@Nonnull EnumFacing facing) + { + this.facing = facing; + } + + @Nonnull + protected abstract FacingSettings getFacingSettings(); + + @Override + public final int getFacingLimitation() + { + return getFacingSettings().facingLimitation.ordinal(); + } + + @Override + public final boolean mirrorFacingOnPlacement(@Nonnull EntityLivingBase placer) + { + return getFacingSettings().shouldMirrorOnPlacement; + } + + @Override + public final boolean canHammerRotate(@Nonnull EnumFacing side, float hitX, float hitY, float hitZ, @Nonnull EntityLivingBase entity) + { + return getFacingSettings().canHammerRotate; + } + + @Override + public final boolean canRotate(@Nonnull EnumFacing axis) + { + return getFacingSettings().canRotate; + } + + public enum FacingLimitation + { + SIDE_CLICKED, + PISTON_LIKE, + HORIZONTAL, + VERTICAL, + XZ_AXIS, + HORIZONTAL_QUADRANT, + HORIZONTAL_TOWARDS_CLICKED + } + + public static class FacingSettings + { + boolean canRotate = false, canHammerRotate = false, shouldMirrorOnPlacement = false; + boolean mirrorable = false; + final FacingLimitation facingLimitation; + + public FacingSettings() + { + this.facingLimitation = FacingLimitation.SIDE_CLICKED; + } + + public FacingSettings(FacingLimitation limitation) + { + this.facingLimitation = limitation; + } + + public FacingSettings withRotation(boolean canRotate) + { + this.canRotate = this.canHammerRotate = canRotate; + return this; + } + + public FacingSettings withHammerRotation(boolean canHammerRotate) + { + this.canHammerRotate = canHammerRotate; + return this; + } + + public FacingSettings withMirroringOnPlacement(boolean shouldMirrorOnPlacement) + { + this.shouldMirrorOnPlacement = shouldMirrorOnPlacement; + return this; + } + + public FacingSettings withMirroring(boolean mirrorable) + { + this.mirrorable = mirrorable; + return this; + } + } +} diff --git a/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIDirectionalConnectable.java b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIDirectionalConnectable.java new file mode 100644 index 000000000..256268584 --- /dev/null +++ b/src/main/java/pl/pabilo8/immersiveintelligence/common/util/tile/TileEntityIIDirectionalConnectable.java @@ -0,0 +1,60 @@ +package pl.pabilo8.immersiveintelligence.common.util.tile; + +import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IDirectionalTile; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.EnumFacing; +import pl.pabilo8.immersiveintelligence.common.util.easynbt.SyncNBT; +import pl.pabilo8.immersiveintelligence.common.util.tile.TileEntityIIDirectional.FacingSettings; + +import javax.annotation.Nonnull; + +/** + * @author Pabilo8 (pabilo@iiteam.net) + * @ii-approved 0.3.1 + * @since 16.07.2026 + */ +public abstract class TileEntityIIDirectionalConnectable extends TileEntityIIConnectable implements IDirectionalTile +{ + @SyncNBT + public EnumFacing facing = EnumFacing.NORTH; + + @Nonnull + @Override + public final EnumFacing getFacing() + { + return facing; + } + + @Override + public final void setFacing(@Nonnull EnumFacing facing) + { + this.facing = facing; + } + + @Nonnull + protected abstract FacingSettings getFacingSettings(); + + @Override + public final int getFacingLimitation() + { + return getFacingSettings().facingLimitation.ordinal(); + } + + @Override + public final boolean mirrorFacingOnPlacement(@Nonnull EntityLivingBase placer) + { + return getFacingSettings().shouldMirrorOnPlacement; + } + + @Override + public final boolean canHammerRotate(@Nonnull EnumFacing side, float hitX, float hitY, float hitZ, @Nonnull EntityLivingBase entity) + { + return getFacingSettings().canHammerRotate; + } + + @Override + public final boolean canRotate(@Nonnull EnumFacing axis) + { + return getFacingSettings().canRotate; + } +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/artillery_howitzer.json b/src/main/resources/assets/immersiveintelligence/advancements/main/artillery_howitzer.json index f0591219a..7a50e27ae 100644 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/artillery_howitzer.json +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/artillery_howitzer.json @@ -4,6 +4,7 @@ "item": "immersiveintelligence:metal_multiblock", "data": 8 }, + "frame": "goal", "title": { "translate": "advancement.immersiveintelligence.artillery_howitzer" }, diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_cpds.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_cpds.json deleted file mode 100644 index e37221f61..000000000 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_cpds.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "display": { - "icon": { - "item": "immersiveintelligence:bullet_magazine", - "data": 6 - }, - "title": { - "translate": "advancement.immersiveintelligence.craft_cpds" - }, - "description": { - "translate": "advancement.immersiveintelligence.craft_cpds.desc" - } - }, - "parent": "immersiveintelligence:main/emplacement", - "criteria": { - "code_trigger": { - "trigger": "minecraft:impossible" - } - } -} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_electric_hammer.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_electric_hammer.json new file mode 100644 index 000000000..5c18f61b7 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_electric_hammer.json @@ -0,0 +1,27 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:electric_hammer" + }, + "frame": "goal", + "title": { + "translate": "advancement.immersiveintelligence.craft_electric_hammer" + }, + "description": { + "translate": "advancement.immersiveintelligence.craft_electric_hammer.desc" + } + }, + "parent": "immersiveengineering:main/craft_hammer", + "criteria": { + "craft_hammer": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { + "item": "immersiveintelligence:electric_hammer" + } + ] + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_electric_wrench.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_electric_wrench.json new file mode 100644 index 000000000..ed6a37f15 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_electric_wrench.json @@ -0,0 +1,27 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:electric_wrench" + }, + "frame": "goal", + "title": { + "translate": "advancement.immersiveintelligence.craft_electric_wrench" + }, + "description": { + "translate": "advancement.immersiveintelligence.craft_electric_wrench.desc" + } + }, + "parent": "immersiveintelligence:main/craft_wrench", + "criteria": { + "craft_hammer": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { + "item": "immersiveintelligence:electric_wrench" + } + ] + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_mg.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_mg.json index 197aafa88..a9e9e51a0 100644 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_mg.json +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_mg.json @@ -3,6 +3,7 @@ "icon": { "item": "immersiveintelligence:machinegun" }, + "frame": "goal", "title": { "translate": "advancement.immersiveintelligence.craft_mg" }, diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_rifle.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_rifle.json index 2adcf083f..edb02c4f4 100644 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_rifle.json +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_rifle.json @@ -3,6 +3,7 @@ "icon": { "item": "immersiveintelligence:rifle" }, + "frame": "goal", "title": { "translate": "advancement.immersiveintelligence.craft_rifle" }, diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_smg.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_smg.json index 02237f8dc..aa40342bc 100644 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_smg.json +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_smg.json @@ -3,6 +3,7 @@ "icon": { "item": "immersiveintelligence:submachinegun" }, + "frame": "goal", "title": { "translate": "advancement.immersiveintelligence.craft_smg" }, diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_stg.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_stg.json index aefca4625..061d5ab81 100644 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_stg.json +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_stg.json @@ -3,6 +3,7 @@ "icon": { "item": "immersiveintelligence:assault_rifle" }, + "frame": "goal", "title": { "translate": "advancement.immersiveintelligence.craft_stg" }, diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/craft_wrench.json b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_wrench.json new file mode 100644 index 000000000..358537aae --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/craft_wrench.json @@ -0,0 +1,26 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:wrench" + }, + "title": { + "translate": "advancement.immersiveintelligence.craft_wrench" + }, + "description": { + "translate": "advancement.immersiveintelligence.craft_wrench.desc" + } + }, + "parent": "immersiveintelligence:main/root", + "criteria": { + "craft_hammer": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { + "item": "immersiveintelligence:wrench" + } + ] + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement.json index 7c1e116b2..f217b8504 100644 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement.json +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement.json @@ -4,6 +4,7 @@ "item": "immersiveintelligence:metal_multiblock1", "data": 9 }, + "frame": "goal", "title": { "translate": "advancement.immersiveintelligence.emplacement" }, diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_autocannon.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_autocannon.json new file mode 100644 index 000000000..88d6876a4 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_autocannon.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 0 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_autocannon" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_autocannon.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/autocannon" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_cpds.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_cpds.json new file mode 100644 index 000000000..6df527d61 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_cpds.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 1 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_cpds" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_cpds.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/cpds" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_guided_missile_launcher.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_guided_missile_launcher.json new file mode 100644 index 000000000..b40d24bad --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_guided_missile_launcher.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 2 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_guided_missile_launcher" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_guided_missile_launcher.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/guided_missile_launcher" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_heavy_chemthrower.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_heavy_chemthrower.json new file mode 100644 index 000000000..a3040500b --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_heavy_chemthrower.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 3 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_heavy_chemthrower" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_heavy_chemthrower.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/heavy_chemthrower" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_heavy_railgun.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_heavy_railgun.json new file mode 100644 index 000000000..257d85c52 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_heavy_railgun.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 4 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_heavy_railgun" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_heavy_railgun.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/heavy_railgun" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_infrared_observer.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_infrared_observer.json new file mode 100644 index 000000000..1945caf2a --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_infrared_observer.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 5 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_infrared_observer" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_infrared_observer.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/infrared_observer" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_light_howitzer.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_light_howitzer.json new file mode 100644 index 000000000..598ac82f8 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_light_howitzer.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 6 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_light_howitzer" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_light_howitzer.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/light_howitzer" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_machinegun.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_machinegun.json new file mode 100644 index 000000000..92ea3ad69 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_machinegun.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 7 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_machinegun" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_machinegun.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/machinegun" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_mortar.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_mortar.json new file mode 100644 index 000000000..26b1cf6cb --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_mortar.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 8 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_mortar" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_mortar.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/mortar" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_rocket_launcher.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_rocket_launcher.json new file mode 100644 index 000000000..7cdbd1023 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_rocket_launcher.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 9 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_rocket_launcher" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_rocket_launcher.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/rocket_launcher" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_searchlight.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_searchlight.json new file mode 100644 index 000000000..300f41ddd --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_searchlight.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 10 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_searchlight" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_searchlight.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/searchlight" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_spotlight_tower.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_spotlight_tower.json new file mode 100644 index 000000000..ac1b2a8da --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_spotlight_tower.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 11 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_spotlight_tower" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_spotlight_tower.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/spotlight_tower" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_tesla.json b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_tesla.json new file mode 100644 index 000000000..ae11205d0 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/emplacement_tesla.json @@ -0,0 +1,28 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 12 + }, + "title": { + "translate": "advancement.immersiveintelligence.emplacement_tesla" + }, + "description": { + "translate": "advancement.immersiveintelligence.emplacement_tesla.desc" + } + }, + "parent": "immersiveintelligence:main/emplacement", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:emplacement/tesla" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/flagpole_unit_post.json b/src/main/resources/assets/immersiveintelligence/advancements/main/flagpole_unit_post.json new file mode 100644 index 000000000..c48e13726 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/flagpole_unit_post.json @@ -0,0 +1,29 @@ +{ + "display": { + "icon": { + "item": "immersiveintelligence:placeholder_icon", + "data": 13 + }, + "frame": "goal", + "title": { + "translate": "advancement.immersiveintelligence.flagpole_unit_post" + }, + "description": { + "translate": "advancement.immersiveintelligence.flagpole_unit_post.desc" + } + }, + "parent": "immersiveintelligence:main/flagpole", + "criteria": { + "install_upgrade": { + "trigger": "immersiveintelligence:upgrade_installed", + "conditions": { + "upgrade": "immersiveintelligence:flagpole/unit_post" + } + } + }, + "requirements": [ + [ + "install_upgrade" + ] + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/advancements/main/prec_assembler.json b/src/main/resources/assets/immersiveintelligence/advancements/main/prec_assembler.json index cfdab340e..b03921fd6 100644 --- a/src/main/resources/assets/immersiveintelligence/advancements/main/prec_assembler.json +++ b/src/main/resources/assets/immersiveintelligence/advancements/main/prec_assembler.json @@ -4,6 +4,7 @@ "item": "immersiveintelligence:metal_multiblock", "data": 6 }, + "frame": "goal", "title": { "translate": "advancement.immersiveintelligence.prec_assembler" }, diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_pier.json b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_pier.json new file mode 100644 index 000000000..c30bc2b11 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_pier.json @@ -0,0 +1,25 @@ +{ + "multipart": [ + { + "apply": { + "model": "immersiveintelligence:harbor/wooden/floor" + } + }, + { + "when": { + "connected": "false" + }, + "apply": { + "model": "immersiveintelligence:harbor/wooden/support_single" + } + }, + { + "when": { + "connected": "true" + }, + "apply": { + "model": "immersiveintelligence:harbor/wooden/support_connected" + } + } + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_pier_item.json b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_pier_item.json new file mode 100644 index 000000000..f8120e863 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_pier_item.json @@ -0,0 +1,17 @@ +{ + "forge_marker": 1, + "variants": { + "inventory,type=wooden_pier": [ + { + "transform": "forge:default-block", + "model": "immersiveintelligence:harbor/wooden_pier_inv.obj", + "custom": { + "flip-v": true + }, + "textures": { + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + } + } + ] + } +} diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_quay.json b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_quay.json new file mode 100644 index 000000000..8649d8de6 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_quay.json @@ -0,0 +1,105 @@ +{ + "multipart": [ + { + "apply": { + "model": "immersiveintelligence:harbor/wooden/floor" + } + }, + { + "when": { + "connected": "false" + }, + "apply": { + "model": "immersiveintelligence:harbor/wooden/support_single" + } + }, + { + "when": { + "connected": "true" + }, + "apply": { + "model": "immersiveintelligence:harbor/wooden/support_connected" + } + }, + { + "when": { + "support": "center" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_center" + } + }, + { + "when": { + "support": "s" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_n" + } + }, + { + "when": { + "support": "sw" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_ne" + } + }, + { + "when": { + "support": "w" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_e" + } + }, + { + "when": { + "support": "nw" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_se" + } + }, + { + "when": { + "support": "n" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_s" + } + }, + { + "when": { + "support": "ne" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_sw" + } + }, + { + "when": { + "support": "e" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_w" + } + }, + { + "when": { + "support": "se" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_nw" + } + }, + { + "when": { + "solid_neighbor": "true" + }, + "apply": { + "model": "immersiveintelligence:harbor/concrete/support_center" + } + } + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_quay_item.json b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_quay_item.json new file mode 100644 index 000000000..2b323113e --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/blockstates/harbor/wooden_quay_item.json @@ -0,0 +1,17 @@ +{ + "forge_marker": 1, + "variants": { + "inventory,type=wooden_quay": [ + { + "transform": "forge:default-block", + "model": "immersiveintelligence:harbor/wooden_quay_inv.obj", + "custom": { + "flip-v": true + }, + "textures": { + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + } + } + ] + } +} diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/harbor_support/wooden_pier_support.json b/src/main/resources/assets/immersiveintelligence/blockstates/harbor_support/wooden_pier_support.json new file mode 100644 index 000000000..1e1f673b6 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/blockstates/harbor_support/wooden_pier_support.json @@ -0,0 +1,28 @@ +{ + "multipart": [ + { + "when": { + "bottom": "false" + }, + "apply": { + "model": "immersiveintelligence:harbor/wooden/pier_support" + } + }, + { + "when": { + "bottom": "true" + }, + "apply": { + "model": "immersiveintelligence:harbor/wooden/pier_support_bottom" + } + }, + { + "when": { + "top": "true" + }, + "apply": { + "model": "immersiveintelligence:harbor/wooden/pier_support_top" + } + } + ] +} diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/harbor_support/wooden_pier_support_item.json b/src/main/resources/assets/immersiveintelligence/blockstates/harbor_support/wooden_pier_support_item.json new file mode 100644 index 000000000..fff943fdb --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/blockstates/harbor_support/wooden_pier_support_item.json @@ -0,0 +1,17 @@ +{ + "forge_marker": 1, + "variants": { + "inventory,type=wooden_pier_support": [ + { + "transform": "forge:default-block", + "model": "immersiveintelligence:harbor/wooden_pier_support_inv.obj", + "custom": { + "flip-v": true + }, + "textures": { + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + } + } + ] + } +} diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock/printing_press.json b/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock/printing_press.json index d4090a9b8..6511fac16 100644 --- a/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock/printing_press.json +++ b/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock/printing_press.json @@ -16,9 +16,9 @@ "transform": { "scale": 0.2, "translation": [ - 0, -0.25, - 0 + -0.125, + 0.125 ], "rotation": [ { diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock1/projectile_workshop.json b/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock1/projectile_workshop.json index 941cfe43a..50907e90a 100644 --- a/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock1/projectile_workshop.json +++ b/src/main/resources/assets/immersiveintelligence/blockstates/metal_multiblock1/projectile_workshop.json @@ -16,8 +16,8 @@ "transform": { "scale": 0.2, "translation": [ - -0.5, - -0.25, + 0, + -0.125, 0 ], "rotation": [ diff --git a/src/main/resources/assets/immersiveintelligence/blockstates/wooden_fortification/wooden_aluminum_chain_fence.json b/src/main/resources/assets/immersiveintelligence/blockstates/wooden_fortification/wooden_aluminum_chain_fence.json new file mode 100644 index 000000000..5c835a361 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/blockstates/wooden_fortification/wooden_aluminum_chain_fence.json @@ -0,0 +1,58 @@ +{ + "variants": { + "inventory,type=wooden_aluminum_chain_fence": [ + { + "model": "immersiveintelligence:chain_fence/chain_fence_wooden_aluminum" + } + ] + }, + "multipart": [ + { + "when": { + "up": "true" + }, + "apply": { + "model": "immersiveintelligence:chain_fence/chain_fence_wooden_aluminum_base" + } + }, + { + "when": { + "north": "true" + }, + "apply": { + "model": "immersiveintelligence:chain_fence/chain_fence_wooden_aluminum_side", + "uvlock": false + } + }, + { + "when": { + "east": "true" + }, + "apply": { + "model": "immersiveintelligence:chain_fence/chain_fence_wooden_aluminum_side", + "y": 90, + "uvlock": false + } + }, + { + "when": { + "south": "true" + }, + "apply": { + "model": "immersiveintelligence:chain_fence/chain_fence_wooden_aluminum_side", + "y": 180, + "uvlock": false + } + }, + { + "when": { + "west": "true" + }, + "apply": { + "model": "immersiveintelligence:chain_fence/chain_fence_wooden_aluminum_side", + "y": 270, + "uvlock": false + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_input_machine.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_input_machine.md index dbd893474..58ecab777 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_input_machine.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_input_machine.md @@ -19,7 +19,6 @@ When editing a variable, you can change its letter by using the up and down arro # punchtapes The **Data Input Machine** is capable of reading a **Packet** from a [written punchtape] and writing the currently stored **Packet** to [a blank one]. Both operations can be performed by inserting a [Punchtape] into the *upper slot*. After processing, the [Punchtape] will be outputted into the *lower slot*. -# punchtapes -The **DIM** can be upgraded with a specialized control panel to allow input of advanced data types like [Vector], [Map] and [Array]. - - +# advanced_data_upgrade +|[upgrade_display]{upgrade:"immersiveintelligence:advanced_data"}| +The **DIM** can be upgraded with a specialized control panel to allow input of advanced data types like [Vector](data_types.md#vector) and [LogiTag](data_types.md#logitag). diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_types.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_types.md index 0612502e7..38fdd7bf8 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_types.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/data_types.md @@ -15,11 +15,14 @@ For example, if in a packet: # default_value2 The same would happen in a packet with numbers stored as a string: |[data_packet]{data:{a:{Type:"string",Value:"123"}}}| -Despite that for a human the *text* "123" seems identical to the *number* 123, a data machine sees it very different. A text type [can't be converted] to a number type, thus it too returns a [defaultized Integer], or simply put: 0. +Despite that for a human the *text* "123" seems identical to the *number* 123, a data machine sees it very different. A text type [can't be evaluated] as a number type, thus it too returns a [defaultized Integer], or simply put: 0. # default_value3 -The only case where such conversion would occur properly, is between two **Compatible Types** - two types storing a similar kind of information, such as when converting a [Float](#float) to an [Integer](#integer) +The only case where such evaluation would occur properly, is between two **Compatible Types** - two types storing a similar kind of information, such as when evaluating a [Float](#float) to an [Integer](#integer) |[data_packet]{data:{a:{Type:"float",Value:123}}}| This mechanism is also one of the core concepts of **Strong Typing**. +# default_value4 +It is possible to convert between types using [Casting Operations](functions/type_conversion.md) in an [Arithmetic-Logic Machine](arithmetic_logic_machine.md) or a [Mainframe Computer], using the [Type Conversion Circuit](functions/_functional_circuits.md). +|[data_operation]{id:"to_string"}| # data_overflow Each **Type** is limited by a size or length number. This feature is necessary to ensure that there is no [Data Overflow] - a situation when a type would take too much space and corrupt the entire packet. @@ -31,7 +34,7 @@ Types are separated into two groups: These are: [Null](#null), [Integer](#integer), [Float](#float), [Boolean](#boolean) and [String](#string). **Compound Types** - consisting of [multiple] **Basic Types** and [joining them into one object], for representing a more sophisticated information. -These include [ItemStack](#itemstack), [Array](#array), [FluidStack](#fluidstack), [Vector](#vector), [Entity](#entity), and [Map](#map). +These include [ItemStack](#itemstack), [Array](#array), [FluidStack](#fluidstack), [Vector](#vector), [Entity](#entity), [LogiTag](#logitag), and [Map](#map). # null |[datatype]{type:"%SECTION%",x:52}| [Null] is a [special] data type, which has [no value]. @@ -62,8 +65,12 @@ It is mainly used to set an item filter or mark a specific item for a machine ta It is mainly used to set an fluid filter or mark a specific fluid for a machine task. # vector |[datatype]{type:"%SECTION%",x:52}| -[Vector] is a [compound] data type storing 3 number values. It can store [integers](#integer), [floats](#float) or [a mix of them]. +[Vector] is a [compound] data type storing 3 number values. It can store [integers](#integer) or [floats](#float). It is used to represent values in 3-dimensional space, such as position or motion of objects. +# logitag +|[datatype]{type:"%SECTION%",x:52}| +[LogiTag] is a [compound] data type storing information of a [Logistic Manifest](../ii_logistics/task_system.md#logitags): its [name], [description], [owner identity], [origin], [destination], [color marker] and [batch number]. +It is used to identify cargo containers, such as crates. # entity |[datatype]{type:"%SECTION%",x:52}| [Entity] is a [compound] data type which holds information about a specific in-world [entity]: its [name], [ID], [position], [motion] and [NBT data]. diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/printing_press.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/printing_press.md index 51e4b6551..962bac350 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/printing_press.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_data/printing_press.md @@ -9,8 +9,8 @@ The **Printing Press** is a machine which can print out text on demand, received |[text]{mb:"II:PrintingPress"}| # press1 -|[machine_recipe]{machine:"metal_press", source:"paper_page"}| -The [Printing Paper] is a special thinned version of a standard one used in books. It is created by [pressing a piece of Paper in a Metal Press]. +|[machine_recipe]{source:"paper_page"}| +The [Printing Paper] is a special thinned version of a standard one used in books. It is created by pressing a piece of Paper in a [Metal Press](metalPress). Printed pages should be removed as soon as possible from the printing press, as the press can only store [12 pages] in its output basket. # press_usage @@ -24,15 +24,23 @@ The printing press can also be [upgraded](#punchtapes_upgrade) to extend its fun |[data_variable]{type:"string", direction:"in", letter:"m", name:"Output mode", description:"The type of document that will be printed", values:[["text","A page containing text"],["code","A page containing code written in the POL programming language"],["blueprint","A page containing a construction blueprint to be used manually or by a Logistics Drone"],["logi_tag","A logistics tag used to mark containers and items"]]}| |[data_variable]{type:"integer", direction:"in", letter:"a", name:"Amount of copies", description:"How many pages will be printed"}| |[data_variable]{type:"string", direction:"in", letter:"t", name:"Text to print", description:"Text content that will be printed", requirements:{m:"text/code"}}| +Printing [Logistic Manifests](../ii_logistics/task_system.md#logitags) requires a different set of variables and is described in [a dedicated page](../ii_logistics/task_system.md#logitag_printing). + # punchtapes_upgrade -|[upgrade_display]{upgrade:"immersiveintelligence:printing_press/punchtape_processor"}| -The [Punchtape Processor] upgrade allows printing [Punchtapes](punchtapes.md) with all the variables of the received packets, except "a" and "m". -**Instead of empty page, supply an [Empty Punchtape].** +|[upgrade_display]{upgrade:"immersiveintelligence:printing_press/punchtapes"}| +The [Punchtape Processor] upgrade allows printing [Punchtapes](punchtapes.md) with all the variables of the received packets, except 'a', 'm' and 't'. +**Instead of empty page, supply an [Empty Punchtape].**
|[data_variable]{type:"string", direction:"in", letter:"m", name:"Output mode", description:"The type of document that will be printed", values:[["punchtape","A punchtape with variables of the received packet, except this one printed. Doesn't use any ink."]]}| - +# enveloper +|[upgrade_display]{upgrade:"immersiveintelligence:printing_press/enveloper"}| +The [Enveloper] upgrade allows printing [Letters in Envelopes](envelopes.md), to be addressed and sent to other players. +|[wip_notice]| +# batching +|[upgrade_display]{upgrade:"immersiveintelligence:printing_press/batching"}| +The [Batching Mechanism] upgrade allows printing multiple pages of text at once, allowing the **Printing Press** to create newspapers and books. +|[wip_notice]| # data_callback -|[text]{text:"Data Callback",bold:1b}| - +**Data Callback:** |[data_callback]{type:"integer", name:"get_ink", label:"Ink Level", returns:"Black ink amount (mB)"}| |[data_callback]{type:"integer", name:"get_ink_cyan", label:"Cyan Ink Level", returns:"Cyan ink amount (mB)"}| |[data_callback]{type:"integer", name:"get_ink_yellow", label:"Yellow Ink Level", returns:"Yellow ink amount (mB)"}| diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/engineers_crates.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/engineers_crates.md index fb04831ac..a1f89f4e8 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/engineers_crates.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/engineers_crates.md @@ -1,11 +1,11 @@ # meta Engineers Crates Matryoshka + # intro @level_advanced_industrial -No engineer is complete without a crate to store his items. Several new types of crates have been developed to store your items in style. -# m_crate -|[crafting]{source:"metalbox"}| +No [Engineer] is complete without a crate to store items. Several new types of crates have been developed to store your items in style.
+|[crafting]{source:"metalbox"}|
An upgrade to simple treated wood crates, the [Metal Crate] is fabricated from steel. # variable_crate_sizes |[item_display]{source:"multicrates1"}| diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/packer.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/packer.md index cd513cd22..658acb7e5 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/packer.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/packer.md @@ -35,6 +35,7 @@ The [Energy Loader] converts the Packer's item storage into [16 million] IF of e # upgrades_railway |[upgrade_display]{upgrade:"immersiveintelligence:packer_railway"}| The [Railway Upgrade] transforms the Packer's loading conveyor into a set of rails. This allows the Packer to fill [storage Minecarts](skycrate_system.md#minecarts). This upgrade can be combined with other upgrades. +|[wip_notice]| # upgrades_labeler |[upgrade_display]{upgrade:"immersiveintelligence:packer_naming"}| The [Naming Stamp] adds a label maker to the Packer and allows it to set the name of the packed item. This upgrade can be combined with other upgrades. diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/task_system.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/task_system.md index 91ad356bb..790563f67 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/task_system.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_logistics/task_system.md @@ -1,9 +1,34 @@ # meta -Task System +Task System and Logistic Tags Keep it Simple, Stupid + # intro -The task system is a concept of organising a machine's work schedule through a single iterable task. Tasks are used by many data devices, such as the [inserters](inserters.md), [packers](packer.md), and the [data routers](small_data_devices.md#router). Tasks are divided into two groups: +**The task system** is a concept of organising a machine's work schedule through a single iterable task. Tasks are used by many data devices, such as the [Inserters](inserters.md), [Packers](packer.md), and the [data routers](small_data_devices.md#router). Tasks are divided into two groups: **Jobs** - tasks which will execute infinitely, unless manually removed. **Requests** - tasks which are temporary, and will remove themselves after they are finished (i.e. after an inserter picks up a certain amount of items) # details -By default, tasks are executed from the oldest to the newest. If a task cannot be executed, it will be skipped. After finishing a [Request] __(successfully or **not**)__, it is checked whether it should be removed. \ No newline at end of file +By default, tasks are executed from the oldest to the newest. If a task cannot be executed, it will be skipped. After finishing a [Request] __(successfully or **not**)__, it is checked whether it should be removed. +# logitags +|[item_display]{source:"logitag_item"}| +**Logistic Manifests** or **LogiTags** for short, are information labels used by II logistic machines, like the [Packer](packer.md) and [Inserters](inserters.md). +They contain information about name of the cargo, its name, text description, [owner identity], origin, destination, color marker, and batch number and can be applied to containers, such as [Crates](engineers_crates.md) through crafting. +# logitag_printing +To create a **Logistic Manifest**, a [Printing Press](../ii_data/printing_press.md) loaded with [Blank Pages] is required. +The easiest way of passing information about the Manifest to a machine is to use a single [LogiTag](../ii_data/data_types.md#logitag) type variable.
+|[data_variable]{type:"logitag", direction:"in", letter:"l", name:"Logistic Tag", description:"Logistic tag that will be printed", requirements:{m:"logi_tag"}}| +Because this type is considered an [advanced one], it requires an upgraded [Data Input Machine](../ii_data/data_input_machine.md). +# logitag_printing2 +An alternative way to pass information about a **LogiTag** is to use an [ItemStack](../ii_data/data_types.md#itemstack) of an existing Logistic Manifest item.
+|[data_variable]{type:"itemstack", direction:"in", letter:"s", name:"Logistic Tag", description:"Logistic tag that will be printed", requirements:{m:"logi_tag"}}| +It is also possible to do it using a set of packet variables:

+|[data_variable]{type:"string", direction:"in", letter:"n", name:"Name", description:"Name of the cargo"}| +|[data_variable]{type:"string", direction:"in", letter:"d", name:"Description", description:"Optional longer text description"}| +# logitag_printing3 +|[data_variable]{type:"integer", direction:"in", letter:"b", name:"Batch Number", description:"Number for cargo in series with the same name"}| +|[data_variable]{type:"string", direction:"in", letter:"f", name:"From", description:"Sender's name"}| +|[data_variable]{type:"string", direction:"in", letter:"t", name:"To", description:"Recipient's name"}| +|[data_variable]{type:"string", direction:"in", letter:"o", name:"Owner", description:"Identity of this cargo's owner."}| +|[data_variable]{type:"string", direction:"in", letter:"p", name:"Color", description:"Color assigned to this cargo in hex format."}| +# logitag_printing4 +This last way is **not recommended**, but it remains a viable option, when you're not concerned about taking space in the [Packet]. +The [Owner Identity](../ii_warfare/terrain_control/owner_identity.md) in a [LogiTag] refers to what's commonly called *the faction system*. \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/armortools/flagpole.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/armortools/flagpole.md deleted file mode 100644 index 2e22257dc..000000000 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/armortools/flagpole.md +++ /dev/null @@ -1,13 +0,0 @@ -# meta -Flagpole -Stand for anthem! -# 0 -@hammer;upgradeable;repairable;paintable -|[multiblock]{mb:"II:Flagpole"}| -The [flagpole] is a multiblock that allows marking of nearby territory, keeping it loaded and under control, while also -displaying a [stylish banner] for friends and foes to recognise. To form it, use a [hammer](introduction#introductionHammer) on the middle razor wire block. -# 1 -The flagpole keeps [8 surrounding chunks loaded along with the one it's placed in] and it does not require any resources to do so. -Another function is a display for [banners] in form of a [flag]. To place a banner, use it on one of the wooden pole blocks. If you have decided to invade someone else's property or just want to update your banner design, you can remove the placed banner by using [wire cutters](wiring#wiringCutters). The flagpole can also be [upgraded](#upgrades). -# upgrades -|[wip_notice]| \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/flagpole.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/flagpole.md new file mode 100644 index 000000000..ded719a0b --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/flagpole.md @@ -0,0 +1,30 @@ +# meta +Flagpole +Stand for anthem! +# intro +@hammer;upgradeable;repairable;paintable +|[multiblock]{mb:"II:Flagpole"}| +The **Flagpole** is a multiblock that allows marking of nearby territory, keeping it loaded and under control, while also +displaying a [stylish banner] for friends and foes to recognise. To form it, use a [hammer](introduction#introductionHammer) on the middle razor wire block. +# chunk_loading +The **Flagpole** [claims](owner_identity.md) and keeps [8 surrounding chunks loaded along with the one it's placed in] for its [owner](owner_identity.md), and it does not require any resources to do so. +Another function is a display for [banners] in form of a [flag]. To place a banner, use it on one of the wooden pole blocks. +If want to update your banner design, you can remove the placed banner by using [wire cutters](wiring#wiringCutters). +# faction)settings +By opening the **Flagpole's** interface, you can see a map of the surrounding area with an ability to view it. +Another section of it allows you to manage basic settings of your [Owner Identity](owner_identity.md). +This view is handy, but very limited compared to one offered by the [Strategic Command Table](../../ii_intel/strategic_command_table.md). +|[wip_notice]| +# capture_defiance +|[upgrade_display]{upgrade:"immersiveintelligence:flagpole/capture_defiance"}| +The [Capture Defiance] upgrade increases the minimum count of enemy troops in proximity required to capture the **Flagpole** from 1 to 3, making it harder to take over. +# taser_locks +|[upgrade_display]{upgrade:"immersiveintelligence:flagpole/taser_locks"}| +The [Taser Locks] upgrade adds a [Tesla Coil](teslaCoil) to the **Flagpole**, which will zap any enemy that comes near it, dealing damage and stunning them for a short time. +# distress_signal +|[upgrade_display]{upgrade:"immersiveintelligence:flagpole/distress_signal"}| +The [Distress Signal] upgrade mounts an [Alarm Siren](../../ii_intel/alarm_siren.md) and a small radio antenna on the **Flagpole**, which will alert nearby friendly troops of an enemy presence, allowing them to respond faster. +# unit_post +|[upgrade_display]{upgrade:"immersiveintelligence:flagpole/unit_post"}| +The [Unit Post] upgrade allows to host a squad of [Troopers] at the **Flagpole**, that will guard it and go on any combat mission assigned to them within the Flagpole's [operation radius]. +|[wip_notice]| diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/owner_identity.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/owner_identity.md new file mode 100644 index 000000000..e18036b48 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/owner_identity.md @@ -0,0 +1,9 @@ +# meta +Ownership and Factions +Divided we fall +# intro +Every chunk, multiblock machine, vehicle, and certain other structures are [Properties](ii_warfare/terrain_control/chunks_and_properties.md), and can be owned. +Ownership is represented by an **Owner Identity** - often simply called a **Faction**. +By default, everyone is *the owner of their personal identity*, but it is possible to join another faction, to share ownership. + +|[wip_notice]| \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/properties.md b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/properties.md new file mode 100644 index 000000000..a75c0412b --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/en_us/ii_warfare/terrain_control/properties.md @@ -0,0 +1,8 @@ +# meta +Properties +It's free real estate! +# intro +A **Property** is any claimable structure, terrain chunk or vehicle placed in the world. +The examples of properties include multiblocks like [Flagpoles](../), [Emplacements](../staticdefense/emplacement.md) or [Strategic Command Tables](../../ii_intel/strategic_command_table.md). +A property under control can restrict interaction - make mining blocks or opening inventories forbidded, depending on the rules set by its [Owner](owner_identity.md). +|[wip_notice]{brief:true}| diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_data/printing_press.md b/src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_data/printing_press.md index 45e91c784..3c614eb96 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_data/printing_press.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_data/printing_press.md @@ -24,11 +24,12 @@ |[data_variable]{type:"string", direction:"in", letter:"m", name:"Output mode", description:"The type of document that will be printed", values:[["text","A page containing text"],["code","A page containing code written in the POL programming language"],["blueprint","A page containing a construction blueprint to be used manually or by a Logistics Drone"],["logi_tag","A logistics tag used to mark containers and items"]]}| |[data_variable]{type:"integer", direction:"in", letter:"a", name:"Amount of copies", description:"How many pages will be printed"}| |[data_variable]{type:"string", direction:"in", letter:"t", name:"Text to print", description:"Text content that will be printed", requirements:{m:"text/code"}}| + # punchtapes_upgrade -|[upgrade_display]{upgrade:"immersiveintelligence:printing_press/punchtape_processor"}| -Улучшение [обработчик перфоленты] позволяет печатать [перфоленты](punchtapes.md) со всеми переменными полученных пакетов, кроме «a» и «m». +|[upgrade_display]{upgrade:"immersiveintelligence:printing_press/punchtapes"}| +Улучшение [обработчик перфоленты] позволяет печатать [перфоленты](punchtapes.md) со всеми переменными полученных пакетов, кроме «a», «m» и «t». **Вместо пустой страницы предоставьте [пустую перфоленту].** -|[data_variable]{type:"string", direction:"in", letter:"m", name:"Output mode", description:"The type of document that will be printed", values:[["punchtape","A punchtape with variables of the received packet, except this one printed. Doesn't use any ink."]]}| +|[data_variable]{type:"string", direction:"in", letter:"m", name:"Output mode", description:"The type of document that will be printed", values:[["punchtape","A punchtape with variables of the received packet, except 'a', 'm' and 't' printed. Doesn't use any ink."]]}| # data_callback |[text]{text:"Data Callback",bold:1b}| diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_warfare/armortools/flagpole.md b/src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_warfare/terrain_control/flagpole.md similarity index 100% rename from src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_warfare/armortools/flagpole.md rename to src/main/resources/assets/immersiveintelligence/ie_manual/ru_ru/ii_warfare/terrain_control/flagpole.md diff --git a/src/main/resources/assets/immersiveintelligence/ie_manual/uk_ua/ii_data/printing_press.md b/src/main/resources/assets/immersiveintelligence/ie_manual/uk_ua/ii_data/printing_press.md index b2c9fc5b7..8cd30116e 100644 --- a/src/main/resources/assets/immersiveintelligence/ie_manual/uk_ua/ii_data/printing_press.md +++ b/src/main/resources/assets/immersiveintelligence/ie_manual/uk_ua/ii_data/printing_press.md @@ -16,8 +16,9 @@ |[data_variable]{type:"string", direction:"in", letter:"m", name:"Output mode", description:"What type of page that will be printed", values:[["text","A page containing text"],["code","A page containing code written in the POL programming language"],["blueprint","A page containing a construction blueprint to be used manually or by a Logistics Drone"],["punchtape","A punchtape with variables of the received packet, except this one printed. Doesn't use any ink."],["orders","A page with step-by-step orders for a military or logistic unit"]]}| |[data_variable]{type:"integer", direction:"in", letter:"a", name:"Amount of copies", description:"How many pages will be printed"}| |[data_variable]{type:"string", direction:"in", letter:"t", name:"Text to print", description:"Text content that will be printed", requirements:{m:"text/code"} }| -# data_inputs_upgrade -[] дозволяє друкувати нові типи +# punchtapes_upgrade +Поліпшення [обробник перфострічки] дозволяє друкувати [перфострічки](punchtapes.md) з усіма змінними отриманих пакетів, крім «a», «m» та «t». +**Замість порожньої сторінки надайте [порожню перфострічку].** |[data_variable]{type:"string", direction:"in", letter:"m", name:"Output mode", description:"What type of page that will be printed", values:[["text","A page containing text"],["code","A page containing code written in the POL programming language"],["blueprint","A page containing a construction blueprint to be used manually or by a Logistics Drone"],["punchtape","A punchtape with variables of the received packet, except this one printed. Doesn't use any ink."],["orders","A page with step-by-step orders for a military or logistic unit"]]}| # data_callback |[text]{text:"Data Callback",bold:1b}| diff --git a/src/main/resources/assets/immersiveintelligence/ii_fluidlogged.json b/src/main/resources/assets/immersiveintelligence/ii_fluidlogged.json index 1f1e0e815..a9a8191ca 100644 --- a/src/main/resources/assets/immersiveintelligence/ii_fluidlogged.json +++ b/src/main/resources/assets/immersiveintelligence/ii_fluidlogged.json @@ -65,10 +65,40 @@ "blockId": "immersiveintelligence:metal_fortification", "canFluidFlow": true }, + { + "blockId": "immersiveintelligence:wooden_fortification", + "canFluidFlow": true + }, + { + "blockId": "immersiveintelligence:harbor", + "boxes": { + "min": 0, + "max": 12 + }, + "states": { + "type": "wooden_quay" + }, + "canFluidFlow": false + }, + { + "blockId": "immersiveintelligence:harbor", + "boxes": { + "min": 0, + "max": 12 + }, + "states": { + "type": "wooden_pier" + }, + "canFluidFlow": true + }, + { + "blockId": "immersiveintelligence:harbor_support", + "canFluidFlow": true + }, { "blockId": "immersiveintelligence:metal_device", "states": { - "type": "latex_collector" + "type": ["latex_collector", "ammunition_crate", "medic_crate", "repair_crate"] }, "canFluidFlow": true }, diff --git a/src/main/resources/assets/immersiveintelligence/lang/en_us.lang b/src/main/resources/assets/immersiveintelligence/lang/en_us.lang index d393c4127..884ad8363 100644 --- a/src/main/resources/assets/immersiveintelligence/lang/en_us.lang +++ b/src/main/resources/assets/immersiveintelligence/lang/en_us.lang @@ -620,6 +620,8 @@ item.immersiveintelligence.naval_mine.bullet.name=Naval Mine item.immersiveintelligence.naval_mine.core.name=Naval Mine Core item.immersiveintelligence.ammo_casing.naval_mine.name=Naval Mine Casing +item.immersiveintelligence.placeholder_icon.name=Placeholder + tile.immersiveintelligence.stone_decoration.sandbags.name=Sandbags tile.immersiveintelligence.metal_device.metal_crate.name=Metal Crate @@ -663,6 +665,12 @@ tile.immersiveintelligence.metal_fortification.aluminium_chain_fence.name=Alumin tile.immersiveintelligence.wooden_fortification.wooden_steel_chain_fence.name=Wooden Steel Chain Fence tile.immersiveintelligence.wooden_fortification.wooden_brass_chain_fence.name=Wooden Brass Chain Fence +tile.immersiveintelligence.wooden_fortification.wooden_aluminum_chain_fence.name=Wooden Aluminium Chain Fence + +#Harbor Decorations +tile.immersiveintelligence.harbor.wooden_pier.name=Wooden Pier +tile.immersiveintelligence.harbor.wooden_quay.name=Wooden Quay +tile.immersiveintelligence.harbor_support.wooden_pier_support.name=Wooden Pier Support tile.immersiveintelligence.metal_fortification1.tank_trap.name=Czech Hedgehog @@ -1245,24 +1253,41 @@ desc.immersiveintelligence.weapon.magazine2=%s - Magazine #2 desc.immersiveintelligence.storage_module=Storage +desc.immersiveintelligence.storage_module.tooltip=Access the machine's inventory desc.immersiveintelligence.variables_module=Variables +desc.immersiveintelligence.variables_module.tooltip=Manage and edit data variables desc.immersiveintelligence.memory_in_module=Input Memory +desc.immersiveintelligence.memory_in_module.tooltip=Manage rules for memory loading pre machine's operation desc.immersiveintelligence.memory_out_module=Output Memory +desc.immersiveintelligence.memory_out_module.tooltip=Manage rules for memory saving post machine's operation desc.immersiveintelligence.data_module=Data +desc.immersiveintelligence.data_module.tooltip=Configure how input data is converted to redstone desc.immersiveintelligence.redstone_module=Redstone +desc.immersiveintelligence.redstone_module.tooltip=Configure how input redstone is converted to data desc.immersiveintelligence.data_to_redstone_module=Data to Redstone +desc.immersiveintelligence.data_to_redstone_module.tooltip=Configure how input data is converted to redstone desc.immersiveintelligence.redstone_to_data_module=Redstone to Data +desc.immersiveintelligence.redstone_to_data_module.tooltip=Configure how input redstone is converted to data desc.immersiveintelligence.tasks_module=Tasks +desc.immersiveintelligence.tasks_module.tooltip=Add and modify tasks of the machine desc.immersiveintelligence.status_module=Status +desc.immersiveintelligence.status_module.tooltip=Check status of the machine desc.immersiveintelligence.labeler_module=Labels +desc.immersiveintelligence.labeler_module.tooltip=Configure how labels are applied to containers desc.immersiveintelligence.map_module=Map +desc.immersiveintelligence.map_module.tooltip=View map of the surrounding area desc.immersiveintelligence.radar_module=Radar +desc.immersiveintelligence.radar_module.tooltip=Access the radar screen desc.immersiveintelligence.faction_module=Faction Management +desc.immersiveintelligence.faction_module.tooltip=Manage your faction desc.immersiveintelligence.configuration_module=Configuration +desc.immersiveintelligence.configuration_module.tooltip=Change settings of the machine desc.immersiveintelligence.targets_module=Targets +desc.immersiveintelligence.targets_module.tooltip=Manage target classification desc.immersiveintelligence.fire_missions_module=Fire Missions +desc.immersiveintelligence.fire_missions_module.tooltip=Add and modify fire missions desc.immersiveintelligence.tooltip.armor=Armor %f%% @@ -1287,6 +1312,15 @@ desc.immersiveintelligence.variable_value.integer_vector=Integer Vector desc.immersiveintelligence.variable_value.integer_vector.tooltip=An Integer Vector does not allow numbers with decimal points. desc.immersiveintelligence.operation=Operation: +desc.immersiveintelligence.text_filter.none=None +desc.immersiveintelligence.text_filter.alphanumeric=Alphanumeric +desc.immersiveintelligence.text_filter.lowercase=Lowercase +desc.immersiveintelligence.text_filter.uppercase=Uppercase +desc.immersiveintelligence.text_filter.decimal=Decimal +desc.immersiveintelligence.text_filter.hexadecimal=Hexadecimal +desc.immersiveintelligence.text_filter.binary=Binary +desc.immersiveintelligence.text_filter.float=Float + desc.immersiveintelligence.conditional_variable=Conditional Variable: desc.immersiveintelligence.conditional_variable.enabled=Use condition desc.immersiveintelligence.conditional_variable.enabled.tooltip=When enabled, the expression will not run, unless the selected variable is present in the packet operated on @@ -1417,18 +1451,31 @@ desc.immersiveintelligence.diplomacy.action.declare_war=Declare War desc.immersiveintelligence.diplomacy.action.declare_alliance=Declare Alliance desc.immersiveintelligence.diplomacy.permission.disband=Disband +desc.immersiveintelligence.diplomacy.permission.disband.tooltip=Allows to disband and change the faction's law form. desc.immersiveintelligence.diplomacy.permission.merge=Merge +desc.immersiveintelligence.diplomacy.permission.merge.tooltip=Allows to send or accept diplomatic requests that involve merging this faction with another one. desc.immersiveintelligence.diplomacy.permission.foreign_affairs=Foreign Affairs +desc.immersiveintelligence.diplomacy.permission.foreign_affairs.tooltip=Allows to send and accept diplomatic requests from other factions. desc.immersiveintelligence.diplomacy.permission.modify_insignia=Modify Insignia +desc.immersiveintelligence.diplomacy.permission.modify_insignia.tooltip=Allows to change the faction's name, color and banner. desc.immersiveintelligence.diplomacy.permission.invite_members=Add Members +desc.immersiveintelligence.diplomacy.permission.invite_members.tooltip=Allows to invite new members to the faction. desc.immersiveintelligence.diplomacy.permission.remove_members=Remove Members +desc.immersiveintelligence.diplomacy.permission.remove_members.tooltip=Allows to remove members from the faction. desc.immersiveintelligence.diplomacy.permission.military_aid=Military Aid +desc.immersiveintelligence.diplomacy.permission.military_aid.tooltip=Troops and defensive installations of this faction will guard and aid anyone with this permission in combat. desc.immersiveintelligence.diplomacy.permission.transit=Transit +desc.immersiveintelligence.diplomacy.permission.transit.tooltip=Allows to freely move through this faction's territory. desc.immersiveintelligence.diplomacy.permission.trade=Trade +desc.immersiveintelligence.diplomacy.permission.trade.tooltip=Allows to trade with this faction's facilities. desc.immersiveintelligence.diplomacy.permission.research=Research +desc.immersiveintelligence.diplomacy.permission.research.tooltip=Grants access to this faction's research progress. desc.immersiveintelligence.diplomacy.permission.logistics=Logistics +desc.immersiveintelligence.diplomacy.permission.logistics.tooltip=Allows to use this faction's logistic network. desc.immersiveintelligence.diplomacy.permission.container_access=Container Access +desc.immersiveintelligence.diplomacy.permission.container_access.tooltip=Allows to open and use this faction's containers. desc.immersiveintelligence.diplomacy.permission.breaking_structures=Breaking Structures +desc.immersiveintelligence.diplomacy.permission.breaking_structures.tooltip=Allows to break this faction's structures. desc.immersiveintelligence.diplomacy.permission_level.owner_allow=Owners desc.immersiveintelligence.diplomacy.permission_level.member_allow=Members @@ -2741,6 +2788,17 @@ advancement.immersiveintelligence.root.desc=RTFM? advancement.immersiveintelligence.connect_belt=Powerful Conveying advancement.immersiveintelligence.connect_belt.desc=Link two mechanical wheels with a belt coil +#Reference to Wallace Hammering meme +advancement.immersiveintelligence.craft_electric_hammer=Wallace, NO- +advancement.immersiveintelligence.craft_electric_hammer.desc=Craft an Engineer's Electric Hammer. + +advancement.immersiveintelligence.craft_wrench=Turning things around +advancement.immersiveintelligence.craft_wrench.desc=Craft an Engineer's Wrench. + +#Reference to ROTATO FASTER BANANA +advancement.immersiveintelligence.craft_electric_wrench=We have reached maximum velocity +advancement.immersiveintelligence.craft_electric_wrench.desc=Craft an Engineer's Electric Wrench. + advancement.immersiveintelligence.craft_gun_barrel_iron=A Gunsmith? advancement.immersiveintelligence.craft_gun_barrel_iron.desc=Craft the Iron Gun Barrel, that allows you to construct basic firearms. @@ -2780,9 +2838,6 @@ advancement.immersiveintelligence.infinite_power.desc=Upgrade the Submachinegun advancement.immersiveintelligence.the_silent_unseen=The Silent Unseen advancement.immersiveintelligence.the_silent_unseen.desc=Upgrade the Submachinegun with a Folding Stock and Suppressor -advancement.immersiveintelligence.craft_cpds=The cost of firing for 12 seconds -advancement.immersiveintelligence.craft_cpds.desc=Craft the CPDS magazine - #Reference to Tiger 1 tank memes advancement.immersiveintelligence.craft_steel_belt=Hans, ze Transmission Works! advancement.immersiveintelligence.craft_steel_belt.desc=Craft a Steel Motor Belt @@ -2914,9 +2969,61 @@ advancement.immersiveintelligence.secret_sorry_for_ceiling.desc=Experience a how advancement.immersiveintelligence.emplacement=The Little Turret is also good... advancement.immersiveintelligence.emplacement.desc=Construct the Emplacement, a defensive structure that can be equipped with heavy weaponry. +advancement.immersiveintelligence.emplacement_machinegun=Death, Fully Automated +advancement.immersiveintelligence.emplacement_machinegun.desc=Install the Machinegun on an Emplacement. + +#Reference to Advance Wars: Black Hole Rising +advancement.immersiveintelligence.emplacement_autocannon=Flak Attack +advancement.immersiveintelligence.emplacement_autocannon.desc=Install the Autocannon on an Emplacement. + +#Reference to Team Fortress 2 +advancement.immersiveintelligence.emplacement_cpds=The cost of firing for 12 seconds +advancement.immersiveintelligence.emplacement_cpds.desc=Install the CPDS on an Emplacement. + +#How come you don't know this mod, hm? +advancement.immersiveintelligence.emplacement_heavy_railgun=Immersive Rail...gunning, yes +advancement.immersiveintelligence.emplacement_heavy_railgun.desc=Install the Heavy Railgun on an Emplacement. + +#Reference to a song by Sabaton +advancement.immersiveintelligence.emplacement_heavy_chemthrower=Toxic Gas and Chemical Warfare +advancement.immersiveintelligence.emplacement_heavy_chemthrower.desc=Install the Heavy Chemthrower on an Emplacement. + +#Reference to an old movie "Evil Brain from Outer Space" +advancement.immersiveintelligence.emplacement_infrared_observer=I know what lies ahead... +advancement.immersiveintelligence.emplacement_infrared_observer.desc=Install the Infrared Observer on an Emplacement. + +advancement.immersiveintelligence.emplacement_light_howitzer=Bertha Jr. +advancement.immersiveintelligence.emplacement_light_howitzer.desc=Install the Light Howitzer on an Emplacement. + +advancement.immersiveintelligence.emplacement_mortar=Without a Pestle +advancement.immersiveintelligence.emplacement_mortar.desc=Install the Mortar on an Emplacement. + +#Reference to the folk song "Katyusha" +advancement.immersiveintelligence.emplacement_rocket_launcher=Apple and Pear Trees Blooming +advancement.immersiveintelligence.emplacement_rocket_launcher.desc=Install the Rocket Launcher on an Emplacement. + +#Reference to the game Witch's House +advancement.immersiveintelligence.emplacement_guided_missile_launcher=A Spool of Thread +advancement.immersiveintelligence.emplacement_guided_missile_launcher.desc=Install the Wire-Guided Missile Launcher on an Emplacement. + +#Reference to Company of Heroes 1 +advancement.immersiveintelligence.emplacement_searchlight=Wakes better than Coffee +advancement.immersiveintelligence.emplacement_searchlight.desc=Install the Searchlight on an Emplacement. + +#Reference to Lord of The Rings +advancement.immersiveintelligence.emplacement_spotlight_tower=Remnant of Barad-Dur +advancement.immersiveintelligence.emplacement_spotlight_tower.desc=Install the Spotlight Tower on an Emplacement. + +#Reference to Command and Conquer: Red Alert 2 +advancement.immersiveintelligence.emplacement_tesla=Electrodes Primed +advancement.immersiveintelligence.emplacement_tesla.desc=Install the Tesla Coil on an Emplacement. + advancement.immersiveintelligence.flagpole=Stand for Anthem! advancement.immersiveintelligence.flagpole.desc=Construct the Flagpole, a decorative landmark and chunkloader. +#Reference to one worker song, that almost sounded like a march +advancement.immersiveintelligence.flagpole_unit_post=Now, Left, Two, Three! +advancement.immersiveintelligence.flagpole_unit_post.desc=Upgrade the Flagpole to a Unit Post. #Manual Categories @@ -2934,6 +3041,7 @@ ie.manual.folder.computers=Computers ie.manual.folder.weaponry=Weaponry ie.manual.folder.armortools=Armor and Tools ie.manual.folder.staticdefense=Defensive Structures +ie.manual.folder.terrain_control=Terrain Control ie.manual.entry.traits.level_beginner=Complexity: Pre-Industrial ie.manual.entry.traits.level_early_industrial=Complexity: Early Industrial @@ -3195,6 +3303,7 @@ death.attack.iiVehiclenoRider=%1$s was driven over death.attack.iiVehicleSuicide=%1$s decided to jump off a moving vehicle #Additional stuff for the Config Panel +ii.config.Overrides=Overrides ii.config.Graphics=Graphics ii.config.Ores=Ores ii.config.Machines=Machines @@ -3203,6 +3312,7 @@ ii.config.Weapons=Weapons ii.config.Wires=Wires ii.config.Tools=Tools ii.config.Vehicles=Vehicles +ii.config.Factions=Factions ii.config.EffectCrates=Effect Crates #Weapon Configs @@ -3397,6 +3507,33 @@ ii.gui.map_display.marker.missiles=Missiles ii.gui.map_display.marker.artillery=Artillery ii.gui.map_display.marker.bullets=Bullets +ii.gui.faction_invitation=Faction +ii.gui.faction_invitation.invalid=Could not get faction data. Please close and open this screen again. +ii.gui.faction_invitation.current=Current Status +ii.gui.faction_invitation.current.alone=%s is owner of their own domain. +#[Name] is [Rank] of [Faction], e.g. Pabilo8 is Owner of Pabulograd +ii.gui.faction_invitation.current.faction=%s is %s of %s +ii.gui.faction_invitation.invitations=Faction Invitations +ii.gui.faction_invitation.invitations.none=No pending invitations + +ii.gui.faction_management.insignia=Insignia +ii.gui.faction_management.insignia.tooltip=Modify the faction's name and color +ii.gui.faction_management.insignia.name=Faction Name +ii.gui.faction_management.insignia.name.tooltip=Name of the faction +ii.gui.faction_management.insignia.banner=Banner +ii.gui.faction_management.insignia.banner.tooltip=Click with a banner to change the faction's banner. +ii.gui.faction_management.members=Members +ii.gui.faction_management.members.tooltip=Manage the faction's members +ii.gui.faction_management.invitations=Invitations +ii.gui.faction_management.invitations.tooltip=Invite new members and nanage existing invitations +ii.gui.faction_management.invitations.username=Username +ii.gui.faction_management.invitations.username.tooltip=Player username to invite +ii.gui.faction_management.permissions=Permissions +ii.gui.faction_management.permissions.tooltip=Manage permissions for faction members +ii.gui.faction_management.permissions.role=Edited role: +ii.gui.faction_management.permissions.role.tooltip=Select role to view or edit permissions for +ii.gui.faction_management.permissions.empty=No roles available + ii.gui.redstone_data_interface.from.redstone=From ii.gui.redstone_data_interface.from.redstone.tooltip=Specifies what redstone signal color will get converted into a data variable ii.gui.redstone_data_interface.from.data=From variable @@ -3519,12 +3656,23 @@ ii.gui_tooltip.button.remove=Remove ii.gui_tooltip.button.edit=Edit ii.gui_tooltip.button.duplicate=Duplicate ii.gui_tooltip.button.clear=Clear +ii.gui_tooltip.button.accept=Accept +ii.gui_tooltip.button.reject=Reject +ii.gui_tooltip.button.help=Show Help +ii.gui_tooltip.button.factions=Factions #GUI Widgets ii.gui_tooltip.widget.style.show=Show Style Customization ii.gui_tooltip.widget.style.hide=Hide Style Customization ii.gui_tooltip.widget.style=Customization -ii.gui_tooltip.widget.style.desc=This multiblock allows customization of style and color. +ii.gui_tooltip.widget.style.main=This %s does not allow customization. +ii.gui_tooltip.widget.style.main.variants=This %s allows customization of style. +ii.gui_tooltip.widget.style.main.color=This %s allows customization of color. +ii.gui_tooltip.widget.style.main.variants.color=This %s allows customization of style and color. +ii.gui_tooltip.widget.style.main.tile=nachine +ii.gui_tooltip.widget.style.main.multiblock=multiblock +ii.gui_tooltip.widget.style.main.vehicle=vehicle +ii.gui_tooltip.widget.style.main.entity=entity ii.gui_tooltip.widget.style.style=Style ii.gui_tooltip.widget.style.color=Color ii.gui_tooltip.widget.ownership.show=Show Ownership Data diff --git a/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum.json b/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum.json new file mode 100644 index 000000000..d6447ab90 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum.json @@ -0,0 +1,7 @@ +{ + "parent": "immersiveintelligence:block/chain_fence/chain_fence", + "textures": { + "texture": "immersiveintelligence:blocks/fortification/wooden_aluminium_chain_fence", + "particle": "immersiveintelligence:blocks/fortification/wooden_aluminium_chain_fence" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum_base.json b/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum_base.json new file mode 100644 index 000000000..a95140ab0 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum_base.json @@ -0,0 +1,7 @@ +{ + "parent": "immersiveintelligence:block/chain_fence/chain_fence_base", + "textures": { + "texture": "immersiveintelligence:blocks/fortification/wooden_aluminium_chain_fence", + "particle": "immersiveintelligence:blocks/fortification/wooden_aluminium_chain_fence" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum_side.json b/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum_side.json new file mode 100644 index 000000000..a3a6dca6f --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/chain_fence/chain_fence_wooden_aluminum_side.json @@ -0,0 +1,7 @@ +{ + "parent": "immersiveintelligence:block/chain_fence/chain_fence_side", + "textures": { + "texture": "immersiveintelligence:blocks/fortification/wooden_aluminium_chain_fence", + "particle": "immersiveintelligence:blocks/fortification/wooden_aluminium_chain_fence" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_center.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_center.json new file mode 100644 index 000000000..50cb9506f --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_center.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "base", + "from": [0, 0, 0], + "to": [16, 8, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, 0] + }, + "faces": { + "north": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "south": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "west": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "up": { + "uv": [2, 2, 6, 6], + "texture": "#0" + }, + "down": { + "uv": [2, 2, 6, 6], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_e.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_e.json new file mode 100644 index 000000000..316d27788 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_e.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "e", + "from": [8, 0, 0], + "to": [16, 8, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [8, 4, -8] + }, + "faces": { + "north": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "south": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "west": { + "uv": [4, 8, 8, 10], + "texture": "#0" + }, + "up": { + "uv": [2, 2, 0, 6], + "texture": "#0" + }, + "down": { + "uv": [6, 2, 8, 6], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_n.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_n.json new file mode 100644 index 000000000..595199c92 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_n.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "n", + "from": [0, 0, 0], + "to": [16, 8, 8], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, -8] + }, + "faces": { + "north": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "south": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "west": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "up": { + "uv": [2, 8, 6, 6], + "texture": "#0" + }, + "down": { + "uv": [2, 6, 6, 8], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_ne.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_ne.json new file mode 100644 index 000000000..5db8586d4 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_ne.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "ne", + "from": [8, 0, 0], + "to": [16, 8, 8], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [8, 4, -8] + }, + "faces": { + "north": { + "uv": [0, 8, 2, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 0, 10], + "texture": "#0" + }, + "south": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "west": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "up": { + "uv": [2, 8, 0, 6], + "texture": "#0" + }, + "down": { + "uv": [6, 6, 8, 8], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_nw.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_nw.json new file mode 100644 index 000000000..9dca00b6a --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_nw.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "nw", + "from": [0, 0, 0], + "to": [8, 8, 8], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, -8] + }, + "faces": { + "north": { + "uv": [6, 8, 8, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "south": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "west": { + "uv": [8, 8, 6, 10], + "texture": "#0" + }, + "up": { + "uv": [0, 6, 2, 8], + "texture": "#0" + }, + "down": { + "uv": [0, 6, 2, 8], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_s.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_s.json new file mode 100644 index 000000000..efdc7a26b --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_s.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "s", + "from": [0, 0, 8], + "to": [16, 8, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, 0] + }, + "faces": { + "north": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "south": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "west": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "up": { + "uv": [2, 2, 6, 0], + "texture": "#0" + }, + "down": { + "uv": [2, 0, 6, 2], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_se.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_se.json new file mode 100644 index 000000000..7907a1dba --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_se.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "se", + "from": [8, 0, 8], + "to": [16, 8, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [8, 4, 0] + }, + "faces": { + "north": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "east": { + "uv": [8, 8, 6, 10], + "texture": "#0" + }, + "south": { + "uv": [6, 8, 8, 10], + "texture": "#0" + }, + "west": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "up": { + "uv": [2, 2, 0, 0], + "texture": "#0" + }, + "down": { + "uv": [6, 0, 8, 2], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_sw.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_sw.json new file mode 100644 index 000000000..096c56d2a --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_sw.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "sw", + "from": [0, 0, 8], + "to": [8, 8, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, 0] + }, + "faces": { + "north": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "south": { + "uv": [0, 8, 2, 10], + "texture": "#0" + }, + "west": { + "uv": [2, 8, 0, 10], + "texture": "#0" + }, + "up": { + "uv": [0, 2, 2, 0], + "texture": "#0" + }, + "down": { + "uv": [0, 0, 2, 2], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_w.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_w.json new file mode 100644 index 000000000..0e8f2a33f --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/concrete/support_w.json @@ -0,0 +1,47 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "w", + "from": [0, 0, 0], + "to": [8, 8, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, -8] + }, + "faces": { + "north": { + "uv": [4, 8, 6, 10], + "texture": "#0" + }, + "east": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "south": { + "uv": [2, 8, 4, 10], + "texture": "#0" + }, + "west": { + "uv": [2, 8, 6, 10], + "texture": "#0" + }, + "up": { + "uv": [0, 2, 2, 6], + "texture": "#0" + }, + "down": { + "uv": [0, 2, 2, 6], + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/floor.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/floor.json new file mode 100644 index 000000000..eaac48938 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/floor.json @@ -0,0 +1,54 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "1": "immersiveintelligence:blocks/harbor/flooring", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "floor", + "from": [0, 12, 0], + "to": [16, 16, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 12, 0] + }, + "faces": { + "north": { + "uv": [4, 12, 8, 13], + "texture": "#0", + "tintindex": 0 + }, + "east": { + "uv": [8, 12, 12, 13], + "texture": "#0", + "tintindex": 0 + }, + "south": { + "uv": [12, 12, 16, 13], + "texture": "#0", + "tintindex": 0 + }, + "west": { + "uv": [4, 12, 8, 13], + "texture": "#0", + "tintindex": 0 + }, + "up": { + "uv": [0, 0, 16, 16], + "texture": "#1", + "tintindex": 1 + }, + "down": { + "uv": [0, 0, 16, 16], + "texture": "#1", + "tintindex": 0 + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support.json new file mode 100644 index 000000000..bafad7e93 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support.json @@ -0,0 +1,53 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "post", + "from": [6, 0, 6], + "to": [10, 16, 10], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, 0] + }, + "faces": { + "north": { + "uv": [0, 14, 4, 13], + "rotation": 90, + "texture": "#0" + }, + "east": { + "uv": [0, 13, 4, 14], + "rotation": 90, + "texture": "#0" + }, + "south": { + "uv": [0, 14, 4, 13], + "rotation": 90, + "texture": "#0" + }, + "west": { + "uv": [0, 13, 4, 14], + "rotation": 90, + "texture": "#0" + }, + "up": { + "uv": [12, 13, 13, 14], + "rotation": 90, + "texture": "#0" + }, + "down": { + "uv": [12, 13, 13, 14], + "rotation": 90, + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support_bottom.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support_bottom.json new file mode 100644 index 000000000..a365987f5 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support_bottom.json @@ -0,0 +1,89 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "post_bottom", + "from": [5, 0, 5], + "to": [11, 8, 11], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, 0] + }, + "faces": { + "north": { + "uv": [0, 10, 1.5, 12], + "texture": "#0" + }, + "east": { + "uv": [0, 10, 1.5, 12], + "texture": "#0" + }, + "south": { + "uv": [0, 10, 1.5, 12], + "texture": "#0" + }, + "west": { + "uv": [0, 10, 1.5, 12], + "texture": "#0" + }, + "up": { + "uv": [1.5, 10, 3, 11.5], + "texture": "#0" + }, + "down": { + "uv": [3, 10, 4.5, 11.5], + "texture": "#0" + } + } + }, + { + "name": "post", + "from": [6, 0, 6], + "to": [10, 16, 10], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 4, 0] + }, + "faces": { + "north": { + "uv": [0, 14, 4, 13], + "rotation": 90, + "texture": "#0" + }, + "east": { + "uv": [0, 13, 4, 14], + "rotation": 90, + "texture": "#0" + }, + "south": { + "uv": [0, 14, 4, 13], + "rotation": 90, + "texture": "#0" + }, + "west": { + "uv": [0, 13, 4, 14], + "rotation": 90, + "texture": "#0" + }, + "up": { + "uv": [12, 13, 13, 14], + "rotation": 90, + "texture": "#0" + }, + "down": { + "uv": [12, 13, 13, 14], + "rotation": 90, + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support_top.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support_top.json new file mode 100644 index 000000000..93a479831 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/pier_support_top.json @@ -0,0 +1,48 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "post_top", + "from": [6, 16, 6], + "to": [10, 24, 10], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 20, 0] + }, + "faces": { + "north": { + "uv": [0, 14, 2, 13], + "rotation": 90, + "texture": "#0" + }, + "east": { + "uv": [0, 13, 2, 14], + "rotation": 90, + "texture": "#0" + }, + "south": { + "uv": [0, 14, 2, 13], + "rotation": 90, + "texture": "#0" + }, + "west": { + "uv": [0, 13, 2, 14], + "rotation": 90, + "texture": "#0" + }, + "up": { + "uv": [12, 13, 13, 14], + "rotation": 90, + "texture": "#0" + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/support_connected.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/support_connected.json new file mode 100644 index 000000000..cd8f43d76 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/support_connected.json @@ -0,0 +1,86 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "support", + "from": [0, 8, 6], + "to": [16, 12, 10], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [8, 10, 4] + }, + "faces": { + "north": { + "uv": [0, 15, 4, 16], + "texture": "#0" + }, + "east": { + "uv": [12, 16, 13, 15], + "texture": "#0" + }, + "south": { + "uv": [0, 16, 4, 15], + "texture": "#0" + }, + "west": { + "uv": [12, 15, 13, 16], + "texture": "#0" + }, + "down": { + "uv": [0, 16, 4, 15], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "name": "support", + "from": [6, 8, 0], + "to": [10, 12, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [0, 8, 0] + }, + "faces": { + "north": { + "uv": [12, 13, 13, 14], + "texture": "#0" + }, + "east": { + "uv": [0, 13, 4, 14], + "texture": "#0" + }, + "south": { + "uv": [12, 14, 13, 13], + "texture": "#0" + }, + "west": { + "uv": [0, 14, 4, 13], + "texture": "#0" + }, + "down": { + "uv": [0, 14, 4, 13], + "rotation": 90, + "texture": "#0" + } + } + } + ], + "groups": [ + { + "name": "support", + "origin": [0, 8, 4], + "scope": 0, + "color": 0, + "children": [0, 1] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/support_single.json b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/support_single.json new file mode 100644 index 000000000..2c5faffc8 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden/support_single.json @@ -0,0 +1,152 @@ +{ + "format_version": "1.9.0", + "credit": "Made by Pabilo8 with Blockbench", + "texture_size": [64, 64], + "textures": { + "0": "immersiveintelligence:blocks/common/common_wooden_pier", + "particle": "immersiveintelligence:blocks/common/common_wooden_pier" + }, + "elements": [ + { + "name": "support", + "from": [10, 8, 0], + "to": [14, 12, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [4, 8, 0] + }, + "faces": { + "north": { + "uv": [12, 13, 13, 14], + "texture": "#0" + }, + "east": { + "uv": [0, 13, 4, 14], + "texture": "#0" + }, + "south": { + "uv": [12, 14, 13, 13], + "texture": "#0" + }, + "west": { + "uv": [0, 14, 4, 13], + "texture": "#0" + }, + "down": { + "uv": [0, 14, 4, 13], + "rotation": 90, + "texture": "#0" + } + } + }, + { + "name": "support", + "from": [2, 8, 0], + "to": [6, 12, 16], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [-4, 8, 0] + }, + "faces": { + "north": { + "uv": [12, 14, 13, 15], + "texture": "#0" + }, + "east": { + "uv": [0, 14, 4, 15], + "texture": "#0" + }, + "south": { + "uv": [12, 15, 13, 14], + "texture": "#0" + }, + "west": { + "uv": [0, 15, 4, 14], + "texture": "#0" + }, + "down": { + "uv": [0, 15, 4, 14], + "rotation": 90, + "texture": "#0" + } + } + }, + { + "name": "support", + "from": [0, 8, 10], + "to": [16, 12, 14], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [8, 10, 8] + }, + "faces": { + "north": { + "uv": [0, 15, 4, 16], + "texture": "#0" + }, + "east": { + "uv": [12, 16, 13, 15], + "texture": "#0" + }, + "south": { + "uv": [0, 16, 4, 15], + "texture": "#0" + }, + "west": { + "uv": [12, 15, 13, 16], + "texture": "#0" + }, + "down": { + "uv": [0, 16, 4, 15], + "rotation": 180, + "texture": "#0" + } + } + }, + { + "name": "support", + "from": [0, 8, 2], + "to": [16, 12, 6], + "rotation": { + "angle": 0, + "axis": "y", + "origin": [8, 10, 8] + }, + "faces": { + "north": { + "uv": [0, 13, 4, 14], + "texture": "#0" + }, + "east": { + "uv": [12, 14, 13, 13], + "texture": "#0" + }, + "south": { + "uv": [0, 14, 4, 13], + "texture": "#0" + }, + "west": { + "uv": [12, 13, 13, 14], + "texture": "#0" + }, + "down": { + "uv": [0, 14, 4, 13], + "rotation": 180, + "texture": "#0" + } + } + } + ], + "groups": [ + { + "name": "support_single", + "origin": [0, 8, 4], + "scope": 0, + "color": 0, + "children": [0, 1, 2, 3] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_inv.mtl b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_inv.mtl new file mode 100644 index 000000000..dce264ee0 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_inv.mtl @@ -0,0 +1,6 @@ +# Made by Pabilo8 with Blockbench + +newmtl common_wooden_pier +map_Kd immersiveintelligence:blocks/common/common_wooden_pier +newmtl flooring +map_Kd immersiveintelligence:blocks/harbor/absolutely_unconnected/flooring \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_inv.obj b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_inv.obj new file mode 100644 index 000000000..4af7fbe6f --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_inv.obj @@ -0,0 +1,258 @@ +# Made by Pabilo8 with Blockbench +# Exported with IIToolkit Plugin on 21/07/2026 +mtllib wooden_pier_inv.mtl + +o pier +v 1.125 0.5 0.625 +v -0.125 0.5 0.625 +v 1.125 0.25 0.625 +v -0.125 0.25 0.625 +v 1.125 0.5 0.875 +v -0.125 0.5 0.875 +v 1.125 0.25 0.875 +v -0.125 0.25 0.875 +v 1.125 0.5 0.125 +v -0.125 0.5 0.125 +v 1.125 0.25 0.125 +v -0.125 0.25 0.125 +v 1.125 0.5 0.375 +v -0.125 0.5 0.375 +v 1.125 0.25 0.375 +v -0.125 0.25 0.375 +v 0.375 0.4875 1.125 +v 0.375 0.4875 -0.125 +v 0.375 0.2375 1.125 +v 0.375 0.2375 -0.125 +v 0.125 0.4875 1.125 +v 0.125 0.4875 -0.125 +v 0.125 0.2375 1.125 +v 0.125 0.2375 -0.125 +v 0.875 0.4875 1.125 +v 0.875 0.4875 -0.125 +v 0.875 0.2375 1.125 +v 0.875 0.2375 -0.125 +v 0.625 0.4875 1.125 +v 0.625 0.4875 -0.125 +v 0.625 0.2375 1.125 +v 0.625 0.2375 -0.125 +v 1 0.75 1 +v 1 0.75 0 +v 1 0.5 1 +v 1 0.5 0 +v 0 0.75 1 +v 0 0.75 0 +v 0 0.5 1 +v 0 0.5 0 +vt 0.3125 0.0625 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.125 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.3125 0 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0.0625 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 0.8125 0 +vt 0.8125 0 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 0.3125 0.0625 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.125 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.3125 0 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0.0625 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 0.8125 0 +vt 0.8125 0 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 1 0.1875 +vt 1 0.25 +vt 0.75 0.25 +vt 0.75 0.1875 +vt 0.5 0.1875 +vt 0.5 0.25 +vt 0.25 0.25 +vt 0.25 0.1875 +vt 1 0 +vt 1 1 +vt 0 1 +vt 0 0 +vt 1 0 +vt 1 1 +vt 0 1 +vt 0 0 +vt 0.75 0.1875 +vt 0.75 0.25 +vt 0.5 0.25 +vt 0.5 0.1875 +vt 0.5 0.1875 +vt 0.5 0.25 +vt 0.25 0.25 +vt 0.25 0.1875 +vn 0 0 -1 +vn 0 0 1 +vn 0 1 0 +vn 0 -1 0 +vn 1 0 0 +vn -1 0 0 +vn 0 0 -1 +vn 0 0 1 +vn 0 1 0 +vn 0 -1 0 +vn 1 0 0 +vn -1 0 0 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +usemtl common_wooden_pier +f 4/1/1 2/2/1 1/3/1 +f 4/1/1 1/3/1 3/4/1 +f 7/5/2 5/6/2 6/7/2 +f 7/5/2 6/7/2 8/8/2 +f 1/9/3 2/10/3 6/11/3 +f 1/9/3 6/11/3 5/12/3 +f 4/13/4 3/14/4 7/15/4 +f 4/13/4 7/15/4 8/16/4 +f 3/17/5 1/18/5 5/19/5 +f 3/17/5 5/19/5 7/20/5 +f 8/21/6 6/22/6 2/23/6 +f 8/21/6 2/23/6 4/24/6 +f 12/25/7 10/26/7 9/27/7 +f 12/25/7 9/27/7 11/28/7 +f 15/29/8 13/30/8 14/31/8 +f 15/29/8 14/31/8 16/32/8 +f 9/33/9 10/34/9 14/35/9 +f 9/33/9 14/35/9 13/36/9 +f 12/37/10 11/38/10 15/39/10 +f 12/37/10 15/39/10 16/40/10 +f 11/41/11 9/42/11 13/43/11 +f 11/41/11 13/43/11 15/44/11 +f 16/45/12 14/46/12 10/47/12 +f 16/45/12 10/47/12 12/48/12 +f 20/49/13 18/50/13 17/51/13 +f 20/49/13 17/51/13 19/52/13 +f 23/53/14 21/54/14 22/55/14 +f 23/53/14 22/55/14 24/56/14 +f 17/57/15 18/58/15 22/59/15 +f 17/57/15 22/59/15 21/60/15 +f 20/61/16 19/62/16 23/63/16 +f 20/61/16 23/63/16 24/64/16 +f 19/65/17 17/66/17 21/67/17 +f 19/65/17 21/67/17 23/68/17 +f 24/69/18 22/70/18 18/71/18 +f 24/69/18 18/71/18 20/72/18 +f 28/73/19 26/74/19 25/75/19 +f 28/73/19 25/75/19 27/76/19 +f 31/77/20 29/78/20 30/79/20 +f 31/77/20 30/79/20 32/80/20 +f 25/81/21 26/82/21 30/83/21 +f 25/81/21 30/83/21 29/84/21 +f 28/85/22 27/86/22 31/87/22 +f 28/85/22 31/87/22 32/88/22 +f 27/89/23 25/90/23 29/91/23 +f 27/89/23 29/91/23 31/92/23 +f 32/93/24 30/94/24 26/95/24 +f 32/93/24 26/95/24 28/96/24 +f 36/97/25 34/98/25 33/99/25 +f 36/97/25 33/99/25 35/100/25 +f 39/101/26 37/102/26 38/103/26 +f 39/101/26 38/103/26 40/104/26 +usemtl flooring +f 33/105/27 34/106/27 38/107/27 +f 33/105/27 38/107/27 37/108/27 +f 36/109/28 35/110/28 39/111/28 +f 36/109/28 39/111/28 40/112/28 +usemtl common_wooden_pier +f 35/113/29 33/114/29 37/115/29 +f 35/113/29 37/115/29 39/116/29 +f 40/117/30 38/118/30 34/119/30 +f 40/117/30 34/119/30 36/120/30 \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_support_inv.mtl b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_support_inv.mtl new file mode 100644 index 000000000..dce264ee0 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_support_inv.mtl @@ -0,0 +1,6 @@ +# Made by Pabilo8 with Blockbench + +newmtl common_wooden_pier +map_Kd immersiveintelligence:blocks/common/common_wooden_pier +newmtl flooring +map_Kd immersiveintelligence:blocks/harbor/absolutely_unconnected/flooring \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_support_inv.obj b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_support_inv.obj new file mode 100644 index 000000000..6187fc328 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_pier_support_inv.obj @@ -0,0 +1,99 @@ +# Made by Pabilo8 with Blockbench +# Exported with IIToolkit Plugin on 21/07/2026 +mtllib wooden_pier_support_inv.mtl + +o pier_support +v 0.6875 0.5 0.6875 +v 0.6875 0.5 0.3125 +v 0.6875 0 0.6875 +v 0.6875 0 0.3125 +v 0.3125 0.5 0.6875 +v 0.3125 0.5 0.3125 +v 0.3125 0 0.6875 +v 0.3125 0 0.3125 +v 0.625 1 0.625 +v 0.625 1 0.375 +v 0.625 0.5 0.625 +v 0.625 0.5 0.375 +v 0.375 1 0.625 +v 0.375 1 0.375 +v 0.375 0.5 0.625 +v 0.375 0.5 0.375 +vt 0.0938 0.25 +vt 0.0938 0.375 +vt 0 0.375 +vt 0 0.25 +vt 0.0938 0.25 +vt 0.0938 0.375 +vt 0 0.375 +vt 0 0.25 +vt 0.1875 0.2813 +vt 0.1875 0.375 +vt 0.0938 0.375 +vt 0.0938 0.2813 +vt 0.2813 0.2813 +vt 0.2813 0.375 +vt 0.1875 0.375 +vt 0.1875 0.2813 +vt 0.0938 0.25 +vt 0.0938 0.375 +vt 0 0.375 +vt 0 0.25 +vt 0.0938 0.25 +vt 0.0938 0.375 +vt 0 0.375 +vt 0 0.25 +vt 0.125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.125 0 +vt 0.125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.125 0 +vt 0.75 0 +vt 0.8125 0 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.125 0 +vt 0 0 +vt 0 0.0625 +vt 0.125 0.0625 +vt 0.125 0 +vt 0 0 +vt 0 0.0625 +vt 0.125 0.0625 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 0 1 +vn 0 0 -1 +usemtl common_wooden_pier +f 4/1/1 2/2/1 1/3/1 +f 4/1/1 1/3/1 3/4/1 +f 7/5/2 5/6/2 6/7/2 +f 7/5/2 6/7/2 8/8/2 +f 1/9/3 2/10/3 6/11/3 +f 1/9/3 6/11/3 5/12/3 +f 4/13/4 3/14/4 7/15/4 +f 4/13/4 7/15/4 8/16/4 +f 3/17/5 1/18/5 5/19/5 +f 3/17/5 5/19/5 7/20/5 +f 8/21/6 6/22/6 2/23/6 +f 8/21/6 2/23/6 4/24/6 +f 12/25/7 10/26/7 9/27/7 +f 12/25/7 9/27/7 11/28/7 +f 15/29/8 13/30/8 14/31/8 +f 15/29/8 14/31/8 16/32/8 +f 9/33/9 10/34/9 14/35/9 +f 9/33/9 14/35/9 13/36/9 +f 11/37/10 9/38/10 13/39/10 +f 11/37/10 13/39/10 15/40/10 +f 16/41/11 14/42/11 10/43/11 +f 16/41/11 10/43/11 12/44/11 \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_quay_inv.mtl b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_quay_inv.mtl new file mode 100644 index 000000000..dce264ee0 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_quay_inv.mtl @@ -0,0 +1,6 @@ +# Made by Pabilo8 with Blockbench + +newmtl common_wooden_pier +map_Kd immersiveintelligence:blocks/common/common_wooden_pier +newmtl flooring +map_Kd immersiveintelligence:blocks/harbor/absolutely_unconnected/flooring \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_quay_inv.obj b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_quay_inv.obj new file mode 100644 index 000000000..d4a017b76 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/models/block/harbor/wooden_quay_inv.obj @@ -0,0 +1,308 @@ +# Made by Pabilo8 with Blockbench +# Exported with IIToolkit Plugin on 21/07/2026 +mtllib wooden_quay_inv.mtl + +o quay +v 1.125 0.75 0.625 +v -0.125 0.75 0.625 +v 1.125 0.5 0.625 +v -0.125 0.5 0.625 +v 1.125 0.75 0.875 +v -0.125 0.75 0.875 +v 1.125 0.5 0.875 +v -0.125 0.5 0.875 +v 1.125 0.75 0.125 +v -0.125 0.75 0.125 +v 1.125 0.5 0.125 +v -0.125 0.5 0.125 +v 1.125 0.75 0.375 +v -0.125 0.75 0.375 +v 1.125 0.5 0.375 +v -0.125 0.5 0.375 +v 0.375 0.7375 1.125 +v 0.375 0.7375 -0.125 +v 0.375 0.4875 1.125 +v 0.375 0.4875 -0.125 +v 0.125 0.7375 1.125 +v 0.125 0.7375 -0.125 +v 0.125 0.4875 1.125 +v 0.125 0.4875 -0.125 +v 0.875 0.7375 1.125 +v 0.875 0.7375 -0.125 +v 0.875 0.4875 1.125 +v 0.875 0.4875 -0.125 +v 0.625 0.7375 1.125 +v 0.625 0.7375 -0.125 +v 0.625 0.4875 1.125 +v 0.625 0.4875 -0.125 +v 1 1 1 +v 1 1 0 +v 1 0.75 1 +v 1 0.75 0 +v 0 1 1 +v 0 1 0 +v 0 0.75 1 +v 0 0.75 0 +v 1 0.5 1 +v 1 0.5 0 +v 1 0 1 +v 1 0 0 +v 0 0.5 1 +v 0 0.5 0 +v 0 0 1 +v 0 0 0 +vt 0.3125 0.0625 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.125 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.3125 0 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0.0625 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 0.8125 0 +vt 0.8125 0 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 0.3125 0.0625 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.125 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0 0.125 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0.125 +vt 0.3125 0.125 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.0625 +vt 0.8125 0.125 +vt 0.75 0.125 +vt 0.75 0.0625 +vt 0.3125 0 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0.0625 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0 0.0625 +vt 0 0 +vt 0.3125 0 +vt 0.3125 0 +vt 0 0 +vt 0 0.0625 +vt 0.3125 0.0625 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 0.8125 0 +vt 0.8125 0 +vt 0.8125 0.0625 +vt 0.75 0.0625 +vt 0.75 0 +vt 1 0.1875 +vt 1 0.25 +vt 0.75 0.25 +vt 0.75 0.1875 +vt 0.5 0.1875 +vt 0.5 0.25 +vt 0.25 0.25 +vt 0.25 0.1875 +vt 1 0 +vt 1 1 +vt 0 1 +vt 0 0 +vt 1 0 +vt 1 1 +vt 0 1 +vt 0 0 +vt 0.75 0.1875 +vt 0.75 0.25 +vt 0.5 0.25 +vt 0.5 0.1875 +vt 0.5 0.1875 +vt 0.5 0.25 +vt 0.25 0.25 +vt 0.25 0.1875 +vt 0.25 0.375 +vt 0.25 0.5 +vt 0 0.5 +vt 0 0.375 +vt 0.25 0.375 +vt 0.25 0.5 +vt 0.5 0.5 +vt 0.5 0.375 +vt 0.375 0.625 +vt 0.375 0.875 +vt 0.125 0.875 +vt 0.125 0.625 +vt 0.375 0.625 +vt 0.375 0.875 +vt 0.125 0.875 +vt 0.125 0.625 +vt 0 0.375 +vt 0 0.5 +vt 0.25 0.5 +vt 0.25 0.375 +vt 0.5 0.375 +vt 0.5 0.5 +vt 0.25 0.5 +vt 0.25 0.375 +vn 0 0 -1 +vn 0 0 1 +vn 0 1 0 +vn 0 -1 0 +vn 1 0 0 +vn -1 0 0 +vn 0 0 -1 +vn 0 0 1 +vn 0 1 0 +vn 0 -1 0 +vn 1 0 0 +vn -1 0 0 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +vn 1 0 0 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +vn 0 0 1 +vn 0 0 -1 +usemtl common_wooden_pier +f 4/1/1 2/2/1 1/3/1 +f 4/1/1 1/3/1 3/4/1 +f 7/5/2 5/6/2 6/7/2 +f 7/5/2 6/7/2 8/8/2 +f 1/9/3 2/10/3 6/11/3 +f 1/9/3 6/11/3 5/12/3 +f 4/13/4 3/14/4 7/15/4 +f 4/13/4 7/15/4 8/16/4 +f 3/17/5 1/18/5 5/19/5 +f 3/17/5 5/19/5 7/20/5 +f 8/21/6 6/22/6 2/23/6 +f 8/21/6 2/23/6 4/24/6 +f 12/25/7 10/26/7 9/27/7 +f 12/25/7 9/27/7 11/28/7 +f 15/29/8 13/30/8 14/31/8 +f 15/29/8 14/31/8 16/32/8 +f 9/33/9 10/34/9 14/35/9 +f 9/33/9 14/35/9 13/36/9 +f 12/37/10 11/38/10 15/39/10 +f 12/37/10 15/39/10 16/40/10 +f 11/41/11 9/42/11 13/43/11 +f 11/41/11 13/43/11 15/44/11 +f 16/45/12 14/46/12 10/47/12 +f 16/45/12 10/47/12 12/48/12 +f 20/49/13 18/50/13 17/51/13 +f 20/49/13 17/51/13 19/52/13 +f 23/53/14 21/54/14 22/55/14 +f 23/53/14 22/55/14 24/56/14 +f 17/57/15 18/58/15 22/59/15 +f 17/57/15 22/59/15 21/60/15 +f 20/61/16 19/62/16 23/63/16 +f 20/61/16 23/63/16 24/64/16 +f 19/65/17 17/66/17 21/67/17 +f 19/65/17 21/67/17 23/68/17 +f 24/69/18 22/70/18 18/71/18 +f 24/69/18 18/71/18 20/72/18 +f 28/73/19 26/74/19 25/75/19 +f 28/73/19 25/75/19 27/76/19 +f 31/77/20 29/78/20 30/79/20 +f 31/77/20 30/79/20 32/80/20 +f 25/81/21 26/82/21 30/83/21 +f 25/81/21 30/83/21 29/84/21 +f 28/85/22 27/86/22 31/87/22 +f 28/85/22 31/87/22 32/88/22 +f 27/89/23 25/90/23 29/91/23 +f 27/89/23 29/91/23 31/92/23 +f 32/93/24 30/94/24 26/95/24 +f 32/93/24 26/95/24 28/96/24 +f 36/97/25 34/98/25 33/99/25 +f 36/97/25 33/99/25 35/100/25 +f 39/101/26 37/102/26 38/103/26 +f 39/101/26 38/103/26 40/104/26 +usemtl flooring +f 33/105/27 34/106/27 38/107/27 +f 33/105/27 38/107/27 37/108/27 +f 36/109/28 35/110/28 39/111/28 +f 36/109/28 39/111/28 40/112/28 +usemtl common_wooden_pier +f 35/113/29 33/114/29 37/115/29 +f 35/113/29 37/115/29 39/116/29 +f 40/117/30 38/118/30 34/119/30 +f 40/117/30 34/119/30 36/120/30 +f 44/121/31 42/122/31 41/123/31 +f 44/121/31 41/123/31 43/124/31 +f 47/125/32 45/126/32 46/127/32 +f 47/125/32 46/127/32 48/128/32 +f 41/129/33 42/130/33 46/131/33 +f 41/129/33 46/131/33 45/132/33 +f 44/133/34 43/134/34 47/135/34 +f 44/133/34 47/135/34 48/136/34 +f 43/137/35 41/138/35 45/139/35 +f 43/137/35 45/139/35 47/140/35 +f 48/141/36 46/142/36 42/143/36 +f 48/141/36 42/143/36 44/144/36 \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/particles/explosion/main.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/explosion/main.fx.amt index 0999b84c5..39bea12f4 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/explosion/main.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/explosion/main.fx.amt @@ -3,6 +3,7 @@ "models": ["smoke/smoke_trace.obj"], "max_lifetime": 6, "draw_stage": "custom_smoke_noise_shader", + "color": "5cffffff", "programs": [ "smoke_transition" ], diff --git a/src/main/resources/assets/immersiveintelligence/particles/phosphorus/orb.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/phosphorus/orb.fx.amt index c46136e14..18495413e 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/phosphorus/orb.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/phosphorus/orb.fx.amt @@ -3,6 +3,7 @@ "models": ["smoke/smoke_trace.obj"], "max_lifetime": 25, "draw_stage": "custom_smoke_noise_shader", + "color": "5cffffff", "programs": [ "smoke_transition" ], diff --git a/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_main.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_main.fx.amt index 9681e2c26..558c9a77a 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_main.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_main.fx.amt @@ -4,7 +4,7 @@ "max_lifetime": 120, "draw_stage": "custom_smoke_noise_shader", "aabb": 1.0, - "color": "ffffff", + "color": "5cffffff", "programs": [ "smoke_transition" ], diff --git a/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_trace.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_trace.fx.amt index c6618ce41..219a1afd6 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_trace.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/phosphorus/smoke_trace.fx.amt @@ -4,7 +4,7 @@ "max_lifetime": 80, "draw_stage": "custom_smoke_noise_shader", "aabb": 0.3, - "color": "ffffff", + "color": "5cffffff", "programs": [ "smoke_transition" ], diff --git a/src/main/resources/assets/immersiveintelligence/particles/smoke/gas_cloud.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/smoke/gas_cloud.fx.amt index c9cbdbf59..9f96288ca 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/smoke/gas_cloud.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/smoke/gas_cloud.fx.amt @@ -3,8 +3,8 @@ "textures": ["immersiveintelligence:particle/gas"], "max_lifetime": 55, "draw_stage": "custom_smoke_noise_shader", + "color": "5c474747", "aabb": 0.3, - "color": "474747", "programs": [ "smoke_transition", "gravity(1.0,-0.002)" diff --git a/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud.fx.amt index 90d1922d3..e0415ff91 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud.fx.amt @@ -4,7 +4,7 @@ "max_lifetime": 20, "draw_stage": "custom_smoke_noise_shader", "aabb": 0.3, - "color": "474747", + "color": "5c474747", "programs": [ "smoke_transition", "gravity(1.0,-0.005)" diff --git a/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud_nofollow.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud_nofollow.fx.amt index 481bcc2ef..498b212ed 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud_nofollow.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/smoke/smoke_cloud_nofollow.fx.amt @@ -4,7 +4,7 @@ "max_lifetime": 20, "draw_stage": "custom_smoke_noise_shader", "aabb": 0.3, - "color": "474747", + "color": "5c474747", "programs": [ "smoke_transition", "gravity(1.0,-0.005)" diff --git a/src/main/resources/assets/immersiveintelligence/particles/vehicle/exhaust_light.fx.amt b/src/main/resources/assets/immersiveintelligence/particles/vehicle/exhaust_light.fx.amt index 059e98eec..057af0394 100644 --- a/src/main/resources/assets/immersiveintelligence/particles/vehicle/exhaust_light.fx.amt +++ b/src/main/resources/assets/immersiveintelligence/particles/vehicle/exhaust_light.fx.amt @@ -3,6 +3,7 @@ "textures": ["immersiveintelligence:particle/smoke/smoke"], "max_lifetime": 35, "draw_stage": "custom_smoke_noise_shader", + "color": "5cffffff", "aabb": 0.3, "color": "2a2a2a", "programs": [ diff --git a/src/main/resources/assets/immersiveintelligence/recipes/decoration/fence/aluminum_chain_wood.json b/src/main/resources/assets/immersiveintelligence/recipes/decoration/fence/aluminum_chain_wood.json new file mode 100644 index 000000000..ca6420a87 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/recipes/decoration/fence/aluminum_chain_wood.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "rcr", + "rcr" + ], + "key": { + "c": { + "type": "forge:ore_dict", + "ore": "wireAluminum" + }, + "r": { + "type": "forge:ore_dict", + "ore": "plankTreatedWood" + } + }, + "result": { + "item": "immersiveintelligence:wooden_fortification", + "data": 2, + "count": 3 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/shaders/noise.frag b/src/main/resources/assets/immersiveintelligence/shaders/noise.frag index 6f761a0d2..5d9e0c65b 100644 --- a/src/main/resources/assets/immersiveintelligence/shaders/noise.frag +++ b/src/main/resources/assets/immersiveintelligence/shaders/noise.frag @@ -8,7 +8,7 @@ uniform sampler2D lightmap; float noise(in vec2 coordinate, in float seed) { - vec2 coordActual = floor(textureSize(bgl_RenderedTexture, 0) * coordinate); + vec2 coordActual = floor(vec2(textureSize(texture, 0)) * coordinate); return fract(sin(dot(coordActual*seed, vec2(12.9898, 78.233)))*43758.5453); } diff --git a/src/main/resources/assets/immersiveintelligence/shaders/noise_no_lightmap.frag b/src/main/resources/assets/immersiveintelligence/shaders/noise_no_lightmap.frag new file mode 100644 index 000000000..5bfca3a0d --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/shaders/noise_no_lightmap.frag @@ -0,0 +1,20 @@ +#version 130 + +//Author: Pabilo8 (pabilo@iiteam.net) +uniform float time; + +uniform sampler2D texture; +uniform sampler2D lightmap; + +float noise(in vec2 coordinate, in float seed) +{ + vec2 coordActual = floor(vec2(textureSize(texture, 0)) * coordinate); + return fract(sin(dot(coordActual * seed, vec2(12.9898, 78.233))) * 43758.5453); +} + +void main() +{ + vec4 tex = texture2D(texture, gl_TexCoord[0].st); + float n = (noise(vec2(gl_TexCoord[0]), time) - 0.5) * 0.25; + gl_FragColor = tex * gl_Color * vec4(1 - n, 1 - n, 1 - n, 1); +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/panzerbeton.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/panzerbeton.png index 7424c8913..bfd727355 100644 Binary files a/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/panzerbeton.png and b/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/panzerbeton.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton.png index 075556bed..b2053ee7a 100644 Binary files a/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton.png and b/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton_grau.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton_grau.png index 9d1e3fc77..3dd2f2ad3 100644 Binary files a/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton_grau.png and b/src/main/resources/assets/immersiveintelligence/textures/blocks/concrete/volksbeton_grau.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/fortification/wooden_aluminium_chain_fence.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/fortification/wooden_aluminium_chain_fence.png new file mode 100644 index 000000000..fdaaa7f64 Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/blocks/fortification/wooden_aluminium_chain_fence.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/absolutely_unconnected/flooring.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/absolutely_unconnected/flooring.png new file mode 100644 index 000000000..cc1bbd233 Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/absolutely_unconnected/flooring.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring.png new file mode 100644 index 000000000..4528fabae Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring.png.mcmeta b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring.png.mcmeta new file mode 100644 index 000000000..c5426e761 --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring.png.mcmeta @@ -0,0 +1,6 @@ +{ + "ctm": { + "ctm_version": 1, + "proxy": "immersiveintelligence:blocks/harbor/flooring_obscured" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_ctm.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_ctm.png new file mode 100644 index 000000000..71af3f420 Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_ctm.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_obscured.png b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_obscured.png new file mode 100644 index 000000000..e3e1f7409 Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_obscured.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_obscured.png.mcmeta b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_obscured.png.mcmeta new file mode 100644 index 000000000..75a52ef1b --- /dev/null +++ b/src/main/resources/assets/immersiveintelligence/textures/blocks/harbor/flooring_obscured.png.mcmeta @@ -0,0 +1,17 @@ +{ + "ctm": { + "ctm_version": 1, + "type": "ctm", + "layer": "CUTOUT_MIPPED", + "textures": [ + "immersiveintelligence:blocks/harbor/flooring_ctm" + ], + "extra": { + "connect_to": [ + { + "block": "immersiveintelligence:harbor" + } + ] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/background/vanilla.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/background/vanilla.png new file mode 100644 index 000000000..580b3990b Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/background/vanilla.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/component/slider_vanilla.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/component/slider_vanilla.png new file mode 100644 index 000000000..902f96b6c Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/component/slider_vanilla.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_accept.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_accept.png new file mode 100644 index 000000000..4dbe7b80b Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_accept.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_help.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_help.png new file mode 100644 index 000000000..63602d213 Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_help.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_reject.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_reject.png new file mode 100644 index 000000000..8dc65e14c Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/action_reject.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/icon_faction_invites.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/icon_faction_invites.png new file mode 100644 index 000000000..1249519e5 Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/icon_faction_invites.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/icon_faction_invites_active.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/icon_faction_invites_active.png new file mode 100644 index 000000000..ec6e1b03b Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/icons/icon_faction_invites_active.png differ diff --git a/src/main/resources/assets/immersiveintelligence/textures/gui/deco/label/label_vanilla.png b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/label/label_vanilla.png new file mode 100644 index 000000000..c4d63cf56 Binary files /dev/null and b/src/main/resources/assets/immersiveintelligence/textures/gui/deco/label/label_vanilla.png differ diff --git a/src/main/resources/immersiveintelligence_at.cfg b/src/main/resources/immersiveintelligence_at.cfg index bb9cbf36f..14149caa7 100644 --- a/src/main/resources/immersiveintelligence_at.cfg +++ b/src/main/resources/immersiveintelligence_at.cfg @@ -10,9 +10,7 @@ public net.minecraft.client.gui.inventory.GuiContainer field_147003_i #guiLeft public net.minecraft.client.gui.inventory.GuiContainer field_147009_r #guiTop public net.minecraft.block.Block field_149782_v #blockHardness public net.minecraft.client.gui.inventory.GuiContainerCreative func_147050_b(Lnet/minecraft/creativetab/CreativeTabs;)V #setCurrentCreativeTab -public net.minecraft.world.Explosion field_77280_f #size -public net.minecraft.world.Explosion field_77286_a #causesFire -public net.minecraft.world.Explosion field_82755_b #damagesTerrain +public net.minecraft.world.Explosion * #all explosion fields public net.minecraft.client.renderer.ItemRenderer field_187469_f #equippedProgressMainHand public net.minecraft.client.renderer.ItemRenderer field_187470_g #prevEquippedProgressMainHand public net.minecraft.client.renderer.ItemRenderer field_187471_h #equippedProgressOffHand