Sailing craft

- Front face of crafters now supports hoppers and co
- Scenes for Sails and Mechanical Crafters
- Fixed a couple strange quirks of Ponder text windows
This commit is contained in:
simibubi 2021-03-21 17:38:33 +01:00
parent c75a0f8aa7
commit 140cd9a85a
15 changed files with 634 additions and 47 deletions

View file

@ -37,7 +37,6 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
public class MechanicalCrafterTileEntity extends KineticTileEntity { public class MechanicalCrafterTileEntity extends KineticTileEntity {
@ -46,7 +45,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
IDLE, ACCEPTING, ASSEMBLING, EXPORTING, WAITING, CRAFTING, INSERTING; IDLE, ACCEPTING, ASSEMBLING, EXPORTING, WAITING, CRAFTING, INSERTING;
} }
static class Inventory extends SmartInventory { public static class Inventory extends SmartInventory {
private MechanicalCrafterTileEntity te; private MechanicalCrafterTileEntity te;
@ -57,7 +56,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
whenContentsChanged(slot -> { whenContentsChanged(slot -> {
if (getStackInSlot(slot).isEmpty()) if (getStackInSlot(slot).isEmpty())
return; return;
if(te.phase == Phase.IDLE) if (te.phase == Phase.IDLE)
te.checkCompletedRecipe(false); te.checkCompletedRecipe(false);
}); });
} }
@ -87,6 +86,8 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
private InvManipulationBehaviour inserting; private InvManipulationBehaviour inserting;
private EdgeInteractionBehaviour connectivity; private EdgeInteractionBehaviour connectivity;
private ItemStack scriptedResult = ItemStack.EMPTY;
public MechanicalCrafterTileEntity(TileEntityType<? extends MechanicalCrafterTileEntity> type) { public MechanicalCrafterTileEntity(TileEntityType<? extends MechanicalCrafterTileEntity> type) {
super(type); super(type);
setLazyTickRate(20); setLazyTickRate(20);
@ -205,10 +206,13 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
if (phase == Phase.ACCEPTING) if (phase == Phase.ACCEPTING)
return; return;
boolean onClient = world.isRemote;
boolean runLogic = !onClient || isVirtual();
if (wasPoweredBefore != world.isBlockPowered(pos)) { if (wasPoweredBefore != world.isBlockPowered(pos)) {
wasPoweredBefore = world.isBlockPowered(pos); wasPoweredBefore = world.isBlockPowered(pos);
if (wasPoweredBefore) { if (wasPoweredBefore) {
if (world.isRemote) if (!runLogic)
return; return;
checkCompletedRecipe(true); checkCompletedRecipe(true);
} }
@ -218,7 +222,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
countDown -= getCountDownSpeed(); countDown -= getCountDownSpeed();
if (countDown < 0) { if (countDown < 0) {
countDown = 0; countDown = 0;
if (world.isRemote) if (!runLogic)
return; return;
if (RecipeGridHandler.getTargetingCrafter(this) != null) { if (RecipeGridHandler.getTargetingCrafter(this) != null) {
phase = Phase.EXPORTING; phase = Phase.EXPORTING;
@ -226,9 +230,11 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
sendData(); sendData();
return; return;
} }
ItemStack result = RecipeGridHandler.tryToApplyRecipe(world, groupedItems);
if (result != null) {
ItemStack result =
isVirtual() ? scriptedResult : RecipeGridHandler.tryToApplyRecipe(world, groupedItems);
if (result != null) {
List<ItemStack> containers = new ArrayList<>(); List<ItemStack> containers = new ArrayList<>();
groupedItems.grid.values() groupedItems.grid.values()
.forEach(stack -> { .forEach(stack -> {
@ -237,6 +243,9 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
.copy()); .copy());
}); });
if (isVirtual())
groupedItemsBeforeCraft = groupedItems;
groupedItems = new GroupedItems(result); groupedItems = new GroupedItems(result);
for (int i = 0; i < containers.size(); i++) { for (int i = 0; i < containers.size(); i++) {
ItemStack stack = containers.get(i); ItemStack stack = containers.get(i);
@ -260,7 +269,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
if (countDown < 0) { if (countDown < 0) {
countDown = 0; countDown = 0;
if (world.isRemote) if (!runLogic)
return; return;
MechanicalCrafterTileEntity targetingCrafter = RecipeGridHandler.getTargetingCrafter(this); MechanicalCrafterTileEntity targetingCrafter = RecipeGridHandler.getTargetingCrafter(this);
@ -283,7 +292,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
if (phase == Phase.CRAFTING) { if (phase == Phase.CRAFTING) {
if (world.isRemote) { if (onClient) {
Direction facing = getBlockState().get(MechanicalCrafterBlock.HORIZONTAL_FACING); Direction facing = getBlockState().get(MechanicalCrafterBlock.HORIZONTAL_FACING);
float progress = countDown / 2000f; float progress = countDown / 2000f;
Vec3d facingVec = new Vec3d(facing.getDirectionVec()); Vec3d facingVec = new Vec3d(facing.getDirectionVec());
@ -319,7 +328,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
countDown -= getCountDownSpeed(); countDown -= getCountDownSpeed();
if (countDown < 0) { if (countDown < 0) {
countDown = 0; countDown = 0;
if (world.isRemote) if (!runLogic)
return; return;
tryInsert(); tryInsert();
return; return;
@ -327,7 +336,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
} }
if (phase == Phase.INSERTING) { if (phase == Phase.INSERTING) {
if (!world.isRemote && isTargetingBelt()) if (runLogic && isTargetingBelt())
tryInsert(); tryInsert();
return; return;
} }
@ -410,7 +419,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
@Override @Override
public void lazyTick() { public void lazyTick() {
super.lazyTick(); super.lazyTick();
if (world.isRemote) if (world.isRemote && !isVirtual())
return; return;
if (phase == Phase.IDLE && craftingItemPresent()) if (phase == Phase.IDLE && craftingItemPresent())
checkCompletedRecipe(false); checkCompletedRecipe(false);
@ -431,7 +440,7 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
protected void checkCompletedRecipe(boolean poweredStart) { protected void checkCompletedRecipe(boolean poweredStart) {
if (getSpeed() == 0) if (getSpeed() == 0)
return; return;
if (world.isRemote) if (world.isRemote && !isVirtual())
return; return;
List<MechanicalCrafterTileEntity> chain = RecipeGridHandler.getAllCraftersOfChainIf(this, List<MechanicalCrafterTileEntity> chain = RecipeGridHandler.getAllCraftersOfChainIf(this,
poweredStart ? MechanicalCrafterTileEntity::craftingItemPresent poweredStart ? MechanicalCrafterTileEntity::craftingItemPresent
@ -471,11 +480,8 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
@Override @Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) { public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { if (isItemHandlerCap(cap))
if (getBlockState().get(HORIZONTAL_FACING) == side)
return LazyOptional.empty();
return invSupplier.cast(); return invSupplier.cast();
}
return super.getCapability(cap, side); return super.getCapability(cap, side);
} }
@ -495,4 +501,8 @@ public class MechanicalCrafterTileEntity extends KineticTileEntity {
return true; return true;
} }
public void setScriptedResult(ItemStack scriptedResult) {
this.scriptedResult = scriptedResult;
}
} }

View file

@ -451,14 +451,13 @@ public class PonderScene {
return ms; return ms;
} }
public void updateSceneRVE() { public void updateSceneRVE(float pt) {
Vec3d v = screenToScene(width / 2, height / 2, 500); Vec3d v = screenToScene(width / 2, height / 2, 500, pt);
renderViewEntity.setPosition(v.x, v.y, v.z); renderViewEntity.setPosition(v.x, v.y, v.z);
} }
public Vec3d screenToScene(double x, double y, int depth) { public Vec3d screenToScene(double x, double y, int depth, float pt) {
refreshMatrix(); refreshMatrix(pt);
float pt = AnimationTickHolder.getPartialTicks();
Vec3d vec = new Vec3d(x, y, depth); Vec3d vec = new Vec3d(x, y, depth);
vec = vec.subtract(width / 2, height / 2, 200 + offset); vec = vec.subtract(width / 2, height / 2, 200 + offset);
@ -479,17 +478,17 @@ public class PonderScene {
return vec; return vec;
} }
public Vec2f sceneToScreen(Vec3d vec) { public Vec2f sceneToScreen(Vec3d vec, float pt) {
refreshMatrix(); refreshMatrix(pt);
Vector4f vec4 = new Vector4f((float) vec.x, (float) vec.y, (float) vec.z, 1); Vector4f vec4 = new Vector4f((float) vec.x, (float) vec.y, (float) vec.z, 1);
vec4.transform(cachedMat); vec4.transform(cachedMat);
return new Vec2f(vec4.getX(), vec4.getY()); return new Vec2f(vec4.getX(), vec4.getY());
} }
protected void refreshMatrix() { protected void refreshMatrix(float pt) {
if (cachedMat != null) if (cachedMat != null)
return; return;
cachedMat = apply(new MatrixStack()).peek() cachedMat = apply(new MatrixStack(), pt, false).peek()
.getModel(); .getModel();
} }

View file

@ -305,8 +305,8 @@ public class PonderUI extends NavigatableSimiScreen {
double mouseX = minecraft.mouseHelper.getMouseX() * w.getScaledWidth() / w.getWidth(); double mouseX = minecraft.mouseHelper.getMouseX() * w.getScaledWidth() / w.getWidth();
double mouseY = minecraft.mouseHelper.getMouseY() * w.getScaledHeight() / w.getHeight(); double mouseY = minecraft.mouseHelper.getMouseY() * w.getScaledHeight() / w.getHeight();
SceneTransform t = activeScene.getTransform(); SceneTransform t = activeScene.getTransform();
Vec3d vec1 = t.screenToScene(mouseX, mouseY, 1000); Vec3d vec1 = t.screenToScene(mouseX, mouseY, 1000, 0);
Vec3d vec2 = t.screenToScene(mouseX, mouseY, -100); Vec3d vec2 = t.screenToScene(mouseX, mouseY, -100, 0);
Pair<ItemStack, BlockPos> pair = activeScene.rayTraceScene(vec1, vec2); Pair<ItemStack, BlockPos> pair = activeScene.rayTraceScene(vec1, vec2);
hoveredTooltipItem = pair.getFirst(); hoveredTooltipItem = pair.getFirst();
hoveredBlockPos = pair.getSecond(); hoveredBlockPos = pair.getSecond();
@ -391,7 +391,7 @@ public class PonderUI extends NavigatableSimiScreen {
ms.push(); ms.push();
story.transform.updateScreenParams(width, height, slide); story.transform.updateScreenParams(width, height, slide);
story.transform.apply(ms, partialTicks, false); story.transform.apply(ms, partialTicks, false);
story.transform.updateSceneRVE(); story.transform.updateSceneRVE(partialTicks);
story.renderScene(buffer, ms, partialTicks); story.renderScene(buffer, ms, partialTicks);
buffer.draw(); buffer.draw();
@ -698,7 +698,7 @@ public class PonderUI extends NavigatableSimiScreen {
RenderSystem.pushMatrix(); RenderSystem.pushMatrix();
PonderScene story = scenes.get(i); PonderScene story = scenes.get(i);
MatrixStack ms = new MatrixStack(); MatrixStack ms = new MatrixStack();
story.renderOverlay(this, ms, partialTicks); story.renderOverlay(this, ms, skipCooling > 0 ? 0 : identifyMode ? ponderPartialTicksPaused : partialTicks);
RenderSystem.popMatrix(); RenderSystem.popMatrix();
} }

View file

@ -10,6 +10,8 @@ import java.util.function.UnaryOperator;
import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel; import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.content.contraptions.base.KineticBlock; import com.simibubi.create.content.contraptions.base.KineticBlock;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.components.crafter.ConnectedInputHandler;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterTileEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueItem; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueItem;
import com.simibubi.create.content.contraptions.particle.RotationIndicatorParticleData; import com.simibubi.create.content.contraptions.particle.RotationIndicatorParticleData;
@ -777,6 +779,17 @@ public class SceneBuilder {
modifyTileEntity(position, FunnelTileEntity.class, funnel -> funnel.flap(!outward)); modifyTileEntity(position, FunnelTileEntity.class, funnel -> funnel.flap(!outward));
} }
public void setCraftingResult(BlockPos crafter, ItemStack output) {
modifyTileEntity(crafter, MechanicalCrafterTileEntity.class, mct -> mct.setScriptedResult(output));
}
public void connectCrafterInvs(BlockPos position1, BlockPos position2) {
addInstruction(s -> {
ConnectedInputHandler.toggleConnection(s.getWorld(), position1, position2);
s.forEach(WorldSectionElement.class, WorldSectionElement::queueRedraw);
});
}
} }
public class DebugInstructions { public class DebugInstructions {

View file

@ -14,6 +14,9 @@ import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pointing; import com.simibubi.create.foundation.utility.Pointing;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.item.DyeColor;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -566,4 +569,91 @@ public class BearingScenes {
} }
} }
public static void sail(SceneBuilder scene, SceneBuildingUtil util) {
sails(scene, util, false);
}
public static void sailFrame(SceneBuilder scene, SceneBuildingUtil util) {
sails(scene, util, true);
}
private static void sails(SceneBuilder scene, SceneBuildingUtil util, boolean frame) {
String plural = frame ? "Sail Frames" : "Sails";
scene.title(frame ? "sail_frame" : "sail", "Assembling Windmills using " + plural);
scene.configureBasePlate(0, 0, 5);
scene.scaleSceneView(0.9f);
scene.world.showSection(util.select.layer(0), Direction.UP);
scene.idle(5);
BlockPos bearingPos = util.grid.at(2, 1, 2);
scene.world.showSection(util.select.position(bearingPos), Direction.DOWN);
scene.idle(5);
ElementLink<WorldSectionElement> plank =
scene.world.showIndependentSection(util.select.position(bearingPos.up()), Direction.DOWN);
scene.idle(10);
for (int i = 0; i < 3; i++) {
for (Direction d : Iterate.horizontalDirections) {
BlockPos location = bearingPos.up(i + 1)
.offset(d);
if (frame)
scene.world.modifyBlock(location, s -> AllBlocks.SAIL_FRAME.getDefaultState()
.with(SailBlock.FACING, s.get(SailBlock.FACING)), false);
scene.world.showSectionAndMerge(util.select.position(location), d.getOpposite(), plank);
scene.idle(2);
}
}
scene.overlay.showText(70)
.text(plural + " are handy blocks to create Windmills with")
.pointAt(util.vector.blockSurface(util.grid.at(1, 3, 2), Direction.WEST))
.placeNearTarget()
.attachKeyFrame();
scene.idle(80);
scene.overlay.showSelectionWithText(util.select.position(bearingPos.up()), 80)
.colored(PonderPalette.GREEN)
.text("They will attach to blocks and each other without the need of Super Glue or Chassis Blocks")
.attachKeyFrame()
.placeNearTarget();
scene.idle(40);
scene.world.configureCenterOfRotation(plank, util.vector.centerOf(bearingPos));
if (!frame) {
scene.world.rotateBearing(bearingPos, 180, 75);
scene.world.rotateSection(plank, 0, 180, 0, 75);
scene.idle(76);
scene.world.rotateBearing(bearingPos, 180, 0);
scene.world.rotateSection(plank, 0, 180, 0, 0);
scene.rotateCameraY(-30);
scene.idle(10);
InputWindowElement input =
new InputWindowElement(util.vector.blockSurface(util.grid.at(2, 3, 1), Direction.NORTH), Pointing.RIGHT)
.withItem(new ItemStack(Items.BLUE_DYE));
scene.overlay.showControls(input, 30);
scene.idle(7);
scene.world.setBlock(util.grid.at(2, 3, 1), AllBlocks.DYED_SAILS[DyeColor.BLUE.ordinal()].getDefaultState()
.with(SailBlock.FACING, Direction.WEST), true);
scene.idle(10);
scene.overlay.showText(40)
.colored(PonderPalette.BLUE)
.text("Right Click with Dye to paint them")
.attachKeyFrame()
.pointAt(util.vector.blockSurface(util.grid.at(2, 3, 1), Direction.WEST))
.placeNearTarget();
scene.idle(20);
scene.overlay.showControls(input, 30);
scene.idle(7);
scene.world.replaceBlocks(util.select.fromTo(2, 2, 1, 2, 4, 1),
AllBlocks.DYED_SAILS[DyeColor.BLUE.ordinal()].getDefaultState()
.with(SailBlock.FACING, Direction.WEST),
true);
scene.idle(20);
}
scene.world.rotateBearing(bearingPos, 720, 300);
scene.world.rotateSection(plank, 0, 720, 0, 300);
}
} }

View file

@ -0,0 +1,453 @@
package com.simibubi.create.foundation.ponder.content;
import java.util.Collection;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterBlock;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterTileEntity;
import com.simibubi.create.foundation.ponder.ElementLink;
import com.simibubi.create.foundation.ponder.SceneBuilder;
import com.simibubi.create.foundation.ponder.SceneBuildingUtil;
import com.simibubi.create.foundation.ponder.Selection;
import com.simibubi.create.foundation.ponder.elements.EntityElement;
import com.simibubi.create.foundation.ponder.elements.InputWindowElement;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pointing;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.items.ItemHandlerHelper;
public class CrafterScenes {
public static void setup(SceneBuilder scene, SceneBuildingUtil util) {
scene.title("mechanical_crafter", "Setting up Mechanical Crafters");
scene.configureBasePlate(0, 0, 5);
scene.world.showSection(util.select.layer(0), Direction.UP);
scene.world.modifyKineticSpeed(util.select.everywhere(), f -> 1.5f * f);
Selection redstone = util.select.fromTo(3, 1, 0, 3, 1, 1);
Selection kinetics = util.select.fromTo(4, 1, 2, 4, 1, 5);
BlockPos depotPos = util.grid.at(0, 1, 2);
Selection crafters = util.select.fromTo(1, 1, 2, 3, 3, 2);
scene.world.modifyBlocks(crafters, s -> s.with(MechanicalCrafterBlock.POINTING, Pointing.DOWN), false);
scene.world.setKineticSpeed(crafters, 0);
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
scene.world.showSection(util.select.position(y == 1 ? x + 1 : 3 - x, y + 1, 2), Direction.DOWN);
scene.idle(2);
}
}
scene.overlay.showText(70)
.text("An array of Mechanical Crafters can be used to automate any Crafting Recipe")
.pointAt(util.vector.blockSurface(util.grid.at(1, 2, 2), Direction.WEST))
.attachKeyFrame()
.placeNearTarget();
scene.idle(80);
scene.overlay.showControls(
new InputWindowElement(util.vector.blockSurface(util.grid.at(2, 3, 2), Direction.NORTH), Pointing.RIGHT)
.rightClick()
.withWrench(),
40);
scene.idle(7);
scene.world.cycleBlockProperty(util.grid.at(2, 3, 2), MechanicalCrafterBlock.POINTING);
scene.idle(10);
scene.overlay.showText(50)
.text("Using a Wrench, the Crafters' paths can be arranged")
.pointAt(util.vector.blockSurface(util.grid.at(2, 3, 2), Direction.NORTH))
.attachKeyFrame()
.placeNearTarget();
scene.idle(60);
BlockPos[] positions = new BlockPos[] { util.grid.at(3, 1, 2), util.grid.at(2, 1, 2), util.grid.at(1, 1, 2) };
for (BlockPos pos : positions) {
scene.overlay.showControls(
new InputWindowElement(util.vector.blockSurface(pos, Direction.NORTH), Pointing.RIGHT).rightClick()
.withWrench(),
10);
scene.idle(7);
scene.world.cycleBlockProperty(pos, MechanicalCrafterBlock.POINTING);
scene.idle(15);
}
scene.overlay.showText(100)
.text("For a valid setup, all paths have to converge into one exit at any side")
.pointAt(util.vector.blockSurface(util.grid.at(1, 1, 2), Direction.WEST)
.add(0, 0, -.5f))
.colored(PonderPalette.GREEN)
.attachKeyFrame()
.placeNearTarget();
scene.idle(60);
Collection<Couple<BlockPos>> couples =
ImmutableList.of(Couple.create(util.grid.at(3, 3, 2), util.grid.at(3, 2, 2)),
Couple.create(util.grid.at(3, 2, 2), util.grid.at(3, 1, 2)),
Couple.create(util.grid.at(2, 3, 2), util.grid.at(1, 3, 2)),
Couple.create(util.grid.at(3, 1, 2), util.grid.at(2, 1, 2)),
Couple.create(util.grid.at(1, 3, 2), util.grid.at(1, 2, 2)),
Couple.create(util.grid.at(2, 2, 2), util.grid.at(2, 1, 2)),
Couple.create(util.grid.at(1, 2, 2), util.grid.at(1, 1, 2)),
Couple.create(util.grid.at(2, 1, 2), util.grid.at(1, 1, 2)),
Couple.create(util.grid.at(1, 1, 2), util.grid.at(0, 1, 2)));
for (Couple<BlockPos> c : couples) {
scene.idle(5);
Vec3d p1 = util.vector.blockSurface(c.getFirst(), Direction.NORTH)
.add(0, 0, -0.125);
Vec3d p2 = util.vector.blockSurface(c.getSecond(), Direction.NORTH)
.add(0, 0, -0.125);
AxisAlignedBB point = new AxisAlignedBB(p1, p1);
AxisAlignedBB line = new AxisAlignedBB(p1, p2);
scene.overlay.chaseBoundingBoxOutline(PonderPalette.GREEN, p1, point, 2);
scene.idle(1);
scene.overlay.chaseBoundingBoxOutline(PonderPalette.GREEN, p1, line, 30);
}
scene.world.showSection(util.select.position(depotPos), Direction.EAST);
scene.idle(20);
scene.overlay.showText(60)
.text("The outputs will be placed into the inventory at the exit")
.pointAt(util.vector.blockSurface(util.grid.at(0, 1, 2), Direction.NORTH))
.placeNearTarget();
scene.idle(70);
scene.rotateCameraY(60);
scene.idle(20);
scene.world.showSection(kinetics, Direction.NORTH);
scene.overlay.showText(60)
.text("Mechanical Crafters require Rotational Force to operate")
.pointAt(util.vector.blockSurface(util.grid.at(4, 1, 2), Direction.NORTH))
.attachKeyFrame()
.placeNearTarget();
scene.idle(8);
scene.world.setKineticSpeed(crafters, -48);
scene.world.multiplyKineticSpeed(util.select.position(3, 2, 2)
.add(util.select.position(2, 3, 2))
.add(util.select.position(1, 2, 2))
.add(util.select.position(2, 1, 2)), -1);
scene.idle(55);
scene.rotateCameraY(-60);
scene.idle(40);
ItemStack planks = new ItemStack(Items.OAK_PLANKS);
scene.overlay.showControls(
new InputWindowElement(util.vector.blockSurface(util.grid.at(1, 3, 2), Direction.NORTH), Pointing.RIGHT)
.rightClick()
.withItem(planks),
40);
scene.idle(7);
Class<MechanicalCrafterTileEntity> type = MechanicalCrafterTileEntity.class;
scene.world.modifyTileEntity(util.grid.at(1, 3, 2), type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.idle(10);
scene.overlay.showText(50)
.text("Right-Click the front to insert Items manually")
.pointAt(util.vector.blockSurface(util.grid.at(1, 3, 2), Direction.NORTH))
.attachKeyFrame()
.placeNearTarget();
scene.idle(60);
ItemStack alloy = AllItems.ANDESITE_ALLOY.asStack();
ItemStack log = new ItemStack(Items.OAK_LOG);
scene.world.setCraftingResult(util.grid.at(1, 1, 2), AllBlocks.ANDESITE_CASING.asStack(4));
scene.world.modifyTileEntity(util.grid.at(2, 3, 2), type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(3, 3, 2), type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(3, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, alloy.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(2, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, log.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(1, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, alloy.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(1, 1, 2), type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(2, 1, 2), type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(3, 1, 2), type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.overlay.showText(80)
.attachKeyFrame()
.text("Once every slot of a path contains an Item, the crafting process will begin")
.pointAt(util.vector.blockSurface(util.grid.at(1, 3, 2), Direction.WEST))
.placeNearTarget();
scene.idle(180);
scene.world.removeItemsFromBelt(depotPos);
ItemStack stick = new ItemStack(Items.STICK);
ItemStack iron = new ItemStack(Items.IRON_INGOT);
scene.world.setCraftingResult(util.grid.at(1, 1, 2), new ItemStack(Items.IRON_PICKAXE));
scene.world.modifyTileEntity(util.grid.at(1, 3, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
scene.idle(2);
scene.world.modifyTileEntity(util.grid.at(2, 3, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
scene.idle(2);
scene.world.modifyTileEntity(util.grid.at(3, 3, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
scene.idle(2);
scene.world.modifyTileEntity(util.grid.at(2, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, stick.copy(), false));
scene.idle(2);
scene.world.modifyTileEntity(util.grid.at(2, 1, 2), type, mct -> mct.getInventory()
.insertItem(0, stick.copy(), false));
scene.world.showSection(redstone, Direction.SOUTH);
scene.idle(10);
scene.overlay.showText(90)
.attachKeyFrame()
.colored(PonderPalette.RED)
.text("For recipes not fully occupying the crafter setup, the start can be forced using a Redstone Pulse")
.pointAt(util.vector.blockSurface(util.grid.at(1, 2, 2), Direction.NORTH))
.placeNearTarget();
scene.idle(100);
scene.effects.indicateRedstone(util.grid.at(3, 1, 0));
scene.world.toggleRedstonePower(redstone);
scene.idle(20);
scene.world.toggleRedstonePower(redstone);
}
public static void connect(SceneBuilder scene, SceneBuildingUtil util) {
scene.title("mechanical_crafter_connect", "Connecting Inventories of Crafters");
scene.configureBasePlate(0, 0, 5);
scene.world.showSection(util.select.layer(0), Direction.UP);
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 2; x++) {
scene.world.showSection(util.select.position(y == 1 ? x + 1 : 2 - x, y + 1, 2), Direction.DOWN);
scene.idle(2);
}
}
Class<MechanicalCrafterTileEntity> type = MechanicalCrafterTileEntity.class;
BlockPos depotPos = util.grid.at(0, 1, 2);
Selection funnel = util.select.fromTo(4, 1, 5, 4, 1, 2)
.add(util.select.fromTo(3, 2, 2, 3, 1, 2));
Selection kinetics = util.select.position(3, 3, 2)
.add(util.select.fromTo(3, 3, 3, 3, 1, 3));
scene.idle(5);
scene.world.showSection(kinetics, Direction.NORTH);
scene.idle(5);
scene.world.showSection(util.select.position(depotPos), Direction.EAST);
scene.idle(10);
scene.world.showSection(funnel, Direction.WEST);
scene.rotateCameraY(60);
ItemStack planks = new ItemStack(Items.OAK_PLANKS);
scene.world.createItemOnBelt(util.grid.at(4, 1, 2), Direction.EAST, planks.copy());
scene.idle(22);
scene.world.modifyTileEntity(util.grid.at(2, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.world.removeItemsFromBelt(util.grid.at(3, 1, 2));
scene.world.flapFunnel(util.grid.at(3, 2, 2), false);
scene.overlay.showSelectionWithText(util.select.position(2, 2, 2), 70)
.attachKeyFrame()
.placeNearTarget()
.pointAt(util.vector.blockSurface(util.grid.at(2, 2, 2), Direction.NORTH))
.text("Items can be inserted to Crafters automatically");
scene.idle(80);
scene.rotateCameraY(-60 - 90 - 30);
scene.idle(40);
Vec3d v = util.vector.blockSurface(util.grid.at(2, 2, 2), Direction.WEST);
AxisAlignedBB bb = new AxisAlignedBB(v, v).grow(.125f, .5, .5);
v = v.add(0, 0, .5);
scene.overlay.chaseBoundingBoxOutline(PonderPalette.WHITE, new Object(), bb, 45);
scene.overlay.showControls(new InputWindowElement(v, Pointing.LEFT).rightClick()
.withWrench(), 40);
scene.idle(7);
scene.world.connectCrafterInvs(util.grid.at(2, 2, 2), util.grid.at(1, 2, 2));
scene.idle(40);
scene.overlay.showSelectionWithText(util.select.fromTo(2, 2, 2, 1, 2, 2), 70)
.attachKeyFrame()
.placeNearTarget()
.pointAt(v)
.text("Using the Wrench at their backs, Mechanical Crafter inputs can be combined");
scene.idle(80);
scene.overlay.showControls(new InputWindowElement(v.add(0, 1, 0), Pointing.LEFT).rightClick()
.withWrench(), 20);
scene.idle(7);
scene.world.connectCrafterInvs(util.grid.at(2, 3, 2), util.grid.at(1, 3, 2));
scene.idle(20);
scene.overlay.showControls(new InputWindowElement(v.add(0, -1, 0), Pointing.LEFT).rightClick()
.withWrench(), 20);
scene.idle(7);
scene.world.connectCrafterInvs(util.grid.at(2, 1, 2), util.grid.at(1, 1, 2));
scene.idle(20);
scene.overlay.showControls(new InputWindowElement(v.add(.5, -.5, 0), Pointing.LEFT).rightClick()
.withWrench(), 20);
scene.idle(7);
scene.world.connectCrafterInvs(util.grid.at(2, 1, 2), util.grid.at(2, 2, 2));
scene.idle(10);
scene.overlay.showControls(new InputWindowElement(v.add(.5, .5, 0), Pointing.LEFT).rightClick()
.withWrench(), 20);
scene.idle(7);
scene.world.connectCrafterInvs(util.grid.at(2, 2, 2), util.grid.at(2, 3, 2));
scene.idle(20);
scene.rotateCameraY(90 + 30);
scene.idle(40);
scene.overlay.showSelectionWithText(util.select.fromTo(1, 1, 2, 2, 3, 2), 70)
.attachKeyFrame()
.placeNearTarget()
.text("All connected Crafters can now be accessed by the same input location");
scene.idle(60);
scene.overlay.showControls(
new InputWindowElement(util.vector.centerOf(util.grid.at(4, 2, 2)), Pointing.DOWN).withItem(planks), 40);
scene.idle(7);
scene.world.createItemOnBelt(util.grid.at(4, 1, 2), Direction.EAST,
ItemHandlerHelper.copyStackWithSize(planks, 16));
scene.idle(22);
scene.world.removeItemsFromBelt(util.grid.at(3, 1, 2));
BlockPos[] positions = new BlockPos[] { util.grid.at(2, 3, 2), util.grid.at(1, 3, 2), util.grid.at(1, 2, 2),
util.grid.at(2, 1, 2), util.grid.at(1, 1, 2) };
scene.world.setCraftingResult(util.grid.at(1, 1, 2), new ItemStack(Items.OAK_DOOR, 3));
for (BlockPos pos : positions) {
scene.world.modifyTileEntity(pos, type, mct -> mct.getInventory()
.insertItem(0, planks.copy(), false));
scene.idle(1);
}
}
public static void covers(SceneBuilder scene, SceneBuildingUtil util) {
scene.title("mechanical_crafter_covers", "Covering slots of Mechanical Crafters");
scene.configureBasePlate(0, 0, 5);
scene.world.showSection(util.select.layer(0), Direction.UP);
scene.world.setBlock(util.grid.at(2, 2, 2), Blocks.AIR.getDefaultState(), false);
Selection kinetics = util.select.fromTo(3, 1, 2, 3, 1, 5);
scene.world.setKineticSpeed(util.select.fromTo(1, 2, 2, 3, 1, 2), 0);
scene.world.showSection(util.select.position(3, 2, 2), Direction.EAST);
scene.idle(5);
scene.world.showSection(util.select.position(2, 1, 2), Direction.DOWN);
scene.idle(5);
scene.world.showSection(util.select.position(1, 2, 2), Direction.WEST);
scene.idle(5);
ItemStack iron = new ItemStack(Items.IRON_INGOT);
Class<MechanicalCrafterTileEntity> type = MechanicalCrafterTileEntity.class;
scene.world.modifyTileEntity(util.grid.at(3, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(2, 1, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
scene.idle(5);
scene.world.modifyTileEntity(util.grid.at(1, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
scene.idle(5);
Selection emptyCrafter = util.select.position(2, 2, 2);
scene.overlay.showSelectionWithText(emptyCrafter, 90)
.attachKeyFrame()
.colored(PonderPalette.RED)
.text("Some recipes will require additional Crafters to bridge gaps in the path")
.placeNearTarget();
scene.idle(70);
scene.world.restoreBlocks(emptyCrafter);
scene.world.setCraftingResult(util.grid.at(2, 2, 2), new ItemStack(Items.BUCKET));
scene.world.showSection(emptyCrafter, Direction.DOWN);
scene.idle(10);
scene.world.showSection(util.select.position(2, 3, 2), Direction.DOWN);
scene.world.showSection(kinetics, Direction.NORTH);
scene.idle(5);
scene.world.setKineticSpeed(util.select.fromTo(3, 1, 2, 1, 2, 2), -32);
scene.world.setKineticSpeed(util.select.position(3, 1, 2)
.add(emptyCrafter), 32);
scene.idle(20);
scene.overlay.showText(90)
.attachKeyFrame()
.colored(PonderPalette.GREEN)
.pointAt(util.vector.blockSurface(util.grid.at(2, 2, 2), Direction.NORTH))
.text("Using Slot Covers, Crafters can be set to act as an Empty Slot in the arrangement")
.placeNearTarget();
scene.idle(100);
scene.overlay
.showControls(new InputWindowElement(util.vector.blockSurface(util.grid.at(2, 2, 2), Direction.NORTH)
.add(0.5, 0, 0), Pointing.RIGHT).withItem(AllItems.CRAFTER_SLOT_COVER.asStack())
.rightClick(),
50);
scene.idle(7);
scene.world.modifyTileNBT(emptyCrafter, type, compound -> compound.putBoolean("Cover", true));
scene.idle(130);
scene.overlay.showControls(
new InputWindowElement(util.vector.blockSurface(util.grid.at(2, 3, 2), Direction.WEST), Pointing.LEFT)
.withItem(new ItemStack(Items.BUCKET)),
40);
scene.idle(50);
scene.world.showSection(util.select.position(4, 2, 2), Direction.DOWN);
scene.world.connectCrafterInvs(util.grid.at(3, 2, 2), util.grid.at(2, 2, 2));
scene.idle(5);
scene.world.connectCrafterInvs(util.grid.at(2, 1, 2), util.grid.at(2, 2, 2));
scene.idle(5);
scene.world.connectCrafterInvs(util.grid.at(1, 2, 2), util.grid.at(2, 2, 2));
scene.idle(10);
scene.overlay.showSelectionWithText(util.select.fromTo(3, 2, 2, 1, 2, 2)
.add(util.select.position(2, 1, 2)), 80)
.attachKeyFrame()
.pointAt(util.vector.blockSurface(util.grid.at(2, 2, 2), Direction.NORTH))
.text("Shared Inputs created with the Wrench at the back can also reach across covered Crafters")
.placeNearTarget();
scene.idle(60);
ElementLink<EntityElement> ingot =
scene.world.createItemEntity(util.vector.centerOf(4, 4, 2), util.vector.of(0, 0.2, 0), iron);
scene.idle(17);
scene.world.modifyEntity(ingot, Entity::remove);
scene.world.modifyTileEntity(util.grid.at(3, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
ingot = scene.world.createItemEntity(util.vector.centerOf(4, 4, 2), util.vector.of(0, 0.2, 0), iron);
scene.idle(17);
scene.world.modifyEntity(ingot, Entity::remove);
scene.world.modifyTileEntity(util.grid.at(2, 1, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
ingot = scene.world.createItemEntity(util.vector.centerOf(4, 4, 2), util.vector.of(0, 0.2, 0), iron);
scene.idle(17);
scene.world.modifyEntity(ingot, Entity::remove);
scene.world.modifyTileEntity(util.grid.at(1, 2, 2), type, mct -> mct.getInventory()
.insertItem(0, iron.copy(), false));
}
}

View file

@ -97,6 +97,13 @@ public class PonderIndex {
PonderRegistry.addStoryBoard(AllBlocks.BLAZE_BURNER, "blaze_burner", ProcessingScenes::blazeBurner); PonderRegistry.addStoryBoard(AllBlocks.BLAZE_BURNER, "blaze_burner", ProcessingScenes::blazeBurner);
PonderRegistry.addStoryBoard(AllBlocks.DEPOT, "depot", BeltScenes::depot); PonderRegistry.addStoryBoard(AllBlocks.DEPOT, "depot", BeltScenes::depot);
// Crafters
PonderRegistry.forComponents(AllBlocks.MECHANICAL_CRAFTER)
.addStoryBoard("mechanical_crafter/setup", CrafterScenes::setup)
.addStoryBoard("mechanical_crafter/connect", CrafterScenes::connect);
PonderRegistry.forComponents(AllBlocks.MECHANICAL_CRAFTER, AllItems.CRAFTER_SLOT_COVER)
.addStoryBoard("mechanical_crafter/covers", CrafterScenes::covers);
// Chutes // Chutes
PonderRegistry.forComponents(AllBlocks.CHUTE) PonderRegistry.forComponents(AllBlocks.CHUTE)
.addStoryBoard("chute/downward", ChuteScenes::downward, PonderTag.LOGISTICS) .addStoryBoard("chute/downward", ChuteScenes::downward, PonderTag.LOGISTICS)
@ -148,6 +155,10 @@ public class PonderIndex {
.addStoryBoard("windmill_bearing/source", BearingScenes::windmillsAsSource, PonderTag.KINETIC_SOURCES) .addStoryBoard("windmill_bearing/source", BearingScenes::windmillsAsSource, PonderTag.KINETIC_SOURCES)
.addStoryBoard("windmill_bearing/structure", BearingScenes::windmillsAnyStructure, .addStoryBoard("windmill_bearing/structure", BearingScenes::windmillsAnyStructure,
PonderTag.MOVEMENT_ANCHOR); PonderTag.MOVEMENT_ANCHOR);
PonderRegistry.forComponents(AllBlocks.SAIL)
.addStoryBoard("sail", BearingScenes::sail);
PonderRegistry.forComponents(AllBlocks.SAIL_FRAME)
.addStoryBoard("sail", BearingScenes::sailFrame);
// Mechanical Bearing // Mechanical Bearing
PonderRegistry.forComponents(AllBlocks.MECHANICAL_BEARING) PonderRegistry.forComponents(AllBlocks.MECHANICAL_BEARING)
@ -332,6 +343,11 @@ public class PonderIndex {
.add(AllBlocks.CREATIVE_FLUID_TANK) .add(AllBlocks.CREATIVE_FLUID_TANK)
.add(AllBlocks.CREATIVE_MOTOR); .add(AllBlocks.CREATIVE_MOTOR);
PonderRegistry.tags.forTag(PonderTag.SAILS)
.add(AllBlocks.SAIL)
.add(AllBlocks.SAIL_FRAME)
.add(Blocks.WHITE_WOOL);
PonderRegistry.tags.forTag(PonderTag.REDSTONE) PonderRegistry.tags.forTag(PonderTag.REDSTONE)
.add(AllBlocks.NIXIE_TUBE) .add(AllBlocks.NIXIE_TUBE)
.add(AllBlocks.REDSTONE_CONTACT) .add(AllBlocks.REDSTONE_CONTACT)

View file

@ -59,6 +59,10 @@ public class PonderTag implements IScreenRenderable {
.defaultLang("Block Attachment Utility", .defaultLang("Block Attachment Utility",
"Tools and Components used to assemble structures moved as an animated Contraption"), "Tools and Components used to assemble structures moved as an animated Contraption"),
SAILS = new PonderTag("windmill_sails").item(AllBlocks.WINDMILL_BEARING.get(), true, true)
.defaultLang("Sails for Windmill Bearings",
"Blocks that count towards the strength of a Windmill Contraption when assembled"),
// FLUID_TRANSFER = new PonderTag("fluid_transfer").idAsIcon(), // FLUID_TRANSFER = new PonderTag("fluid_transfer").idAsIcon(),
// //
// OPEN_INVENTORY = new PonderTag("open_inventory").item(AllBlocks.BASIN.get() // OPEN_INVENTORY = new PonderTag("open_inventory").item(AllBlocks.BASIN.get()

View file

@ -93,7 +93,7 @@ public class InputWindowElement extends AnimatedOverlayElement {
if (fade < 1 / 16f) if (fade < 1 / 16f)
return; return;
Vec2f sceneToScreen = scene.getTransform() Vec2f sceneToScreen = scene.getTransform()
.sceneToScreen(sceneSpace); .sceneToScreen(sceneSpace, partialTicks);
if (hasIcon) { if (hasIcon) {
width += 24; width += 24;

View file

@ -246,7 +246,7 @@ public class ParrotElement extends AnimatedSceneElement {
double mouseX = minecraft.mouseHelper.getMouseX() * w.getScaledWidth() / w.getWidth(); double mouseX = minecraft.mouseHelper.getMouseX() * w.getScaledWidth() / w.getWidth();
double mouseY = minecraft.mouseHelper.getMouseY() * w.getScaledHeight() / w.getHeight(); double mouseY = minecraft.mouseHelper.getMouseY() * w.getScaledHeight() / w.getHeight();
return scene.getTransform() return scene.getTransform()
.screenToScene(mouseX, mouseY, 300); .screenToScene(mouseX, mouseY, 300, 0);
} }
} }

View file

@ -10,7 +10,6 @@ import com.simibubi.create.foundation.ponder.PonderScene;
import com.simibubi.create.foundation.ponder.PonderUI; import com.simibubi.create.foundation.ponder.PonderUI;
import com.simibubi.create.foundation.ponder.content.PonderPalette; import com.simibubi.create.foundation.ponder.content.PonderPalette;
import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.foundation.utility.FontHelper;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec2f; import net.minecraft.util.math.Vec2f;
@ -87,7 +86,7 @@ public class TextWindowElement extends AnimatedOverlayElement {
if (fade < 1 / 16f) if (fade < 1 / 16f)
return; return;
Vec2f sceneToScreen = vec != null ? scene.getTransform() Vec2f sceneToScreen = vec != null ? scene.getTransform()
.sceneToScreen(vec) : new Vec2f(screen.width / 2, (screen.height - 200) / 2 + y - 8); .sceneToScreen(vec, partialTicks) : new Vec2f(screen.width / 2, (screen.height - 200) / 2 + y - 8);
float yDiff = (screen.height / 2 - sceneToScreen.y - 10) / 100f; float yDiff = (screen.height / 2 - sceneToScreen.y - 10) / 100f;
int targetX = (int) (screen.width * MathHelper.lerp(yDiff * yDiff, 6f / 8, 5f / 8)); int targetX = (int) (screen.width * MathHelper.lerp(yDiff * yDiff, 6f / 8, 5f / 8));
@ -122,8 +121,11 @@ public class TextWindowElement extends AnimatedOverlayElement {
RenderSystem.popMatrix(); RenderSystem.popMatrix();
} }
FontHelper.drawSplitString(screen.getFontRenderer(), bakedText, targetX - 10, 3, textWidth, for (int i = 0; i < list.size(); i++) {
ColorHelper.applyAlpha(brighterColor, fade)); String s = list.get(i);
screen.getFontRenderer()
.drawString(s, targetX - 10, 3 + 9 * i, ColorHelper.applyAlpha(brighterColor, fade));
}
RenderSystem.popMatrix(); RenderSystem.popMatrix();
} }

Binary file not shown.

Binary file not shown.