Skip to content

Commit

Permalink
Refactor Mario blocks to enable loots
Browse files Browse the repository at this point in the history
  • Loading branch information
Hugman76 committed Jan 23, 2023
1 parent a0280f6 commit 4f6940b
Show file tree
Hide file tree
Showing 26 changed files with 734 additions and 540 deletions.
6 changes: 4 additions & 2 deletions src/main/java/fr/hugman/mubble/Mubble.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package fr.hugman.mubble;

import fr.hugman.dawn.Registrar;
import fr.hugman.mubble.registry.SuperMarioContent;
import fr.hugman.mubble.registry.MubbleSounds;
import fr.hugman.mubble.registry.SuperMario;
import fr.hugman.mubble.world.MubbleGamerules;
import net.fabricmc.api.ModInitializer;
import net.minecraft.util.Identifier;
Expand All @@ -15,8 +16,9 @@ public class Mubble implements ModInitializer {
@Override
public void onInitialize() {
MubbleGamerules.init();
MubbleSounds.init();

SuperMarioContent.init(REGISTRAR);
SuperMario.init(REGISTRAR);
}

public static Identifier id(String path) {
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/fr/hugman/mubble/MubbleClient.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package fr.hugman.mubble;

import fr.hugman.mubble.client.render.BumpedBlockEntityRenderer;
import fr.hugman.mubble.registry.SuperMarioContent;
import fr.hugman.mubble.client.render.BumpableBlockEntityRenderer;
import fr.hugman.mubble.registry.SuperMario;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
Expand All @@ -13,9 +13,9 @@
public class MubbleClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
BlockRenderLayerMap.INSTANCE.putBlock(SuperMarioContent.RED_BEEP_BLOCK, RenderLayer.getCutout());
BlockRenderLayerMap.INSTANCE.putBlock(SuperMarioContent.BLUE_BEEP_BLOCK, RenderLayer.getCutout());
BlockRenderLayerMap.INSTANCE.putBlock(SuperMario.RED_BEEP_BLOCK, RenderLayer.getCutout());
BlockRenderLayerMap.INSTANCE.putBlock(SuperMario.BLUE_BEEP_BLOCK, RenderLayer.getCutout());

BlockEntityRendererRegistry.register(SuperMarioContent.BUMPED_BLOCK_ENTITY_TYPE, BumpedBlockEntityRenderer::new);
BlockEntityRendererRegistry.register(SuperMario.BUMPABLE_BLOCK_ENTITY_TYPE, BumpableBlockEntityRenderer::new);
}
}
37 changes: 0 additions & 37 deletions src/main/java/fr/hugman/mubble/block/BrickBlock.java

This file was deleted.

179 changes: 168 additions & 11 deletions src/main/java/fr/hugman/mubble/block/BumpableBlock.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,188 @@
package fr.hugman.mubble.block;

import fr.hugman.mubble.block.entity.BumpedBlockEntity;
import fr.hugman.mubble.block.bump.BumpConfig;
import fr.hugman.mubble.block.entity.BumpableBlockEntity;
import fr.hugman.mubble.registry.MubbleSounds;
import fr.hugman.mubble.registry.SuperMario;
import net.minecraft.block.Block;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.block.BlockWithEntity;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.util.ItemScatterer;
import net.minecraft.util.hit.BlockHitResult;
import org.jetbrains.annotations.Nullable;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.event.GameEvent;

import java.util.Optional;

/**
* @author haykam
* @author Hugman
* @since v4.0.0
*/
public interface BumpableBlock {
public abstract class BumpableBlock extends BlockWithEntity implements HittableBlock {
public static final BooleanProperty BUMPING = BooleanProperty.of("bumping");

private final BumpConfig defaultBumpConfig;

public BumpableBlock(BumpConfig defaultBumpConfig, Settings settings) {
super(settings);
this.defaultBumpConfig = defaultBumpConfig;
this.setDefaultState(this.stateManager.getDefaultState().with(BUMPING, false));
}

/*==========*/
/* STATES */
/*==========*/

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

/*===========*/
/* GETTERS */
/*===========*/

public BumpConfig getDefaultBumpConfigInstance() {
return defaultBumpConfig.copy();
}

/*================*/
/* BLOCK ENTITY */
/*================*/

@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BumpableBlockEntity(pos, state, this.getDefaultBumpConfigInstance());
}

@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
return checkType(type, SuperMario.BUMPABLE_BLOCK_ENTITY_TYPE, (w, p, s, e) -> e.tick(w, p, s));
}

/*=============*/
/* RENDERING */
/*=============*/

@Override
public BlockRenderType getRenderType(BlockState state) {
return state.get(BUMPING) ? BlockRenderType.ENTITYBLOCK_ANIMATED : BlockRenderType.MODEL;
}

/*============*/
/* BEHAVIOR */
/*============*/

/**
* Called when a block is at the peak of being bumped.
* This method is called before the block entity receives the new data.
*
* @return true if the block should be bumped, false otherwise
*/
void onBump(BumpedBlockEntity entity, BlockHitResult hit);
public boolean canBump(World world, BlockPos pos, BlockState state, BumpableBlockEntity blockEntity, Entity entity, BlockHitResult hit) {
return !state.get(BUMPING);
}

