Implement shrine to reset skillpoints, custom shrine model, worldgen for the shrine, and other stuff

This commit is contained in:
2025-11-12 11:11:15 +01:00
parent 20ca8c3e43
commit f72c2a54fe
20 changed files with 709 additions and 8 deletions
@@ -3,6 +3,7 @@ package net.fretux.ascend;
import net.fretux.ascend.command.AscendCommand;
import net.fretux.ascend.config.AscendConfig;
import net.fretux.ascend.network.PacketHandler;
import net.fretux.ascend.registry.ModBlocks;
import net.fretux.ascend.registry.ModItems;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -24,6 +25,7 @@ public class AscendMod {
System.out.println("[Ascend] Mod loaded! Side=" + FMLEnvironment.dist);
net.minecraftforge.fml.ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, AscendConfig.COMMON_SPEC);
ModItems.register(modBus);
ModBlocks.register(modBus);
}
private void onCommonSetup(final FMLCommonSetupEvent event) {
@@ -0,0 +1,32 @@
package net.fretux.ascend.block;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockState;
public class ShrineOfRemembranceBlock extends Block {
public ShrineOfRemembranceBlock(Properties props) {
super(props);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos,
Player player, InteractionHand hand,
net.minecraft.world.phys.BlockHitResult hit) {
if (level.isClientSide) {
net.minecraft.client.Minecraft.getInstance().setScreen(new net.fretux.ascend.client.screen.ShrineScreen());
return InteractionResult.SUCCESS;
}
return InteractionResult.CONSUME;
}
}
@@ -0,0 +1,65 @@
package net.fretux.ascend.client.screen;
import com.mojang.blaze3d.systems.RenderSystem;
import net.fretux.ascend.network.PacketHandler;
import net.fretux.ascend.network.ServerboundShrineChoicePacket;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
public class ShrineScreen extends Screen {
private static final int WIDTH = 200;
private static final int HEIGHT = 120;
private int leftPos;
private int topPos;
public ShrineScreen() {
super(Component.literal("Shrine of Remembrance"));
}
@Override
protected void init() {
super.init();
this.leftPos = (this.width - WIDTH) / 2;
this.topPos = (this.height - HEIGHT) / 2;
this.clearWidgets();
// Dialogue text
int buttonY = topPos + 60;
Button forgetButton = Button.builder(Component.literal("I WISH TO FORGET"), (btn) -> {
PacketHandler.INSTANCE.sendToServer(new ServerboundShrineChoicePacket("forget"));
Minecraft.getInstance().setScreen(null);
}).bounds(leftPos + 20, buttonY, WIDTH - 40, 20).build();
Button cancelButton = Button.builder(Component.literal("LEAVE"), (btn) -> {
Minecraft.getInstance().setScreen(null);
}).bounds(leftPos + 20, buttonY + 25, WIDTH - 40, 20).build();
addRenderableWidget(forgetButton);
addRenderableWidget(cancelButton);
}
@Override
public void render(GuiGraphics gui, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(gui);
RenderSystem.enableBlend();
gui.fill(leftPos - 6, topPos - 6, leftPos + WIDTH + 6, topPos + HEIGHT + 6, 0xAA000000);
RenderSystem.disableBlend();
gui.drawCenteredString(this.font, "SPEAK, LITTLE ONE.", this.width / 2, topPos + 10, 0xAA55FF);
gui.drawCenteredString(this.font, "WHAT IS IT YOU SEEK TO UNMAKE?", this.width / 2, topPos + 25, 0xAA55FF);
super.render(gui, mouseX, mouseY, partialTicks);
}
@Override
public boolean isPauseScreen() {
return false;
}
}
@@ -25,13 +25,13 @@ public class AscendConfig {
builder.push("Ascend Progression");
xpPerMobKill = builder.comment("Ascend XP gained for killing a mob.")
.defineInRange("xpPerMobKill", 5, 0, 1000);
.defineInRange("xpPerMobKill", 10, 0, 1000);
xpPerBlockBreak = builder.comment("Ascend XP gained for breaking a block (ore/stone/log).")
.defineInRange("xpPerBlockBreak", 2, 0, 1000);
xpPerCraft = builder.comment("Ascend XP gained for crafting items.")
.defineInRange("xpPerCraft", 3, 0, 1000);
.defineInRange("xpPerCraft", 1, 0, 1000);
xpPerSmelt = builder.comment("Ascend XP gained for smelting items.")
.defineInRange("xpPerSmelt", 2, 0, 1000);
.defineInRange("xpPerSmelt", 1, 0, 1000);
xpPerTrade = builder.comment("Ascend XP gained for trading with villagers.")
.defineInRange("xpPerTrade", 5, 0, 1000);
xpPerDamageTaken = builder.comment("Ascend XP gained for taking damage.")
@@ -1,6 +1,9 @@
package net.fretux.ascend.datagen;
import net.fretux.ascend.AscendMod;
import net.fretux.ascend.worldgen.ModWorldGenProvider;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.PackOutput;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.data.ExistingFileHelper;
@@ -14,11 +17,13 @@ public class AscendDataGenerators {
@OnlyIn(Dist.CLIENT)
@SubscribeEvent
public static void gatherData(GatherDataEvent event) {
var generator = event.getGenerator();
ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
DataGenerator generator = event.getGenerator();
PackOutput output = generator.getPackOutput();
ExistingFileHelper helper = event.getExistingFileHelper();
if (event.includeClient()) {
generator.addProvider(true, new AscendItemModelProvider(generator.getPackOutput(), existingFileHelper));
}
generator.addProvider(event.includeClient(), new ModBlockStateProvider(output, helper));
generator.addProvider(event.includeClient(), new AscendItemModelProvider(output, helper));
generator.addProvider(event.includeServer(), new ModLootTableProvider(output));
generator.addProvider(event.includeServer(), new ModWorldGenProvider(output, event.getLookupProvider()));
}
}
@@ -1,6 +1,7 @@
package net.fretux.ascend.datagen;
import net.fretux.ascend.AscendMod;
import net.fretux.ascend.registry.ModBlocks;
import net.fretux.ascend.registry.ModItems;
import net.minecraft.data.PackOutput;
import net.minecraftforge.client.model.generators.ItemModelProvider;
@@ -18,6 +19,8 @@ public class AscendItemModelProvider extends ItemModelProvider {
protected void registerModels() {
assert ModItems.MOONSEYE_TOME.getId() != null;
basicItem(ModItems.MOONSEYE_TOME.getId().getPath());
withExistingParent(ModBlocks.SHRINE_OF_REMEMBRANCE.getId().getPath(),
modLoc("block/" + ModBlocks.SHRINE_OF_REMEMBRANCE.getId().getPath()));
}
private void basicItem(String name) {
@@ -0,0 +1,24 @@
package net.fretux.ascend.datagen;
import net.fretux.ascend.registry.ModBlocks;
import net.minecraft.data.loot.BlockLootSubProvider;
import net.minecraft.world.flag.FeatureFlags;
import java.util.Set;
public class ModBlockLootProvider extends BlockLootSubProvider {
public ModBlockLootProvider() {
super(Set.of(), FeatureFlags.REGISTRY.allFlags());
}
@Override
protected void generate() {
}
@Override
protected Iterable<net.minecraft.world.level.block.Block> getKnownBlocks() {
return Set.of(ModBlocks.SHRINE_OF_REMEMBRANCE.get());
}
}
@@ -0,0 +1,21 @@
package net.fretux.ascend.datagen;
import net.fretux.ascend.AscendMod;
import net.fretux.ascend.registry.ModBlocks;
import net.minecraft.data.PackOutput;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.common.data.ExistingFileHelper;
public class ModBlockStateProvider extends BlockStateProvider {
public ModBlockStateProvider(PackOutput output, ExistingFileHelper exFileHelper) {
super(output, AscendMod.MODID, exFileHelper);
}
@Override
protected void registerStatesAndModels() {
ModelFile shrineModel = models().getExistingFile(modLoc("block/shrine_of_remembrance"));
simpleBlockWithItem(ModBlocks.SHRINE_OF_REMEMBRANCE.get(), shrineModel);
}
}
@@ -0,0 +1,16 @@
package net.fretux.ascend.datagen;
import net.minecraft.data.PackOutput;
import net.minecraft.data.loot.LootTableProvider;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import java.util.List;
import java.util.Set;
public class ModLootTableProvider extends LootTableProvider {
public ModLootTableProvider(PackOutput output) {
super(output, Set.of(), List.of(
new SubProviderEntry(ModBlockLootProvider::new, LootContextParamSets.BLOCK)
));
}
}
@@ -30,5 +30,10 @@ public class PacketHandler {
.decoder(ClientboundSyncStatsPacket::new)
.consumerMainThread(ClientboundSyncStatsPacket::handle)
.add();
INSTANCE.registerMessage(id++, ServerboundShrineChoicePacket.class,
ServerboundShrineChoicePacket::toBytes,
ServerboundShrineChoicePacket::new,
ServerboundShrineChoicePacket::handle);
}
}
@@ -0,0 +1,48 @@
package net.fretux.ascend.network;
import net.fretux.ascend.player.PlayerStatsProvider;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.PacketDistributor;
import java.util.function.Supplier;
public class ServerboundShrineChoicePacket {
private final String choice;
public ServerboundShrineChoicePacket(String choice) {
this.choice = choice;
}
public ServerboundShrineChoicePacket(FriendlyByteBuf buf) {
this.choice = buf.readUtf();
}
public void toBytes(FriendlyByteBuf buf) {
buf.writeUtf(choice);
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
ServerPlayer player = ctx.get().getSender();
if (player == null) return;
player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(stats -> {
if ("forget".equals(choice)) {
if (stats.getKnowledge() >= 15) {
stats.addKnowledge(-15);
stats.refundAllPoints();
player.sendSystemMessage(Component.literal("FOOLISH... YET HONEST."));
} else {
player.sendSystemMessage(Component.literal("YOUR MIND IS TOO EMPTY TO BE WORTH MY TOUCH."));
player.kill();
}
PlayerStatsProvider.sync(player);
}
});
});
ctx.get().setPacketHandled(true);
}
}
@@ -109,6 +109,16 @@ public class PlayerStats {
knowledge = Math.max(0, knowledge + amount);
}
public void refundAllPoints() {
int totalSpent = 0;
for (String key : attributes.keySet()) {
int lvl = attributes.get(key);
totalSpent += lvl;
attributes.put(key, 0);
}
unspentPoints += totalSpent;
}
public boolean hasUsedMoonseye() {
return hasUsedMoonseye;
}
@@ -0,0 +1,35 @@
package net.fretux.ascend.registry;
import net.fretux.ascend.AscendMod;
import net.fretux.ascend.block.ShrineOfRemembranceBlock;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.MapColor;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
public class ModBlocks {
public static final DeferredRegister<Block> BLOCKS =
DeferredRegister.create(ForgeRegistries.BLOCKS, AscendMod.MODID);
public static final RegistryObject<Block> SHRINE_OF_REMEMBRANCE = BLOCKS.register(
"shrine_of_remembrance",
() -> new ShrineOfRemembranceBlock(
BlockBehaviour.Properties.of()
.mapColor(MapColor.COLOR_PURPLE)
.strength(-1.0F, 3600000.0F) // unbreakable
.noLootTable()
.lightLevel(s -> 6)
.noOcclusion()
.requiresCorrectToolForDrops()
)
);
public static void register(IEventBus bus) {
BLOCKS.register(bus);
}
}
@@ -0,0 +1,55 @@
package net.fretux.ascend.worldgen;
import com.mojang.serialization.Codec;
import net.fretux.ascend.AscendMod;
import net.fretux.ascend.registry.ModBlocks;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
import net.minecraft.data.worldgen.BootstapContext;
import net.minecraft.data.worldgen.placement.PlacementUtils;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.placement.RarityFilter;
import net.minecraftforge.common.data.DatapackBuiltinEntriesProvider;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
* Generates structure and worldgen JSON entries for the Shrine of Remembrance.
*/
public class ModWorldGenProvider extends DatapackBuiltinEntriesProvider {
public static final ResourceKey<ConfiguredFeature<?, ?>> SHRINE_CONFIGURED =
ResourceKey.create(Registries.CONFIGURED_FEATURE, new ResourceLocation(AscendMod.MODID, "shrine_of_remembrance"));
public static final ResourceKey<PlacedFeature> SHRINE_PLACED =
ResourceKey.create(Registries.PLACED_FEATURE, new ResourceLocation(AscendMod.MODID, "shrine_of_remembrance"));
public ModWorldGenProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
super(output, registries, new RegistrySetBuilder()
.add(Registries.CONFIGURED_FEATURE, ModWorldGenProvider::bootstrapConfigured)
.add(Registries.PLACED_FEATURE, ModWorldGenProvider::bootstrapPlaced),
Set.of(AscendMod.MODID));
}
private static void bootstrapConfigured(BootstapContext<ConfiguredFeature<?, ?>> ctx) {
ctx.register(SHRINE_CONFIGURED,
new ConfiguredFeature<>(Feature.NO_OP, NoneFeatureConfiguration.INSTANCE));
}
private static void bootstrapPlaced(BootstapContext<PlacedFeature> ctx) {
var configured = ctx.lookup(Registries.CONFIGURED_FEATURE).getOrThrow(SHRINE_CONFIGURED);
ctx.register(SHRINE_PLACED,
new PlacedFeature(configured, List.of(
RarityFilter.onAverageOnceEvery(400) // roughly like mineshafts
)));
}
}