Skip to content

Commit

Permalink
add pipe bomb functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
MoriyaShiine committed Oct 18, 2024
1 parent 286fef1 commit 3123d3a
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 86 deletions.
1 change: 1 addition & 0 deletions src/main/java/ladysnake/blast/common/Blast.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public void onInitialize() {
BlastComponentTypes.init();
BlastEntities.init();
BlastItems.init();
BlastRecipeSerializers.init();
BlastSoundEvents.initialize();
}

Expand Down
123 changes: 83 additions & 40 deletions src/main/java/ladysnake/blast/common/entity/PipeBombEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,63 @@

import ladysnake.blast.common.init.BlastItems;
import ladysnake.blast.common.init.BlastSoundEvents;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.ChargedProjectilesComponent;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.FlyingItemEntity;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.projectile.FireworkRocketEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;

public class PipeBombEntity extends PersistentProjectileEntity implements FlyingItemEntity {
public final int MAX_FUSE = 20;
import java.util.ArrayList;
import java.util.List;

public class PipeBombEntity extends PersistentProjectileEntity implements FlyingItemEntity {
private static final TrackedData<Integer> FUSE = DataTracker.registerData(PipeBombEntity.class, TrackedDataHandlerRegistry.INTEGER);
public double rotationXmod;
public double rotationYmod;
public double rotationZmod;
public float rotationX;
public float rotationY;
public float rotationZ;
public float ticksUntilExplosion = -1;
public Vec3d prevVelocity;
public final float bounciness = 0.3f;
private static final int MAX_FUSE = 20;
private static final float BOUNCINESS = 0.3f;
private ItemStack stack = getDefaultItemStack();
private final List<ItemStack> fireworks = new ArrayList<>();

public PipeBombEntity(EntityType<PipeBombEntity> variant, World world) {
super(variant, world);
setFuse(MAX_FUSE);
}

@Override
public void writeCustomDataToNbt(NbtCompound nbt) {
super.writeCustomDataToNbt(nbt);
if (!fireworks.isEmpty()) {
stack.set(DataComponentTypes.CHARGED_PROJECTILES, ChargedProjectilesComponent.of(fireworks));
}
nbt.put("Stack", stack.encode(getRegistryManager(), new NbtCompound()));
}

@Override
public void readCustomDataFromNbt(NbtCompound nbt) {
super.readCustomDataFromNbt(nbt);
fireworks.clear();
stack = ItemStack.fromNbt(getRegistryManager(), nbt.getCompound("Stack")).orElse(getDefaultItemStack());
if (stack.contains(DataComponentTypes.CHARGED_PROJECTILES)) {
fireworks.addAll(stack.get(DataComponentTypes.CHARGED_PROJECTILES).getProjectiles());
}
}

@Override
protected void initDataTracker(DataTracker.Builder builder) {
super.initDataTracker(builder);
builder.add(FUSE, 40);

rotationX = getWorld().random.nextFloat() * 360f;
rotationY = getWorld().random.nextFloat() * 360f;
rotationZ = getWorld().random.nextFloat() * 360f;
rotationXmod = getWorld().random.nextFloat() * 10f * (getWorld().random.nextBoolean() ? -1 : 1);
rotationYmod = getWorld().random.nextFloat() * 10f * (getWorld().random.nextBoolean() ? -1 : 1);
rotationZmod = getWorld().random.nextFloat() * 10f * (getWorld().random.nextBoolean() ? -1 : 1);
}

@Override
Expand All @@ -55,29 +68,21 @@ protected ItemStack getDefaultItemStack() {

@Override
public ItemStack getStack() {
return getDefaultItemStack();
return stack;
}

@Override
public void tick() {
if (age >= 18000) {
discard();
}
prevVelocity = getVelocity();
super.tick();
if (ticksUntilExplosion >= 0) {
if (ticksUntilExplosion++ >= 5) {
getWorld().createExplosion(this, getX(), getY(), getZ(), 4.0F, World.ExplosionSourceType.NONE);
discard();
}
}
if (getFuse() % 5 == 0) {
playSound(BlastSoundEvents.PIPE_BOMB_TICK, 1.0f, 1.0f + Math.abs((float) (getFuse() - MAX_FUSE) / MAX_FUSE));
playSound(BlastSoundEvents.PIPE_BOMB_TICK, 1, 1 + Math.abs((float) (getFuse() - MAX_FUSE) / MAX_FUSE));
}
// shorten the fuse
setFuse(getFuse() - 1);
if (getFuse() <= 0) {
explode();
if (getFuse() <= 0 && explode()) {
discard();
}
}
Expand All @@ -94,17 +99,17 @@ public double getDamage() {

@Override
protected void onBlockHit(BlockHitResult blockHitResult) {
if (prevVelocity != null && getVelocity().length() > 0.3f) {
float xMod = bounciness;
float yMod = bounciness;
float zMod = bounciness;
if (getVelocity().length() > 0.3f) {
float xMod = BOUNCINESS;
float yMod = BOUNCINESS;
float zMod = BOUNCINESS;
switch (blockHitResult.getSide()) {
case DOWN, UP -> yMod = -yMod;
case NORTH, SOUTH -> xMod = -xMod;
case WEST, EAST -> zMod = -zMod;
}
setVelocity(prevVelocity.getX() * xMod, prevVelocity.getY() * yMod, prevVelocity.getZ() * zMod);
playSound(SoundEvents.BLOCK_COPPER_HIT, 1.0f, 1.5f);
setVelocity(getVelocity().getX() * xMod, getVelocity().getY() * yMod, getVelocity().getZ() * zMod);
playSound(getHitSound(), 1, 1.5f);
} else {
super.onBlockHit(blockHitResult);
}
Expand All @@ -114,6 +119,13 @@ protected void onBlockHit(BlockHitResult blockHitResult) {
protected void onEntityHit(EntityHitResult entityHitResult) {
}

public void setItem(ItemStack item) {
stack = item;
if (item.contains(DataComponentTypes.CHARGED_PROJECTILES)) {
fireworks.addAll(item.get(DataComponentTypes.CHARGED_PROJECTILES).getProjectiles());
}
}

public int getFuse() {
return dataTracker.get(FUSE);
}
Expand All @@ -122,10 +134,41 @@ public void setFuse(int fuse) {
dataTracker.set(FUSE, fuse);
}

private void explode() {
if (!getWorld().isClient) {
getWorld().createExplosion(getOwner(), getX(), getY(), getZ(), 2f, World.ExplosionSourceType.NONE);
// TODO Explosion
private boolean explode() {
if (getWorld() instanceof ServerWorld world) {
if (random.nextInt(5) == 0 || !isInvisible()) {
ItemStack stack = null;
if (!fireworks.isEmpty()) {
stack = fireworks.getFirst();
}
float rad = 1.2f;
float randX = (float) random.nextGaussian() * rad;
float randY = random.nextFloat() * rad;
float randZ = (float) random.nextGaussian() * rad;
if (!isInvisible()) {
setInvisible(true);
randX = 0;
randY = 0;
randZ = 0;
if (stack == null) {
playSound(SoundEvents.BLOCK_CANDLE_EXTINGUISH, 3, 1);
world.spawnParticles(ParticleTypes.SMOKE, getX(), getY(), getZ(), 50, 0.1, 0.1, 0.1, 0);
}
}
if (stack != null) {
ItemStack firework = stack.copy();
for (int i = 0; i < firework.getCount(); i++) {
FireworkRocketEntity rocket = new FireworkRocketEntity(world, getX() + randX, getY() + randY, getZ() + randZ, stack);
world.spawnEntity(rocket);
rocket.explodeAndRemove();
playSound(BlastSoundEvents.PIPE_BOMB_EXPLODE, 5, (float) (1 + random.nextGaussian() / 10f));
}
fireworks.removeFirst();
return fireworks.isEmpty();
}
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ladysnake.blast.common.init;

import ladysnake.blast.common.Blast;
import ladysnake.blast.common.recipe.PipeBombRecipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.SpecialRecipeSerializer;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;

public class BlastRecipeSerializers {
public static final RecipeSerializer<PipeBombRecipe> PIPE_BOMB = new SpecialRecipeSerializer<>(PipeBombRecipe::new);

public static void init() {
Registry.register(Registries.RECIPE_SERIALIZER, Blast.id("pipe_bomb"), PIPE_BOMB);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

public class BlastSoundEvents {
public static final SoundEvent PIPE_BOMB_TICK = SoundEvent.of(Blast.id("entity.pipe_bomb.tick"));
public static final SoundEvent PIPE_BOMB_EXPLODE = SoundEvent.of(Blast.id("entity.pipe_bomb.explode"));

public static void initialize() {
Registry.register(Registries.SOUND_EVENT, PIPE_BOMB_TICK.getId(), PIPE_BOMB_TICK);
Registry.register(Registries.SOUND_EVENT, PIPE_BOMB_EXPLODE.getId(), PIPE_BOMB_EXPLODE);
}
}
6 changes: 3 additions & 3 deletions src/main/java/ladysnake/blast/common/item/PipeBombItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;

// todo migrate pipe bomb to bomb entity and remove this class
public class PipeBombItem extends Item {
public PipeBombItem(Settings settings) {
super(settings);
Expand All @@ -31,9 +30,10 @@ public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand h
ItemStack stack = player.getStackInHand(hand);
if (!world.isClient) {
PipeBombEntity entity = BlastEntities.PIPE_BOMB.create(world);
entity.setVelocity(player, player.getPitch(), player.getYaw(), 0.0F, 1.5F, 1.0F);
entity.setVelocity(player, player.getPitch(), player.getYaw(), 0, 1.5F, 1);
entity.setPos(player.getX(), player.getY() + player.getStandingEyeHeight() - 0.1, player.getZ());
entity.setOwner(player);
entity.setItem(stack);
world.spawnEntity(entity);
stack.decrementUnlessCreative(1, player);
player.incrementStat(Stats.USED.getOrCreateStat(this));
Expand All @@ -44,6 +44,6 @@ public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand h

private void playSoundEffects(World world, PlayerEntity playerEntity) {
world.playSound(null, playerEntity.getX(), playerEntity.getY(), playerEntity.getZ(), SoundEvents.ENTITY_SNOWBALL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (playerEntity.getRandom().nextFloat() * 0.4F + 0.8F));
world.playSound(null, playerEntity.getX(), playerEntity.getY(), playerEntity.getZ(), SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.NEUTRAL, 0.5F, 0.4F / (playerEntity.getRandom().nextFloat() * 0.4F + 0.8F));
world.playSound(null, playerEntity.getX(), playerEntity.getY(), playerEntity.getZ(), SoundEvents.BLOCK_TRIPWIRE_CLICK_OFF, SoundCategory.NEUTRAL, 1, 1);
}
}
82 changes: 40 additions & 42 deletions src/main/java/ladysnake/blast/common/recipe/PipeBombRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
*/
package ladysnake.blast.common.recipe;

import ladysnake.blast.common.init.BlastItems;
import ladysnake.blast.common.init.BlastRecipeSerializers;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.ChargedProjectilesComponent;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.recipe.Ingredient;
Expand All @@ -13,59 +17,53 @@
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.world.World;

public class PipeBombRecipe extends SpecialCraftingRecipe {
import java.util.ArrayList;
import java.util.List;

private static final Ingredient PAPER = Ingredient.ofItems(Items.PAPER);
private static final Ingredient DURATION_MODIFIER = Ingredient.ofItems(Items.GUNPOWDER);
private static final Ingredient FIREWORK_STAR = Ingredient.ofItems(Items.FIREWORK_STAR);
public class PipeBombRecipe extends SpecialCraftingRecipe {
private static final Ingredient BOMB = Ingredient.ofItems(BlastItems.BOMB);
private static final Ingredient FIREWORK_ROCKET = Ingredient.ofItems(Items.FIREWORK_ROCKET);

public PipeBombRecipe() {
super(CraftingRecipeCategory.EQUIPMENT);
public PipeBombRecipe(CraftingRecipeCategory category) {
super(category);
}

@Override
public boolean matches(CraftingRecipeInput input, World world) {
boolean bl = false;
int i = 0;
for (int j = 0; j < input.getSize(); ++j) {
ItemStack itemStack = input.getStackInSlot(j);
if (itemStack.isEmpty()) continue;
if (PAPER.test(itemStack)) {
if (bl) {
return false;
}
bl = true;
continue;
int bombCount = 0;
int fireworkCount = 0;
for (int i = 0; i < input.getSize(); ++i) {
ItemStack stack = input.getStackInSlot(i);
if (BOMB.test(stack)) {
bombCount++;
} else if (FIREWORK_ROCKET.test(stack)) {
fireworkCount++;
} else if (!stack.isEmpty()) {
return false;
}
if (!(DURATION_MODIFIER.test(itemStack) ? ++i > 3 : !FIREWORK_STAR.test(itemStack))) continue;
return false;
}
return bl && i >= 1;
return bombCount == 1 && fireworkCount > 0;
}

@Override
public ItemStack craft(CraftingRecipeInput input, RegistryWrapper.WrapperLookup lookup) {
ItemStack itemStack = new ItemStack(Items.FIREWORK_ROCKET, 3);
// todo fix crafting
// NbtCompound nbtCompound = itemStack.getOrCreateSubNbt("Fireworks");
// NbtList nbtList = new NbtList();
// int i = 0;
// for (int j = 0; j < input.getSize(); ++j) {
// NbtCompound nbtCompound2;
// ItemStack itemStack2 = input.getStackInSlot(j);
// if (itemStack2.isEmpty()) continue;
// if (DURATION_MODIFIER.test(itemStack2)) {
// ++i;
// continue;
// }
// if (!FIREWORK_STAR.test(itemStack2) || (nbtCompound2 = itemStack2.getSubNbt("Explosion")) == null) continue;
// nbtList.add(nbtCompound2);
// }
// nbtCompound.putByte("Flight", (byte) i);
// if (!nbtList.isEmpty()) {
// nbtCompound.put("Explosions", nbtList);
// }
return itemStack;
ItemStack pipeBomb = BlastItems.PIPE_BOMB.getDefaultStack();
List<ItemStack> fireworks = new ArrayList<>();
int bombCount = 0;
int fireworkCount = 0;
for (int i = 0; i < input.getSize(); ++i) {
ItemStack invStack = input.getStackInSlot(i);
if (BOMB.test(invStack)) {
bombCount++;
} else if (FIREWORK_ROCKET.test(invStack)) {
fireworkCount++;
fireworks.add(input.getStackInSlot(i).copyWithCount(1));
} else if (!invStack.isEmpty()) {
return ItemStack.EMPTY;
}
}
pipeBomb.set(DataComponentTypes.CHARGED_PROJECTILES, ChargedProjectilesComponent.of(fireworks));
return bombCount == 1 && fireworkCount > 0 ? pipeBomb : ItemStack.EMPTY;
}

@Override
Expand All @@ -75,7 +73,7 @@ public boolean fits(int width, int height) {

@Override
public RecipeSerializer<?> getSerializer() {
return RecipeSerializer.FIREWORK_ROCKET;
return BlastRecipeSerializers.PIPE_BOMB;
}
}

3 changes: 2 additions & 1 deletion src/main/resources/assets/blast/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@
"block.blast.dried_folly_red_paint": "Dried Folly Red Paint",
"death.attack.amethyst_shard": "%1$s was shanked by an amethyst shard from %2$s's amethyst bomb",
"death.attack.icicle": "%1$s was iced by %2$s",
"subtitles.blast.entity.pipe_bomb.tick": "Pipe Bomb ticks"
"subtitles.blast.entity.pipe_bomb.tick": "Pipe Bomb ticks",
"subtitles.blast.entity.pipe_bomb.explode": "Pipe Bomb explodes"
}
6 changes: 6 additions & 0 deletions src/main/resources/assets/blast/sounds.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@
"blast:pipe_bomb_tick"
],
"subtitle": "subtitles.blast.entity.pipe_bomb.tick"
},
"entity.pipe_bomb.explode": {
"sounds": [
"blast:pipe_bomb_explode"
],
"subtitle": "subtitles.blast.entity.pipe_bomb.explode"
}
}
Binary file not shown.
Loading

0 comments on commit 3123d3a

Please sign in to comment.