Waystones (Fabric Edition)

Waystones (Fabric Edition)

3M Downloads

Configurable name generation

jaimemccann opened this issue ยท 0 comments

commented

Background

I love this mod. It elegantly addresses a problem in vanilla Minecraft, whereby once you've setup your home base, it's difficult to safely and immersively explore the world and interact meaningfully with it at locations far away from that base.

However, I've noticed, once enough waystones have been activated, the generated names start to blur together and become indistinct and unmemorable, making it difficult to recall at a glance where a waystone is situated.

It's easy enough to manually solve this problem per-waystone by breaking the waystone, placing it down again, and assigning it a custom name, but this breaks immersion in the mod, as the player will forever remember the name of the waystone as a name the player chose, for a waystone the player placed, as opposed to what the player might otherwise like to imagine is simply the preexisting name of that location in the world.

A couple waystone-to-waypoint mods are also out there that help tackle this problem, but their implementation is map-mod-specific, which is, of course, the reason they have to be separate mods. Furthermore, waystone-to-waypoint mods require map mods to begin with, which tend to render actual map items quite useless, and arguably take some of the joy and magic out of exploring a Minecraft world. As such, it would be nice to have a solution that can be localized to the Waystones mod itself.

Suggestion begins here

As such, I think it would be very nice to be able to configure automatic name generation to be contingent on details about the waystone's surroundings.

Consider the following for example:

# waystones-common.toml
[common]
    [common.worldgen]
        nameGenTemplate = "$BIOME $FEATURE of $RANDOMNAME"
        # example results:
        # Plains of Mjolzermed
        # Forest Village of [blah blah]

This would allow players and modpack creators to make each waystone name automatically self-describing while still preserving the flavor provided by the existing random name system.

Example partial implementation

I know next to nothing about Minecraft modding, but using FTB Chunks source code as a reference, I tried modding the Waystones mod for non-configurable support for the biome piece specifically. I won't submit a pull request, since, novice that I am, I'm sure this feature could be implemented much better than I tried to implement it. Furthermore, I can't even test my modifications, due to issue #313 . Nonetheless, my code does compile, and so, for reference by more experienced modders, here's my patch:

// src/main/java/net/blay0/mods/waystones/tileentity/WaystoneTileEntity.java

package net.blay09.mods.waystones.tileentity;

import net.blay09.mods.waystones.api.IWaystone;
import net.blay09.mods.waystones.block.WaystoneBlock;
import net.blay09.mods.waystones.container.WaystoneSelectionContainer;
import net.blay09.mods.waystones.container.WaystoneSettingsContainer;
import net.blay09.mods.waystones.core.*;
import net.blay09.mods.waystones.worldgen.namegen.NameGenerator;
import net.minecraft.block.BlockState;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.state.properties.DoubleBlockHalf;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.IServerWorld;
import net.minecraftforge.common.util.Constants;

import javax.annotation.Nullable;
import java.util.Objects;
import java.util.UUID;

/** JAIME'S MODIFICATIONS **/
import net.minecraft.world.biome.Biome;
import net.minecraft.util.RegistryKey;
/** END JAIME'S MODIFICATIONS **/

public class WaystoneTileEntity extends TileEntity {

    private IWaystone waystone = InvalidWaystone.INSTANCE;
    private boolean shouldNotInitialize;

    public WaystoneTileEntity() {
        super(ModTileEntities.waystone);
    }

    @Override
    public CompoundNBT write(CompoundNBT tagCompound) {
        super.write(tagCompound);

        IWaystone waystone = getWaystone();
        if (waystone.isValid()) {
            tagCompound.put("UUID", NBTUtil.func_240626_a_(waystone.getWaystoneUid()));
        }

        return tagCompound;
    }

    @Override
    public void read(BlockState state, CompoundNBT tagCompound) {
        super.read(state, tagCompound);
        if (tagCompound.contains("UUID", Constants.NBT.TAG_INT_ARRAY)) {
            waystone = new WaystoneProxy(NBTUtil.readUniqueId(Objects.requireNonNull(tagCompound.get("UUID"))));
        }
    }

