Handheld Worldshaper

- Fixed crash caused by suicidal deployers
- Fixed blockzapper rendering inconsistencies before a block is selected
- Added the Handheld Worldshaper with 3 brushes and 6 modes
- Entities on belts no longer get blocked by entities they are being ridden by
This commit is contained in:
simibubi 2020-03-05 19:52:10 +01:00
parent 5d2bbc930b
commit c3278c91c8
39 changed files with 2079 additions and 518 deletions

View file

@ -17,6 +17,7 @@ import com.simibubi.create.modules.curiosities.deforester.DeforesterItem;
import com.simibubi.create.modules.curiosities.symmetry.SymmetryWandItem;
import com.simibubi.create.modules.curiosities.tools.SandPaperItem;
import com.simibubi.create.modules.curiosities.zapper.blockzapper.BlockzapperItem;
import com.simibubi.create.modules.curiosities.zapper.terrainzapper.TerrainzapperItem;
import com.simibubi.create.modules.gardens.TreeFertilizerItem;
import com.simibubi.create.modules.logistics.item.filter.FilterItem;
import com.simibubi.create.modules.schematics.item.SchematicAndQuillItem;
@ -95,6 +96,7 @@ public enum AllItems {
__CURIOSITIES__(module()),
TREE_FERTILIZER(TreeFertilizerItem::new),
PLACEMENT_HANDGUN(BlockzapperItem::new),
TERRAIN_ZAPPER(TerrainzapperItem::new),
DEFORESTER(DeforesterItem::new),
SYMMETRY_WAND(SymmetryWandItem::new),

View file

@ -12,7 +12,7 @@ import com.simibubi.create.foundation.packet.SimplePacketBase;
import com.simibubi.create.foundation.utility.ServerSpeedProvider;
import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionStallPacket;
import com.simibubi.create.modules.curiosities.symmetry.SymmetryEffectPacket;
import com.simibubi.create.modules.curiosities.zapper.blockzapper.BlockzapperBeamPacket;
import com.simibubi.create.modules.curiosities.zapper.ZapperBeamPacket;
import com.simibubi.create.modules.logistics.item.filter.FilterScreenPacket;
import com.simibubi.create.modules.logistics.packet.ConfigureFlexcratePacket;
import com.simibubi.create.modules.logistics.packet.ConfigureStockswitchPacket;
@ -42,7 +42,7 @@ public enum AllPackets {
// Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new),
SERVER_SPEED(ServerSpeedProvider.Packet.class, ServerSpeedProvider.Packet::new),
BEAM_EFFECT(BlockzapperBeamPacket.class, BlockzapperBeamPacket::new),
BEAM_EFFECT(ZapperBeamPacket.class, ZapperBeamPacket::new),
CONFIGURE_CONFIG(ConfigureConfigPacket.class, ConfigureConfigPacket::new),
CONTRAPTION_STALL(ContraptionStallPacket.class, ContraptionStallPacket::new),

View file

@ -14,6 +14,7 @@ import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.modules.contraptions.components.contraptions.ChassisRangeDisplay;
import com.simibubi.create.modules.contraptions.components.turntable.TurntableHandler;
import com.simibubi.create.modules.contraptions.relays.belt.BeltConnectorItemHandler;
import com.simibubi.create.modules.curiosities.zapper.terrainzapper.TerrainZapperRenderHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
@ -60,6 +61,7 @@ public class ClientEvents {
public static void onGameTick() {
CreateClient.gameTick();
BeltConnectorItemHandler.gameTick();
TerrainZapperRenderHandler.tick();
}
@SubscribeEvent
@ -69,6 +71,7 @@ public class ClientEvents {
CreateClient.schematicHologram.render();
KineticDebugger.renderSourceOutline();
ChassisRangeDisplay.renderOutlines(event.getPartialTicks());
TerrainZapperRenderHandler.render();
}
@SubscribeEvent

View file

@ -10,7 +10,9 @@ public enum ScreenResources {
// Inventories
PLAYER_INVENTORY("player_inventory.png", 176, 108),
WAND_SYMMETRY("wand_symmetry.png", 207, 58),
PLACEMENT_GUN("placement_handgun.png", 217, 70),
BLOCKZAPPER("zapper.png", 217, 70),
TERRAINZAPPER("zapper.png", 0, 70, 217, 105),
TERRAINZAPPER_INACTIVE_PARAM("zapper.png", 0, 175, 14, 14),
SCHEMATIC_TABLE("schematic_table.png", 207, 89),
SCHEMATIC_TABLE_PROGRESS("schematic_table.png", 209, 0, 24, 17),
@ -127,6 +129,15 @@ public enum ScreenResources {
I_REPLACE_SOLID(1, 2),
I_REPLACE_ANY(2, 2),
I_REPLACE_EMPTY(3, 2),
I_CENTERED(4, 2),
I_ATTACHED(5, 2),
I_INSERTED(6, 2),
I_FILL(7, 2),
I_PLACE(8, 2),
I_REPLACE(9, 2),
I_CLEAR(10, 2),
I_OVERLAY(11, 2),
I_FLATTEN(12, 2),
I_TOOL_DEPLOY(0, 3),
I_SKIP_TILES(2, 3),

View file

@ -259,7 +259,8 @@ public class DeployerTileEntity extends KineticTileEntity {
player.rotationPitch = direction == Direction.UP ? -90 : direction == Direction.DOWN ? 90 : 0;
DeployerHandler.activate(player, center, clickedPos, movementVector, mode);
heldItem = player.getHeldItemMainhand();
if (player != null)
heldItem = player.getHeldItemMainhand();
}
protected void returnAndDeposit() {

View file

@ -4,6 +4,8 @@ import static net.minecraft.entity.MoverType.SELF;
import static net.minecraft.util.Direction.AxisDirection.NEGATIVE;
import static net.minecraft.util.Direction.AxisDirection.POSITIVE;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.BeltAttachmentState;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part;
@ -62,9 +64,9 @@ public class BeltMovementHandler {
TileEntity te = world.getTileEntity(pos);
TileEntity tileEntityBelowPassenger = world.getTileEntity(entityIn.getPosition());
BlockState blockState = info.lastCollidedState;
Direction movementFacing = Direction.getFacingFromAxisDirection(
blockState.get(BlockStateProperties.HORIZONTAL_FACING).getAxis(),
beltTe.getSpeed() < 0 ? POSITIVE : NEGATIVE);
Direction movementFacing =
Direction.getFacingFromAxisDirection(blockState.get(BlockStateProperties.HORIZONTAL_FACING).getAxis(),
beltTe.getSpeed() < 0 ? POSITIVE : NEGATIVE);
boolean collidedWithBelt = te instanceof BeltTileEntity;
boolean betweenBelts = tileEntityBelowPassenger instanceof BeltTileEntity && tileEntityBelowPassenger != te;
@ -105,8 +107,8 @@ public class BeltMovementHandler {
float movementSpeed = beltTe.getBeltMovementSpeed();
final Direction movementDirection = Direction.getFacingFromAxis(axis == Axis.X ? NEGATIVE : POSITIVE, axis);
Vec3i centeringDirection = Direction.getFacingFromAxis(POSITIVE, beltFacing.rotateY().getAxis())
.getDirectionVec();
Vec3i centeringDirection =
Direction.getFacingFromAxis(POSITIVE, beltFacing.rotateY().getAxis()).getDirectionVec();
Vec3d movement = new Vec3d(movementDirection.getDirectionVec()).scale(movementSpeed);
double diffCenter = axis == Axis.Z ? (pos.getX() + .5f - entityIn.posX) : (pos.getZ() + .5f - entityIn.posZ);
@ -145,10 +147,11 @@ public class BeltMovementHandler {
Vec3d checkDistance = movement.normalize().scale(0.5);
AxisAlignedBB bb = entityIn.getBoundingBox();
AxisAlignedBB checkBB = new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
if (!world
.getEntitiesWithinAABBExcludingEntity(entityIn, checkBB.offset(checkDistance)
.grow(-Math.abs(checkDistance.x), -Math.abs(checkDistance.y), -Math.abs(checkDistance.z)))
.isEmpty()) {
checkBB = checkBB.offset(checkDistance).grow(-Math.abs(checkDistance.x), -Math.abs(checkDistance.y),
-Math.abs(checkDistance.z));
List<Entity> list = world.getEntitiesWithinAABBExcludingEntity(entityIn, checkBB);
list.removeIf(e -> entityIn.isRidingOrBeingRiddenBy(e));
if (!list.isEmpty()) {
entityIn.setMotion(0, 0, 0);
info.ticksSinceLastCollision--;
return;

View file

@ -0,0 +1,63 @@
package com.simibubi.create.modules.curiosities.zapper;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import com.google.common.base.Predicates;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockPos;
public enum PlacementPatterns {
Solid(ScreenResources.I_PATTERN_SOLID),
Checkered(ScreenResources.I_PATTERN_CHECKERED),
InverseCheckered(ScreenResources.I_PATTERN_CHECKERED_INVERSED),
Chance25(ScreenResources.I_PATTERN_CHANCE_25),
Chance50(ScreenResources.I_PATTERN_CHANCE_50),
Chance75(ScreenResources.I_PATTERN_CHANCE_75);
public String translationKey;
public ScreenResources icon;
private PlacementPatterns(ScreenResources icon) {
this.translationKey = Lang.asId(name());
this.icon = icon;
}
public static void applyPattern(List<BlockPos> blocksIn, ItemStack stack) {
CompoundNBT tag = stack.getTag();
PlacementPatterns pattern =
!tag.contains("Pattern") ? Solid : valueOf(tag.getString("Pattern"));
Random r = new Random();
Predicate<BlockPos> filter = Predicates.alwaysFalse();
switch (pattern) {
case Chance25:
filter = pos -> r.nextBoolean() || r.nextBoolean();
break;
case Chance50:
filter = pos -> r.nextBoolean();
break;
case Chance75:
filter = pos -> r.nextBoolean() && r.nextBoolean();
break;
case Checkered:
filter = pos -> (pos.getX() + pos.getY() + pos.getZ()) % 2 == 0;
break;
case InverseCheckered:
filter = pos -> (pos.getX() + pos.getY() + pos.getZ()) % 2 != 0;
break;
case Solid:
default:
break;
}
blocksIn.removeIf(filter);
}
}

View file

@ -1,9 +1,9 @@
package com.simibubi.create.modules.curiosities.zapper.blockzapper;
package com.simibubi.create.modules.curiosities.zapper;
import java.util.function.Supplier;
import com.simibubi.create.foundation.packet.SimplePacketBase;
import com.simibubi.create.modules.curiosities.zapper.blockzapper.BlockzapperHandler.LaserBeam;
import com.simibubi.create.modules.curiosities.zapper.ZapperRenderHandler.LaserBeam;
import net.minecraft.client.Minecraft;
import net.minecraft.network.PacketBuffer;
@ -14,21 +14,21 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.network.NetworkEvent.Context;
public class BlockzapperBeamPacket extends SimplePacketBase {
public class ZapperBeamPacket extends SimplePacketBase {
public Vec3d start;
public Vec3d target;
public Hand hand;
public boolean self;
public BlockzapperBeamPacket(Vec3d start, Vec3d target, Hand hand, boolean self) {
public ZapperBeamPacket(Vec3d start, Vec3d target, Hand hand, boolean self) {
this.start = start;
this.target = target;
this.hand = hand;
this.self = self;
}
public BlockzapperBeamPacket(PacketBuffer buffer) {
public ZapperBeamPacket(PacketBuffer buffer) {
start = new Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble());
target = new Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble());
hand = buffer.readBoolean()? Hand.MAIN_HAND : Hand.OFF_HAND;
@ -51,12 +51,12 @@ public class BlockzapperBeamPacket extends SimplePacketBase {
context.get().enqueueWork(() -> DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
if (Minecraft.getInstance().player.getPositionVector().distanceTo(start) > 100)
return;
BlockzapperHandler.addBeam(new LaserBeam(start, target).followPlayer(self, hand == Hand.MAIN_HAND));
ZapperRenderHandler.addBeam(new LaserBeam(start, target).followPlayer(self, hand == Hand.MAIN_HAND));
if (self)
BlockzapperHandler.shoot(hand);
ZapperRenderHandler.shoot(hand);
else
BlockzapperHandler.playSound(hand, new BlockPos(start));
ZapperRenderHandler.playSound(hand, new BlockPos(start));
}));
context.get().setPacketHandled(true);
}

View file

@ -2,28 +2,47 @@ package com.simibubi.create.modules.curiosities.zapper;
import java.util.List;
import com.simibubi.create.AllPackets;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.foundation.item.ItemDescription;
import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.item.Rarity;
import net.minecraft.item.UseAction;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.state.properties.StairsShape;
import net.minecraft.util.ActionResult;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.HandSide;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceContext.BlockMode;
import net.minecraft.util.math.RayTraceContext.FluidMode;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.network.PacketDistributor;
public abstract class ZapperItem extends Item {
@ -35,13 +54,27 @@ public abstract class ZapperItem extends Item {
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, World worldIn, List<ITextComponent> tooltip, ITooltipFlag flagIn) {
if (stack.hasTag() && stack.getTag().contains("BlockUsed")) {
String usedblock = NBTUtil.readBlockState(stack.getTag().getCompound("BlockUsed")).getBlock()
.getTranslationKey();
String usedblock =
NBTUtil.readBlockState(stack.getTag().getCompound("BlockUsed")).getBlock().getTranslationKey();
ItemDescription.add(tooltip, TextFormatting.DARK_GRAY + Lang.translate("blockzapper.usingBlock",
TextFormatting.GRAY + new TranslationTextComponent(usedblock).getFormattedText()));
}
}
@Override
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
boolean differentBlock = false;
if (oldStack.hasTag() && newStack.hasTag() && oldStack.getTag().contains("BlockUsed")
&& newStack.getTag().contains("BlockUsed"))
differentBlock = NBTUtil.readBlockState(oldStack.getTag().getCompound("BlockUsed")) != NBTUtil
.readBlockState(newStack.getTag().getCompound("BlockUsed"));
return slotChanged || !isZapper(newStack) || differentBlock;
}
public boolean isZapper(ItemStack newStack) {
return newStack.getItem() instanceof ZapperItem;
}
@Override
public ActionResultType onItemUse(ItemUseContext context) {
// Shift -> open GUI
@ -57,11 +90,110 @@ public abstract class ZapperItem extends Item {
return super.onItemUse(context);
}
@Override
public ActionResult<ItemStack> onItemRightClick(World world, PlayerEntity player, Hand hand) {
ItemStack item = player.getHeldItem(hand);
CompoundNBT nbt = item.getOrCreateTag();
// Shift -> Open GUI
if (player.isSneaking()) {
if (world.isRemote) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
openHandgunGUI(item, hand == Hand.OFF_HAND);
});
applyCooldown(player, item, false);
}
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
boolean mainHand = hand == Hand.MAIN_HAND;
boolean isSwap = item.getTag().contains("_Swap");
boolean gunInOtherHand = isZapper(player.getHeldItem(mainHand ? Hand.OFF_HAND : Hand.MAIN_HAND));
// Pass To Offhand
if (mainHand && isSwap && gunInOtherHand)
return new ActionResult<ItemStack>(ActionResultType.FAIL, item);
if (mainHand && !isSwap && gunInOtherHand)
item.getTag().putBoolean("_Swap", true);
if (!mainHand && isSwap)
item.getTag().remove("_Swap");
if (!mainHand && gunInOtherHand)
player.getHeldItem(Hand.MAIN_HAND).getTag().remove("_Swap");
player.setActiveHand(hand);
// Check if can be used
String msg = validateUsage(item);
if (msg != null) {
world.playSound(player, player.getPosition(), AllSoundEvents.BLOCKZAPPER_DENY.get(), SoundCategory.BLOCKS,
1f, 0.5f);
player.sendStatusMessage(new StringTextComponent(TextFormatting.RED + msg), true);
return new ActionResult<ItemStack>(ActionResultType.FAIL, item);
}
BlockState stateToUse = Blocks.AIR.getDefaultState();
if (nbt.contains("BlockUsed"))
stateToUse = NBTUtil.readBlockState(nbt.getCompound("BlockUsed"));
// Raytrace - Find the target
Vec3d start = player.getPositionVec().add(0, player.getEyeHeight(), 0);
Vec3d range = player.getLookVec().scale(getRange(item));
BlockRayTraceResult raytrace = world.rayTraceBlocks(
new RayTraceContext(start, start.add(range), BlockMode.OUTLINE, FluidMode.NONE, player));
BlockPos pos = raytrace.getPos();
BlockState stateReplaced = world.getBlockState(pos);
// No target
if (pos == null || stateReplaced.getBlock() == Blocks.AIR) {
applyCooldown(player, item, gunInOtherHand);
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
// Find exact position of gun barrel for VFX
float yaw = (float) ((player.rotationYaw) / -180 * Math.PI);
float pitch = (float) ((player.rotationPitch) / -180 * Math.PI);
Vec3d barrelPosNoTransform =
new Vec3d(mainHand == (player.getPrimaryHand() == HandSide.RIGHT) ? -.35f : .35f, -0.1f, 1);
Vec3d barrelPos = start.add(barrelPosNoTransform.rotatePitch(pitch).rotateYaw(yaw));
// Client side
if (world.isRemote) {
ZapperRenderHandler.dontAnimateItem(hand);
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
// Server side
if (activate(world, player, item, stateToUse, raytrace)) {
applyCooldown(player, item, gunInOtherHand);
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> player),
new ZapperBeamPacket(barrelPos, raytrace.getHitVec(), hand, false));
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player),
new ZapperBeamPacket(barrelPos, raytrace.getHitVec(), hand, true));
}
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
public String validateUsage(ItemStack item) {
CompoundNBT tag = item.getOrCreateTag();
if (!canActivateWithoutSelectedBlock(item) && !tag.contains("BlockUsed"))
return Lang.translate("blockzapper.leftClickToSet");
return null;
}
protected abstract boolean activate(World world, PlayerEntity player, ItemStack item, BlockState stateToUse,
BlockRayTraceResult raytrace);
@OnlyIn(Dist.CLIENT)
protected abstract void openHandgunGUI(ItemStack item, boolean b);
protected abstract int getCooldownDelay(ItemStack item);
protected abstract int getRange(ItemStack stack);
protected boolean canActivateWithoutSelectedBlock(ItemStack stack) {
return false;
}
protected void applyCooldown(PlayerEntity playerIn, ItemStack item, boolean dual) {
int delay = getCooldownDelay(item);
playerIn.getCooldownTracker().setCooldown(item.getItem(), dual ? delay * 2 / 3 : delay);
@ -77,4 +209,50 @@ public abstract class ZapperItem extends Item {
return UseAction.NONE;
}
@Override
public boolean onEntitySwing(ItemStack stack, LivingEntity entity) {
if (!(entity instanceof PlayerEntity))
return false;
if (entity.isSneaking())
return true;
Vec3d start = entity.getPositionVec().add(0, entity.getEyeHeight(), 0);
Vec3d range = entity.getLookVec().scale(getRange(stack));
BlockRayTraceResult raytrace = entity.world.rayTraceBlocks(
new RayTraceContext(start, start.add(range), BlockMode.OUTLINE, FluidMode.NONE, entity));
BlockPos pos = raytrace.getPos();
if (pos == null)
return true;
entity.world.sendBlockBreakProgress(entity.getEntityId(), pos, -1);
BlockState newState = entity.world.getBlockState(pos);
if (BlockHelper.getRequiredItem(newState).isEmpty())
return true;
if (entity.world.getTileEntity(pos) != null)
return true;
if (newState.has(BlockStateProperties.DOUBLE_BLOCK_HALF))
return true;
if (newState.has(BlockStateProperties.ATTACHED))
return true;
if (newState.has(BlockStateProperties.HANGING))
return true;
if (newState.has(BlockStateProperties.BED_PART))
return true;
if (newState.has(BlockStateProperties.STAIRS_SHAPE))
newState = newState.with(BlockStateProperties.STAIRS_SHAPE, StairsShape.STRAIGHT);
if (newState.has(BlockStateProperties.PERSISTENT))
newState = newState.with(BlockStateProperties.PERSISTENT, true);
CompoundNBT tag = stack.getOrCreateTag();
if (tag.contains("BlockUsed") && NBTUtil.readBlockState(stack.getTag().getCompound("BlockUsed")) == newState)
return true;
tag.put("BlockUsed", NBTUtil.writeBlockState(newState));
entity.world.playSound((PlayerEntity) entity, entity.getPosition(), AllSoundEvents.BLOCKZAPPER_CONFIRM.get(),
SoundCategory.BLOCKS, 0.5f, 0.8f);
return true;
}
}

View file

@ -0,0 +1,32 @@
package com.simibubi.create.modules.curiosities.zapper;
import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.block.BlockState;
import net.minecraft.block.FourWayBlock;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.tileentity.ItemStackTileEntityRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTUtil;
public abstract class ZapperItemRenderer extends ItemStackTileEntityRenderer {
protected void renderBlockUsed(ItemStack stack, ItemRenderer itemRenderer) {
BlockState state = NBTUtil.readBlockState(stack.getTag().getCompound("BlockUsed"));
GlStateManager.pushMatrix();
GlStateManager.translatef(-0.3F, -0.45F, -0.0F);
GlStateManager.scalef(0.25F, 0.25F, 0.25F);
IBakedModel modelForState = Minecraft.getInstance().getBlockRendererDispatcher().getModelForState(state);
if (state.getBlock() instanceof FourWayBlock)
modelForState = Minecraft.getInstance().getItemRenderer()
.getModelWithOverrides(new ItemStack(state.getBlock()));
itemRenderer.renderItem(new ItemStack(state.getBlock()), modelForState);
GlStateManager.popMatrix();
}
}

View file

@ -11,11 +11,11 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
public class ZapLog {
public class ZapperLog {
private World activeWorld;
private List<List<BlockInfo>> log = new LinkedList<>();
private int redoIndex;
// private int redoIndex;
/*
* Undo and redo operations applied by tools what information is necessary?
@ -42,7 +42,7 @@ public class ZapLog {
}).collect(Collectors.toList());
log.add(0, blocks);
redoIndex = 0;
// redoIndex = 0;
if (maxLogLength() < log.size())
log.remove(log.size() - 1);

View file

@ -1,15 +1,14 @@
package com.simibubi.create.modules.curiosities.zapper.blockzapper;
package com.simibubi.create.modules.curiosities.zapper;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import com.simibubi.create.AllSoundEvents;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.foundation.utility.TessellatorHelper;
import net.minecraft.client.Minecraft;
@ -40,7 +39,7 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@SuppressWarnings("deprecation")
@EventBusSubscriber(value = Dist.CLIENT)
public class BlockzapperHandler {
public class ZapperRenderHandler {
public static List<LaserBeam> cachedBeams;
public static float leftHandAnimation;
@ -171,10 +170,9 @@ public class BlockzapperHandler {
@SubscribeEvent
public static void onRenderPlayerHand(RenderSpecificHandEvent event) {
ItemStack heldItem = event.getItemStack();
if (!AllItems.PLACEMENT_HANDGUN.typeOf(heldItem))
if (!(heldItem.getItem() instanceof ZapperItem))
return;
boolean idle = !heldItem.getOrCreateTag().contains("BlockUsed");
Minecraft mc = Minecraft.getInstance();
boolean rightHand = event.getHand() == Hand.MAIN_HAND ^ mc.player.getPrimaryHand() == HandSide.LEFT;
@ -189,8 +187,6 @@ public class BlockzapperHandler {
equipProgress = 0;
if (!rightHand && (leftHandAnimation > .01f || dontReequipLeft))
equipProgress = 0;
if (idle)
equipProgress = 1 - event.getEquipProgress();
// Render arm
float f = rightHand ? 1.0F : -1.0F;

View file

@ -0,0 +1,185 @@
package com.simibubi.create.modules.curiosities.zapper;
import java.util.Vector;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllPackets;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.widgets.IconButton;
import com.simibubi.create.foundation.packet.NbtPacket;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.client.model.data.EmptyModelData;
@SuppressWarnings("deprecation")
public class ZapperScreen extends AbstractSimiScreen {
protected ItemStack zapper;
protected boolean offhand;
protected float animationProgress;
protected ScreenResources background;
protected final String patternSection = Lang.translate("gui.blockzapper.patternSection");
protected String title;
protected Vector<IconButton> patternButtons;
protected int brightColor;
protected int fontColor;
public ZapperScreen(ScreenResources background, ItemStack zapper, boolean offhand) {
super();
this.background = background;
this.zapper = zapper;
this.offhand = offhand;
title = "";
brightColor = 0xCCDDFF;
fontColor = ScreenResources.FONT_COLOR;
}
@Override
protected void init() {
animationProgress = 0;
setWindowSize(background.width + 40, background.height);
super.init();
widgets.clear();
int i = guiLeft - 20;
int j = guiTop;
CompoundNBT nbt = zapper.getOrCreateTag();
patternButtons = new Vector<>(6);
for (int row = 0; row <= 1; row++) {
for (int col = 0; col <= 2; col++) {
int id = patternButtons.size();
PlacementPatterns pattern = PlacementPatterns.values()[id];
patternButtons.add(new IconButton(i + 147 + col * 18, j + 23 + row * 18, pattern.icon));
patternButtons.get(id).setToolTip(Lang.translate("gui.blockzapper.pattern." + pattern.translationKey));
}
}
if (nbt.contains("Pattern"))
patternButtons.get(PlacementPatterns.valueOf(nbt.getString("Pattern")).ordinal()).active = false;
widgets.addAll(patternButtons);
}
@Override
protected void renderWindow(int mouseX, int mouseY, float partialTicks) {
int i = guiLeft - 20;
int j = guiTop;
background.draw(this, i, j);
drawOnBackground(i, j);
minecraft.getTextureManager().bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE);
GlStateManager.enableBlend();
renderBlock();
renderZapper();
}
protected void drawOnBackground(int i, int j) {
font.drawStringWithShadow(title, i + 8, j + 10, brightColor);
font.drawString(patternSection, i + 148, j + 11, fontColor);
}
@Override
public void tick() {
super.tick();
animationProgress += 5;
}
@Override
public void onClose() {
CompoundNBT nbt = zapper.getTag();
writeAdditionalOptions(nbt);
AllPackets.channel.sendToServer(new NbtPacket(zapper, offhand ? Hand.OFF_HAND : Hand.MAIN_HAND));
super.onClose();
}
@Override
public boolean mouseClicked(double x, double y, int button) {
CompoundNBT nbt = zapper.getTag();
for (IconButton patternButton : patternButtons) {
if (patternButton.isHovered()) {
patternButtons.forEach(b -> b.active = true);
patternButton.active = false;
patternButton.playDownSound(Minecraft.getInstance().getSoundHandler());
nbt.putString("Pattern", PlacementPatterns.values()[patternButtons.indexOf(patternButton)].name());
}
}
return super.mouseClicked(x, y, button);
}
protected void renderZapper() {
GlStateManager.pushLightingAttributes();
GlStateManager.pushMatrix();
RenderHelper.enableStandardItemLighting();
GlStateManager.enableBlend();
GlStateManager.enableRescaleNormal();
GlStateManager.enableAlphaTest();
GlStateManager.alphaFunc(516, 0.1F);
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.translated((this.width - this.sWidth) / 2 + 260, this.height / 2 - this.sHeight / 4, 100);
GlStateManager.rotatef(90 + 0.2f * animationProgress, 0, 1, 0);
GlStateManager.rotatef(-40, .8f, 0, -.0f);
GlStateManager.scaled(100, -100, 100);
IBakedModel model = itemRenderer.getModelWithOverrides(zapper);
model.handlePerspective(TransformType.FIXED);
itemRenderer.renderItem(zapper, model);
GlStateManager.disableAlphaTest();
GlStateManager.disableRescaleNormal();
GlStateManager.disableLighting();
GlStateManager.popMatrix();
GlStateManager.popAttributes();
}
protected void renderBlock() {
GlStateManager.pushMatrix();
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
GlStateManager.translated(guiLeft + 1.7f, guiTop - 49, 120);
GlStateManager.rotatef(-30f, .5f, .9f, -.1f);
GlStateManager.scaled(20, -20, 20);
BlockState state = Blocks.AIR.getDefaultState();
if (zapper.hasTag() && zapper.getTag().contains("BlockUsed"))
state = NBTUtil.readBlockState(zapper.getTag().getCompound("BlockUsed"));
minecraft.getBlockRendererDispatcher().renderBlock(state, new BlockPos(0, -5, 0), minecraft.world, buffer,
minecraft.world.rand, EmptyModelData.INSTANCE);
Tessellator.getInstance().draw();
GlStateManager.popMatrix();
}
protected void writeAdditionalOptions(CompoundNBT nbt) {
}
}

View file

@ -3,14 +3,9 @@ package com.simibubi.create.modules.curiosities.zapper.blockzapper;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.function.Predicate;
import com.google.common.base.Predicates;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllPackets;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.block.render.CustomRenderedItemModel;
import com.simibubi.create.foundation.gui.ScreenOpener;
@ -20,34 +15,25 @@ import com.simibubi.create.foundation.item.ItemDescription.Palette;
import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.modules.curiosities.zapper.PlacementPatterns;
import com.simibubi.create.modules.curiosities.zapper.ZapperItem;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.fluid.IFluidState;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.state.properties.StairsShape;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.HandSide;
import net.minecraft.util.NonNullList;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceContext;
@ -63,8 +49,6 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.network.PacketDistributor;
public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel {
@ -72,23 +56,6 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
super(properties);
}
public static enum ComponentTier {
None(TextFormatting.DARK_GRAY), Brass(TextFormatting.GOLD), Chromatic(TextFormatting.LIGHT_PURPLE),
;
public TextFormatting color;
private ComponentTier(TextFormatting color) {
this.color = color;
}
}
public static enum Components {
Body, Amplifier, Accelerator, Retriever, Scope
}
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, World worldIn, List<ITextComponent> tooltip, ITooltipFlag flagIn) {
@ -99,10 +66,10 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
for (Components c : Components.values()) {
ComponentTier tier = getTier(c, stack);
ItemDescription.add(tooltip,
"> " + TextFormatting.GRAY + Lang.translate("blockzapper.component." + Lang.asId(c.name()))
+ ": " + tier.color
+ Lang.translate("blockzapper.componentTier." + Lang.asId(tier.name())));
String componentName =
TextFormatting.GRAY + Lang.translate("blockzapper.component." + Lang.asId(c.name()));
String tierName = tier.color + Lang.translate("blockzapper.componentTier." + Lang.asId(tier.name()));
ItemDescription.add(tooltip, "> " + componentName + ": " + tierName);
}
}
}
@ -125,183 +92,51 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
}
}
@Override
public ActionResult<ItemStack> onItemRightClick(World world, PlayerEntity player, Hand hand) {
ItemStack item = player.getHeldItem(hand);
CompoundNBT nbt = item.getOrCreateTag();
// Shift -> Open GUI
if (player.isSneaking()) {
if (world.isRemote) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
openHandgunGUI(item, hand == Hand.OFF_HAND);
});
applyCooldown(player, item, false);
}
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
boolean mainHand = hand == Hand.MAIN_HAND;
boolean isSwap = item.getTag().contains("_Swap");
boolean gunInOtherHand = AllItems.PLACEMENT_HANDGUN
.typeOf(player.getHeldItem(mainHand ? Hand.OFF_HAND : Hand.MAIN_HAND));
// Pass To Offhand
if (mainHand && isSwap && gunInOtherHand)
return new ActionResult<ItemStack>(ActionResultType.FAIL, item);
if (mainHand && !isSwap && gunInOtherHand)
item.getTag().putBoolean("_Swap", true);
if (!mainHand && isSwap)
item.getTag().remove("_Swap");
if (!mainHand && gunInOtherHand)
player.getHeldItem(Hand.MAIN_HAND).getTag().remove("_Swap");
player.setActiveHand(hand);
// Check if block setting is present
BlockState stateToUse = Blocks.AIR.getDefaultState();
if (nbt.contains("BlockUsed"))
stateToUse = NBTUtil.readBlockState(nbt.getCompound("BlockUsed"));
else {
world.playSound(player, player.getPosition(), AllSoundEvents.BLOCKZAPPER_DENY.get(), SoundCategory.BLOCKS, 1f,
0.5f);
player.sendStatusMessage(
new StringTextComponent(TextFormatting.RED + Lang.translate("blockzapper.leftClickToSet")), true);
return new ActionResult<ItemStack>(ActionResultType.FAIL, item);
}
// Raytrace - Find the target
Vec3d start = player.getPositionVec().add(0, player.getEyeHeight(), 0);
Vec3d range = player.getLookVec().scale(getReachDistance(item));
BlockRayTraceResult raytrace = world.rayTraceBlocks(
new RayTraceContext(start, start.add(range), BlockMode.OUTLINE, FluidMode.NONE, player));
BlockPos pos = raytrace.getPos();
BlockState stateReplaced = world.getBlockState(pos);
// No target
if (pos == null || stateReplaced.getBlock() == Blocks.AIR) {
applyCooldown(player, item, gunInOtherHand);
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
// Find exact position of gun barrel for VFX
float yaw = (float) ((player.rotationYaw) / -180 * Math.PI);
float pitch = (float) ((player.rotationPitch) / -180 * Math.PI);
Vec3d barrelPosNoTransform = new Vec3d(mainHand == (player.getPrimaryHand() == HandSide.RIGHT) ? -.35f : .35f,
-0.1f, 1);
Vec3d barrelPos = start.add(barrelPosNoTransform.rotatePitch(pitch).rotateYaw(yaw));
// Client side
if (world.isRemote) {
BlockzapperHandler.dontAnimateItem(hand);
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
// Server side - Replace Blocks
protected boolean activate(World world, PlayerEntity player, ItemStack stack, BlockState selectedState,
BlockRayTraceResult raytrace) {
CompoundNBT nbt = stack.getOrCreateTag();
boolean replace = nbt.contains("Replace") && nbt.getBoolean("Replace");
List<BlockPos> selectedBlocks = getSelectedBlocks(item, world, player);
applyPattern(selectedBlocks, item);
List<BlockPos> selectedBlocks = getSelectedBlocks(stack, world, player);
PlacementPatterns.applyPattern(selectedBlocks, stack);
Direction face = raytrace.getFace();
for (BlockPos placed : selectedBlocks) {
if (world.getBlockState(placed) == stateToUse)
if (world.getBlockState(placed) == selectedState)
continue;
if (!stateToUse.isValidPosition(world, placed))
if (!selectedState.isValidPosition(world, placed))
continue;
if (!player.isCreative() && !canBreak(item, world.getBlockState(placed), world, placed))
if (!player.isCreative() && !canBreak(stack, world.getBlockState(placed), world, placed))
continue;
if (!player.isCreative() && BlockHelper.findAndRemoveInInventory(stateToUse, player, 1) == 0) {
player.getCooldownTracker().setCooldown(item.getItem(), 20);
if (!player.isCreative() && BlockHelper.findAndRemoveInInventory(selectedState, player, 1) == 0) {
player.getCooldownTracker().setCooldown(stack.getItem(), 20);
player.sendStatusMessage(
new StringTextComponent(TextFormatting.RED + Lang.translate("blockzapper.empty")), true);
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
return false;
}
if (!player.isCreative() && replace)
dropBlocks(world, player, item, face, placed);
dropBlocks(world, player, stack, face, placed);
for (Direction updateDirection : Direction.values())
stateToUse = stateToUse.updatePostPlacement(updateDirection,
selectedState = selectedState.updatePostPlacement(updateDirection,
world.getBlockState(placed.offset(updateDirection)), world, placed,
placed.offset(updateDirection));
BlockSnapshot blocksnapshot = BlockSnapshot.getBlockSnapshot(world, placed);
IFluidState ifluidstate = world.getFluidState(placed);
world.setBlockState(placed, ifluidstate.getBlockState(), 18);
world.setBlockState(placed, stateToUse);
world.setBlockState(placed, selectedState);
if (ForgeEventFactory.onBlockPlace(player, blocksnapshot, Direction.UP)) {
blocksnapshot.restore(true, false);
return new ActionResult<ItemStack>(ActionResultType.FAIL, item);
return false;
}
if (player instanceof ServerPlayerEntity)
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayerEntity) player, placed,
new ItemStack(stateToUse.getBlock()));
new ItemStack(selectedState.getBlock()));
}
applyCooldown(player, item, gunInOtherHand);
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> player),
new BlockzapperBeamPacket(barrelPos, raytrace.getHitVec(), hand, false));
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player),
new BlockzapperBeamPacket(barrelPos, raytrace.getHitVec(), hand, true));
return new ActionResult<ItemStack>(ActionResultType.SUCCESS, item);
}
@Override
public boolean onBlockDestroyed(ItemStack stack, World worldIn, BlockState state, BlockPos pos,
LivingEntity entityLiving) {
if (entityLiving instanceof PlayerEntity && ((PlayerEntity) entityLiving).isCreative()) {
worldIn.setBlockState(pos, state);
return false;
}
return super.onBlockDestroyed(stack, worldIn, state, pos, entityLiving);
}
@Override
public boolean onEntitySwing(ItemStack stack, LivingEntity entity) {
if (!(entity instanceof PlayerEntity))
return false;
if (entity.isSneaking())
return true;
Vec3d start = entity.getPositionVec().add(0, entity.getEyeHeight(), 0);
Vec3d range = entity.getLookVec().scale(getReachDistance(stack));
BlockRayTraceResult raytrace = entity.world.rayTraceBlocks(
new RayTraceContext(start, start.add(range), BlockMode.OUTLINE, FluidMode.NONE, entity));
BlockPos pos = raytrace.getPos();
if (pos == null)
return true;
entity.world.sendBlockBreakProgress(entity.getEntityId(), pos, -1);
BlockState newState = entity.world.getBlockState(pos);
if (BlockHelper.getRequiredItem(newState).isEmpty())
return true;
if (entity.world.getTileEntity(pos) != null)
return true;
if (newState.has(BlockStateProperties.DOUBLE_BLOCK_HALF))
return true;
if (newState.has(BlockStateProperties.ATTACHED))
return true;
if (newState.has(BlockStateProperties.HANGING))
return true;
if (newState.has(BlockStateProperties.BED_PART))
return true;
if (newState.has(BlockStateProperties.STAIRS_SHAPE))
newState = newState.with(BlockStateProperties.STAIRS_SHAPE, StairsShape.STRAIGHT);
if (newState.has(BlockStateProperties.PERSISTENT))
newState = newState.with(BlockStateProperties.PERSISTENT, true);
if (stack.getTag().contains("BlockUsed")
&& NBTUtil.readBlockState(stack.getTag().getCompound("BlockUsed")) == newState)
return true;
stack.getTag().put("BlockUsed", NBTUtil.writeBlockState(newState));
entity.world.playSound((PlayerEntity) entity, entity.getPosition(), AllSoundEvents.BLOCKZAPPER_CONFIRM.get(),
SoundCategory.BLOCKS, 0.5f, 0.8f);
return true;
}
@ -322,38 +157,13 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
}
}
@Override
public boolean onBlockStartBreak(ItemStack itemstack, BlockPos pos, PlayerEntity player) {
return true;
}
@Override
public int getUseDuration(ItemStack stack) {
return 0;
}
@Override
public void onPlayerStoppedUsing(ItemStack stack, World worldIn, LivingEntity entityLiving, int timeLeft) {
super.onPlayerStoppedUsing(stack, worldIn, entityLiving, timeLeft);
}
@Override
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
boolean differentBlock = true;
if (oldStack.hasTag() && newStack.hasTag() && oldStack.getTag().contains("BlockUsed")
&& newStack.getTag().contains("BlockUsed"))
differentBlock = NBTUtil.readBlockState(oldStack.getTag().getCompound("BlockUsed")) != NBTUtil
.readBlockState(newStack.getTag().getCompound("BlockUsed"));
return slotChanged || !AllItems.PLACEMENT_HANDGUN.typeOf(newStack) || differentBlock;
}
@Override
@OnlyIn(Dist.CLIENT)
protected void openHandgunGUI(ItemStack handgun, boolean offhand) {
ScreenOpener.open(new BlockzapperScreen(handgun, offhand));
}
public static List<BlockPos> getSelectedBlocks(ItemStack stack, World worldIn, PlayerEntity player) {
public List<BlockPos> getSelectedBlocks(ItemStack stack, World worldIn, PlayerEntity player) {
List<BlockPos> list = new LinkedList<>();
CompoundNBT tag = stack.getTag();
if (tag == null)
@ -368,7 +178,7 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
List<BlockPos> frontier = new LinkedList<>();
Vec3d start = player.getPositionVec().add(0, player.getEyeHeight(), 0);
Vec3d range = player.getLookVec().scale(getReachDistance(stack));
Vec3d range = player.getLookVec().scale(getRange(stack));
BlockRayTraceResult raytrace = player.world.rayTraceBlocks(
new RayTraceContext(start, start.add(range), BlockMode.COLLIDER, FluidMode.NONE, player));
BlockPos pos = raytrace.getPos().toImmutable();
@ -483,7 +293,8 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
return 20;
}
public static int getReachDistance(ItemStack stack) {
@Override
protected int getRange(ItemStack stack) {
ComponentTier tier = getTier(Components.Scope, stack);
if (tier == ComponentTier.None)
return 15;
@ -495,37 +306,6 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
return 0;
}
public static void applyPattern(List<BlockPos> blocksIn, ItemStack stack) {
CompoundNBT tag = stack.getTag();
PlacementPatterns pattern = !tag.contains("Pattern") ? PlacementPatterns.Solid
: PlacementPatterns.valueOf(tag.getString("Pattern"));
Random r = new Random();
Predicate<BlockPos> filter = Predicates.alwaysFalse();
switch (pattern) {
case Chance25:
filter = pos -> r.nextBoolean() || r.nextBoolean();
break;
case Chance50:
filter = pos -> r.nextBoolean();
break;
case Chance75:
filter = pos -> r.nextBoolean() && r.nextBoolean();
break;
case Checkered:
filter = pos -> (pos.getX() + pos.getY() + pos.getZ()) % 2 == 0;
break;
case InverseCheckered:
filter = pos -> (pos.getX() + pos.getY() + pos.getZ()) % 2 != 0;
break;
case Solid:
default:
break;
}
blocksIn.removeIf(filter);
}
protected static void dropBlocks(World worldIn, PlayerEntity playerIn, ItemStack item, Direction face,
BlockPos placed) {
TileEntity tileentity = worldIn.getBlockState(placed).hasTileEntity() ? worldIn.getTileEntity(placed) : null;
@ -554,6 +334,20 @@ public class BlockzapperItem extends ZapperItem implements IHaveCustomItemModel
stack.getOrCreateTag().putString(component.name(), NBTHelper.writeEnum(tier));
}
public static enum ComponentTier {
None(TextFormatting.DARK_GRAY), Brass(TextFormatting.GOLD), Chromatic(TextFormatting.LIGHT_PURPLE);
public TextFormatting color;
private ComponentTier(TextFormatting color) {
this.color = color;
}
}
public static enum Components {
Body, Amplifier, Accelerator, Retriever, Scope
}
@Override
@OnlyIn(value = Dist.CLIENT)
public CustomRenderedItemModel createModel(IBakedModel original) {

View file

@ -9,24 +9,22 @@ import static com.simibubi.create.modules.curiosities.zapper.blockzapper.Blockza
import com.mojang.blaze3d.platform.GLX;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.modules.curiosities.zapper.ZapperRenderHandler;
import com.simibubi.create.modules.curiosities.zapper.ZapperItemRenderer;
import com.simibubi.create.modules.curiosities.zapper.blockzapper.BlockzapperItem.ComponentTier;
import com.simibubi.create.modules.curiosities.zapper.blockzapper.BlockzapperItem.Components;
import net.minecraft.block.BlockState;
import net.minecraft.block.FourWayBlock;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.tileentity.ItemStackTileEntityRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.HandSide;
import net.minecraft.util.math.MathHelper;
@SuppressWarnings("deprecation")
public class BlockzapperItemRenderer extends ItemStackTileEntityRenderer {
public class BlockzapperItemRenderer extends ZapperItemRenderer {
@Override
public void renderByItem(ItemStack stack) {
@ -56,10 +54,10 @@ public class BlockzapperItemRenderer extends ItemStackTileEntityRenderer {
boolean leftHanded = player.getPrimaryHand() == HandSide.LEFT;
boolean mainHand = player.getHeldItemMainhand() == stack;
boolean offHand = player.getHeldItemOffhand() == stack;
float last = mainHand ^ leftHanded ? BlockzapperHandler.lastRightHandAnimation
: BlockzapperHandler.lastLeftHandAnimation;
float current = mainHand ^ leftHanded ? BlockzapperHandler.rightHandAnimation
: BlockzapperHandler.leftHandAnimation;
float last = mainHand ^ leftHanded ? ZapperRenderHandler.lastRightHandAnimation
: ZapperRenderHandler.lastLeftHandAnimation;
float current = mainHand ^ leftHanded ? ZapperRenderHandler.rightHandAnimation
: ZapperRenderHandler.leftHandAnimation;
float animation = MathHelper.clamp(MathHelper.lerp(pt, last, current) * 5, 0, 1);
// Core glows
@ -90,22 +88,6 @@ public class BlockzapperItemRenderer extends ItemStackTileEntityRenderer {
GlStateManager.popMatrix();
}
public void renderBlockUsed(ItemStack stack, ItemRenderer itemRenderer) {
BlockState state = NBTUtil.readBlockState(stack.getTag().getCompound("BlockUsed"));
GlStateManager.pushMatrix();
GlStateManager.translatef(-0.3F, -0.45F, -0.0F);
GlStateManager.scalef(0.25F, 0.25F, 0.25F);
IBakedModel modelForState = Minecraft.getInstance().getBlockRendererDispatcher().getModelForState(state);
if (state.getBlock() instanceof FourWayBlock)
modelForState = Minecraft.getInstance().getItemRenderer()
.getModelWithOverrides(new ItemStack(state.getBlock()));
itemRenderer.renderItem(new ItemStack(state.getBlock()), modelForState);
GlStateManager.popMatrix();
}
public void renderComponent(ItemStack stack, BlockzapperModel model, Components component,
ItemRenderer itemRenderer) {
ComponentTier tier = BlockzapperItem.getTier(component, stack);

View file

@ -1,48 +1,22 @@
package com.simibubi.create.modules.curiosities.zapper.blockzapper;
import java.util.Collections;
import java.util.Vector;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllPackets;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.widgets.IconButton;
import com.simibubi.create.foundation.gui.widgets.Indicator;
import com.simibubi.create.foundation.gui.widgets.Indicator.State;
import com.simibubi.create.foundation.gui.widgets.Label;
import com.simibubi.create.foundation.gui.widgets.ScrollInput;
import com.simibubi.create.foundation.packet.NbtPacket;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.modules.curiosities.zapper.ZapperScreen;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.model.data.EmptyModelData;
@SuppressWarnings("deprecation")
public class BlockzapperScreen extends AbstractSimiScreen {
public class BlockzapperScreen extends ZapperScreen {
private ItemStack item;
private boolean offhand;
private float animationProgress;
private final String title = Lang.translate("gui.blockzapper.title");
private final String patternSection = Lang.translate("gui.blockzapper.patternSection");
private final String needsUpgradedAmplifier = Lang.translate("gui.blockzapper.needsUpgradedAmplifier");
private IconButton replaceModeButton;
@ -55,25 +29,19 @@ public class BlockzapperScreen extends AbstractSimiScreen {
private ScrollInput spreadRangeInput;
private Label spreadRangeLabel;
private Vector<IconButton> patternButtons;
public BlockzapperScreen(ItemStack handgun, boolean offhand) {
super();
item = handgun;
this.offhand = offhand;
public BlockzapperScreen(ItemStack zapper, boolean offhand) {
super(ScreenResources.BLOCKZAPPER, zapper, offhand);
title = Lang.translate("gui.blockzapper.title");
}
@Override
protected void init() {
animationProgress = 0;
setWindowSize(ScreenResources.PLACEMENT_GUN.width + 40, ScreenResources.PLACEMENT_GUN.height);
super.init();
int i = guiLeft - 20;
int j = guiTop;
CompoundNBT nbt = zapper.getOrCreateTag();
CompoundNBT nbt = item.getOrCreateTag();
widgets.clear();
replaceModeIndicator = new Indicator(i + 51, j + 36, "");
replaceModeButton = new IconButton(i + 51, j + 41, ScreenResources.I_REPLACE_SOLID);
if (nbt.contains("Replace") && nbt.getBoolean("Replace"))
@ -93,39 +61,22 @@ public class BlockzapperScreen extends AbstractSimiScreen {
spreadMaterialButton.setToolTip(Lang.translate("gui.blockzapper.searchFuzzy"));
spreadRangeLabel = new Label(i + 119, j + 46, "").withShadow().withSuffix("m");
spreadRangeInput = new ScrollInput(i + 115, j + 43, 22, 14).withRange(1, BlockzapperItem.getMaxAoe(item))
spreadRangeInput = new ScrollInput(i + 115, j + 43, 22, 14).withRange(1, BlockzapperItem.getMaxAoe(zapper))
.setState(1).titled(Lang.translate("gui.blockzapper.range")).writingTo(spreadRangeLabel);
if (nbt.contains("SearchDistance"))
spreadRangeInput.setState(nbt.getInt("SearchDistance"));
if (BlockzapperItem.getMaxAoe(item) == 2)
if (BlockzapperItem.getMaxAoe(zapper) == 2)
spreadRangeInput.getToolTip().add(1, TextFormatting.RED + needsUpgradedAmplifier);
Collections.addAll(widgets, replaceModeButton, replaceModeIndicator, spreadDiagonallyButton,
spreadDiagonallyIndicator, spreadMaterialButton, spreadMaterialIndicator, spreadRangeLabel,
spreadRangeInput);
patternButtons = new Vector<>(6);
for (int row = 0; row <= 1; row++) {
for (int col = 0; col <= 2; col++) {
int id = patternButtons.size();
PlacementPatterns pattern = PlacementPatterns.values()[id];
patternButtons.add(new IconButton(i + 147 + col * 18, j + 23 + row * 18, pattern.icon));
patternButtons.get(id).setToolTip(Lang.translate("gui.blockzapper.pattern." + pattern.translationKey));
}
}
if (nbt.contains("Pattern"))
patternButtons.get(PlacementPatterns.valueOf(nbt.getString("Pattern")).ordinal()).active = false;
widgets.addAll(patternButtons);
}
@Override
public boolean mouseClicked(double x, double y, int button) {
CompoundNBT nbt = item.getTag();
CompoundNBT nbt = zapper.getTag();
if (replaceModeButton.isHovered()) {
boolean mode = nbt.contains("Replace") && nbt.getBoolean("Replace");
@ -148,91 +99,13 @@ public class BlockzapperScreen extends AbstractSimiScreen {
nbt.putBoolean("SearchFuzzy", mode);
}
for (IconButton patternButton : patternButtons) {
if (patternButton.isHovered()) {
patternButtons.forEach(b -> b.active = true);
patternButton.active = false;
patternButton.playDownSound(Minecraft.getInstance().getSoundHandler());
nbt.putString("Pattern", PlacementPatterns.values()[patternButtons.indexOf(patternButton)].name());
}
}
return super.mouseClicked(x, y, button);
}
@Override
public void onClose() {
CompoundNBT nbt = item.getTag();
protected void writeAdditionalOptions(CompoundNBT nbt) {
nbt.putInt("SearchDistance", spreadRangeInput.getState());
AllPackets.channel.sendToServer(new NbtPacket(item, offhand ? -2 : -1));
super.onClose();
}
@Override
public void tick() {
super.tick();
animationProgress += 5;
}
@Override
protected void renderWindow(int mouseX, int mouseY, float partialTicks) {
int i = guiLeft - 20;
int j = guiTop;
ScreenResources.PLACEMENT_GUN.draw(this, i, j);
font.drawStringWithShadow(title, i + 8, j + 10, 0xCCDDFF);
font.drawString(patternSection, i + 148, j + 11, ScreenResources.FONT_COLOR);
minecraft.getTextureManager().bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE);
GlStateManager.enableBlend();
renderBlock();
GlStateManager.pushLightingAttributes();
GlStateManager.pushMatrix();
RenderHelper.enableStandardItemLighting();
GlStateManager.enableBlend();
GlStateManager.enableRescaleNormal();
GlStateManager.enableAlphaTest();
GlStateManager.alphaFunc(516, 0.1F);
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.translated((this.width - this.sWidth) / 2 + 260, this.height / 2 - this.sHeight / 4, 100);
GlStateManager.rotatef(90 + 0.2f * animationProgress, 0, 1, 0);
GlStateManager.rotatef(-40, .8f, 0, -.0f);
GlStateManager.scaled(100, -100, 100);
IBakedModel model = itemRenderer.getModelWithOverrides(item);
model.handlePerspective(TransformType.FIXED);
itemRenderer.renderItem(item, model);
GlStateManager.disableAlphaTest();
GlStateManager.disableRescaleNormal();
GlStateManager.disableLighting();
GlStateManager.popMatrix();
GlStateManager.popAttributes();
}
private void renderBlock() {
GlStateManager.pushMatrix();
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
GlStateManager.translated(guiLeft + 1.7f, guiTop - 49, 120);
GlStateManager.rotatef(-30f, .5f, .9f, -.1f);
GlStateManager.scaled(20, -20, 20);
BlockState state = Blocks.BEACON.getDefaultState();
if (item.hasTag() && item.getTag().contains("BlockUsed"))
state = NBTUtil.readBlockState(item.getTag().getCompound("BlockUsed"));
minecraft.getBlockRendererDispatcher().renderBlock(state, new BlockPos(0, -5, 0), minecraft.world, buffer,
minecraft.world.rand, EmptyModelData.INSTANCE);
Tessellator.getInstance().draw();
GlStateManager.popMatrix();
}
}

View file

@ -1,23 +0,0 @@
package com.simibubi.create.modules.curiosities.zapper.blockzapper;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.utility.Lang;
public enum PlacementPatterns {
Solid(ScreenResources.I_PATTERN_SOLID),
Checkered(ScreenResources.I_PATTERN_CHECKERED),
InverseCheckered(ScreenResources.I_PATTERN_CHECKERED_INVERSED),
Chance25(ScreenResources.I_PATTERN_CHANCE_25),
Chance50(ScreenResources.I_PATTERN_CHANCE_50),
Chance75(ScreenResources.I_PATTERN_CHANCE_75);
public String translationKey;
public ScreenResources icon;
private PlacementPatterns(ScreenResources icon) {
this.translationKey = Lang.asId(name());
this.icon = icon;
}
}

View file

@ -0,0 +1,54 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import java.util.List;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
public abstract class Brush {
protected int param0;
protected int param1;
protected int param2;
int amtParams;
public Brush(int amtParams) {
this.amtParams = amtParams;
}
public void set(int param0, int param1, int param2) {
this.param0 = param0;
this.param1 = param1;
this.param2 = param2;
}
int getMax(int paramIndex) {
return Integer.MAX_VALUE;
}
int getMin(int paramIndex) {
return 0;
}
String getParamLabel(int paramIndex) {
return Lang
.translate(paramIndex == 0 ? "generic.width" : paramIndex == 1 ? "generic.height" : "generic.length");
}
public int get(int paramIndex) {
return paramIndex == 0 ? param0 : paramIndex == 1 ? param1 : param2;
}
public BlockPos getOffset(Vec3d ray, Direction face, PlacementOptions option) {
return BlockPos.ZERO;
}
abstract VoxelShape getSelectionBox();
abstract List<BlockPos> getIncludedPositions();
}

View file

@ -0,0 +1,79 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
public class CuboidBrush extends Brush {
public static final int MAX_SIZE = 32;
private VoxelShape shape;
private List<BlockPos> positions;
public CuboidBrush() {
super(3);
shape = VoxelShapes.empty();
positions = new ArrayList<>();
}
@Override
public void set(int param0, int param1, int param2) {
boolean updateShape = this.param0 != param0 || this.param1 != param1 || this.param2 != param2;
super.set(param0, param1, param2);
if (updateShape) {
BlockPos zero = BlockPos.ZERO;
shape = VoxelShapes.create(new AxisAlignedBB(zero).grow(1 / 32f)
.grow(((param0 - 1) / 2f), ((param1 - 1) / 2f), ((param2 - 1) / 2f))
.offset((1 - param0 % 2) * .5f, (1 - param1 % 2) * .5f, (1 - param2 % 2) * .5f));
positions = BlockPos
.getAllInBox(zero.add((param0 - 1) / -2, (param1 - 1) / -2, (param2 - 1) / -2),
zero.add((param0) / 2, (param1) / 2, (param2) / 2))
.map(BlockPos::new).collect(Collectors.toList());
}
}
@Override
int getMin(int paramIndex) {
return 1;
}
@Override
int getMax(int paramIndex) {
return MAX_SIZE;
}
@Override
public BlockPos getOffset(Vec3d ray, Direction face, PlacementOptions option) {
if (option == PlacementOptions.Merged)
return BlockPos.ZERO;
int offset =
option == PlacementOptions.Attached ? face.getAxisDirection() == AxisDirection.NEGATIVE ? 2 : 1 : 0;
int x = (param0 + (param0 == 0 ? 0 : offset)) / 2;
int y = (param1 + (param1 == 0 ? 0 : offset)) / 2;
int z = (param2 + (param2 == 0 ? 0 : offset)) / 2;
return BlockPos.ZERO.offset(face,
face.getAxis().getCoordinate(x, y, z) * (option == PlacementOptions.Attached ? 1 : -1));
}
@Override
List<BlockPos> getIncludedPositions() {
return positions;
}
@Override
VoxelShape getSelectionBox() {
return shape;
}
}

View file

@ -1,5 +1,6 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -7,45 +8,95 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
public class CylinderBrush {
public class CylinderBrush extends Brush {
public static final int MAX_RADIUS = 6;
public static final int MAX_HEIGHT = 6;
public static final int MAX_HEIGHT = 8;
private Map<Pair<Integer, Integer>, Pair<List<BlockPos>, VoxelShape>> cachedBrushes;
public CylinderBrush() {
super(2);
cachedBrushes = new HashMap<>();
VoxelShape fullCube = VoxelShapes.fullCube();
VoxelShape fullCube = Block.makeCuboidShape(-.5f, -.5f, -.5f, 16.5f, 16.5f, 16.5f);
for (int i = 0; i <= MAX_RADIUS; i++) {
int radius = i;
VoxelShape shape = VoxelShapes.empty();
List<BlockPos> positions = BlockPos.getAllInBox(BlockPos.ZERO.add(-i, 0, -i), BlockPos.ZERO.add(i, 0, i))
.filter(p -> p.withinDistance(BlockPos.ZERO, radius)).collect(Collectors.toList());
List<BlockPos> positions =
BlockPos.getAllInBox(BlockPos.ZERO.add(-i - 1, 0, -i - 1), BlockPos.ZERO.add(i + 1, 0, i + 1))
.map(BlockPos::new).filter(p -> VecHelper.getCenterOf(p)
.distanceTo(VecHelper.getCenterOf(BlockPos.ZERO)) < radius + .42f)
.collect(Collectors.toList());
for (BlockPos p : positions)
shape = VoxelShapes.or(shape, fullCube.withOffset(p.getX(), p.getY(), p.getZ()));
for (int h = 0; h <= MAX_HEIGHT; h++) {
List<BlockPos> stackedPositions = new ArrayList<>();
VoxelShape stackedShape = shape.simplify();
for (int layer = 0; layer <= layer; i++)
stackedShape = VoxelShapes.or(stackedShape, shape.withOffset(0, layer - h / 2, 0));
cachedBrushes.put(Pair.of(i, h), Pair.of(positions, stackedShape.simplify()));
for (int layer = 0; layer < h; layer++) {
int yOffset = layer - h / 2;
stackedShape = VoxelShapes.or(stackedShape, shape.withOffset(0, yOffset, 0));
for (BlockPos p : positions)
stackedPositions.add(p.up(yOffset));
}
cachedBrushes.put(Pair.of(i, h), Pair.of(stackedPositions, stackedShape.simplify()));
}
}
}
public VoxelShape getSelectionBox(int radius, int height) {
return get(radius, height).getRight();
@Override
public BlockPos getOffset(Vec3d ray, Direction face, PlacementOptions option) {
if (option == PlacementOptions.Merged)
return BlockPos.ZERO;
int offset = option == PlacementOptions.Attached ? 0 : -1;
boolean negative = face.getAxisDirection() == AxisDirection.NEGATIVE;
int yOffset = option == PlacementOptions.Attached ? negative ? 1 : 2 : negative ? 0 : -1;
int r = (param0 + 1 + offset);
int y = (param1 + (param1 == 0 ? 0 : yOffset)) / 2;
return BlockPos.ZERO.offset(face,
(face.getAxis().isVertical() ? y : r) * (option == PlacementOptions.Attached ? 1 : -1));
}
public List<BlockPos> getIncludedPositions(int radius, int height) {
return get(radius, height).getLeft();
@Override
int getMax(int paramIndex) {
return paramIndex == 0 ? MAX_RADIUS : MAX_HEIGHT;
}
protected Pair<List<BlockPos>, VoxelShape> get(int radius, int height) {
@Override
int getMin(int paramIndex) {
return paramIndex == 0 ? 0 : 1;
}
@Override
String getParamLabel(int paramIndex) {
return paramIndex == 0 ? Lang.translate("generic.radius") : super.getParamLabel(paramIndex);
}
@Override
VoxelShape getSelectionBox() {
return getEntry(param0, param1).getRight();
}
@Override
public List<BlockPos> getIncludedPositions() {
return getEntry(param0, param1).getLeft();
}
protected Pair<List<BlockPos>, VoxelShape> getEntry(int radius, int height) {
return cachedBrushes.get(Pair.of(Integer.valueOf(radius), Integer.valueOf(height)));
}
}

View file

@ -0,0 +1,180 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import net.minecraft.block.BlockState;
import net.minecraft.block.FlowingFluidBlock;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class FlattenTool {
// gaussian with sig=1
static float[][] kernel = {
{ 0.003765f, 0.015019f, 0.023792f, 0.015019f, 0.003765f },
{ 0.015019f, 0.059912f, 0.094907f, 0.059912f, 0.015019f },
{ 0.023792f, 0.094907f, 0.150342f, 0.094907f, 0.023792f },
{ 0.015019f, 0.059912f, 0.094907f, 0.059912f, 0.015019f },
{ 0.003765f, 0.015019f, 0.023792f, 0.015019f, 0.003765f },
};
private static int[][] applyKernel(int[][] values) {
int[][] result = new int[values.length][values[0].length];
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values[i].length; j++) {
int value = values[i][j];
float newValue = 0;
for (int iOffset = -2; iOffset <= 2; iOffset++) {
for (int jOffset = -2; jOffset <= 2; jOffset++) {
int iTarget = i + iOffset;
int jTarget = j + jOffset;
int ref = 0;
if (iTarget < 0 || iTarget >= values.length || jTarget < 0 || jTarget >= values[0].length)
ref = value;
else
ref = values[iTarget][jTarget];
if (ref == Integer.MIN_VALUE)
ref = value;
newValue += kernel[iOffset + 2][jOffset + 2] * ref;
}
}
result[i][j] = MathHelper.floor(newValue + .5f);
}
}
return result;
}
public static void apply(World world, List<BlockPos> targetPositions, Direction facing) {
List<BlockPos> surfaces = new ArrayList<>();
Map<Pair<Integer, Integer>, Integer> heightMap = new HashMap<>();
int offset = facing.getAxisDirection().getOffset();
int minEntry = Integer.MAX_VALUE;
int minCoord1 = Integer.MAX_VALUE;
int minCoord2 = Integer.MAX_VALUE;
int maxEntry = Integer.MIN_VALUE;
int maxCoord1 = Integer.MIN_VALUE;
int maxCoord2 = Integer.MIN_VALUE;
for (BlockPos p : targetPositions) {
Pair<Integer, Integer> coords = getCoords(p, facing);
BlockState belowSurface = world.getBlockState(p);
minCoord1 = Math.min(minCoord1, coords.getKey());
minCoord2 = Math.min(minCoord2, coords.getValue());
maxCoord1 = Math.max(maxCoord1, coords.getKey());
maxCoord2 = Math.max(maxCoord2, coords.getValue());
if (TerrainTools.isReplaceable(belowSurface)) {
if (!heightMap.containsKey(coords))
heightMap.put(coords, Integer.MIN_VALUE);
continue;
}
p = p.offset(facing);
BlockState surface = world.getBlockState(p);
if (!TerrainTools.isReplaceable(surface)) {
if (!heightMap.containsKey(coords) || heightMap.get(coords).equals(Integer.MIN_VALUE))
heightMap.put(coords, Integer.MAX_VALUE);
continue;
}
surfaces.add(p);
int coordinate = facing.getAxis().getCoordinate(p.getX(), p.getY(), p.getZ());
if (!heightMap.containsKey(coords) || heightMap.get(coords).equals(Integer.MAX_VALUE)
|| heightMap.get(coords).equals(Integer.MIN_VALUE)
|| heightMap.get(coords) * offset < coordinate * offset) {
heightMap.put(coords, coordinate);
maxEntry = Math.max(maxEntry, coordinate);
minEntry = Math.min(minEntry, coordinate);
}
}
if (surfaces.isEmpty())
return;
// fill heightmap
int[][] heightMapArray = new int[maxCoord1 - minCoord1 + 1][maxCoord2 - minCoord2 + 1];
for (int i = 0; i < heightMapArray.length; i++) {
for (int j = 0; j < heightMapArray[i].length; j++) {
Pair<Integer, Integer> pair = Pair.of(minCoord1 + i, minCoord2 + j);
if (!heightMap.containsKey(pair)) {
heightMapArray[i][j] = Integer.MIN_VALUE;
continue;
}
Integer height = heightMap.get(pair);
if (height.equals(Integer.MAX_VALUE)) {
heightMapArray[i][j] = offset == 1 ? maxEntry + 2 : minEntry - 2;
continue;
}
if (height.equals(Integer.MIN_VALUE)) {
heightMapArray[i][j] = offset == 1 ? minEntry - 2 : maxEntry + 2;
continue;
}
heightMapArray[i][j] = height;
}
}
heightMapArray = applyKernel(heightMapArray);
for (BlockPos p : surfaces) {
Pair<Integer, Integer> coords = getCoords(p, facing);
int surfaceCoord = facing.getAxis().getCoordinate(p.getX(), p.getY(), p.getZ()) * offset;
int targetCoord = heightMapArray[coords.getKey() - minCoord1][coords.getValue() - minCoord2] * offset;
// Keep surface
if (surfaceCoord == targetCoord)
continue;
// Lower surface
BlockState blockState = world.getBlockState(p);
int timeOut = 1000;
while (surfaceCoord > targetCoord) {
BlockPos below = p.offset(facing.getOpposite());
world.setBlockState(below, blockState);
world.setBlockState(p, blockState.getFluidState().getBlockState());
p = p.offset(facing.getOpposite());
surfaceCoord--;
if (timeOut-- <= 0)
break;
}
// Raise surface
while (surfaceCoord < targetCoord) {
BlockPos above = p.offset(facing);
if (!(blockState.getBlock() instanceof FlowingFluidBlock))
world.setBlockState(above, blockState);
world.setBlockState(p, world.getBlockState(p.offset(facing.getOpposite())));
p = p.offset(facing);
surfaceCoord++;
if (timeOut-- <= 0)
break;
}
}
}
private static Pair<Integer, Integer> getCoords(BlockPos pos, Direction facing) {
switch (facing.getAxis()) {
case X:
return Pair.of(pos.getZ(), pos.getY());
case Y:
return Pair.of(pos.getX(), pos.getZ());
case Z:
return Pair.of(pos.getX(), pos.getY());
}
return null;
}
}

View file

@ -0,0 +1,20 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.utility.Lang;
public enum PlacementOptions {
Merged(ScreenResources.I_CENTERED),
Attached(ScreenResources.I_ATTACHED),
Inserted(ScreenResources.I_INSERTED);
public String translationKey;
public ScreenResources icon;
private PlacementOptions(ScreenResources icon) {
this.translationKey = Lang.asId(name());
this.icon = icon;
}
}

View file

@ -7,23 +7,34 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
public class SphereBrush {
public class SphereBrush extends Brush {
public static final int MAX_RADIUS = 6;
private Map<Integer, Pair<List<BlockPos>, VoxelShape>> cachedBrushes;
public SphereBrush() {
super(1);
cachedBrushes = new HashMap<>();
for (int i = 0; i <= MAX_RADIUS; i++) {
int radius = i;
VoxelShape shape = VoxelShapes.empty();
List<BlockPos> positions = BlockPos.getAllInBox(BlockPos.ZERO.add(-i, -i, -i), BlockPos.ZERO.add(i, i, i))
.filter(p -> p.withinDistance(BlockPos.ZERO, radius)).collect(Collectors.toList());
VoxelShape fullCube = VoxelShapes.fullCube();
List<BlockPos> positions =
BlockPos.getAllInBox(BlockPos.ZERO.add(-i - 1, -i - 1, -i - 1), BlockPos.ZERO.add(i + 1, i + 1, i + 1))
.map(BlockPos::new).filter(p -> VecHelper.getCenterOf(p)
.distanceTo(VecHelper.getCenterOf(BlockPos.ZERO)) < radius + .5f)
.collect(Collectors.toList());
VoxelShape fullCube = Block.makeCuboidShape(-.5f, -.5f, -.5f, 16.5f, 16.5f, 16.5f);
for (BlockPos p : positions)
shape = VoxelShapes.or(shape, fullCube.withOffset(p.getX(), p.getY(), p.getZ()));
shape = shape.simplify();
@ -31,15 +42,38 @@ public class SphereBrush {
}
}
public VoxelShape getSelectionBox(int size) {
return get(size).getRight();
@Override
public BlockPos getOffset(Vec3d ray, Direction face, PlacementOptions option) {
if (option == PlacementOptions.Merged)
return BlockPos.ZERO;
int offset = option == PlacementOptions.Attached ? 0 : -1;
int r = (param0 + 1 + offset);
return BlockPos.ZERO.offset(face, r * (option == PlacementOptions.Attached ? 1 : -1));
}
public List<BlockPos> getIncludedPositions(int size) {
return get(size).getLeft();
@Override
int getMax(int paramIndex) {
return MAX_RADIUS;
}
protected Pair<List<BlockPos>, VoxelShape> get(int size) {
@Override
VoxelShape getSelectionBox() {
return getEntry(param0).getRight();
}
@Override
String getParamLabel(int paramIndex) {
return Lang.translate("generic.radius");
}
@Override
List<BlockPos> getIncludedPositions() {
return getEntry(param0).getLeft();
}
protected Pair<List<BlockPos>, VoxelShape> getEntry(int size) {
return cachedBrushes.get(Integer.valueOf(size));
}

View file

@ -0,0 +1,25 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
public enum TerrainBrushes {
Cuboid(new CuboidBrush()),
Sphere(new SphereBrush()),
Cylinder(new CylinderBrush()),
;
private Brush brush;
private TerrainBrushes(Brush brush) {
this.brush = brush;
}
public Brush get() {
// if (this == Cylinder)
// brush = new CylinderBrush();
// if (this == Sphere)
// brush = new SphereBrush();
return brush;
}
}

View file

@ -0,0 +1,89 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import java.util.List;
import javax.annotation.Nullable;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public enum TerrainTools {
Fill(ScreenResources.I_FILL),
Place(ScreenResources.I_PLACE),
Replace(ScreenResources.I_REPLACE),
Clear(ScreenResources.I_CLEAR),
Overlay(ScreenResources.I_OVERLAY),
Flatten(ScreenResources.I_FLATTEN);
public String translationKey;
public ScreenResources icon;
private TerrainTools(ScreenResources icon) {
this.translationKey = Lang.asId(name());
this.icon = icon;
}
public boolean requiresSelectedBlock() {
return this != Clear && this != Flatten;
}
public void run(World world, List<BlockPos> targetPositions, Direction facing, @Nullable BlockState paintedState) {
switch (this) {
case Clear:
targetPositions.forEach(p -> world.setBlockState(p, Blocks.AIR.getDefaultState()));
break;
case Fill:
targetPositions.forEach(p -> {
BlockState toReplace = world.getBlockState(p);
if (!isReplaceable(toReplace))
return;
world.setBlockState(p, paintedState);
});
break;
case Flatten:
FlattenTool.apply(world, targetPositions, facing);
break;
case Overlay:
targetPositions.forEach(p -> {
BlockState toOverlay = world.getBlockState(p);
if (isReplaceable(toOverlay))
return;
if (toOverlay == paintedState)
return;
p = p.up();
BlockState toReplace = world.getBlockState(p);
if (!isReplaceable(toReplace))
return;
world.setBlockState(p, paintedState);
});
break;
case Place:
targetPositions.forEach(p -> {
world.setBlockState(p, paintedState);
});
break;
case Replace:
targetPositions.forEach(p -> {
BlockState toReplace = world.getBlockState(p);
if (isReplaceable(toReplace))
return;
world.setBlockState(p, paintedState);
});
break;
}
}
public static boolean isReplaceable(BlockState toReplace) {
return toReplace.getMaterial().isReplaceable();
}
}

View file

@ -1,24 +0,0 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import com.simibubi.create.modules.curiosities.zapper.ZapperItem;
import net.minecraft.item.ItemStack;
public class TerrainZapperItem extends ZapperItem {
public TerrainZapperItem(Properties properties) {
super(properties);
}
@Override
protected void openHandgunGUI(ItemStack item, boolean b) {
// TODO Auto-generated method stub
}
@Override
protected int getCooldownDelay(ItemStack item) {
return 2;
}
}

View file

@ -0,0 +1,94 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllItems;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.TessellatorHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceContext.BlockMode;
import net.minecraft.util.math.RayTraceContext.FluidMode;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
public class TerrainZapperRenderHandler {
private static VoxelShape renderedShape;
private static BlockPos renderedPosition;
public static void tick() {
ClientPlayerEntity player = Minecraft.getInstance().player;
ItemStack heldMain = player.getHeldItemMainhand();
ItemStack heldOff = player.getHeldItemOffhand();
boolean zapperInMain = AllItems.TERRAIN_ZAPPER.typeOf(heldMain);
boolean zapperInOff = AllItems.TERRAIN_ZAPPER.typeOf(heldOff);
if (zapperInMain) {
CompoundNBT tag = heldMain.getOrCreateTag();
if (!tag.contains("_Swap")) {
createBrushOutline(tag, player, heldMain);
return;
}
}
if (zapperInOff) {
CompoundNBT tag = heldOff.getOrCreateTag();
createBrushOutline(tag, player, heldOff);
return;
}
renderedPosition = null;
}
public static void createBrushOutline(CompoundNBT tag, ClientPlayerEntity player, ItemStack zapper) {
if (!tag.contains("BrushParams")) {
renderedPosition = null;
return;
}
Brush brush = NBTHelper.readEnum(tag.getString("Brush"), TerrainBrushes.class).get();
PlacementOptions placement = NBTHelper.readEnum(tag.getString("Placement"), PlacementOptions.class);
BlockPos params = NBTUtil.readBlockPos(tag.getCompound("BrushParams"));
brush.set(params.getX(), params.getY(), params.getZ());
renderedShape = brush.getSelectionBox();
Vec3d start = player.getPositionVec().add(0, player.getEyeHeight(), 0);
Vec3d range = player.getLookVec().scale(128);
BlockRayTraceResult raytrace = player.world.rayTraceBlocks(
new RayTraceContext(start, start.add(range), BlockMode.OUTLINE, FluidMode.NONE, player));
if (raytrace == null || raytrace.getType() == Type.MISS) {
renderedPosition = null;
return;
}
BlockPos pos = raytrace.getPos();
renderedPosition = pos.add(brush.getOffset(player.getLookVec(), raytrace.getFace(), placement));
}
public static void render() {
if (renderedPosition == null)
return;
GlStateManager.lineWidth(2);
TessellatorHelper.prepareForDrawing();
GlStateManager.disableTexture();
GlStateManager.translated(renderedPosition.getX(), renderedPosition.getY(), renderedPosition.getZ());
WorldRenderer.drawShape(renderedShape, 0, 0, 0, 0f, 0f, 0f, 0.5f);
GlStateManager.enableTexture();
TessellatorHelper.cleanUpAfterDrawing();
GlStateManager.lineWidth(1);
}
}

View file

@ -0,0 +1,90 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.foundation.block.render.CustomRenderedItemModel;
import com.simibubi.create.foundation.gui.ScreenOpener;
import com.simibubi.create.foundation.item.IHaveCustomItemModel;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.modules.curiosities.zapper.PlacementPatterns;
import com.simibubi.create.modules.curiosities.zapper.ZapperItem;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class TerrainzapperItem extends ZapperItem implements IHaveCustomItemModel {
public TerrainzapperItem(Properties properties) {
super(properties);
}
@Override
protected void openHandgunGUI(ItemStack item, boolean b) {
ScreenOpener.open(new TerrainzapperScreen(item, b));
}
@Override
protected int getRange(ItemStack stack) {
return 128;
}
@Override
protected int getCooldownDelay(ItemStack item) {
return 2;
}
@Override
@OnlyIn(value = Dist.CLIENT)
public CustomRenderedItemModel createModel(IBakedModel original) {
return new TerrainzapperModel(original);
}
@Override
public String validateUsage(ItemStack item) {
if (!item.getOrCreateTag().contains("BrushParams"))
return Lang.translate("terrainzapper.shiftRightClickToSet");
return super.validateUsage(item);
}
@Override
protected boolean canActivateWithoutSelectedBlock(ItemStack stack) {
CompoundNBT tag = stack.getOrCreateTag();
TerrainTools tool = NBTHelper.readEnum(tag.getString("Tool"), TerrainTools.class);
return !tool.requiresSelectedBlock();
}
@Override
protected boolean activate(World world, PlayerEntity player, ItemStack stack, BlockState stateToUse,
BlockRayTraceResult raytrace) {
BlockPos targetPos = raytrace.getPos();
List<BlockPos> affectedPositions = new ArrayList<>();
CompoundNBT tag = stack.getOrCreateTag();
Brush brush = NBTHelper.readEnum(tag.getString("Brush"), TerrainBrushes.class).get();
BlockPos params = NBTUtil.readBlockPos(tag.getCompound("BrushParams"));
PlacementOptions option = NBTHelper.readEnum(tag.getString("Placement"), PlacementOptions.class);
TerrainTools tool = NBTHelper.readEnum(tag.getString("Tool"), TerrainTools.class);
brush.set(params.getX(), params.getY(), params.getZ());
targetPos = targetPos.add(brush.getOffset(player.getLookVec(), raytrace.getFace(), option));
for (BlockPos blockPos : brush.getIncludedPositions())
affectedPositions.add(targetPos.add(blockPos));
PlacementPatterns.applyPattern(affectedPositions, stack);
tool.run(world, affectedPositions, raytrace.getFace(), stateToUse);
return true;
}
}

View file

@ -0,0 +1,76 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import com.mojang.blaze3d.platform.GLX;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.modules.curiosities.zapper.ZapperRenderHandler;
import com.simibubi.create.modules.curiosities.zapper.ZapperItemRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.HandSide;
import net.minecraft.util.math.MathHelper;
@SuppressWarnings("deprecation")
public class TerrainzapperItemRenderer extends ZapperItemRenderer {
@Override
public void renderByItem(ItemStack stack) {
ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
TerrainzapperModel mainModel = (TerrainzapperModel) itemRenderer.getModelWithOverrides(stack);
float pt = Minecraft.getInstance().getRenderPartialTicks();
float worldTime = AnimationTickHolder.getRenderTick() / 20;
GlStateManager.pushMatrix();
GlStateManager.translatef(0.5F, 0.5F, 0.5F);
float lastCoordx = GLX.lastBrightnessX;
float lastCoordy = GLX.lastBrightnessY;
GLX.glMultiTexCoord2f(GLX.GL_TEXTURE1, Math.min(lastCoordx + 60, 240), Math.min(lastCoordy + 120, 240));
itemRenderer.renderItem(stack, mainModel.getBakedModel());
// Block indicator
if (mainModel.getCurrentPerspective() == TransformType.GUI && stack.hasTag()
&& stack.getTag().contains("BlockUsed"))
renderBlockUsed(stack, itemRenderer);
ClientPlayerEntity player = Minecraft.getInstance().player;
boolean leftHanded = player.getPrimaryHand() == HandSide.LEFT;
boolean mainHand = player.getHeldItemMainhand() == stack;
boolean offHand = player.getHeldItemOffhand() == stack;
float last = mainHand ^ leftHanded ? ZapperRenderHandler.lastRightHandAnimation
: ZapperRenderHandler.lastLeftHandAnimation;
float current = mainHand ^ leftHanded ? ZapperRenderHandler.rightHandAnimation
: ZapperRenderHandler.leftHandAnimation;
float animation = MathHelper.clamp(MathHelper.lerp(pt, last, current) * 5, 0, 1);
// Core glows
GlStateManager.disableLighting();
float multiplier = MathHelper.sin(worldTime * 5);
if (mainHand || offHand) {
multiplier = animation;
}
GLX.glMultiTexCoord2f(GLX.GL_TEXTURE1, multiplier * 240, 120);
itemRenderer.renderItem(stack, mainModel.getPartial("terrain_core"));
GLX.glMultiTexCoord2f(GLX.GL_TEXTURE1, lastCoordx, lastCoordy);
GlStateManager.enableLighting();
// Accelerator spins
float angle = worldTime * -25;
if (mainHand || offHand)
angle += 360 * animation;
angle %= 360;
float offset = -.155f;
GlStateManager.translatef(0, offset, 0);
GlStateManager.rotatef(angle, 0, 0, 1);
GlStateManager.translatef(0, -offset, 0);
itemRenderer.renderItem(stack, mainModel.getPartial("terrain_accelerator"));
GlStateManager.popMatrix();
}
}

View file

@ -0,0 +1,20 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import com.simibubi.create.foundation.block.render.CustomRenderedItemModel;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.tileentity.ItemStackTileEntityRenderer;
public class TerrainzapperModel extends CustomRenderedItemModel {
public TerrainzapperModel(IBakedModel template) {
super(template, "blockzapper");
addPartials("terrain_core", "terrain_accelerator");
}
@Override
public ItemStackTileEntityRenderer createRenderer() {
return new TerrainzapperItemRenderer();
}
}

View file

@ -0,0 +1,181 @@
package com.simibubi.create.modules.curiosities.zapper.terrainzapper;
import java.util.List;
import java.util.Vector;
import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.gui.widgets.IconButton;
import com.simibubi.create.foundation.gui.widgets.Label;
import com.simibubi.create.foundation.gui.widgets.ScrollInput;
import com.simibubi.create.foundation.gui.widgets.SelectionScrollInput;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.modules.curiosities.zapper.ZapperScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos;
public class TerrainzapperScreen extends ZapperScreen {
protected final String placementSection = Lang.translate("gui.terrainzapper.placement");
protected final String toolSection = Lang.translate("gui.terrainzapper.tool");
protected final List<String> brushOptions =
Lang.translatedOptions("gui.terrainzapper.brush", "cuboid", "sphere", "cylinder");
protected Vector<IconButton> toolButtons;
protected Vector<IconButton> placementButtons;
protected ScrollInput brushInput;
protected Label brushLabel;
protected Vector<ScrollInput> brushParams;
protected Vector<Label> brushParamLabels;
private int i;
private int j;
private CompoundNBT nbt;
public TerrainzapperScreen(ItemStack zapper, boolean offhand) {
super(ScreenResources.TERRAINZAPPER, zapper, offhand);
brightColor = 0xDFF6FF;
fontColor = 0x436B77;
title = Lang.translate("gui.terrainzapper.title");
nbt = zapper.getOrCreateTag();
}
@Override
protected void init() {
super.init();
i = guiLeft - 20;
j = guiTop;
brushLabel = new Label(i + 58, j + 28, "").withShadow();
brushInput = new SelectionScrollInput(i + 55, j + 25, 78, 14).forOptions(brushOptions)
.titled(Lang.translate("gui.terrainzapper.brush")).writingTo(brushLabel).calling(this::brushChanged);
if (nbt.contains("Brush"))
brushInput.setState(NBTHelper.readEnum(nbt.getString("Brush"), TerrainBrushes.class).ordinal());
widgets.add(brushLabel);
widgets.add(brushInput);
initBrushParams();
toolButtons = new Vector<>(6);
TerrainTools[] toolValues = TerrainTools.values();
for (int id = 0; id < toolValues.length; id++) {
TerrainTools tool = toolValues[id];
toolButtons.add(new IconButton(i + 8 + id * 18, j + 76, tool.icon));
toolButtons.get(id).setToolTip(Lang.translate("gui.terrainzapper.tool." + tool.translationKey));
}
if (nbt.contains("Tool"))
toolButtons.get(NBTHelper.readEnum(nbt.getString("Tool"), TerrainTools.class).ordinal()).active = false;
widgets.addAll(toolButtons);
placementButtons = new Vector<>(3);
PlacementOptions[] placementValues = PlacementOptions.values();
for (int id = 0; id < placementValues.length; id++) {
PlacementOptions option = placementValues[id];
placementButtons.add(new IconButton(i + 147 + id * 18, j + 76, option.icon));
placementButtons.get(id).setToolTip(Lang.translate("gui.terrainzapper.placement." + option.translationKey));
}
if (nbt.contains("Placement"))
placementButtons
.get(NBTHelper.readEnum(nbt.getString("Placement"), PlacementOptions.class).ordinal()).active =
false;
widgets.addAll(placementButtons);
}
public void initBrushParams() {
if (brushParams != null) {
nbt.put("BrushParams", NBTUtil.writeBlockPos(new BlockPos(brushParams.get(0).getState(),
brushParams.get(1).getState(), brushParams.get(2).getState())));
widgets.removeAll(brushParamLabels);
widgets.removeAll(brushParams);
}
brushParamLabels = new Vector<>(3);
brushParams = new Vector<>(3);
BlockPos data = NBTUtil.readBlockPos(nbt.getCompound("BrushParams"));
int[] params = new int[] { data.getX(), data.getY(), data.getZ() };
Brush currentBrush = TerrainBrushes.values()[brushInput.getState()].get();
for (int index = 0; index < 3; index++) {
Label label = new Label(i + 62 + 18 * index, j + 46, "").withShadow();
brushParamLabels.add(label);
int indexFinal = index;
ScrollInput input = new ScrollInput(i + 55 + 18 * index, j + 43, 14, 14)
.withRange(currentBrush.getMin(index), currentBrush.getMax(index) + 1).writingTo(label)
.titled(currentBrush.getParamLabel(index)).calling(state -> {
label.x = i + 62 + 18 * indexFinal - font.getStringWidth(label.text) / 2;
});
input.setState(params[index]);
input.onChanged();
if (index >= currentBrush.amtParams) {
input.visible = false;
label.visible = false;
input.active = false;
}
brushParams.add(input);
}
widgets.addAll(brushParamLabels);
widgets.addAll(brushParams);
}
private void brushChanged(int brushIndex) {
initBrushParams();
}
@Override
public boolean mouseClicked(double x, double y, int button) {
CompoundNBT nbt = zapper.getTag();
for (IconButton placementButton : placementButtons) {
if (placementButton.isHovered()) {
placementButtons.forEach(b -> b.active = true);
placementButton.active = false;
placementButton.playDownSound(Minecraft.getInstance().getSoundHandler());
nbt.putString("Placement", PlacementOptions.values()[placementButtons.indexOf(placementButton)].name());
}
}
for (IconButton toolButton : toolButtons) {
if (toolButton.isHovered()) {
toolButtons.forEach(b -> b.active = true);
toolButton.active = false;
toolButton.playDownSound(Minecraft.getInstance().getSoundHandler());
nbt.putString("Tool", TerrainTools.values()[toolButtons.indexOf(toolButton)].name());
}
}
return super.mouseClicked(x, y, button);
}
@Override
protected void drawOnBackground(int i, int j) {
super.drawOnBackground(i, j);
Brush currentBrush = TerrainBrushes.values()[brushInput.getState()].get();
for (int index = 2; index >= currentBrush.amtParams; index--) {
ScreenResources.TERRAINZAPPER_INACTIVE_PARAM.draw(i + 55 + index * 18, j + 43);
}
font.drawString(toolSection, i + 8, j + 64, fontColor);
font.drawString(placementSection, i + 148, j + 64, fontColor);
}
@Override
protected void writeAdditionalOptions(CompoundNBT nbt) {
super.writeAdditionalOptions(nbt);
nbt.putString("Brush", NBTHelper.writeEnum(TerrainBrushes.values()[brushInput.getState()]));
nbt.put("BrushParams", NBTUtil.writeBlockPos(new BlockPos(brushParams.get(0).getState(),
brushParams.get(1).getState(), brushParams.get(2).getState())));
}
}

View file

@ -4,6 +4,7 @@
"item.create.symmetry_wand": "Staff of Symmetry",
"item.create.placement_handgun": "Handheld Blockzapper",
"item.create.terrain_zapper": "Handheld Worldshaper",
"item.create.tree_fertilizer": "Tree Fertilizer",
"item.create.empty_blueprint": "Empty Schematic",
"item.create.andesite_alloy": "Andesite Alloy",
@ -298,6 +299,9 @@
"create.generic.range": "Range",
"create.generic.radius": "Radius",
"create.generic.width": "Width",
"create.generic.height": "Height",
"create.generic.length": "Length",
"create.generic.speed": "Speed",
"create.generic.delay": "Delay",
"create.generic.unit.ticks": "Ticks",
@ -340,7 +344,7 @@
"create.gui.blockzapper.searchFuzzy": "Ignore Material Borders",
"create.gui.blockzapper.range": "Spread Range",
"create.gui.blockzapper.needsUpgradedAmplifier": "Requires Upgraded Amplifier",
"create.gui.blockzapper.patternSection": "Patterns",
"create.gui.blockzapper.patternSection": "Pattern",
"create.gui.blockzapper.pattern.solid": "Solid",
"create.gui.blockzapper.pattern.checkered": "Checkerboard",
"create.gui.blockzapper.pattern.inversecheckered": "Inversed Checkerboard",
@ -348,6 +352,24 @@
"create.gui.blockzapper.pattern.chance50": "50% Roll",
"create.gui.blockzapper.pattern.chance75": "75% Roll",
"create.gui.terrainzapper.title": "Handheld Worldshaper",
"create.gui.terrainzapper.placement": "Placement",
"create.gui.terrainzapper.placement.merged": "Merged",
"create.gui.terrainzapper.placement.attached": "Attached",
"create.gui.terrainzapper.placement.inserted": "Inserted",
"create.gui.terrainzapper.brush": "Brush",
"create.gui.terrainzapper.brush.cuboid": "Cuboid",
"create.gui.terrainzapper.brush.sphere": "Sphere",
"create.gui.terrainzapper.brush.cylinder": "Cylinder",
"create.gui.terrainzapper.tool": "Tool",
"create.gui.terrainzapper.tool.fill": "Fill",
"create.gui.terrainzapper.tool.place": "Place",
"create.gui.terrainzapper.tool.replace": "Replace",
"create.gui.terrainzapper.tool.clear": "Clear",
"create.gui.terrainzapper.tool.overlay": "Overlay",
"create.gui.terrainzapper.tool.flatten": "Flatten",
"create.terrainzapper.shiftRightClickToSet": "Shift-Right-Click to Select a Shape",
"create.blockzapper.usingBlock": "Using: %1$s",
"create.blockzapper.componentUpgrades": "Component Upgrades:",
"create.blockzapper.component.body": "Body",
@ -668,6 +690,15 @@
"item.create.placement_handgun.tooltip.control3": "R-Click while Sneaking",
"item.create.placement_handgun.tooltip.action3": "Opens the _Configuration_ _Interface_",
"item.create.terrain_zapper.tooltip": "HANDHELD WORLDSHAPER",
"item.create.terrain_zapper.tooltip.summary": "Handy tool for creating _landscapes_ and _terrain_ _features._",
"item.create.terrain_zapper.tooltip.control1": "L-Click at Block",
"item.create.terrain_zapper.tooltip.action1": "Sets blocks placed by the tool to the targeted block.",
"item.create.terrain_zapper.tooltip.control2": "R-Click at Block",
"item.create.terrain_zapper.tooltip.action2": "Applies the currently selected _Brush_ and _Tool_ at the targeted location.",
"item.create.terrain_zapper.tooltip.control3": "R-Click while Sneaking",
"item.create.terrain_zapper.tooltip.action3": "Opens the _Configuration_ _Interface_",
"item.create.tree_fertilizer.tooltip": "TREE FERTILIZER",
"item.create.tree_fertilizer.tooltip.summary": "A powerful combination of minerals suitable for growing common tree types more quickly",
"item.create.tree_fertilizer.tooltip.condition1": "When used on Sapling",

View file

@ -0,0 +1,104 @@
{
"credit": "Made with Blockbench",
"parent": "create:item/placement_handgun",
"textures": {
"cog": "block/prismarine_bricks",
"particle": "block/obsidian"
},
"elements": [
{
"name": "Cog",
"from": [7.5, 3, 11],
"to": [8.5, 8, 14],
"faces": {
"north": {"uv": [6, 7, 7, 12], "texture": "#cog"},
"east": {"uv": [5, 3, 8, 8], "texture": "#cog"},
"south": {"uv": [4, 4, 5, 9], "texture": "#cog"},
"west": {"uv": [4, 6, 7, 11], "texture": "#cog"},
"up": {"uv": [4, 6, 5, 9], "texture": "#cog"},
"down": {"uv": [5, 6, 6, 9], "texture": "#cog"}
}
},
{
"name": "Cog",
"from": [7.5, 3, 11],
"to": [8.5, 8, 14],
"rotation": {"angle": 45, "axis": "z", "origin": [8, 5.5, 8]},
"faces": {
"north": {"uv": [6, 7, 7, 12], "texture": "#cog"},
"east": {"uv": [5, 3, 8, 8], "texture": "#cog"},
"south": {"uv": [4, 4, 5, 9], "texture": "#cog"},
"west": {"uv": [4, 6, 7, 11], "texture": "#cog"},
"up": {"uv": [4, 6, 5, 9], "texture": "#cog"},
"down": {"uv": [5, 6, 6, 9], "texture": "#cog"}
}
},
{
"name": "Cog",
"from": [7.5, 3, 11],
"to": [8.5, 8, 14],
"rotation": {"angle": -45, "axis": "z", "origin": [8, 5.5, 8]},
"faces": {
"north": {"uv": [6, 7, 7, 12], "texture": "#cog"},
"east": {"uv": [5, 3, 8, 8], "texture": "#cog"},
"south": {"uv": [4, 4, 5, 9], "texture": "#cog"},
"west": {"uv": [4, 6, 7, 11], "texture": "#cog"},
"up": {"uv": [4, 6, 5, 9], "texture": "#cog"},
"down": {"uv": [5, 6, 6, 9], "texture": "#cog"}
}
},
{
"name": "Cog",
"from": [5.5, 5, 11],
"to": [10.5, 6, 14],
"faces": {
"north": {"uv": [6, 7, 11, 8], "texture": "#cog"},
"east": {"uv": [5, 3, 8, 4], "texture": "#cog"},
"south": {"uv": [4, 4, 9, 5], "texture": "#cog"},
"west": {"uv": [4, 6, 7, 7], "texture": "#cog"},
"up": {"uv": [4, 6, 9, 9], "texture": "#cog"},
"down": {"uv": [5, 6, 10, 9], "texture": "#cog"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [1, 0, 0],
"translation": [0, 4, -2.5],
"scale": [0.8, 0.8, 0.8]
},
"thirdperson_lefthand": {
"rotation": [1, 0, 0],
"translation": [0, 4, -2.5],
"scale": [0.8, 0.8, 0.8]
},
"firstperson_righthand": {
"rotation": [10, 0, 10],
"translation": [1, 4, 1]
},
"firstperson_lefthand": {
"rotation": [10, 0, 10],
"translation": [1, 4, 1]
},
"ground": {
"rotation": [0, 0, 90],
"translation": [-2.25, -1, -0.75],
"scale": [0.75, 0.75, 0.75]
},
"gui": {
"rotation": [30, 45, 0],
"translation": [-0.5, 3.5, 0]
},
"fixed": {
"rotation": [0, 90, 0],
"translation": [-1.25, 4.25, -1]
}
},
"groups": [
{
"name": "accelerator",
"origin": [8, 8, 8],
"children": [0, 1, 2, 3]
}
]
}

View file

@ -0,0 +1,114 @@
{
"credit": "Made with Blockbench",
"parent": "create:item/placement_handgun",
"textures": {
"2": "block/white_concrete_powder",
"3": "block/white_stained_glass",
"particle": "block/obsidian"
},
"elements": [
{
"name": "Core",
"from": [6.8, 5, 0.7],
"to": [9.2, 6, 10.7],
"rotation": {"angle": 0, "axis": "y", "origin": [8.5, 8, 8]},
"faces": {
"north": {"uv": [6, 5, 8.4, 6], "texture": "#2"},
"east": {"uv": [3, 5, 13, 6], "texture": "#2"},
"west": {"uv": [3, 7, 13, 8], "texture": "#2"}
}
},
{
"name": "Core Glow",
"from": [6.6, 4.6, 0.5],
"to": [9.4, 6.4, 10.4],
"rotation": {"angle": 0, "axis": "y", "origin": [8.5, 8, 8]},
"faces": {
"north": {"uv": [6, 7, 8.8, 8.8], "texture": "#3"},
"east": {"uv": [3, 7, 12.9, 8.8], "texture": "#3"},
"south": {"uv": [5, 7, 7.8, 8.8], "texture": "#3"},
"west": {"uv": [4, 7, 13.9, 8.8], "texture": "#3"},
"up": {"uv": [7, 3, 9.8, 12.9], "texture": "#3"},
"down": {"uv": [7, 4, 9.8, 13.9], "texture": "#3"}
}
},
{
"name": "Amplifier Core",
"from": [6.8, 3, 2.7],
"to": [9.2, 4, 7.7],
"rotation": {"angle": 0, "axis": "y", "origin": [8.5, 8, 8]},
"faces": {
"north": {"uv": [6, 5, 8.4, 6], "texture": "#2"},
"east": {"uv": [3, 5, 8, 6], "texture": "#2"},
"south": {"uv": [0, 0, 2.4, 1], "texture": "#2"},
"west": {"uv": [3, 7, 8, 8], "texture": "#2"},
"up": {"uv": [0, 0, 2.4, 5], "texture": "#2"},
"down": {"uv": [0, 0, 2.4, 5], "texture": "#2"}
}
},
{
"name": "Amplifier Core Glow",
"from": [6.6, 2.6, 2.5],
"to": [9.4, 4.4, 7.4],
"rotation": {"angle": 0, "axis": "y", "origin": [8.5, 8, 8]},
"faces": {
"north": {"uv": [6, 7, 8.8, 8.8], "texture": "#3"},
"east": {"uv": [3, 7, 7.9, 8.8], "texture": "#3"},
"south": {"uv": [5, 7, 7.8, 8.8], "texture": "#3"},
"west": {"uv": [4, 7, 8.9, 8.8], "texture": "#3"},
"up": {"uv": [7, 3, 9.8, 7.9], "texture": "#3"},
"down": {"uv": [7, 4, 9.8, 8.9], "texture": "#3"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [1, 0, 0],
"translation": [0, 4, -2.5],
"scale": [0.8, 0.8, 0.8]
},
"thirdperson_lefthand": {
"rotation": [1, 0, 0],
"translation": [0, 4, -2.5],
"scale": [0.8, 0.8, 0.8]
},
"firstperson_righthand": {
"rotation": [10, 0, 10],
"translation": [1, 4, 1]
},
"firstperson_lefthand": {
"rotation": [10, 0, 10],
"translation": [1, 4, 1]
},
"ground": {
"rotation": [0, 0, 90],
"translation": [-2.25, -1, -0.75],
"scale": [0.75, 0.75, 0.75]
},
"gui": {
"rotation": [30, 45, 0],
"translation": [-0.5, 3.5, 0]
},
"fixed": {
"rotation": [0, 90, 0],
"translation": [-1.25, 4.25, -1]
}
},
"groups": [
{
"name": "core",
"origin": [8, 8, 8],
"children": [0, 1]
},
{
"name": "core",
"origin": [8, 8, 8],
"children": []
},
{
"name": "amplifier_core",
"origin": [8, 8, 8],
"children": [2, 3]
}
]
}

View file

@ -0,0 +1,243 @@
{
"credit": "Made with Blockbench",
"parent": "create:item/placement_handgun",
"textures": {
"0": "create:block/andesite_alloy_mesh",
"1": "block/smooth_stone_slab_side",
"particle": "block/obsidian",
"3_0": "block/obsidian",
"mesh": "create:block/terrain_zapper_mesh"
},
"elements": [
{
"name": "Connector",
"from": [6.5, 4, 0],
"to": [9.5, 5, 11],
"faces": {
"north": {"uv": [0, 0, 3, 1], "texture": "#0"},
"east": {"uv": [0, 0, 11, 1], "texture": "#0"},
"south": {"uv": [0, 0, 3, 1], "texture": "#0"},
"west": {"uv": [0, 0, 11, 1], "texture": "#0"},
"up": {"uv": [0, 0, 3, 11], "texture": "#0"},
"down": {"uv": [0, 0, 3, 11], "texture": "#0"}
}
},
{
"name": "Rod Back Cap",
"from": [6.5, 4, 14],
"to": [9.5, 7, 15],
"rotation": {"angle": -45, "axis": "z", "origin": [8, 5.5, 8]},
"faces": {
"north": {"uv": [0, 0, 3, 3], "texture": "#mesh"},
"east": {"uv": [8, 13, 9, 16], "texture": "#mesh"},
"south": {"uv": [2, 7, 5, 10], "texture": "#mesh"},
"west": {"uv": [2, 8, 3, 11], "texture": "#mesh"},
"up": {"uv": [3, 9, 6, 10], "texture": "#mesh"},
"down": {"uv": [7, 7, 10, 8], "texture": "#mesh"}
}
},
{
"name": "Rod Back Cap",
"from": [7, 4.5, 15],
"to": [9, 6.5, 17],
"rotation": {"angle": -45, "axis": "z", "origin": [8, 5.5, 8]},
"faces": {
"north": {"uv": [0, 0, 2, 2], "texture": "#0"},
"east": {"uv": [0, 0, 2, 2], "texture": "#0"},
"south": {"uv": [0, 0, 2, 2], "texture": "#0"},
"west": {"uv": [0, 0, 2, 2], "texture": "#0"},
"up": {"uv": [0, 0, 2, 2], "texture": "#0"},
"down": {"uv": [0, 0, 2, 2], "texture": "#0"}
}
},
{
"name": "Connector",
"from": [6.5, 6, 0],
"to": [9.5, 7, 11],
"faces": {
"north": {"uv": [0, 0, 3, 1], "texture": "#0"},
"east": {"uv": [0, 0, 11, 1], "texture": "#0"},
"south": {"uv": [0, 0, 3, 1], "texture": "#0"},
"west": {"uv": [0, 0, 11, 1], "texture": "#0"},
"up": {"uv": [0, 0, 3, 11], "texture": "#0"},
"down": {"uv": [0, 0, 3, 11], "texture": "#0"}
}
},
{
"name": "Connector",
"from": [5.75, 4, 6],
"to": [10.25, 8, 8],
"rotation": {"angle": 45, "axis": "x", "origin": [8, 4, 8]},
"faces": {
"north": {"uv": [0, 0, 4.5, 4], "texture": "#mesh"},
"east": {"uv": [12, 3, 16, 5], "rotation": 90, "texture": "#mesh"},
"south": {"uv": [4, 7, 8.5, 11], "texture": "#mesh"},
"west": {"uv": [0, 3, 4, 5], "rotation": 270, "texture": "#mesh"},
"up": {"uv": [5, 3, 9.5, 5], "texture": "#mesh"},
"down": {"uv": [5, 3, 9.5, 5], "texture": "#mesh"}
}
},
{
"name": "Connector",
"from": [5.75, 4, 8],
"to": [10.25, 6, 10],
"rotation": {"angle": 45, "axis": "x", "origin": [8, 4, 8]},
"faces": {
"north": {"uv": [0, 0, 4.5, 2], "texture": "#mesh"},
"east": {"uv": [12, 3, 14, 5], "rotation": 180, "texture": "#mesh"},
"south": {"uv": [5, 3, 9.5, 5], "rotation": 180, "texture": "#mesh"},
"west": {"uv": [4, 3, 6, 5], "rotation": 180, "texture": "#mesh"},
"up": {"uv": [5, 4, 9.5, 6], "texture": "#mesh"},
"down": {"uv": [0, 3, 4.5, 5], "texture": "#mesh"}
}
},
{
"name": "Rod Back Cap",
"from": [6, 4.5, 9],
"to": [10, 6.5, 11],
"rotation": {"angle": 0, "axis": "z", "origin": [8, 5.5, 8]},
"faces": {
"north": {"uv": [0, 0, 4, 2], "texture": "#0"},
"east": {"uv": [0, 0, 2, 2], "texture": "#0"},
"south": {"uv": [0, 0, 4, 2], "texture": "#0"},
"west": {"uv": [0, 0, 2, 2], "texture": "#0"},
"up": {"uv": [0, 0, 4, 2], "texture": "#0"},
"down": {"uv": [0, 0, 4, 2], "texture": "#0"}
}
},
{
"name": "Grip",
"from": [7.1, 1, 12],
"to": [8.9, 5, 15],
"rotation": {"angle": -22.5, "axis": "x", "origin": [8, 2, 14]},
"faces": {
"north": {"uv": [5, 7, 6.8, 11], "texture": "#1"},
"east": {"uv": [13, 7, 16, 11], "texture": "#1"},
"south": {"uv": [7, 7, 8.8, 11], "texture": "#1"},
"west": {"uv": [16, 7, 13, 11], "texture": "#1"},
"up": {"uv": [5, 2, 6.8, 5], "texture": "#1"},
"down": {"uv": [7, 11, 8.8, 14], "texture": "#1"}
}
},
{
"name": "Trigger",
"from": [7.4, 3, 11],
"to": [8.6, 6, 13],
"rotation": {"angle": -22.5, "axis": "x", "origin": [8, 2, 14]},
"faces": {
"north": {"uv": [6, 4, 7, 7], "texture": "#3_0"},
"east": {"uv": [6, 4, 8, 7], "texture": "#3_0"},
"south": {"uv": [6, 4, 7, 7], "texture": "#3_0"},
"west": {"uv": [5, 4, 7, 7], "texture": "#3_0"},
"up": {"uv": [6, 6, 7, 8], "texture": "#3_0"},
"down": {"uv": [5, 6, 6, 8], "texture": "#3_0"}
}
},
{
"name": "Scope",
"from": [7.5, 7.5, 5.5],
"to": [8.5, 8.5, 10.5],
"rotation": {"angle": 45, "axis": "z", "origin": [8, 8, 4]},
"faces": {
"north": {"uv": [0, 0, 1, 1], "texture": "#1"},
"east": {"uv": [8, 1, 9, 6], "rotation": 90, "texture": "#mesh"},
"south": {"uv": [0, 0, 1, 1], "texture": "#1"},
"west": {"uv": [7, 1, 8, 6], "rotation": 270, "texture": "#mesh"},
"up": {"uv": [7, 1, 8, 6], "texture": "#mesh"},
"down": {"uv": [7, 1, 8, 6], "texture": "#mesh"}
}
},
{
"name": "Scope Connector",
"from": [7.5, 7, 7],
"to": [8.5, 8, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 4]},
"faces": {
"north": {"uv": [0, 0, 1, 1], "texture": "#mesh"},
"east": {"uv": [0, 0, 1, 1], "texture": "#mesh"},
"south": {"uv": [0, 0, 1, 1], "texture": "#mesh"},
"west": {"uv": [0, 0, 1, 1], "texture": "#mesh"},
"up": {"uv": [0, 0, 1, 1], "texture": "#mesh"},
"down": {"uv": [0, 0, 1, 1], "texture": "#mesh"}
}
},
{
"name": "Amplifier Connector",
"from": [6.5, 2, 2],
"to": [9.5, 3, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8.5, 8, 8]},
"faces": {
"north": {"uv": [3, 8, 6, 9], "texture": "#mesh"},
"east": {"uv": [0, 12, 7, 13], "rotation": 180, "texture": "#mesh"},
"south": {"uv": [1, 8, 4, 9], "texture": "#mesh"},
"west": {"uv": [0, 12, 7, 13], "rotation": 180, "texture": "#mesh"},
"up": {"uv": [3, 7, 10, 10], "rotation": 90, "texture": "#mesh"},
"down": {"uv": [3, 7, 10, 10], "rotation": 90, "texture": "#mesh"}
}
},
{
"name": "Amplifier Connector",
"from": [6.5, 3, 7],
"to": [9.5, 4, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8.5, 8, 8]},
"faces": {
"north": {"uv": [0, 0, 3, 1], "texture": "#mesh"},
"east": {"uv": [0, 0, 2, 1], "texture": "#mesh"},
"south": {"uv": [0, 0, 3, 1], "texture": "#mesh"},
"west": {"uv": [0, 0, 2, 1], "texture": "#mesh"},
"up": {"uv": [0, 0, 3, 2], "texture": "#mesh"},
"down": {"uv": [0, 0, 3, 2], "texture": "#mesh"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [1, 0, 0],
"translation": [0, 4, -2.5],
"scale": [0.8, 0.8, 0.8]
},
"thirdperson_lefthand": {
"rotation": [1, 0, 0],
"translation": [0, 4, -2.5],
"scale": [0.8, 0.8, 0.8]
},
"firstperson_righthand": {
"rotation": [10, 0, 10],
"translation": [1, 4, 1]
},
"firstperson_lefthand": {
"rotation": [10, 0, 10],
"translation": [1, 4, 1]
},
"ground": {
"rotation": [0, 0, 90],
"translation": [-2.25, -1, -0.75],
"scale": [0.75, 0.75, 0.75]
},
"gui": {
"rotation": [30, 45, 0],
"translation": [-0.5, 3.5, 0]
},
"fixed": {
"rotation": [0, 90, 0],
"translation": [-1.25, 4.25, -1]
}
},
"groups": [0, 1, 2, 3, 4, 5, 6,
{
"name": "placement_handgun",
"origin": [8, 8, 8],
"children": [7, 8]
},
{
"name": "chorus_scope",
"origin": [8, 8, 8],
"children": [9, 10]
},
{
"name": "chorus_amplifier",
"origin": [8, 8, 8],
"children": [11, 12]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB