Skip to content

Commit

Permalink
work in progress arcane lift and dispel magic spells, make table brea…
Browse files Browse the repository at this point in the history
…kable with axe, new glyph particles
  • Loading branch information
reoseah committed Nov 1, 2024
1 parent 73b37f8 commit 35b3eb3
Show file tree
Hide file tree
Showing 41 changed files with 533 additions and 72 deletions.
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# Magisterium

Construct your own spell book from the magical pages scattered around the world!
Construct your own spell book from the magical pages scattered around the world!

It's made as an entry to Modfest 1.21, although not particularly on theme, which is "Time, Technology & Throwbacks". It may not look like one to you, bit it *is* a throwback - a throwback to Modfest 1.17, when I had the idea for book centered magic mod, yet had abandoned it.
It's made as an entry to Modfest 1.21, although not particularly on theme, which is "Time, Technology & Throwbacks". It
may not look like one to you, bit it *is* a throwback - a throwback to Modfest 1.17, when I had the idea for book
centered magic mod, yet had abandoned it.

I didn't see how it all could fit together to make a fun experience, but now (= three years later), halfway through the current modfest, the idea finished cooking in my head, and I felt like working on it. So, let's cast some spells!
I didn't see how it all could fit together to make a fun experience, but now (= three years later), halfway through the
current modfest, the idea finished cooking in my head, and I felt like working on it. So, let's cast some spells!

## Overview

The mod is centered on using spell books, which double as a container for the spell pages and as an actual book you can read and use to cast the spells within.
The mod is centered on using spell books, which double as a container for the spell pages and as an actual book you can
read and use to cast the spells within.

**Spell Pages** are found in the world, currently as loot in dungeons, desert and jungle temples and strongholds.

Expand All @@ -18,15 +22,32 @@ They can be inserted into a spell book in the **Arcane Table**, alongside some u

![Arcane table GUI](https://github.com/reoseah/magisterium/blob/modfest-1.21/docs/img/arcane%20table%20gui%202.png?raw=true)

Then, use the spell book to read about the spell, fill ingredients and comply with the requirements, if any, and hold the chant button!
Then, use the spell book to read about the spell, fill ingredients and comply with the requirements, if any, and hold
the chant button!

![Reading a spell](https://github.com/reoseah/magisterium/blob/modfest-1.21/docs/img/illusory%20wall%20reading.png?raw=true)

A number of spells require **Arcane Glyphs** to be placed on the ground, which will define the area of effect of the spell.
A number of spells require **Arcane Glyphs** to be placed on the ground, which will define the area of effect of the
spell.
Simply right click with Lapis Lazuli on the ground to place them.

![Placing glyphs](https://github.com/reoseah/magisterium/blob/modfest-1.21/docs/img/glyphs%20placement.png?raw=true)

Woila! You've cast a spell!

![The spell aftermath](https://github.com/reoseah/magisterium/blob/modfest-1.21/docs/img/illusory%20wall%20result.png?raw=true)

## Playgrounds gamerule and commands

As I want to allow players to try out the mod on ModFest showcase server, without completely breaking the adventure mode,
I've added a mini world protection system to the mod, which may be removed in the future.

### `/gamerule enableMagisteriumPlaygrounds [true|false]`

This gamerule, when set to true, disables the spell effects in the world, so survival/adventure players can't grief the
world with them.

### `/magisterium playgrounds [list|get|set|remove] <...>`

Use these commands to manage the playgrounds, which are areas where the spell effects are enabled when the gamerule is
set to true.
16 changes: 11 additions & 5 deletions src/main/java/io/github/reoseah/magisterium/Magisterium.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,12 @@ public class Magisterium implements ModInitializer {

public static final Logger LOGGER = LoggerFactory.getLogger(Magisterium.class);


@Override
public void onInitialize() {
Registry.register(Registries.BLOCK, "magisterium:arcane_table", ArcaneTableBlock.INSTANCE);
Registry.register(Registries.BLOCK, "magisterium:glyph", GlyphBlock.INSTANCE);
Registry.register(Registries.BLOCK, "magisterium:illusory_wall", IllusoryWallBlock.INSTANCE);

Registry.register(Registries.BLOCK, "magisterium:test", TestBlock.INSTANCE);
Registry.register(Registries.BLOCK, "magisterium:arcane_lift", ArcaneLiftBlock.INSTANCE);

Registry.register(Registries.BLOCK_ENTITY_TYPE, "magisterium:illusory_wall", IllusoryWallBlockEntity.TYPE);

Expand All @@ -71,6 +69,8 @@ public void onInitialize() {
Registry.register(Registries.ITEM, "magisterium:illusory_wall_page", SpellPageItem.ILLUSORY_WALL);
Registry.register(Registries.ITEM, "magisterium:unstable_charge_page", SpellPageItem.UNSTABLE_CHARGE);
Registry.register(Registries.ITEM, "magisterium:cold_snap_page", SpellPageItem.COLD_SNAP);
Registry.register(Registries.ITEM, "magisterium:arcane_lift_page", SpellPageItem.ARCANE_LIFT);
Registry.register(Registries.ITEM, "magisterium:dispel_magic_page", SpellPageItem.DISPEL_MAGIC);
Registry.register(Registries.ITEM, "magisterium:bookmark", BookmarkItem.INSTANCE);

Registry.register(Registries.DATA_COMPONENT_TYPE, "magisterium:current_page", SpellBookItem.CURRENT_PAGE);
Expand All @@ -91,6 +91,8 @@ public void onInitialize() {
entries.add(SpellPageItem.ILLUSORY_WALL);
// entries.add(SpellPageItem.UNSTABLE_CHARGE);
entries.add(SpellPageItem.COLD_SNAP);
entries.add(SpellPageItem.ARCANE_LIFT);
entries.add(SpellPageItem.DISPEL_MAGIC);
entries.add(BookmarkItem.INSTANCE);
}) //
.build();
Expand All @@ -109,6 +111,8 @@ public void onInitialize() {
Registry.register(Registries.RECIPE_SERIALIZER, "magisterium:illusory_wall", IllusoryWallRecipe.SERIALIZER);
Registry.register(Registries.RECIPE_SERIALIZER, "magisterium:unstable_charge", UnstableChargeRecipe.SERIALIZER);
Registry.register(Registries.RECIPE_SERIALIZER, "magisterium:cold_snap", ColdSnapRecipe.SERIALIZER);
Registry.register(Registries.RECIPE_SERIALIZER, "magisterium:arcane_lift", ArcaneLiftRecipe.SERIALIZER);
Registry.register(Registries.RECIPE_SERIALIZER, "magisterium:dispel_magic", DispelMagicRecipe.SERIALIZER);

Registry.register(Registries.SCREEN_HANDLER, "magisterium:spell_book", SpellBookScreenHandler.TYPE);
Registry.register(Registries.SCREEN_HANDLER, "magisterium:arcane_table", ArcaneTableScreenHandler.TYPE);
Expand All @@ -117,6 +121,8 @@ public void onInitialize() {
Registry.register(Registries.PARTICLE_TYPE, "magisterium:glyph_a", MagisteriumParticles.GLYPH_A);
Registry.register(Registries.PARTICLE_TYPE, "magisterium:glyph_b", MagisteriumParticles.GLYPH_B);
Registry.register(Registries.PARTICLE_TYPE, "magisterium:glyph_c", MagisteriumParticles.GLYPH_C);
Registry.register(Registries.PARTICLE_TYPE, "magisterium:glyph_d", MagisteriumParticles.GLYPH_D);
Registry.register(Registries.PARTICLE_TYPE, "magisterium:glyph_e", MagisteriumParticles.GLYPH_E);

MagisteriumGameRules.initialize();
MagisteriumCommands.initialize();
Expand Down Expand Up @@ -145,8 +151,8 @@ public void onInitialize() {
}
});
ServerPlayNetworking.registerGlobalReceiver(UseBookmarkPayload.ID, (payload, context) -> {
if (context.player().currentScreenHandler instanceof SpellBookScreenHandler hemonomiconScreen) {
hemonomiconScreen.currentPage.set(payload.page());
if (context.player().currentScreenHandler instanceof SpellBookScreenHandler handler) {
handler.currentPage.set(payload.page());
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ public void onInitializeClient() {
ParticleFactoryRegistry.getInstance().register(MagisteriumParticles.GLYPH_A, GlyphParticle.Factory::new);
ParticleFactoryRegistry.getInstance().register(MagisteriumParticles.GLYPH_B, GlyphParticle.Factory::new);
ParticleFactoryRegistry.getInstance().register(MagisteriumParticles.GLYPH_C, GlyphParticle.Factory::new);
ParticleFactoryRegistry.getInstance().register(MagisteriumParticles.GLYPH_D, GlyphParticle.Factory::new);
ParticleFactoryRegistry.getInstance().register(MagisteriumParticles.GLYPH_E, GlyphParticle.Factory::new);
}
}
176 changes: 176 additions & 0 deletions src/main/java/io/github/reoseah/magisterium/block/ArcaneLiftBlock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package io.github.reoseah.magisterium.block;

import io.github.reoseah.magisterium.particle.MagisteriumParticles;
import net.minecraft.block.*;
import net.minecraft.block.piston.PistonBehavior;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.IntProperty;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;

public class ArcaneLiftBlock extends Block implements Dispelable {
// public static final BooleanProperty BASE = BooleanProperty.of("base");
public static final IntProperty HEIGHT = IntProperty.of("height", 0, 15);

public static final Block INSTANCE = new ArcaneLiftBlock(Settings.create() //
.resistance(6000000.0F) //
.replaceable() //
.breakInstantly() //
.noBlockBreakParticles() //
.nonOpaque() //
.noCollision() //
.pistonBehavior(PistonBehavior.DESTROY)//
.luminance(state -> state.get(HEIGHT) == 0 ? 13 : 8) //
);

public ArcaneLiftBlock(Settings settings) {
super(settings);
this.setDefaultState(this.getStateManager().getDefaultState().with(HEIGHT, 0));
}

@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder);
builder.add(HEIGHT);
}

@Override
protected BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.INVISIBLE;
}

@Override
protected VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return VoxelShapes.empty();
}

@Override
public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) {

if (random.nextInt(4) == 0) {
double x = pos.getX() - .5 + random.nextFloat() * 2;
double y = pos.getY() - .5 + random.nextFloat() * 2;
double z = pos.getZ() - .5 + random.nextFloat() * 2;
var particle = MagisteriumParticles.GLYPHS[random.nextInt(MagisteriumParticles.GLYPHS.length)];
world.addParticle(particle, x, y, z, 0, 0, 0);
}
boolean isBase = state.get(HEIGHT) == 0;
for (int i = 0; i < (isBase ? 3 : 6); i++) {
double x = pos.getX() - .5 + random.nextFloat() + random.nextFloat();
double y = pos.getY() - .5 + random.nextFloat() + random.nextFloat();
double z = pos.getZ() - .5 + random.nextFloat() + random.nextFloat();
world.addParticle(MagisteriumParticles.ENERGY, x, y, z, //
0.01 * random.nextFloat(), 0.05, 0.01 * random.nextFloat());
}
if (isBase) {
for (int i = 0; i < 6; i++) {
double y = pos.getY() + random.nextFloat() * .5;
double x, z;
if (random.nextBoolean()) {
x = pos.getX() + (random.nextBoolean() ? -.5 : 1.5);
z = pos.getZ() - .5 + 2 * random.nextFloat();
} else {
z = pos.getZ() + (random.nextBoolean() ? -.5 : 1.5);
x = pos.getX() - .5 + 2 * random.nextFloat();
}
double centerX = pos.getX() + .5;
double centerZ = pos.getZ() + .5;
double velocityX = (centerX - x) * 0.03;
double velocityZ = (centerZ - z) * 0.03;
world.addParticle(MagisteriumParticles.ENERGY, //
x - .25 + random.nextFloat() * .5, y, z - .25 + random.nextFloat() * .5, //
velocityX, 0.05, velocityZ);
}
}
}

@Override
protected void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) {
var velocity = entity.getVelocity();
if (entity.isDescending()) {
double velocityY = Math.max(velocity.y, -0.1);
entity.setVelocity(velocity.x, velocityY, velocity.z);
} else {
double velocityY = MathHelper.clamp(velocity.y + 0.03, 0.25, 0.75);
entity.setVelocity(velocity.x, velocityY, velocity.z);
}
entity.limitFallDistance();
}

@Override
protected BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
world.scheduleBlockTick(pos, this, 5);
return state;
}

@Override
protected void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
super.onBlockAdded(state, world, pos, oldState, notify);
world.scheduleBlockTick(pos, this, 5);
}

@Override
protected void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
if (state.get(HEIGHT) == 0) {
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx == 0 && dz == 0) {
continue;
}
var neighborPos = pos.add(dx, 0, dz);
var neighborState = world.getBlockState(neighborPos);
if (neighborState.getBlock() != GlyphBlock.INSTANCE) {
world.removeBlock(pos, false);
return;
}
}
}
} else {
var below = pos.down();
var stateBelow = world.getBlockState(below);
if (stateBelow.getBlock() != this) {
world.removeBlock(pos, false);
return;
}
}

if (state.get(HEIGHT) < 15) {
var above = pos.up();
if (world.isAir(above)) {
world.setBlockState(above, state.with(HEIGHT, state.get(HEIGHT) + 1));
}
}
}

@Override
public void dispel(World world, BlockPos pos, PlayerEntity player) {
var state = world.getBlockState(pos);
var height = state.get(HEIGHT);
for (var ipos : BlockPos.iterate(pos, pos.add(0, -height, 0))) {
if (world.getBlockState(ipos).getBlock() != this) {
break;
}
world.setBlockState(ipos, Blocks.AIR.getDefaultState(), 3);
world.syncWorldEvent(null, 2001, ipos, Block.getRawIdFromState(state));
}
for (var ipos : BlockPos.iterate(pos, pos.add(0, 15 - height, 0))) {
if (world.getBlockState(ipos).getBlock() != GlyphBlock.INSTANCE) {
break;
}
world.setBlockState(ipos, Blocks.AIR.getDefaultState(), 3);
world.syncWorldEvent(null, 2001, ipos, Block.getRawIdFromState(state));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.reoseah.magisterium.block;

import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

public interface Dispelable {
void dispel(World world, BlockPos pos, PlayerEntity player);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import java.util.ArrayDeque;
import java.util.HashSet;

public class IllusoryWallBlock extends BlockWithEntity {
public class IllusoryWallBlock extends BlockWithEntity implements Dispelable {
public static final MapCodec<IllusoryWallBlock> CODEC = createCodec(IllusoryWallBlock::new);
public static final Settings SETTINGS = Settings.create().nonOpaque().noCollision().strength(0.5F);
public static final Block INSTANCE = new IllusoryWallBlock(SETTINGS);
Expand Down Expand Up @@ -81,4 +81,9 @@ public void onBroken(WorldAccess world, BlockPos pos, BlockState state) {
}
}
}

@Override
public void dispel(World world, BlockPos pos, PlayerEntity player) {
MagisteriumPlaygrounds.trySetBlockState(world, pos, Blocks.AIR.getDefaultState(), player);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@

public class MagisteriumBlockTags {
public static final TagKey<Block> AWAKEN_THE_FIRE_TARGETS = TagKey.of(RegistryKeys.BLOCK, Identifier.of("magisterium:awaken_the_fire_targets"));
public static final TagKey<Block> DISPEL_MAGIC_SUSCEPTIBLE = TagKey.of(RegistryKeys.BLOCK, Identifier.of("magisterium:dispel_magic_susceptible"));
}
49 changes: 0 additions & 49 deletions src/main/java/io/github/reoseah/magisterium/block/TestBlock.java

This file was deleted.

Loading

0 comments on commit 35b3eb3

Please sign in to comment.