Chest Boats; More Features In The Difficulty Stages; The Diviner; Advancements
All checks were successful
Twine/pipeline/head This commit looks good
All checks were successful
Twine/pipeline/head This commit looks good
This commit is contained in:
parent
c4a2e8e529
commit
135131b448
10
README.md
10
README.md
@ -27,7 +27,11 @@ Glowing Obsidian heals monsters and hurts everything else. It naturally generate
|
||||
<tr><th></th><th>Redstone</th><th></th></tr>
|
||||
</table>
|
||||
|
||||
## Chest Boats
|
||||
Right-Click a Boat with a Chest to place it in the Boat, you can also use an Ender Chest. Shift-Right-Click the Boat to open the Chest. You can also open the Chest inside the Boat by opening your inventory.
|
||||
|
||||
## Difficulty Stages
|
||||
Each player has a "personal" stage of an area, the highest online player's difficulty stage in an area will be the "effective" difficulty stage of that area.
|
||||
|
||||
### Stage 1
|
||||
- Normal Gameplay
|
||||
@ -39,6 +43,7 @@ Glowing Obsidian heals monsters and hurts everything else. It naturally generate
|
||||
|
||||
### Stage 4
|
||||
- Villages Kick You Out (Using Iron Golems)
|
||||
- Creepers Target Artificial Blocks
|
||||
|
||||
### Stage 5
|
||||
- Neural Mobs Are Always Hostile
|
||||
@ -46,4 +51,7 @@ Glowing Obsidian heals monsters and hurts everything else. It naturally generate
|
||||
### Stage 6
|
||||
|
||||
### Technical Details
|
||||
Each player has a 6-element long list in their data. Each element in the list has a chunk position and time value. Every tick the player will first, find the stage element its current position is in, and increase the time value of that stage element. If the time value when increased is over 24,000 ticks the next stage element will have its chunk set to the players current position, have its time value reset, the old stage element will have the next stage element's old chunk, and have its time value reset. To determine if a player is in a stage element, it checks if the player is within 16 chunks of the stage element's chunk, it starts searching at the last element, ending at the first. The "global" stage element of a chunk can be found by searching every player for their stage element for the chunk and then picking the highest value.
|
||||
Each player has a 6-element long list in their data. Each element in the list has a chunk position and time value. Every tick the player will first, find the stage element its current position is in, and increase the time value of that stage element. If the time value when increased is over 24,000 ticks the next stage element will have its chunk set to the players current position, have its time value reset, the old stage element will have the next stage element's old chunk, and have its time value reset. To determine if a player is in a stage element, it checks if the player is within 16 chunks of the stage element's chunk, it starts searching at the last element, ending at the first. The "effective" stage element of a chunk can be found by searching every **online** player for their stage element for the chunk and then picking the highest value.
|
||||
|
||||
## Credits
|
||||
Thanks to ```sirikaros``` for the backpack textures!
|
@ -1,16 +1,23 @@
|
||||
package com.thebrokenrail.twine;
|
||||
|
||||
import com.thebrokenrail.twine.advancement.ChestBoatCriterion;
|
||||
import com.thebrokenrail.twine.advancement.EnderChestBoatCriterion;
|
||||
import com.thebrokenrail.twine.block.GlowingObsidianBlock;
|
||||
import com.thebrokenrail.twine.item.BackpackItem;
|
||||
import com.thebrokenrail.twine.item.DivinerItem;
|
||||
import com.thebrokenrail.twine.mixin.CriteriaHook;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.fabricmc.fabric.api.tag.TagRegistry;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.screen.GenericContainerScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.tag.Tag;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
@ -26,9 +33,16 @@ public class Twine implements ModInitializer {
|
||||
public static final Item SMALL_BACKPACK = new BackpackItem(false);
|
||||
public static final Item LARGE_BACKPACK = new BackpackItem(true);
|
||||
|
||||
public static final Item DIVINER = new DivinerItem();
|
||||
|
||||
public static final Tag<Block> ARTIFICIAL_BLOCKS_TAG = TagRegistry.block(new Identifier(NAMESPACE, "artificial_blocks"));
|
||||
|
||||
public static final int STAGE_COUNT = 6;
|
||||
public static final int STAGE_TIME = 24000;
|
||||
|
||||
public static ChestBoatCriterion CHEST_BOAT_CRITERION = CriteriaHook.callRegister(new ChestBoatCriterion());
|
||||
public static EnderChestBoatCriterion ENDER_CHEST_BOAT_CRITERION = CriteriaHook.callRegister(new EnderChestBoatCriterion());
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ContainerProviderRegistry.INSTANCE.registerFactory(BACKPACK_SCREEN, (i, identifier, playerEntity, buf) -> {
|
||||
@ -41,6 +55,8 @@ public class Twine implements ModInitializer {
|
||||
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "small_backpack"), SMALL_BACKPACK);
|
||||
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "large_backpack"), LARGE_BACKPACK);
|
||||
|
||||
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "diviner"), DIVINER);
|
||||
|
||||
Registry.register(Registry.BLOCK, new Identifier(NAMESPACE, "glowing_obsidian"), GLOWING_OBSIDIAN);
|
||||
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "glowing_obsidian"), new BlockItem(GLOWING_OBSIDIAN, new Item.Settings().group(ITEM_GROUP)));
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.thebrokenrail.twine.advancement;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.thebrokenrail.twine.Twine;
|
||||
import net.minecraft.advancement.criterion.AbstractCriterion;
|
||||
import net.minecraft.advancement.criterion.AbstractCriterionConditions;
|
||||
import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer;
|
||||
import net.minecraft.predicate.entity.EntityPredicate;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ChestBoatCriterion extends AbstractCriterion<AbstractCriterionConditions> {
|
||||
private static final Identifier ID = new Identifier(Twine.NAMESPACE, "chest_boat");
|
||||
|
||||
public void trigger(ServerPlayerEntity player) {
|
||||
this.test(player, conditions -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractCriterionConditions conditionsFromJson(JsonObject obj, EntityPredicate.Extended playerPredicate, AdvancementEntityPredicateDeserializer predicateDeserializer) {
|
||||
return new AbstractCriterionConditions(ID, playerPredicate) {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.thebrokenrail.twine.advancement;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.thebrokenrail.twine.Twine;
|
||||
import net.minecraft.advancement.criterion.AbstractCriterion;
|
||||
import net.minecraft.advancement.criterion.AbstractCriterionConditions;
|
||||
import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer;
|
||||
import net.minecraft.predicate.entity.EntityPredicate;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class EnderChestBoatCriterion extends AbstractCriterion<AbstractCriterionConditions> {
|
||||
private static final Identifier ID = new Identifier(Twine.NAMESPACE, "ender_chest_boat");
|
||||
|
||||
public void trigger(ServerPlayerEntity player) {
|
||||
this.test(player, conditions -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractCriterionConditions conditionsFromJson(JsonObject obj, EntityPredicate.Extended playerPredicate, AdvancementEntityPredicateDeserializer predicateDeserializer) {
|
||||
return new AbstractCriterionConditions(ID, playerPredicate) {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.PersistentState;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -43,15 +44,26 @@ public class StageDataComponent extends PersistentState {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int findStageOfChunk(ChunkPos pos) {
|
||||
Collection<StageData[]> values = data.values();
|
||||
public int findEffectiveStageOfChunk(ServerWorld world, ChunkPos pos) {
|
||||
int stage = 0;
|
||||
for (StageData[] value : values) {
|
||||
stage = Math.max(stage, findStageOfChunk(pos, value));
|
||||
for (Map.Entry<UUID, StageData[]> entry : data.entrySet()) {
|
||||
if (world.getPlayerByUuid(entry.getKey()) != null) {
|
||||
stage = Math.max(stage, findStageOfChunk(pos, entry.getValue()));
|
||||
}
|
||||
}
|
||||
return stage;
|
||||
}
|
||||
|
||||
public long findEffectiveTimeOfChunk(ServerWorld world, ChunkPos pos) {
|
||||
long time = 0;
|
||||
for (Map.Entry<UUID, StageData[]> entry : data.entrySet()) {
|
||||
if (world.getPlayerByUuid(entry.getKey()) != null) {
|
||||
time = Math.max(time, entry.getValue()[findStageOfChunk(pos, entry.getValue())].time);
|
||||
}
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
public static StageDataComponent getFromWorld(ServerWorld world) {
|
||||
return world.getPersistentStateManager().getOrCreate(() -> new StageDataComponent(STAGE_DATA_ID), STAGE_DATA_ID);
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package com.thebrokenrail.twine.entity;
|
||||
|
||||
import com.thebrokenrail.twine.Twine;
|
||||
import com.thebrokenrail.twine.component.StageDataComponent;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.ai.goal.MoveToTargetPosGoal;
|
||||
import net.minecraft.entity.mob.CreeperEntity;
|
||||
import net.minecraft.entity.mob.MobEntityWithAi;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.WorldView;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
|
||||
public class ExplodeArtificialBlockGoal extends MoveToTargetPosGoal {
|
||||
public ExplodeArtificialBlockGoal(MobEntityWithAi mob) {
|
||||
super(mob, 1d, 24, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTargetPos(WorldView world, BlockPos pos) {
|
||||
Chunk chunk = world.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.FULL, false);
|
||||
if (chunk == null) {
|
||||
return false;
|
||||
} else {
|
||||
return chunk.getBlockState(pos).isOpaqueFullCube(world, pos) && chunk.getBlockState(pos.up()).isAir() && chunk.getBlockState(pos.up(2)).isAir() && (isArtificial(world, chunk, pos) || isArtificial(world, chunk, pos.up().north()) || isArtificial(world, chunk, pos.up().south()) || isArtificial(world, chunk, pos.up().east()) || isArtificial(world, chunk, pos.up().west()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isArtificial(WorldView world, Chunk chunk, BlockPos pos) {
|
||||
ChunkPos newPos = new ChunkPos(pos.getX() >> 4, pos.getZ() >> 4);
|
||||
if (!chunk.getPos().equals(newPos)) {
|
||||
chunk = world.getChunk(newPos.x, newPos.z, ChunkStatus.FULL, false);
|
||||
if (chunk == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
BlockState state = chunk.getBlockState(pos);
|
||||
return state != null && (state.isIn(Twine.ARTIFICIAL_BLOCKS_TAG) || state.getLuminance() > 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDesiredSquaredDistanceToTarget() {
|
||||
return 2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (this.hasReached() && mob instanceof CreeperEntity) {
|
||||
((CreeperEntity) mob).setIgnited();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canStart() {
|
||||
StageDataComponent component = StageDataComponent.getFromWorld((ServerWorld) mob.getEntityWorld());
|
||||
ChunkPos chunkPos = new ChunkPos(mob.getBlockPos());
|
||||
int stage = component.findEffectiveStageOfChunk((ServerWorld) mob.getEntityWorld(), chunkPos);
|
||||
|
||||
return stage >= 3 && super.canStart();
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ public class FollowPassiveEntityGoal extends FollowTargetGoal<LivingEntity> {
|
||||
super(mob, LivingEntity.class, 10, false, false, entity -> {
|
||||
StageDataComponent component = StageDataComponent.getFromWorld((ServerWorld) mob.getEntityWorld());
|
||||
ChunkPos chunkPos = new ChunkPos(mob.getBlockPos());
|
||||
int stage = component.findStageOfChunk(chunkPos);
|
||||
int stage = component.findEffectiveStageOfChunk((ServerWorld) mob.getEntityWorld(), chunkPos);
|
||||
|
||||
return entity instanceof PassiveEntity || (entity instanceof PlayerEntity && stage >= 4);
|
||||
});
|
||||
@ -25,7 +25,7 @@ public class FollowPassiveEntityGoal extends FollowTargetGoal<LivingEntity> {
|
||||
public boolean canStart() {
|
||||
StageDataComponent component = StageDataComponent.getFromWorld((ServerWorld) mob.getEntityWorld());
|
||||
ChunkPos chunkPos = new ChunkPos(mob.getBlockPos());
|
||||
int stage = component.findStageOfChunk(chunkPos);
|
||||
int stage = component.findEffectiveStageOfChunk((ServerWorld) mob.getEntityWorld(), chunkPos);
|
||||
|
||||
return mob instanceof HostileEntity && (!(mob instanceof Angerable) || stage >= 4) && stage >= 2 && super.canStart();
|
||||
}
|
||||
|
@ -1,22 +1,17 @@
|
||||
package com.thebrokenrail.twine.item;
|
||||
|
||||
import com.thebrokenrail.twine.Twine;
|
||||
import com.thebrokenrail.twine.component.StageDataComponent;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.Inventories;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.DyeableItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BackpackItem extends Item implements DyeableItem {
|
||||
@ -116,18 +111,6 @@ public class BackpackItem extends Item implements DyeableItem {
|
||||
buf.writeBoolean(large);
|
||||
buf.writeEnumConstant(hand);
|
||||
});
|
||||
|
||||
StageDataComponent component = StageDataComponent.getFromWorld((ServerWorld) world);
|
||||
ChunkPos chunkPos = new ChunkPos(user.getBlockPos());
|
||||
|
||||
int stage = component.findStageOfChunk(chunkPos);
|
||||
user.sendMessage(new LiteralText("Global Stage: " + stage), false);
|
||||
|
||||
StageDataComponent.StageData[] personalData = component.getData(user.getUuid());
|
||||
int personalStage = StageDataComponent.findStageOfChunk(chunkPos, personalData);
|
||||
user.sendMessage(new LiteralText("Personal Stage: " + personalStage), false);
|
||||
user.sendMessage(new LiteralText("Personal Center Chunk X: " + personalData[personalStage].x + " Personal Center Chunk Z: " + personalData[personalStage].z), false);
|
||||
user.sendMessage(new LiteralText("Time To Personal Stage Increase: " + (Twine.STAGE_TIME - personalData[personalStage].time) + " Ticks"), false);
|
||||
}
|
||||
return new TypedActionResult<>(ActionResult.CONSUME, stack);
|
||||
}
|
||||
|
46
src/main/java/com/thebrokenrail/twine/item/DivinerItem.java
Normal file
46
src/main/java/com/thebrokenrail/twine/item/DivinerItem.java
Normal file
@ -0,0 +1,46 @@
|
||||
package com.thebrokenrail.twine.item;
|
||||
|
||||
import com.thebrokenrail.twine.Twine;
|
||||
import com.thebrokenrail.twine.component.StageDataComponent;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.SwordItem;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class DivinerItem extends Item {
|
||||
public DivinerItem() {
|
||||
super(new Settings().group(Twine.ITEM_GROUP).maxDamage(6));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
|
||||
ItemStack stack = user.getStackInHand(hand);
|
||||
if (!world.isClient()) {
|
||||
StageDataComponent component = StageDataComponent.getFromWorld((ServerWorld) world);
|
||||
ChunkPos chunkPos = new ChunkPos(user.getBlockPos());
|
||||
|
||||
int stage = component.findEffectiveStageOfChunk((ServerWorld) world, chunkPos);
|
||||
|
||||
StageDataComponent.StageData[] personalData = component.getData(user.getUuid());
|
||||
int effectiveStage = StageDataComponent.findStageOfChunk(chunkPos, personalData);
|
||||
|
||||
user.sendMessage(new TranslatableText("chat.twine.diviner_info_1", new LiteralText(String.valueOf(stage + 1)).formatted(Formatting.WHITE)).formatted(Formatting.YELLOW), false);
|
||||
user.sendMessage(new TranslatableText("chat.twine.diviner_info_2", (stage + 1 >= Twine.STAGE_COUNT ? new TranslatableText("chat.twine.diviner_info_never") : new LiteralText(String.valueOf(Twine.STAGE_TIME - component.findEffectiveTimeOfChunk((ServerWorld) world, chunkPos)))).formatted(Formatting.WHITE)).formatted(Formatting.YELLOW), false);
|
||||
user.sendMessage(new TranslatableText("chat.twine.diviner_info_3", new LiteralText(String.valueOf(effectiveStage + 1)).formatted(Formatting.WHITE)).formatted(Formatting.YELLOW), false);
|
||||
user.sendMessage(new TranslatableText("chat.twine.diviner_info_4", new LiteralText(String.valueOf(personalData[effectiveStage].x << 4)).formatted(Formatting.WHITE), new LiteralText(String.valueOf(personalData[effectiveStage].z << 4)).formatted(Formatting.WHITE)).formatted(Formatting.YELLOW), false);
|
||||
user.sendMessage(new TranslatableText("chat.twine.diviner_info_5", (effectiveStage + 1 >= Twine.STAGE_COUNT ? new TranslatableText("chat.twine.diviner_info_never") : new LiteralText(String.valueOf(Twine.STAGE_TIME - personalData[effectiveStage].time))).formatted(Formatting.WHITE)).formatted(Formatting.YELLOW), false);
|
||||
|
||||
stack.damage(1, user, e -> e.sendToolBreakStatus(hand));
|
||||
}
|
||||
return new TypedActionResult<>(ActionResult.SUCCESS, stack);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.thebrokenrail.twine.mixin;
|
||||
|
||||
import net.minecraft.advancement.criterion.Criteria;
|
||||
import net.minecraft.advancement.criterion.Criterion;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(Criteria.class)
|
||||
public interface CriteriaHook {
|
||||
@Invoker
|
||||
static <T extends Criterion<?>> T callRegister(T object) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
216
src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntity.java
Normal file
216
src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntity.java
Normal file
@ -0,0 +1,216 @@
|
||||
package com.thebrokenrail.twine.mixin;
|
||||
|
||||
import com.thebrokenrail.twine.Twine;
|
||||
import com.thebrokenrail.twine.advancement.ChestBoatCriterion;
|
||||
import com.thebrokenrail.twine.util.BoatInventory;
|
||||
import com.thebrokenrail.twine.util.BoatUtil;
|
||||
import com.thebrokenrail.twine.util.EnderChestInventoryWrapper;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.EnderChestBlock;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.vehicle.BoatEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.screen.GenericContainerScreenHandler;
|
||||
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.GameRules;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(BoatEntity.class)
|
||||
public class MixinBoatEntity implements BoatUtil {
|
||||
@Inject(at = @At("RETURN"), method = "initDataTracker")
|
||||
public void initDataTracker(CallbackInfo info) {
|
||||
((BoatEntity) (Object) this).getDataTracker().startTracking(BoatUtil.HAS_CHEST, BoatChestMode.NONE.name());
|
||||
}
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "canAddPassenger", cancellable = true)
|
||||
public void canAddPassenger(Entity passenger, CallbackInfoReturnable<Boolean> info) {
|
||||
if (((BoatEntity) (Object) this).getPassengerList().size() > 0 && hasChest() != BoatChestMode.NONE) {
|
||||
info.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(at = @At(value = "INVOKE", target = "Ljava/util/List;size()I"), method = "updatePassengerPosition")
|
||||
public int updatePassengerPosition(List<Entity> list) {
|
||||
if (hasChest() != BoatChestMode.NONE) {
|
||||
return list.size() + 1;
|
||||
} else {
|
||||
return list.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "interact", cancellable = true)
|
||||
public void interact(PlayerEntity player, Hand hand, CallbackInfoReturnable<ActionResult> info) {
|
||||
BoatChestMode hasChest = hasChest();
|
||||
ItemStack stack = player.getStackInHand(hand);
|
||||
if ((stack.getItem() == Items.CHEST || stack.getItem() == Items.ENDER_CHEST) && hasChest == BoatChestMode.NONE) {
|
||||
List<Entity> passengers = ((BoatEntity) (Object) this).getPassengerList();
|
||||
for (int i = 1; i < passengers.size(); i++) {
|
||||
passengers.get(i).stopRiding();
|
||||
}
|
||||
if (!player.getEntityWorld().isClient()) {
|
||||
if (stack.getItem() == Items.CHEST) {
|
||||
Twine.CHEST_BOAT_CRITERION.trigger((ServerPlayerEntity) player);
|
||||
setHasChest(BoatChestMode.CHEST);
|
||||
} else {
|
||||
Twine.ENDER_CHEST_BOAT_CRITERION.trigger((ServerPlayerEntity) player);
|
||||
setHasChest(BoatChestMode.ENDER_CHEST);
|
||||
}
|
||||
updateInventory();
|
||||
}
|
||||
if (!player.isCreative()) {
|
||||
stack.decrement(1);
|
||||
}
|
||||
info.setReturnValue(ActionResult.SUCCESS);
|
||||
} else if (player.isSneaking()) {
|
||||
if (!player.getEntityWorld().isClient()) {
|
||||
openInventory(player);
|
||||
}
|
||||
info.setReturnValue(ActionResult.SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void setHasChest(BoatChestMode mode) {
|
||||
((BoatEntity) (Object) this).getDataTracker().set(BoatUtil.HAS_CHEST, mode.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item getChestItem() {
|
||||
switch (hasChest()) {
|
||||
case CHEST: {
|
||||
return Items.CHEST;
|
||||
}
|
||||
case ENDER_CHEST: {
|
||||
return Items.ENDER_CHEST;
|
||||
}
|
||||
default: {
|
||||
return Items.AIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void dropItemsOnDestroy() {
|
||||
if (((BoatEntity) (Object) this).getEntityWorld().getGameRules().getBoolean(GameRules.DO_ENTITY_DROPS)) {
|
||||
((BoatEntity) (Object) this).dropStack(new ItemStack(getChestItem()));
|
||||
dropItems();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/vehicle/BoatEntity;remove()V"), method = "damage")
|
||||
public void damage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> info) {
|
||||
dropItemsOnDestroy();
|
||||
}
|
||||
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/vehicle/BoatEntity;remove()V"), method = "fall")
|
||||
public void fall(double heightDifference, boolean onGround, BlockState landedState, BlockPos landedPosition, CallbackInfo info) {
|
||||
dropItemsOnDestroy();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private BoatInventory items;
|
||||
|
||||
@Unique
|
||||
private void updateInventory() {
|
||||
items = hasChest() == BoatChestMode.CHEST ? new BoatInventory((BoatEntity) (Object) this, 27) : null;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void dropItems() {
|
||||
if (items != null) {
|
||||
for (int i = 0; i < items.size(); ++i) {
|
||||
ItemStack itemStack = items.getStack(i);
|
||||
if (!itemStack.isEmpty() && !EnchantmentHelper.hasVanishingCurse(itemStack)) {
|
||||
((BoatEntity) (Object) this).dropStack(itemStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "writeCustomDataToTag")
|
||||
public void writeCustomDataToTag(CompoundTag tag, CallbackInfo info) {
|
||||
BoatChestMode hasChest = hasChest();
|
||||
tag.putString("HasChest", hasChest.name());
|
||||
if (hasChest == BoatChestMode.CHEST) {
|
||||
ListTag listTag = new ListTag();
|
||||
|
||||
for (int i = 2; i < this.items.size(); ++i) {
|
||||
ItemStack itemStack = this.items.getStack(i);
|
||||
if (!itemStack.isEmpty()) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
compoundTag.putByte("Slot", (byte)i);
|
||||
itemStack.toTag(compoundTag);
|
||||
listTag.add(compoundTag);
|
||||
}
|
||||
}
|
||||
|
||||
tag.put("Items", listTag);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "readCustomDataFromTag")
|
||||
public void readCustomDataFromTag(CompoundTag tag, CallbackInfo info) {
|
||||
BoatChestMode hasChest;
|
||||
try {
|
||||
hasChest = BoatChestMode.valueOf(tag.getString("HasChest"));
|
||||
} catch (IllegalArgumentException e) {
|
||||
hasChest = BoatChestMode.NONE;
|
||||
}
|
||||
setHasChest(hasChest);
|
||||
updateInventory();
|
||||
if (hasChest == BoatChestMode.CHEST) {
|
||||
ListTag listTag = tag.getList("Items", 10);
|
||||
|
||||
for (int i = 0; i < listTag.size(); ++i) {
|
||||
CompoundTag compoundTag = listTag.getCompound(i);
|
||||
int j = compoundTag.getByte("Slot") & 255;
|
||||
if (j >= 2 && j < items.size()) {
|
||||
items.setStack(j, ItemStack.fromTag(compoundTag));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoatChestMode hasChest() {
|
||||
try {
|
||||
return BoatChestMode.valueOf(((BoatEntity) (Object) this).getDataTracker().get(BoatUtil.HAS_CHEST));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return BoatChestMode.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openInventory(PlayerEntity player) {
|
||||
if (hasChest() == BoatUtil.BoatChestMode.CHEST) {
|
||||
player.openHandledScreen(new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> GenericContainerScreenHandler.createGeneric9x3(i, playerInventory, getInventory()), ((BoatEntity) (Object) this).getDisplayName()));
|
||||
} else {
|
||||
player.openHandledScreen(new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> GenericContainerScreenHandler.createGeneric9x3(i, playerInventory, new EnderChestInventoryWrapper((BoatEntity) (Object) this, player.getEnderChestInventory())), EnderChestBlock.CONTAINER_NAME));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.thebrokenrail.twine.mixin;
|
||||
|
||||
import com.thebrokenrail.twine.util.BoatUtil;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockRenderType;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.BoatEntityRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.client.util.math.Vector3f;
|
||||
import net.minecraft.entity.vehicle.BoatEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(BoatEntityRenderer.class)
|
||||
public class MixinBoatEntityRenderer {
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), method = "render")
|
||||
public void render(BoatEntity boatEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo info) {
|
||||
BlockState blockState = Block.getBlockFromItem(((BoatUtil) boatEntity).getChestItem()).getDefaultState();
|
||||
if (blockState.getRenderType() != BlockRenderType.INVISIBLE) {
|
||||
matrixStack.push();
|
||||
matrixStack.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(180.0f));
|
||||
matrixStack.scale(0.75f, 0.75f, 0.75f);
|
||||
matrixStack.translate(-1d, -0.25f, 0.5d);
|
||||
matrixStack.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(90.0f));
|
||||
renderBlock(blockState, matrixStack, vertexConsumerProvider, i);
|
||||
matrixStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private void renderBlock(BlockState state, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i) {
|
||||
MinecraftClient.getInstance().getBlockRenderManager().renderBlockAsEntity(state, matrixStack, vertexConsumerProvider, i, OverlayTexture.DEFAULT_UV);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.thebrokenrail.twine.mixin;
|
||||
|
||||
import com.thebrokenrail.twine.util.BoatUtil;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
||||
import net.minecraft.entity.vehicle.BoatEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Mixin(ClientPlayerInteractionManager.class)
|
||||
public class MixinClientPlayerInteractionManager {
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "hasRidingInventory", cancellable = true)
|
||||
public void hasRidingInventory(CallbackInfoReturnable<Boolean> info) {
|
||||
assert client.player != null;
|
||||
if (client.player.hasVehicle() && client.player.getVehicle() instanceof BoatEntity && ((BoatUtil) client.player.getVehicle()).hasChest() != BoatUtil.BoatChestMode.NONE) {
|
||||
info.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.thebrokenrail.twine.mixin;
|
||||
|
||||
import com.thebrokenrail.twine.component.StageDataComponent;
|
||||
import com.thebrokenrail.twine.entity.ExplodeArtificialBlockGoal;
|
||||
import com.thebrokenrail.twine.entity.FollowPassiveEntityGoal;
|
||||
import com.thebrokenrail.twine.entity.StandOnGlowingObsidian;
|
||||
import net.minecraft.entity.EntityType;
|
||||
@ -36,13 +37,16 @@ public class MixinMobEntity {
|
||||
if (this instanceof Monster) {
|
||||
if ((Object) this instanceof MobEntityWithAi) {
|
||||
goalSelector.add(1, new StandOnGlowingObsidian((MobEntityWithAi) (Object) this));
|
||||
if ((Object) this instanceof CreeperEntity) {
|
||||
goalSelector.add(3, new ExplodeArtificialBlockGoal((MobEntityWithAi) (Object) this));
|
||||
}
|
||||
}
|
||||
targetSelector.add(2, new FollowPassiveEntityGoal((MobEntity) (Object) this));
|
||||
} else if ((Object) this instanceof IronGolemEntity) {
|
||||
targetSelector.add(3, new FollowTargetGoal<>((IronGolemEntity) (Object) this, PlayerEntity.class, 10, true, false, entity -> {
|
||||
StageDataComponent component = StageDataComponent.getFromWorld((ServerWorld) ((IronGolemEntity) (Object) this).getEntityWorld());
|
||||
ChunkPos chunkPos = new ChunkPos(((IronGolemEntity) (Object) this).getBlockPos());
|
||||
int stage = component.findStageOfChunk(chunkPos);
|
||||
int stage = component.findEffectiveStageOfChunk((ServerWorld) (((IronGolemEntity) (Object) this).getEntityWorld()), chunkPos);
|
||||
|
||||
return stage >= 3 && !((IronGolemEntity) (Object) this).isPlayerCreated();
|
||||
}));
|
||||
|
@ -0,0 +1,25 @@
|
||||
package com.thebrokenrail.twine.mixin;
|
||||
|
||||
import com.thebrokenrail.twine.util.BoatUtil;
|
||||
import net.minecraft.entity.vehicle.BoatEntity;
|
||||
import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerPlayNetworkHandler.class)
|
||||
public class MixinServerPlayNetworkHandler {
|
||||
@Shadow
|
||||
public ServerPlayerEntity player;
|
||||
|
||||
@Inject(at = @At("TAIL"), method = "onClientCommand")
|
||||
public void onClientCommand(ClientCommandC2SPacket packet, CallbackInfo info) {
|
||||
if (packet.getMode() == ClientCommandC2SPacket.Mode.OPEN_INVENTORY && player.hasVehicle() && player.getVehicle() instanceof BoatEntity && ((BoatUtil) player.getVehicle()).hasChest() != BoatUtil.BoatChestMode.NONE) {
|
||||
((BoatUtil) player.getVehicle()).openInventory(player);
|
||||
}
|
||||
}
|
||||
}
|
@ -31,26 +31,24 @@ public abstract class MixinServerPlayerEntity {
|
||||
StageDataComponent.StageData[] data = component.getData(((ServerPlayerEntity) (Object) this).getUuid());
|
||||
int stage = StageDataComponent.findStageOfChunk(getChunkPos(), data);
|
||||
StageDataComponent.StageData stageObj = data[stage];
|
||||
if (lastTime != -1) {
|
||||
long diff = Math.max(0, getServerWorld().getTime() - lastTime);
|
||||
if (diff > 5) {
|
||||
System.out.println(diff);
|
||||
if (stage + 1 < Twine.STAGE_COUNT) {
|
||||
if (lastTime != -1L) {
|
||||
stageObj.time = stageObj.time + Math.max(0, getServerWorld().getTime() - lastTime);
|
||||
}
|
||||
if (stageObj.time >= Twine.STAGE_TIME) {
|
||||
StageDataComponent.StageData old = data[stage + 1];
|
||||
if (old != null) {
|
||||
old.time = 0;
|
||||
}
|
||||
StageDataComponent.StageData newData = new StageDataComponent.StageData();
|
||||
ChunkPos pos = getChunkPos();
|
||||
newData.x = pos.x;
|
||||
newData.z = pos.z;
|
||||
data[stage + 1] = newData;
|
||||
data[stage] = old;
|
||||
}
|
||||
stageObj.time = stageObj.time + Math.max(0, getServerWorld().getTime() - lastTime);
|
||||
}
|
||||
lastTime = getServerWorld().getTime();
|
||||
if (stageObj.time >= 24000 && stage + 1 < Twine.STAGE_COUNT) {
|
||||
StageDataComponent.StageData old = data[stage + 1];
|
||||
if (old != null) {
|
||||
old.time = 0;
|
||||
}
|
||||
StageDataComponent.StageData newData = new StageDataComponent.StageData();
|
||||
ChunkPos pos = getChunkPos();
|
||||
newData.x = pos.x;
|
||||
newData.z = pos.z;
|
||||
data[stage + 1] = newData;
|
||||
data[stage] = old;
|
||||
}
|
||||
component.markDirty();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package com.thebrokenrail.twine.util;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.SimpleInventory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
|
||||
public class BoatInventory extends SimpleInventory {
|
||||
private final Entity entity;
|
||||
|
||||
public BoatInventory(Entity entity, int size) {
|
||||
super(size);
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayerUse(PlayerEntity player) {
|
||||
return BoatUtil.canReachEntity(player, entity) && entity.isAlive() && ((BoatUtil) entity).hasChest() == BoatUtil.BoatChestMode.CHEST && super.canPlayerUse(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(PlayerEntity player) {
|
||||
BoatUtil.playSound(entity, SoundEvents.BLOCK_CHEST_OPEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(PlayerEntity player) {
|
||||
BoatUtil.playSound(entity, SoundEvents.BLOCK_CHEST_CLOSE);
|
||||
}
|
||||
}
|
42
src/main/java/com/thebrokenrail/twine/util/BoatUtil.java
Normal file
42
src/main/java/com/thebrokenrail/twine/util/BoatUtil.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.thebrokenrail.twine.util;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.data.DataTracker;
|
||||
import net.minecraft.entity.data.TrackedData;
|
||||
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.vehicle.BoatEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public interface BoatUtil {
|
||||
TrackedData<String> HAS_CHEST = DataTracker.registerData(BoatEntity.class, TrackedDataHandlerRegistry.STRING);
|
||||
|
||||
Item getChestItem();
|
||||
|
||||
BoatChestMode hasChest();
|
||||
|
||||
Inventory getInventory();
|
||||
|
||||
void openInventory(PlayerEntity player);
|
||||
|
||||
static boolean canReachEntity(PlayerEntity player, Entity entity) {
|
||||
return player.squaredDistanceTo(entity.getPos().getX(), entity.getPos().getY(), entity.getPos().getZ()) <= 64d;
|
||||
}
|
||||
|
||||
enum BoatChestMode {
|
||||
ENDER_CHEST,
|
||||
CHEST,
|
||||
NONE
|
||||
}
|
||||
|
||||
static void playSound(Entity vehicle, SoundEvent sound) {
|
||||
Vec3d pos = vehicle.getPos();
|
||||
vehicle.getEntityWorld().playSound(null, pos.getX(), pos.getY(), pos.getZ(), sound, SoundCategory.BLOCKS, 0.5F, vehicle.getEntityWorld().random.nextFloat() * 0.1F + 0.9F);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package com.thebrokenrail.twine.util;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.EnderChestInventory;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
|
||||
public class EnderChestInventoryWrapper implements Inventory {
|
||||
private final EnderChestInventory inventory;
|
||||
private final Entity entity;
|
||||
|
||||
public EnderChestInventoryWrapper(Entity entity, EnderChestInventory inventory) {
|
||||
this.entity = entity;
|
||||
this.inventory = inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return inventory.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return inventory.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStack(int slot) {
|
||||
return inventory.getStack(slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeStack(int slot, int amount) {
|
||||
return inventory.removeStack(slot, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeStack(int slot) {
|
||||
return inventory.removeStack(slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStack(int slot, ItemStack stack) {
|
||||
inventory.setStack(slot, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markDirty() {
|
||||
inventory.markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayerUse(PlayerEntity player) {
|
||||
return BoatUtil.canReachEntity(player, entity) && entity.isAlive() && ((BoatUtil) entity).hasChest() == BoatUtil.BoatChestMode.ENDER_CHEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(PlayerEntity player) {
|
||||
BoatUtil.playSound(entity, SoundEvents.BLOCK_ENDER_CHEST_OPEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(PlayerEntity player) {
|
||||
BoatUtil.playSound(entity, SoundEvents.BLOCK_ENDER_CHEST_CLOSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
inventory.clear();
|
||||
}
|
||||
}
|
@ -1,6 +1,34 @@
|
||||
{
|
||||
"itemGroup.twine.item_group": "Twine",
|
||||
|
||||
"item.twine.large_backpack": "Large Backpack",
|
||||
"item.twine.small_backpack": "Small Backpack",
|
||||
"itemGroup.twine.item_group": "Twine",
|
||||
"block.twine.glowing_obsidian": "Glowing Obsidian"
|
||||
|
||||
"block.twine.glowing_obsidian": "Glowing Obsidian",
|
||||
|
||||
"advancements.twine.root.title": "Twine",
|
||||
"advancements.twine.root.description": "A simple survival mod for Minecraft encouraging a nomadic lifestyle",
|
||||
|
||||
"advancements.twine.chest_boat.title": "TEAM SWAMP!",
|
||||
"advancements.twine.chest_boat.description": "Put a Chest in a Boat",
|
||||
|
||||
"advancements.twine.ender_chest_boat.title": "Why not?",
|
||||
"advancements.twine.ender_chest_boat.description": "Put an Ender Chest in a Boat",
|
||||
|
||||
"advancements.twine.small_backpack.title": "On the go!",
|
||||
"advancements.twine.small_backpack.description": "Construct a Small Backpack with a Crafting Table",
|
||||
|
||||
"advancements.twine.large_backpack.title": "Go big or go home!",
|
||||
"advancements.twine.large_backpack.description": "Construct a Large Backpack with a Smithing Table by combing a Small Backpack and a Gold Ingot",
|
||||
|
||||
"advancements.twine.diviner.title": "Divination",
|
||||
"advancements.twine.diviner.description": "Construct a Diviner with a Crafting Table",
|
||||
|
||||
"item.twine.diviner": "Diviner",
|
||||
"chat.twine.diviner_info_1": "Effective Stage Of Area: %s",
|
||||
"chat.twine.diviner_info_2": "Effective Stage Of Area Increases In: %s Ticks",
|
||||
"chat.twine.diviner_info_3": "Personal Stage Of Area: %s",
|
||||
"chat.twine.diviner_info_4": "Personal Center Of Area: X: %s Z: %s",
|
||||
"chat.twine.diviner_info_5": "Personal Stage Of Area Increases In: %s Ticks",
|
||||
"chat.twine.diviner_info_never": "Never"
|
||||
}
|
6
src/main/resources/assets/twine/models/item/diviner.json
Normal file
6
src/main/resources/assets/twine/models/item/diviner.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "twine:item/diviner"
|
||||
}
|
||||
}
|
BIN
src/main/resources/assets/twine/textures/item/diviner.png
Normal file
BIN
src/main/resources/assets/twine/textures/item/diviner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
@ -0,0 +1,6 @@
|
||||
{
|
||||
"animation": {
|
||||
"interpolate": true,
|
||||
"frametime": 64
|
||||
}
|
||||
}
|
19
src/main/resources/data/twine/advancements/chest_boat.json
Normal file
19
src/main/resources/data/twine/advancements/chest_boat.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"display": {
|
||||
"icon": {
|
||||
"item": "minecraft:oak_boat"
|
||||
},
|
||||
"title": {
|
||||
"translate": "advancements.twine.chest_boat.title"
|
||||
},
|
||||
"description": {
|
||||
"translate": "advancements.twine.chest_boat.description"
|
||||
}
|
||||
},
|
||||
"parent": "twine:root",
|
||||
"criteria": {
|
||||
"chest_boat": {
|
||||
"trigger": "twine:chest_boat"
|
||||
}
|
||||
}
|
||||
}
|
26
src/main/resources/data/twine/advancements/diviner.json
Normal file
26
src/main/resources/data/twine/advancements/diviner.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"display": {
|
||||
"icon": {
|
||||
"item": "twine:diviner"
|
||||
},
|
||||
"title": {
|
||||
"translate": "advancements.twine.diviner.title"
|
||||
},
|
||||
"description": {
|
||||
"translate": "advancements.twine.diviner.description"
|
||||
}
|
||||
},
|
||||
"parent": "twine:root",
|
||||
"criteria": {
|
||||
"chest_boat": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"item": "twine:diviner"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"display": {
|
||||
"icon": {
|
||||
"item": "minecraft:oak_boat"
|
||||
},
|
||||
"title": {
|
||||
"translate": "advancements.twine.ender_chest_boat.title"
|
||||
},
|
||||
"description": {
|
||||
"translate": "advancements.twine.ender_chest_boat.description"
|
||||
}
|
||||
},
|
||||
"parent": "twine:chest_boat",
|
||||
"criteria": {
|
||||
"chest_boat": {
|
||||
"trigger": "twine:ender_chest_boat"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"display": {
|
||||
"icon": {
|
||||
"item": "twine:large_backpack"
|
||||
},
|
||||
"title": {
|
||||
"translate": "advancements.twine.large_backpack.title"
|
||||
},
|
||||
"description": {
|
||||
"translate": "advancements.twine.large_backpack.description"
|
||||
}
|
||||
},
|
||||
"parent": "twine:small_backpack",
|
||||
"criteria": {
|
||||
"chest_boat": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"item": "twine:large_backpack"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
src/main/resources/data/twine/advancements/root.json
Normal file
28
src/main/resources/data/twine/advancements/root.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"display": {
|
||||
"icon": {
|
||||
"item": "twine:glowing_obsidian"
|
||||
},
|
||||
"title": {
|
||||
"translate": "advancements.twine.root.title"
|
||||
},
|
||||
"description": {
|
||||
"translate": "advancements.twine.root.description"
|
||||
},
|
||||
"background": "minecraft:textures/gui/advancements/backgrounds/stone.png",
|
||||
"show_toast": false,
|
||||
"announce_to_chat": false
|
||||
},
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"twine:glowing_obsidian",
|
||||
"twine:small_backpack",
|
||||
"twine:diviner"
|
||||
]
|
||||
},
|
||||
"criteria": {
|
||||
"tick": {
|
||||
"trigger": "minecraft:tick"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"display": {
|
||||
"icon": {
|
||||
"item": "twine:small_backpack"
|
||||
},
|
||||
"title": {
|
||||
"translate": "advancements.twine.small_backpack.title"
|
||||
},
|
||||
"description": {
|
||||
"translate": "advancements.twine.small_backpack.description"
|
||||
}
|
||||
},
|
||||
"parent": "twine:root",
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"twine:large_backpack"
|
||||
]
|
||||
},
|
||||
"criteria": {
|
||||
"chest_boat": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"item": "twine:small_backpack"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
src/main/resources/data/twine/recipes/diviner.json
Normal file
20
src/main/resources/data/twine/recipes/diviner.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" I ",
|
||||
"IEI",
|
||||
" I "
|
||||
],
|
||||
"key": {
|
||||
"I": {
|
||||
"item": "minecraft:iron_ingot"
|
||||
},
|
||||
"E": {
|
||||
"item": "minecraft:ender_pearl"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "twine:small_diviner",
|
||||
"count": 1
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"#minecraft:doors",
|
||||
"#minecraft:fences",
|
||||
"#minecraft:fence_gates",
|
||||
"#minecraft:planks",
|
||||
"#minecraft:signs",
|
||||
"#minecraft:slabs",
|
||||
"#minecraft:stairs",
|
||||
"#minecraft:trapdoors",
|
||||
"#minecraft:walls",
|
||||
"#minecraft:wool",
|
||||
"#minecraft:anvil",
|
||||
"#minecraft:beacon_base_blocks",
|
||||
"#minecraft:beds",
|
||||
"#minecraft:shulker_boxes",
|
||||
"#minecraft:stone_bricks",
|
||||
"minecraft:nether_portal",
|
||||
"minecraft:cobblestone"
|
||||
]
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
"issues": "https://gitea.thebrokenrail.com/TheBrokenRail/Twine/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"icon": "assets/twine/icon.png",
|
||||
"icon": "assets/twine/textures/block/glowing_obsidian.png",
|
||||
"environment": "*",
|
||||
"mixins": [
|
||||
"twine.mixins.json"
|
||||
|
@ -4,11 +4,18 @@
|
||||
"package": "com.thebrokenrail.twine.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"CriteriaHook",
|
||||
"MixinBoatEntity",
|
||||
"MixinDefaultBiomeFeatures",
|
||||
"MixinMobEntity",
|
||||
"MixinServerPlayerEntity",
|
||||
"MixinServerPlayNetworkHandler",
|
||||
"MixinSlot"
|
||||
],
|
||||
"client": [
|
||||
"MixinBoatEntityRenderer",
|
||||
"MixinClientPlayerInteractionManager"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
|
Reference in New Issue
Block a user