diff --git a/gradle.properties b/gradle.properties index b50c41fc3..179b53901 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false # mod version info -mod_version=0.3.1c +mod_version=0.3.2 minecraft_version=1.16.5 forge_version=36.0.42 diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index 2b20bd785..2c2d57364 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -1654,7 +1654,7 @@ d080b1b25e5bc8baf5aee68691b08c7f12ece3b0 assets/create/models/item/windmill_bear a80fb25a0b655e76be986b5b49fcb0f03461a1ab assets/create/models/item/zinc_nugget.json b1689617190c05ef34bd18456b0c7ae09bb3210f assets/create/models/item/zinc_ore.json 6490fa0587db770cf7c794b47f3bcd2b691f4226 assets/create/sounds.json -0f1b4b980afba9bf2caf583b88e261bba8b10313 data/create/advancements/aesthetics.json +5d0cc4c0255dc241e61c173b31ddca70c88d08e4 data/create/advancements/aesthetics.json 187921fa131b06721bfaf63f2623a28c141aae9a data/create/advancements/andesite_alloy.json 0ea2db7173b5be28b289ea7c9a6a0cf5805c60c7 data/create/advancements/andesite_casing.json 83c046bd200623933545c9e4326f782fb02c87fa data/create/advancements/arm_blaze_burner.json @@ -1779,6 +1779,7 @@ c368cadffa9177fefb9e92ff4453b40bc8dd670d data/create/advancements/recipes/create 8fffce2a5c5dd88d52e3b006fa92fb18cf2f1571 data/create/advancements/recipes/create.base/blasting/zinc_ingot_from_crushed.json 4bb60ef5e186f12a9d52e61319db8c78300c64ab data/create/advancements/recipes/create.base/blasting/zinc_ingot_from_ore.json d1d8cf6e1c95b7d99bf873fa6fee033103f995fd data/create/advancements/recipes/create.base/crafting/appliances/copper_backtank.json +f2dc28c600011e6e8e515cb4d56118b1bd45b743 data/create/advancements/recipes/create.base/crafting/appliances/crafting_blueprint.json 46c04e685ab345a80598176f7ac68a044a76cd76 data/create/advancements/recipes/create.base/crafting/appliances/diving_boots.json 5f06b7dcf2af11f30c2e10ade4ac3fd172bc04df data/create/advancements/recipes/create.base/crafting/appliances/diving_helmet.json dd487f98c411f1ff22cb7fc208b8cc24b27deb2f data/create/advancements/recipes/create.base/crafting/appliances/dough.json @@ -2858,6 +2859,7 @@ f7879d404d7a848d818278b4e788f285a9087e63 data/create/recipes/compacting/blaze_ca 7b2ef15dd28d1d8a450ea49a82dfb361d1adde4c data/create/recipes/compacting/diorite_from_flint.json 7657603e95ccf83dd0d4b104635db66e531d092a data/create/recipes/compacting/granite_from_flint.json 30030b15caa11b3a7c0104adb62fe74e8c7c0df1 data/create/recipes/crafting/appliances/copper_backtank.json +c077375d16b4505e52548613fbc9356993556e6b data/create/recipes/crafting/appliances/crafting_blueprint.json 9ad82ac5ce02654b7af7f1a570a6b2c01e140da3 data/create/recipes/crafting/appliances/diving_boots.json 813081c6421b34e161ec44e8e470994c282f76be data/create/recipes/crafting/appliances/diving_helmet.json 19526da3a59fc136654ff1bc93c0251581f397a9 data/create/recipes/crafting/appliances/dough.json diff --git a/src/generated/resources/data/create/advancements/aesthetics.json b/src/generated/resources/data/create/advancements/aesthetics.json index d723cbe38..59a86f429 100644 --- a/src/generated/resources/data/create/advancements/aesthetics.json +++ b/src/generated/resources/data/create/advancements/aesthetics.json @@ -28,8 +28,8 @@ "trigger": "create:bracket_apply", "conditions": { "accepted_entries": [ - "create:cogwheel", - "create:large_cogwheel" + "create:large_cogwheel", + "create:cogwheel" ] } }, diff --git a/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/crafting_blueprint.json b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/crafting_blueprint.json new file mode 100644 index 000000000..e1d2038ea --- /dev/null +++ b/src/generated/resources/data/create/advancements/recipes/create.base/crafting/appliances/crafting_blueprint.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "rewards": { + "recipes": [ + "create:crafting/appliances/crafting_blueprint" + ] + }, + "criteria": { + "has_item": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { + "item": "minecraft:crafting_table" + } + ] + } + }, + "has_the_recipe": { + "trigger": "minecraft:recipe_unlocked", + "conditions": { + "recipe": "create:crafting/appliances/crafting_blueprint" + } + } + }, + "requirements": [ + [ + "has_item", + "has_the_recipe" + ] + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/crafting/appliances/crafting_blueprint.json b/src/generated/resources/data/create/recipes/crafting/appliances/crafting_blueprint.json new file mode 100644 index 000000000..880a71332 --- /dev/null +++ b/src/generated/resources/data/create/recipes/crafting/appliances/crafting_blueprint.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:painting" + }, + { + "item": "minecraft:crafting_table" + } + ], + "result": { + "item": "create:crafting_blueprint" + } +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java index a0fe6a992..832fc2138 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java @@ -477,9 +477,13 @@ public abstract class KineticTileEntity extends SmartTileEntity return d.getAxisDirection() == AxisDirection.POSITIVE ? axisSpeed : -axisSpeed; } - public static float convertToLinear(float speed) { return speed / 512f; } + public static float convertToLinear(float speed) { + return speed / 512f; + } - public static float convertToAngular(float speed) { return speed * 3 / 10f; } + public static float convertToAngular(float speed) { + return speed * 3 / 10f; + } public boolean isOverStressed() { return overStressed; @@ -556,9 +560,8 @@ public abstract class KineticTileEntity extends SmartTileEntity @Override public void requestModelDataUpdate() { super.requestModelDataUpdate(); - if (!this.removed) { - FastRenderDispatcher.enqueueUpdate(this); - } + if (!this.removed) + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> FastRenderDispatcher.enqueueUpdate(this)); } protected AxisAlignedBB cachedBoundingBox; diff --git a/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintEntity.java b/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintEntity.java index 99d6deae3..344bd3405 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintEntity.java +++ b/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintEntity.java @@ -59,6 +59,7 @@ import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.hooks.BasicEventHooks; @@ -243,6 +244,36 @@ public class BlueprintEntity extends HangingEntity return 16 * size; } + @Override + public boolean hitByEntity(Entity source) { + if (!(source instanceof PlayerEntity) || world.isRemote) + return super.hitByEntity(source); + + PlayerEntity player = (PlayerEntity) source; + double attrib = player.getAttribute(ForgeMod.REACH_DISTANCE.get()) + .getValue() + (player.isCreative() ? 0 : -0.5F); + + Vector3d eyePos = source.getEyePosition(1); + Vector3d look = source.getLook(1); + Vector3d target = eyePos.add(look.scale(attrib)); + + Optional rayTrace = getBoundingBox().rayTrace(eyePos, target); + if (!rayTrace.isPresent()) + return super.hitByEntity(source); + + Vector3d hitVec = rayTrace.get(); + BlueprintSection sectionAt = getSectionAt(hitVec.subtract(getPositionVec())); + ItemStackHandler items = sectionAt.getItems(); + + if (items.getStackInSlot(9) + .isEmpty()) + return super.hitByEntity(source); + for (int i = 0; i < items.getSlots(); i++) + items.setStackInSlot(i, ItemStack.EMPTY); + sectionAt.save(items); + return true; + } + @Override public void onBroken(@Nullable Entity p_110128_1_) { if (!world.getGameRules() @@ -309,98 +340,88 @@ public class BlueprintEntity extends HangingEntity if (player instanceof FakePlayer) return ActionResultType.PASS; + boolean holdingWrench = AllItems.WRENCH.isIn(player.getHeldItem(hand)); BlueprintSection section = getSectionAt(vec); + ItemStackHandler items = section.getItems(); - if (!AllItems.WRENCH.isIn(player.getHeldItem(hand)) && !world.isRemote) { - boolean empty = true; - ItemStackHandler items = section.getItems(); - for (int i = 0; i < 9; i++) { - if (!items.getStackInSlot(i) - .isEmpty()) { - empty = false; + if (!holdingWrench && !world.isRemote && !items.getStackInSlot(9) + .isEmpty()) { + + IItemHandlerModifiable playerInv = new InvWrapper(player.inventory); + boolean firstPass = true; + int amountCrafted = 0; + ForgeHooks.setCraftingPlayer(player); + Optional recipe = Optional.empty(); + + do { + Map stacksTaken = new HashMap<>(); + Map craftingGrid = new HashMap<>(); + boolean success = true; + + Search: for (int i = 0; i < 9; i++) { + ItemStack requestedItem = items.getStackInSlot(i); + if (requestedItem.isEmpty()) { + craftingGrid.put(i, ItemStack.EMPTY); + continue; + } + + for (int slot = 0; slot < playerInv.getSlots(); slot++) { + if (!FilterItem.test(world, playerInv.getStackInSlot(slot), requestedItem)) + continue; + ItemStack currentItem = playerInv.extractItem(slot, 1, false); + if (stacksTaken.containsKey(slot)) + stacksTaken.get(slot) + .grow(1); + else + stacksTaken.put(slot, currentItem.copy()); + craftingGrid.put(i, currentItem); + continue Search; + } + + success = false; break; } - } - if (!empty) { - IItemHandlerModifiable playerInv = new InvWrapper(player.inventory); - boolean firstPass = true; - int amountCrafted = 0; - ForgeHooks.setCraftingPlayer(player); - Optional recipe = Optional.empty(); + if (success) { + CraftingInventory craftingInventory = new BlueprintCraftingInventory(craftingGrid); - do { - Map stacksTaken = new HashMap<>(); - Map craftingGrid = new HashMap<>(); - boolean success = true; - - Search: for (int i = 0; i < 9; i++) { - ItemStack requestedItem = items.getStackInSlot(i); - if (requestedItem.isEmpty()) { - craftingGrid.put(i, ItemStack.EMPTY); - continue; - } - - for (int slot = 0; slot < playerInv.getSlots(); slot++) { - if (!FilterItem.test(world, playerInv.getStackInSlot(slot), requestedItem)) - continue; - ItemStack currentItem = playerInv.extractItem(slot, 1, false); - if (stacksTaken.containsKey(slot)) { - stacksTaken.get(slot) - .grow(1); - } else { - stacksTaken.put(slot, currentItem.copy()); - } - craftingGrid.put(i, currentItem); - continue Search; - } + if (!recipe.isPresent()) + recipe = world.getRecipeManager() + .getRecipe(IRecipeType.CRAFTING, craftingInventory, world); + ItemStack result = recipe.filter(r -> r.matches(craftingInventory, world)) + .map(r -> r.getCraftingResult(craftingInventory)) + .orElse(ItemStack.EMPTY); + if (result.isEmpty()) { success = false; - break; + } else if (result.getCount() + amountCrafted > 64) { + success = false; + } else { + amountCrafted += result.getCount(); + result.onCrafting(player.world, player, 1); + BasicEventHooks.firePlayerCraftingEvent(player, result, craftingInventory); + NonNullList nonnulllist = world.getRecipeManager() + .getRecipeNonNull(IRecipeType.CRAFTING, craftingInventory, world); + + if (firstPass) + world.playSound(null, player.getBlockPos(), SoundEvents.ENTITY_ITEM_PICKUP, + SoundCategory.PLAYERS, .2f, 1f + Create.RANDOM.nextFloat()); + player.inventory.placeItemBackInInventory(world, result); + for (ItemStack itemStack : nonnulllist) + player.inventory.placeItemBackInInventory(world, itemStack); + firstPass = false; } + } - if (success) { - CraftingInventory craftingInventory = new BlueprintCraftingInventory(craftingGrid); + if (!success) { + for (Entry entry : stacksTaken.entrySet()) + playerInv.insertItem(entry.getKey(), entry.getValue(), false); + break; + } - if (!recipe.isPresent()) - recipe = world.getRecipeManager() - .getRecipe(IRecipeType.CRAFTING, craftingInventory, world); - ItemStack result = recipe.filter(r -> r.matches(craftingInventory, world)) - .map(r -> r.getCraftingResult(craftingInventory)) - .orElse(ItemStack.EMPTY); - - if (result.isEmpty()) { - success = false; - } else if (result.getCount() + amountCrafted > 64) { - success = false; - } else { - amountCrafted += result.getCount(); - result.onCrafting(player.world, player, 1); - BasicEventHooks.firePlayerCraftingEvent(player, result, craftingInventory); - NonNullList nonnulllist = world.getRecipeManager() - .getRecipeNonNull(IRecipeType.CRAFTING, craftingInventory, world); - - if (firstPass) - world.playSound(null, player.getBlockPos(), SoundEvents.ENTITY_ITEM_PICKUP, - SoundCategory.PLAYERS, .2f, 1f + Create.RANDOM.nextFloat()); - player.inventory.placeItemBackInInventory(world, result); - for (ItemStack itemStack : nonnulllist) - player.inventory.placeItemBackInInventory(world, itemStack); - firstPass = false; - } - } - - if (!success) { - for (Entry entry : stacksTaken.entrySet()) - playerInv.insertItem(entry.getKey(), entry.getValue(), false); - break; - } - - } while (player.isSneaking()); - ForgeHooks.setCraftingPlayer(null); - - return ActionResultType.SUCCESS; - } + } while (player.isSneaking()); + ForgeHooks.setCraftingPlayer(null); + return ActionResultType.SUCCESS; } int i = section.index; diff --git a/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintRenderer.java b/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintRenderer.java index 13825d42c..3cd2bcd82 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintRenderer.java +++ b/src/main/java/com/simibubi/create/content/curiosities/tools/BlueprintRenderer.java @@ -17,8 +17,11 @@ import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.inventory.container.PlayerContainer; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Matrix3f; public class BlueprintRenderer extends EntityRenderer { @@ -40,15 +43,30 @@ public class BlueprintRenderer extends EntityRenderer { .translate(-.5, -1 / 32f, -.5); if (entity.size == 2) sbb.translate(.5, 0, -.5); - sbb.light(light) - .renderInto(ms, buffer.getBuffer(RenderType.getSolid())); + + RenderType entitySolid = RenderType.getEntitySolid(PlayerContainer.BLOCK_ATLAS_TEXTURE); + sbb.asEntityModel() + .light(light) + .renderInto(ms, buffer.getBuffer(entitySolid)); super.render(entity, yaw, pt, ms, buffer, light); ms.push(); + float fakeNormalXRotation = -15; + int bl = light >> 4 & 0xf; + int sl = light >> 20 & 0xf; + boolean vertical = entity.rotationPitch != 0; + if (entity.rotationPitch == -90) + fakeNormalXRotation = -45; + else if (entity.rotationPitch == 90 || yaw % 180 != 0) { + bl /= 1.35; + sl /= 1.35; + } + int itemLight = MathHelper.floor(sl + .5) << 20 | (MathHelper.floor(bl + .5) & 0xf) << 4; + MatrixStacker.of(ms) - .rotateY(-yaw) - .rotateX(entity.rotationPitch == -90 ? -45 : entity.rotationPitch == 0 ? -15 : -5); + .rotateY(vertical ? 0 : -yaw) + .rotateX(fakeNormalXRotation); Matrix3f copy = ms.peek() .getNormal() .copy(); @@ -60,28 +78,34 @@ public class BlueprintRenderer extends EntityRenderer { .rotateY(-yaw) .rotateX(entity.rotationPitch) .translate(0, 0, 1 / 32f + .001); - + if (entity.size == 3) ms.translate(-1, -1, 0); + MatrixStack squashedMS = new MatrixStack(); + squashedMS.peek() + .getModel() + .multiply(ms.peek() + .getModel()); + for (int x = 0; x < entity.size; x++) { - ms.push(); + squashedMS.push(); for (int y = 0; y < entity.size; y++) { BlueprintSection section = entity.getSection(x * entity.size + y); Couple displayItems = section.getDisplayItems(); - ms.push(); - ms.scale(.5f, .5f, 1 / 1024f); + squashedMS.push(); + squashedMS.scale(.5f, .5f, 1 / 1024f); displayItems.forEachWithContext((stack, primary) -> { if (stack.isEmpty()) return; - ms.push(); + squashedMS.push(); if (!primary) { - ms.translate(0.325f, -0.325f, 1); - ms.scale(.625f, .625f, 1); + squashedMS.translate(0.325f, -0.325f, 1); + squashedMS.scale(.625f, .625f, 1); } - Matrix3f n = ms.peek() + Matrix3f n = squashedMS.peek() .getNormal(); n.a00 = copy.a00; n.a01 = copy.a01; @@ -95,14 +119,14 @@ public class BlueprintRenderer extends EntityRenderer { Minecraft.getInstance() .getItemRenderer() - .renderItem(stack, TransformType.GUI, light, overlay, ms, buffer); - ms.pop(); + .renderItem(stack, TransformType.GUI, itemLight, OverlayTexture.DEFAULT_UV, squashedMS, buffer); + squashedMS.pop(); }); - ms.pop(); - ms.translate(1, 0, 0); + squashedMS.pop(); + squashedMS.translate(1, 0, 0); } - ms.pop(); - ms.translate(0, 1, 0); + squashedMS.pop(); + squashedMS.translate(0, 1, 0); } ms.pop(); diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java index 4f6998cc9..f65f63baa 100644 --- a/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/StandardRecipeGen.java @@ -968,6 +968,10 @@ public class StandardRecipeGen extends CreateRecipeProvider { .patternLine("P P") .patternLine("P P") .patternLine("G G")), + + CRAFTING_BLUEPRINT = create(AllItems.CRAFTING_BLUEPRINT).unlockedBy(() -> Items.CRAFTING_TABLE) + .viaShapeless(b -> b.addIngredient(Items.PAINTING) + .addIngredient(Items.CRAFTING_TABLE)), SLIME_BALL = create(() -> Items.SLIME_BALL).unlockedBy(AllItems.DOUGH::get) .viaShapeless(b -> b.addIngredient(AllItems.DOUGH.get()) diff --git a/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java b/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java index f3ff7ef80..1fa2a55ad 100644 --- a/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java +++ b/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java @@ -13,6 +13,7 @@ import it.unimi.dsi.fastutil.longs.Long2DoubleMap; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; @@ -37,6 +38,7 @@ public class SuperByteBuffer extends TemplateBuffer { // Vertex Texture Coords private SpriteShiftFunc spriteShiftFunc; + private boolean isEntityModel; // Vertex Lighting private boolean shouldLight; @@ -78,7 +80,6 @@ public class SuperByteBuffer extends TemplateBuffer { Matrix3f normalMat = transforms.peek() .getNormal() .copy(); - // normalMat.multiply(transforms.peek().getNormal()); Matrix4f modelMat = input.peek() .getModel() @@ -119,9 +120,9 @@ public class SuperByteBuffer extends TemplateBuffer { pos.transform(modelMat); builder.vertex(pos.getX(), pos.getY(), pos.getZ()); - // builder.color((byte) Math.max(0, nx * 255), (byte) Math.max(0, ny * 255), (byte) Math.max(0, nz * 255), a); - if (shouldColor) { - // float lum = (r < 0 ? 255 + r : r) / 256f; + if (isEntityModel) { + builder.color(255, 255, 255, 255); + } else if (shouldColor) { int colorR = Math.min(255, (int) (((float) this.r) * instanceDiffuse)); int colorG = Math.min(255, (int) (((float) this.g) * instanceDiffuse)); int colorB = Math.min(255, (int) (((float) this.b) * instanceDiffuse)); @@ -141,7 +142,10 @@ public class SuperByteBuffer extends TemplateBuffer { spriteShiftFunc.shift(builder, u, v); } else builder.texture(u, v); - + + if (isEntityModel) + builder.overlay(OverlayTexture.DEFAULT_UV); + if (shouldLight) { int light = packedLightCoords; if (lightTransform != null) { @@ -158,14 +162,18 @@ public class SuperByteBuffer extends TemplateBuffer { } else builder.light(getLight(buffer, i)); - builder.normal(nx, ny, nz) - .endVertex(); + if (isEntityModel) + builder.normal(input.peek().getNormal(), nx, ny, nz); + else + builder.normal(nx, ny, nz); + builder.endVertex(); } transforms = new MatrixStack(); spriteShiftFunc = null; shouldColor = false; + isEntityModel = false; shouldLight = false; otherBlockLight = -1; } @@ -286,6 +294,11 @@ public class SuperByteBuffer extends TemplateBuffer { a = 255; return this; } + + public SuperByteBuffer asEntityModel() { + isEntityModel = true; + return this; + } private static int getLight(World world, Vector4f lightPos) { BlockPos.Mutable pos = new BlockPos.Mutable(); @@ -295,7 +308,7 @@ public class SuperByteBuffer extends TemplateBuffer { block += blockLightCache.computeIfAbsent(pos.toLong(), $ -> world.getLightLevel(LightType.BLOCK, pos)); return ((int) sky) << 20 | ((int) block) << 4; } - + public boolean isEmpty() { return ((Buffer) template).limit() == 0; }