/**
* Called when a block is at the peak of being bumped. (middle of the animation)
* Called when the block is getting bumped.
*/
void onBumpPeak(BumpedBlockEntity entity);
public void onBump(World world, BlockPos pos, BlockState state, BumpableBlockEntity blockEntity) {
var bumpAuthor = blockEntity.getBumpAuthor();
//TODO: change the game event to something more appropriate
world.emitGameEvent(bumpAuthor, GameEvent.BLOCK_OPEN, pos);
if(bumpAuthor instanceof PlayerEntity player) {
//TODO: create a new "Bumped Blocks" stat
//player.incrementStat(MubbleStats.BUMPED_BLOCKS);
}
}

/**
* Called when a block is at the middle of being bumped.
*/
public void onBumpMiddle(World world, BlockPos pos, BlockState state, BumpableBlockEntity blockEntity) {
if(world != null && !world.isClient()) {
if(blockEntity.getBumpConfig().shouldDestroy()) {
Vec3d center = blockEntity.getPos().toCenterPos();

this.loot(world, pos, state, blockEntity);
world.breakBlock(blockEntity.getPos(), false);
world.playSound(null, center.getX(), center.getY(), center.getZ(), MubbleSounds.BUMPABLE_BLOCK_DESTROY, SoundCategory.BLOCKS, 1.0F, 1.0F);
}
}
}

/**
* Called when a block finishes being bumped.
*
* @return the new state of the block
*/
@Nullable
BlockState onBumpCompleted(BumpedBlockEntity entity);
public void onBumpEnd(World world, BlockPos pos, BlockState state, BumpableBlockEntity blockEntity) {
if(world != null && !world.isClient()) {
this.loot(world, pos, state, blockEntity);
if(blockEntity.getBumpConfig().shouldDestroy()) {
// this should never happen since it already happened in onBumpMiddle
world.breakBlock(blockEntity.getPos(), false);
return;
}
var newState = blockEntity.getBumpConfig().state();
if(newState != null) {
world.setBlockState(pos, newState);
}
else {
world.setBlockState(pos, state.with(BUMPING, false));
}
}
}


@Override
public void onHit(World world, BlockPos pos, BlockState state, Entity entity, BlockHitResult hit) {
if(world.isClient()) {
return;
}

Optional<BumpableBlockEntity> opt = world.getBlockEntity(pos, SuperMario.BUMPABLE_BLOCK_ENTITY_TYPE);
opt.ifPresent(blockEntity -> {
if(this.canBump(world, pos, state, blockEntity, entity, hit)) {
blockEntity.bump(world, pos, state, entity, hit.getSide().getOpposite());
}
});
}

public void loot(World world, BlockPos pos, BlockState state, BumpableBlockEntity blockEntity) {
var config = blockEntity.getBumpConfig();
var stack = config.stack();
if(stack == null) {
return;
}

var actualState = world.getBlockState(pos);

// TODO
// if there is no block at the position, drop in the center of the block like a regular drop
// else, drop at the center of the block but apply the direction offset

var center = pos.toCenterPos();
if(actualState.isAir()) {
ItemScatterer.spawn(world, center.getX(), center.getY(), center.getZ(), stack);
}
else {
var direction = blockEntity.getBumpDirection();
var x = center.getX() + direction.getOffsetX() * 0.75D;
var y = center.getY() + direction.getOffsetY() * 0.75D;
var z = center.getZ() + direction.getOffsetZ() * 0.75D;
ItemScatterer.spawn(world, x, y, z, stack);
}
blockEntity.setBumpConfig(config.withStack(null));
world.playSound(null, center.getX(), center.getY(), center.getZ(), MubbleSounds.BUMPABLE_BLOCK_LOOT, SoundCategory.BLOCKS, 1.0F, 1.0F);
}
}
54 changes: 0 additions & 54 deletions src/main/java/fr/hugman/mubble/block/BumpedBlock.java

This file was deleted.

14 changes: 7 additions & 7 deletions src/main/java/fr/hugman/mubble/block/EmptyBlock.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
package fr.hugman.mubble.block;

import fr.hugman.mubble.registry.SuperMarioContent;
import fr.hugman.mubble.registry.MubbleSounds;
import fr.hugman.mubble.registry.SuperMario;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;

/**
* @author Hugman
* @since v4.0.0
*/
public class EmptyBlock extends Block implements UnderHittableBlock {
public class EmptyBlock extends Block implements HittableBlock {
public EmptyBlock(Settings settings) {
super(settings);
}

@Override
public void onHitFromUnder(BlockState state, World world, BlockPos blockPos, BlockHitResult hit, Entity entity) {
if(!world.isClient()) {
Vec3d pos = hit.getPos();
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), SuperMarioContent.BUMPABLE_BLOCK_BUMP, SoundCategory.BLOCKS, 1F, 1F);
public void onHit(World world, BlockPos blockPos, BlockState state, Entity entity, BlockHitResult hit) {
var pos = hit.getPos();
if(world != null) {
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), MubbleSounds.BUMPABLE_BLOCK_BUMP, SoundCategory.BLOCKS, 1.0F, 1.0F);
}
}
}
24 changes: 24 additions & 0 deletions src/main/java/fr/hugman/mubble/block/HittableBlock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package fr.hugman.mubble.block;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

//TODO: move this to the Dawn API
/**
* Represents blocks that can be hit physically. The hit method will be triggered by:
* <ul>
* <li>An entity hitting the block by under, with the superior part of the hitbox (generally the head)</li>
* </ul>
* Projectile hits are not handled by this interface.
*
* @author Hugman
* @since v4.0.0
* @see Block#onProjectileHit
*/
public interface HittableBlock {
void onHit(World world, BlockPos pos, BlockState state, Entity entity, BlockHitResult hit);
}
Loading

0 comments on commit 4f6940b

Please sign in to comment.