    @Override
    public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
        super.onDataPacket(net, pkt);
        read(getBlockState(), pkt.getNbtCompound());
    }

    @Override
    public CompoundNBT getUpdateTag() {
        return write(new CompoundNBT());
    }

    @Nullable
    @Override
    public SUpdateTileEntityPacket getUpdatePacket() {
        return new SUpdateTileEntityPacket(pos, 0, getUpdateTag());
    }

    @Override
    public AxisAlignedBB getRenderBoundingBox() {
        return new AxisAlignedBB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 2, pos.getZ() + 1);
    }

    public IWaystone getWaystone() {
        if (!waystone.isValid() && world != null && !world.isRemote && !shouldNotInitialize) {
            BlockState state = getBlockState();
            if (state.getBlock() instanceof WaystoneBlock) {
                DoubleBlockHalf half = state.get(WaystoneBlock.HALF);
                if (half == DoubleBlockHalf.LOWER) {
                    initializeWaystone((IServerWorld) Objects.requireNonNull(world), null, true);
                } else if (half == DoubleBlockHalf.UPPER) {
                    TileEntity tileEntity = world.getTileEntity(pos.down());
                    if (tileEntity instanceof WaystoneTileEntity) {
                        initializeFromBase(((WaystoneTileEntity) tileEntity));
                    }
                }
            }
        }

        return waystone;
    }

    public void initializeWaystone(IServerWorld world, @Nullable LivingEntity player, boolean wasGenerated) {
        Waystone waystone = new Waystone(UUID.randomUUID(), world.getWorld().getDimensionKey(), pos, wasGenerated, player != null ? player.getUniqueID() : null);
        String name = NameGenerator.get().getName(waystone, world.getRandom());
        // waystone.setName(name);
        /** JAIME'S MODIFICATIONS **/
        waystone.setName(appendBiomeName(world, name));
        /** END JAIME'S MODIFICATIONS **/
        WaystoneManager.get().addWaystone(waystone);
        this.waystone = waystone;
    }

    /** JAIME'S MODIFICATIONS **/
    private String appendBiomeName(IServerWorld world, String appendTo) {
        RegistryKey<Biome> rkb = world.func_242406_i(pos).orElse(null);
        if (rkb != null) {
            return (new TranslationTextComponent(
                "biome." + rkb.getLocation().getNamespace() +
                "." + rkb.getLocation().getPath()
            )).getString() + " of " + appendTo;
        } else {
            return appendTo;
        }
    }
    /** END JAIME'S MODIFICATIONS **/

    public void initializeFromBase(WaystoneTileEntity tileEntity) {
        waystone = tileEntity.getWaystone();
    }

    public void uninitializeWaystone() {
        if (waystone.isValid()) {
            WaystoneManager.get().removeWaystone(waystone);
            PlayerWaystoneManager.removeKnownWaystone(waystone);
        }

        waystone = InvalidWaystone.INSTANCE;
        shouldNotInitialize = true;

        DoubleBlockHalf half = getBlockState().get(WaystoneBlock.HALF);
        BlockPos otherPos = half == DoubleBlockHalf.UPPER ? pos.down() : pos.up();
        TileEntity tileEntity = Objects.requireNonNull(world).getTileEntity(otherPos);
        if (tileEntity instanceof WaystoneTileEntity) {
            WaystoneTileEntity waystoneTile = (WaystoneTileEntity) tileEntity;
            waystoneTile.waystone = InvalidWaystone.INSTANCE;
            waystoneTile.shouldNotInitialize = true;
        }
    }

    public INamedContainerProvider getWaystoneSelectionContainerProvider() {
        return new INamedContainerProvider() {
            @Override
            public ITextComponent getDisplayName() {
                return new TranslationTextComponent("container.waystones.waystone_selection");
            }

            @Override
            public Container createMenu(int i, PlayerInventory playerInventory, PlayerEntity playerEntity) {
                return new WaystoneSelectionContainer(i, WarpMode.WAYSTONE_TO_WAYSTONE, getWaystone());
            }
        };
    }

    public INamedContainerProvider getWaystoneSettingsContainerProvider() {
        return new INamedContainerProvider() {
            @Override
            public ITextComponent getDisplayName() {
                return new TranslationTextComponent("container.waystones.waystone_settings");
            }

            @Override
            public Container createMenu(int i, PlayerInventory playerInventory, PlayerEntity playerEntity) {
                return new WaystoneSettingsContainer(i, getWaystone());
            }
        };
    }
}

If the patch works correctly -- which, again, I cannot verify -- then it will cause each waystone's default generated name to be prefixed with "[Localized biome name] of ", e.g. "Lukewarm Ocean of Doria." In the absence of actually configurable name generation, I think even just this small adjustment in functionality would greatly enhance the mod.

Related idea: custom syllables

In searching the source tree for the proper file to modify for my above example code, I noticed the syllables currently used for random name generation are hard-coded. It would be neat to be able to define custom syllables to use in name generation.

In conclusion

I hope you will consider something along these lines for a future version. In the meantime, if you can be troubled to take a look at the tangentially related bug report I've opened -- issue #313 -- I would greatly appreciate any guidance as to getting my own adjustments to work.