Skip to content

Commit

Permalink
feat(avaritia): add fire ball entity and related functionalities
Browse files Browse the repository at this point in the history
- Implement FireBallEntity class with necessary methods for behavior.
- Add client-side rendering for fire balls with FireBallRender class.- Integrate fire ball usage and interaction logic in PlayerUtils and ToolUtils.- Ensure creative mode players can use fire balls without consuming them.
- Adjust EndestPearlEntity and EndestPearlItem for compatibility and efficiency.
  • Loading branch information
cnlimiter committed Aug 7, 2024
1 parent a3c481c commit 496d750
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package committee.nova.mods.avaritia.client.render.entity;

import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import committee.nova.mods.avaritia.common.entity.FireBallEntity;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.projectile.DragonFireball;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix3f;
import org.joml.Matrix4f;

/**
* @Project: Avaritia
* @Author: cnlimiter
* @CreateTime: 2024/8/7 下午8:49
* @Description:
*/
public class FireBallRender extends EntityRenderer<FireBallEntity> {
private static final ResourceLocation TEXTURE_LOCATION = new ResourceLocation("textures/entity/enderdragon/dragon_fireball.png");
private static final RenderType RENDER_TYPE = RenderType.entityCutoutNoCull(TEXTURE_LOCATION);

public FireBallRender(EntityRendererProvider.Context pContext) {
super(pContext);
}

@Override
protected int getBlockLightLevel(@NotNull FireBallEntity pEntity, @NotNull BlockPos pPos) {
return 15;
}

@Override
public void render(@NotNull FireBallEntity pEntity, float pEntityYaw, float pPartialTicks, PoseStack pPoseStack, MultiBufferSource pBuffer, int pPackedLight) {
pPoseStack.pushPose();
pPoseStack.scale(2.0F, 2.0F, 2.0F);
pPoseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
pPoseStack.mulPose(Axis.YP.rotationDegrees(180.0F));
PoseStack.Pose posestack$pose = pPoseStack.last();
Matrix4f matrix4f = posestack$pose.pose();
Matrix3f matrix3f = posestack$pose.normal();
VertexConsumer vertexconsumer = pBuffer.getBuffer(RENDER_TYPE);
vertex(vertexconsumer, matrix4f, matrix3f, pPackedLight, 0.0F, 0, 0, 1);
vertex(vertexconsumer, matrix4f, matrix3f, pPackedLight, 1.0F, 0, 1, 1);
vertex(vertexconsumer, matrix4f, matrix3f, pPackedLight, 1.0F, 1, 1, 0);
vertex(vertexconsumer, matrix4f, matrix3f, pPackedLight, 0.0F, 1, 0, 0);
pPoseStack.popPose();
super.render(pEntity, pEntityYaw, pPartialTicks, pPoseStack, pBuffer, pPackedLight);
}

private static void vertex(VertexConsumer pConsumer, Matrix4f pPose, Matrix3f pNormal, int pLightmapUV, float pX, int pY, int pU, int pV) {
pConsumer.vertex(pPose, pX - 0.5F, (float)pY - 0.25F, 0.0F).color(255, 255, 255, 255).uv((float)pU, (float)pV).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(pLightmapUV).normal(pNormal, 0.0F, 1.0F, 0.0F).endVertex();
}

/**
* Returns the location of an entity's texture.
*/
@Override
public @NotNull ResourceLocation getTextureLocation(@NotNull FireBallEntity pEntity) {
return TEXTURE_LOCATION;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,11 @@ public EndestPearlEntity(Level level, double x, double y, double z) {
setPos(x, y, z);
}

public EndestPearlEntity(Level level, LivingEntity shooter) {
this(ModEntities.ENDER_PEARL.get(), level);
this.setShooter(shooter);
}

@Override
protected @NotNull Item getDefaultItem() {
return ModItems.endest_pearl.get();
}


@Override
public @NotNull Packet<ClientGamePacketListener> getAddEntityPacket() {
return NetworkHooks.getEntitySpawningPacket(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package committee.nova.mods.avaritia.common.entity;

import committee.nova.mods.avaritia.util.PlayerUtils;
import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.ThrowableProjectile;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraftforge.network.NetworkHooks;
import org.jetbrains.annotations.NotNull;

/**
* @Project: Avaritia
* @Author: cnlimiter
* @CreateTime: 2024/8/7 下午8:26
* @Description:
*/
public class FireBallEntity extends ThrowableProjectile {
public FireBallEntity(EntityType<? extends ThrowableProjectile> pEntityType, Level pLevel) {
super(pEntityType, pLevel);
}


@Override
protected void onHit(@NotNull HitResult result) {
super.onHit(result);
this.discard();
}

@Override
protected void onHitBlock(@NotNull BlockHitResult result) {
super.onHitBlock(result);
if (!this.level().isClientSide) {
Entity owner = this.getOwner();
if (owner instanceof ServerPlayer player) {
BlockPos pos = result.getBlockPos();
BlockState state = this.level().getBlockState(pos);
if (state.is(Blocks.OBSIDIAN)) {
this.level().setBlockAndUpdate(pos, Blocks.LAVA.defaultBlockState());
} else if (state.is(Blocks.SAND)) {
BlockPos.betweenClosedStream(pos.offset(-2, -2, -2), pos.offset(2, 2, 2)).forEach((currentPos) -> {
if (this.level().getBlockState(currentPos).is(Blocks.SAND)) {
PlayerUtils.checkedPlaceBlock(player, pos.immutable(), Blocks.GLASS.defaultBlockState());
}

});
} else {
BlockPos.betweenClosedStream(pos.offset(-1, -1, -1), pos.offset(1, 1, 1)).forEach((currentPos) -> {
if (this.level().isEmptyBlock(currentPos)) {
PlayerUtils.checkedPlaceBlock(player, currentPos.immutable(), Blocks.FIRE.defaultBlockState());
}

});
}
}
}

}

@Override
protected void onHitEntity(@NotNull EntityHitResult result) {
super.onHitEntity(result);
if (!this.level().isClientSide) {
Entity owner = this.getOwner();
if (owner instanceof Player) {
Entity ent = result.getEntity();
ent.setSecondsOnFire(100);
ent.hurt(this.level().damageSources().inFire(), 50.0F);
}
}

}

@Override
public @NotNull Packet<ClientGamePacketListener> getAddEntityPacket() {
return NetworkHooks.getEntitySpawningPacket(this);
}

@Override
public boolean ignoreExplosion() {
return true;
}

@Override
protected void defineSynchedData() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

import committee.nova.mods.avaritia.api.common.item.BaseItem;
import committee.nova.mods.avaritia.common.entity.EndestPearlEntity;
import committee.nova.mods.avaritia.init.registry.ModEntities;
import committee.nova.mods.avaritia.init.registry.ModRarities;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;

Expand All @@ -36,17 +35,19 @@ public EndestPearlItem() {
if (!player.isCreative()) {
stack.shrink(1);
}

world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.ENDER_PEARL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (world.random.nextFloat() * 0.4F + 0.8F));

if (!world.isClientSide) {
EndestPearlEntity pearl = new EndestPearlEntity(world, player);
pearl.setItem(stack);
pearl.setPos(player.getX(), player.getEyeY() + 0.1, player.getZ());
pearl.shootFromRotation(player, player.getXRot(), player.getYRot(), 0.0F, 1.5F, 1.0F);
world.addFreshEntity(pearl);
player.getCooldowns().addCooldown(stack.getItem(), 30);
EndestPearlEntity pearl = ModEntities.ENDER_PEARL.get().create(player.level());
if (pearl != null){
pearl.setItem(stack);
pearl.setShooter(player);
pearl.setPos(player.getX(), player.getEyeY() + 0.1, player.getZ());
pearl.shootFromRotation(player, player.getXRot(), player.getYRot(), 0.0F, 1.5F, 1.0F);
world.addFreshEntity(pearl);
player.getCooldowns().addCooldown(stack.getItem(), 30);
}
}
world.playSound(player, player.getOnPos(), SoundEvents.ENDER_PEARL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (world.random.nextFloat() * 0.4F + 0.8F));

return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
package committee.nova.mods.avaritia.common.item.tools;

import committee.nova.mods.avaritia.common.entity.FireBallEntity;
import committee.nova.mods.avaritia.common.entity.ImmortalItemEntity;
import committee.nova.mods.avaritia.init.config.ModConfig;
import committee.nova.mods.avaritia.init.registry.ModEntities;
import committee.nova.mods.avaritia.init.registry.ModRarities;
import committee.nova.mods.avaritia.init.registry.ModTiers;
import committee.nova.mods.avaritia.util.ToolUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.DragonFireball;
import net.minecraft.world.entity.projectile.SmallFireball;
import net.minecraft.world.item.*;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -46,6 +56,27 @@ public void appendHoverText(@NotNull ItemStack stack, @Nullable Level worldIn, L
tooltip.add(Component.translatable(ChatFormatting.DARK_GRAY + "" + ChatFormatting.ITALIC + I18n.get("tooltip.skullfire_sword.desc")));
}

@Override
public @NotNull InteractionResultHolder<ItemStack> use(Level level, Player player, @NotNull InteractionHand hand) {
var heldItem = player.getItemInHand(hand);
if (!level.isClientSide) {
List<Entity> entities = level.getEntities(player, player.getBoundingBox().deflate(10));
double d2 = 0;
if (!entities.isEmpty()) d2 = entities.get(0).getY(0.5D) - player.getY(0.5D);

FireBallEntity fireBallEntity = ModEntities.FIRE_BALL.get().create(level);
if (fireBallEntity != null){
fireBallEntity.setOwner(player);
fireBallEntity.setPos(player.getX(), player.getEyeY() + 0.1, player.getZ());
fireBallEntity.shootFromRotation(player, player.getXRot(), player.getYRot(), 0.0F, 1.5F, 1.0F);
level.addFreshEntity(fireBallEntity);
}

}
level.playSound(player, player.getOnPos(), SoundEvents.SNOWBALL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (level.random.nextFloat() * 0.4F + 0.8F));
return InteractionResultHolder.success(heldItem);
}

@Override
public boolean hasCustomEntity(ItemStack stack) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
import committee.nova.mods.avaritia.common.entity.arrow.HeavenArrowEntity;
import committee.nova.mods.avaritia.common.entity.arrow.HeavenSubArrowEntity;
import committee.nova.mods.avaritia.common.entity.arrow.TraceArrowEntity;
import net.minecraft.client.renderer.entity.EntityRenderers;
import net.minecraft.client.renderer.entity.IronGolemRenderer;
import net.minecraft.client.renderer.entity.ItemEntityRenderer;
import net.minecraft.client.renderer.entity.ThrownItemRenderer;
import net.minecraft.client.renderer.entity.*;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.entity.projectile.Fireball;
import net.minecraft.world.entity.projectile.SmallFireball;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.DeferredRegister;
Expand Down Expand Up @@ -68,13 +67,19 @@ public class ModEntities {
.build(new ResourceLocation(Static.MOD_ID, "trace_arrow").toString()));

public static final RegistryObject<EntityType<InfinityGolem>> INFINITY_GOLEM = ENTITIES.register("infinity_golem",
() -> EntityType.Builder.<InfinityGolem>of(InfinityGolem::new, MobCategory.MISC)
() -> EntityType.Builder.of(InfinityGolem::new, MobCategory.MISC)
.sized(1.4F, 2.7F)
.clientTrackingRange(10)
.fireImmune()
.setShouldReceiveVelocityUpdates(true)
.build(new ResourceLocation(Static.MOD_ID, "infinity_golem").toString()));

public static final RegistryObject<EntityType<FireBallEntity>> FIRE_BALL = ENTITIES.register("fire_ball",
() -> EntityType.Builder.of(FireBallEntity::new, MobCategory.MISC)
.setTrackingRange(256)
.setUpdateInterval(10)
.build(new ResourceLocation(Static.MOD_ID, "fire_ball").toString()));

@OnlyIn(Dist.CLIENT)
public static void onClientSetup() {
EntityRenderers.register(ModEntities.IMMORTAL.get(), ItemEntityRenderer::new);
Expand All @@ -84,6 +89,7 @@ public static void onClientSetup() {
EntityRenderers.register(ModEntities.HEAVEN_SUB_ARROW.get(), HeavenSubArrowRender::new);
EntityRenderers.register(ModEntities.TRACE_ARROW.get(), TracerArrowRender::new);
EntityRenderers.register(ModEntities.INFINITY_GOLEM.get(), InfinityGolemRenderer::new);
EntityRenderers.register(ModEntities.FIRE_BALL.get(), FireBallRender::new);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ private static boolean itemInvHasItem(ItemStack itemInv, ItemStack stack) {
return hasItem.get();
}

public static ItemStack findFirstItem(Player player, Item consumeFrom) {
return player.getInventory().items.stream().filter((s) -> !s.isEmpty() && s.getItem() == consumeFrom).findFirst().orElse(ItemStack.EMPTY);
}

public static int getFirstSlotWithStack(ItemStack itemInv, ItemStack stack) {
AtomicInteger slot = new AtomicInteger(-1);
itemInv.getCapability(ForgeCapabilities.ITEM_HANDLER).ifPresent(h -> {
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/committee/nova/mods/avaritia/util/PlayerUtils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
package committee.nova.mods.avaritia.util;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.server.ServerLifecycleHooks;

import java.util.Arrays;

/**
* PlayerUtil
Expand All @@ -14,4 +28,29 @@ public class PlayerUtils {
public static boolean isPlayingMode(Player player) {
return !player.isCreative() && !player.isSpectator();
}

public static boolean hasEditPermission(ServerPlayer player, BlockPos pos) {
return !ServerLifecycleHooks.getCurrentServer().isUnderSpawnProtection((ServerLevel) player.level(), pos, player)
&& Arrays.stream(Direction.values()).allMatch((e) -> player.mayUseItemAt(pos, e, ItemStack.EMPTY));
}

public static boolean checkedPlaceBlock(ServerPlayer player, BlockPos pos, BlockState state) {
if (!hasEditPermission(player, pos)) {
return false;
} else {
Level level = player.level();
BlockSnapshot before = BlockSnapshot.create(level.dimension(), level, pos);
level.setBlockAndUpdate(pos, state);
BlockEvent.EntityPlaceEvent evt = new BlockEvent.EntityPlaceEvent(before, Blocks.AIR.defaultBlockState(), player);
MinecraftForge.EVENT_BUS.post(evt);
if (evt.isCanceled()) {
level.restoringBlockSnapshots = true;
before.restore(true, false);
level.restoringBlockSnapshots = false;
return false;
} else {
return true;
}
}
}
}
Loading

0 comments on commit 496d750

Please sign in to comment.