From 68890a3b408f42f551fb1bff1dae093832e89c3a Mon Sep 17 00:00:00 2001 From: Atsuishio <842960157@qq.com> Date: Sat, 28 Jun 2025 02:30:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=8A=84tacz=E7=9A=84?= =?UTF-8?q?=E5=BC=B9=E5=AD=94=EF=BC=8C=E4=BD=86=E6=98=AF=E4=B8=8D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=EF=BC=88=E6=81=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/particle/BulletDecalOption.java | 75 ++++++++ .../client/particle/BulletDecalParticle.java | 170 ++++++++++++++++++ .../client/particle/BulletHoleParticle.java | 49 ----- .../entity/projectile/ProjectileEntity.java | 24 ++- .../superbwarfare/init/ModParticleTypes.java | 28 ++- .../superbwarfare/init/ModParticles.java | 4 +- .../superbwarfare/particles/bullet_decal.json | 1 + .../superbwarfare/particles/bullet_hole.json | 5 - .../textures/particle/bullet_hole.png | Bin 99 -> 0 bytes 9 files changed, 292 insertions(+), 64 deletions(-) create mode 100644 src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalOption.java create mode 100644 src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalParticle.java delete mode 100644 src/main/java/com/atsuishio/superbwarfare/client/particle/BulletHoleParticle.java create mode 100644 src/main/resources/assets/superbwarfare/particles/bullet_decal.json delete mode 100644 src/main/resources/assets/superbwarfare/particles/bullet_hole.json delete mode 100644 src/main/resources/assets/superbwarfare/textures/particle/bullet_hole.png diff --git a/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalOption.java b/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalOption.java new file mode 100644 index 000000000..aa10150ca --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalOption.java @@ -0,0 +1,75 @@ +package com.atsuishio.superbwarfare.client.particle; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.tacz.guns.init.ModParticles; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.registries.ForgeRegistries; + +public class BulletDecalOption implements ParticleOptions { + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group(Codec.INT.fieldOf("dir").forGetter(option -> option.direction.ordinal()), + Codec.LONG.fieldOf("pos").forGetter(option -> option.pos.asLong()) + ).apply(builder, BulletDecalOption::new)); + + @SuppressWarnings("deprecation") + public static final ParticleOptions.Deserializer DESERIALIZER = new ParticleOptions.Deserializer<>() { + @Override + public BulletDecalOption fromCommand(ParticleType particleType, StringReader reader) throws CommandSyntaxException { + reader.expect(' '); + int dir = reader.readInt(); + reader.expect(' '); + long pos = reader.readLong(); + return new BulletDecalOption(dir, pos); + } + + @Override + public BulletDecalOption fromNetwork(ParticleType particleType, FriendlyByteBuf buffer) { + return new BulletDecalOption(buffer.readVarInt(), buffer.readLong()); + } + }; + + private final Direction direction; + private final BlockPos pos; + + public BulletDecalOption(int dir, long pos) { + this.direction = Direction.values()[dir]; + this.pos = BlockPos.of(pos); + + } + + public BulletDecalOption(Direction dir, BlockPos pos) { + this.direction = dir; + this.pos = pos; + } + + public Direction getDirection() { + return this.direction; + } + + public BlockPos getPos() { + return this.pos; + } + + @Override + public ParticleType getType() { + return ModParticles.BULLET_HOLE.get(); + } + + @Override + public void writeToNetwork(FriendlyByteBuf buffer) { + buffer.writeEnum(this.direction); + buffer.writeBlockPos(this.pos); + } + + @Override + public String writeToString() { + return ForgeRegistries.PARTICLE_TYPES.getKey(this.getType()) + " " + this.direction.getName(); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalParticle.java b/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalParticle.java new file mode 100644 index 000000000..165d88a4f --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletDecalParticle.java @@ -0,0 +1,170 @@ +package com.atsuishio.superbwarfare.client.particle; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +/** + * Author: Forked from MrCrayfish, continued by Timeless devs + */ +public class BulletDecalParticle extends TextureSheetParticle { + private final Direction direction; + private final BlockPos pos; + private int uOffset; + private int vOffset; + private float textureDensity; + + public BulletDecalParticle(ClientLevel world, double x, double y, double z, Direction direction, BlockPos pos) { + super(world, x, y, z); + this.setSprite(this.getSprite(pos)); + this.direction = direction; + this.pos = pos; + this.lifetime = 200; + this.hasPhysics = false; + this.gravity = 0.0F; + this.quadSize = 0.05F; + + // 如果方块是空气,则立即移除粒子 + if (world.getBlockState(pos).isAir()) { + this.remove(); + } + + this.rCol = 0; + this.gCol = 0; + this.bCol = 0; + + this.alpha = 0.9F; + } + + @Override + protected void setSprite(@NotNull TextureAtlasSprite sprite) { + super.setSprite(sprite); + this.uOffset = this.random.nextInt(16); + this.vOffset = this.random.nextInt(16); + // 材质应该都是方形 + this.textureDensity = (sprite.getU1() - sprite.getU0()) / 16.0F; + } + + private TextureAtlasSprite getSprite(BlockPos pos) { + Minecraft minecraft = Minecraft.getInstance(); + Level world = minecraft.level; + if (world != null) { + BlockState state = world.getBlockState(pos); + return Minecraft.getInstance().getBlockRenderer().getBlockModelShaper().getTexture(state, world, pos); + } + return Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(MissingTextureAtlasSprite.getLocation()); + } + + @Override + protected float getU0() { + return this.sprite.getU0() + this.uOffset * this.textureDensity; + } + + @Override + protected float getV0() { + return this.sprite.getV0() + this.vOffset * this.textureDensity; + } + + @Override + protected float getU1() { + return this.getU0() + this.textureDensity; + } + + @Override + protected float getV1() { + return this.getV0() + this.textureDensity; + } + + @Override + public void tick() { + super.tick(); + if (this.level.getBlockState(this.pos).isAir()) { + this.remove(); + } + } + + @Override + public void render(@NotNull VertexConsumer buffer, Camera renderInfo, float partialTicks) { + Vec3 view = renderInfo.getPosition(); + float particleX = (float) (Mth.lerp(partialTicks, this.xo, this.x) - view.x()); + float particleY = (float) (Mth.lerp(partialTicks, this.yo, this.y) - view.y()); + float particleZ = (float) (Mth.lerp(partialTicks, this.zo, this.z) - view.z()); + Quaternionf quaternion = this.direction.getRotation(); + Vector3f[] points = new Vector3f[]{ + // Y 值稍微大一点点,防止 z-fight + new Vector3f(-1.0F, 0.01F, -1.0F), + new Vector3f(-1.0F, 0.01F, 1.0F), + new Vector3f(1.0F, 0.01F, 1.0F), + new Vector3f(1.0F, 0.01F, -1.0F) + }; + float scale = this.getQuadSize(partialTicks); + + for (int i = 0; i < 4; ++i) { + Vector3f vector3f = points[i]; + vector3f.rotate(quaternion); + vector3f.mul(scale); + vector3f.add(particleX, particleY, particleZ); + } + + // UV 坐标 + float u0 = this.getU0(); + float u1 = this.getU1(); + float v0 = this.getV0(); + float v1 = this.getV1(); + + // 0 - 30 tick 内,从 15 亮度到 0 亮度 + int light = Math.max(15 - this.age / 2, 0); + int lightColor = LightTexture.pack(light, light); + + // 颜色,逐渐渐变到 0 0 0,也就是黑色 + float colorPercent = light / 15.0f; + float red = this.rCol * colorPercent; + float green = this.gCol * colorPercent; + float blue = this.bCol * colorPercent; + + // 透明度,逐渐变成 0,也就是透明 + double threshold = 0.98 * this.lifetime; + float fade = 1.0f - (float) (Math.max(this.age - threshold, 0) / (this.lifetime - threshold)); + float alphaFade = this.alpha * fade; + + buffer.addVertex(points[0].x(), points[0].y(), points[0].z()).setUv(u1, v1).setColor(red, green, blue, alphaFade).setLight(lightColor); + buffer.addVertex(points[1].x(), points[1].y(), points[1].z()).setUv(u1, v0).setColor(red, green, blue, alphaFade).setLight(lightColor); + buffer.addVertex(points[2].x(), points[2].y(), points[2].z()).setUv(u0, v0).setColor(red, green, blue, alphaFade).setLight(lightColor); + buffer.addVertex(points[3].x(), points[3].y(), points[3].z()).setUv(u0, v1).setColor(red, green, blue, alphaFade).setLight(lightColor); + } + + @Override + public @NotNull ParticleRenderType getRenderType() { + return ParticleRenderType.TERRAIN_SHEET; + } + + @OnlyIn(Dist.CLIENT) + public static class Provider implements ParticleProvider { + public Provider() { + } + + @Override + public BulletDecalParticle createParticle(@NotNull BulletDecalOption option, @NotNull ClientLevel world, double x, double y, double z, double pXSpeed, double pYSpeed, double pZSpeed) { + return new BulletDecalParticle(world, x, y, z, option.getDirection(), option.getPos()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletHoleParticle.java b/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletHoleParticle.java deleted file mode 100644 index d537b761b..000000000 --- a/src/main/java/com/atsuishio/superbwarfare/client/particle/BulletHoleParticle.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.atsuishio.superbwarfare.client.particle; - -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.particle.*; -import net.minecraft.core.particles.SimpleParticleType; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.api.distmarker.OnlyIn; -import org.jetbrains.annotations.NotNull; - -@OnlyIn(Dist.CLIENT) -public class BulletHoleParticle extends TextureSheetParticle { - public static BulletholeParticleProvider provider(SpriteSet spriteSet) { - return new BulletholeParticleProvider(spriteSet); - } - - public static class BulletholeParticleProvider implements ParticleProvider { - private final SpriteSet spriteSet; - - public BulletholeParticleProvider(SpriteSet spriteSet) { - this.spriteSet = spriteSet; - } - - public Particle createParticle(@NotNull SimpleParticleType typeIn, @NotNull ClientLevel worldIn, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { - return new BulletHoleParticle(worldIn, x, y, z, xSpeed, ySpeed, zSpeed, this.spriteSet); - } - } - - protected BulletHoleParticle(ClientLevel world, double x, double y, double z, double vx, double vy, double vz, SpriteSet spriteSet) { - super(world, x, y, z); - this.setSize(0f, 0f); - this.lifetime = 100; - this.gravity = 0f; - this.hasPhysics = false; - this.xd = vx * 0; - this.yd = vy * 0; - this.zd = vz * 0; - this.pickSprite(spriteSet); - } - - @Override - public @NotNull ParticleRenderType getRenderType() { - return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; - } - - @Override - public void tick() { - super.tick(); - } -} \ No newline at end of file diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/ProjectileEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/ProjectileEntity.java index 8e0b409bf..065e234cf 100644 --- a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/ProjectileEntity.java +++ b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/ProjectileEntity.java @@ -2,6 +2,7 @@ package com.atsuishio.superbwarfare.entity.projectile; import com.atsuishio.superbwarfare.block.BarbedWireBlock; import com.atsuishio.superbwarfare.component.ModDataComponents; +import com.atsuishio.superbwarfare.client.particle.BulletDecalOption; import com.atsuishio.superbwarfare.config.server.ProjectileConfig; import com.atsuishio.superbwarfare.entity.DPSGeneratorEntity; import com.atsuishio.superbwarfare.entity.OBBEntity; @@ -18,6 +19,7 @@ import com.mojang.datafixers.util.Pair; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; +import net.minecraft.core.particles.BlockParticleOption; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.RegistryFriendlyByteBuf; @@ -413,7 +415,7 @@ public class ProjectileEntity extends Projectile implements IEntityWithComplexSp recordHitScore(rings, dis); } - this.onHitBlock(hitVec); + this.onHitBlock(hitVec, blockHitResult); if (heLevel > 0) { explosionBullet(this, this.damage, heLevel, monsterMultiplier + 1, hitVec); } @@ -492,13 +494,27 @@ public class ProjectileEntity extends Projectile implements IEntityWithComplexSp } } - protected void onHitBlock(Vec3 location) { + protected void onHitBlock(Vec3 location, BlockHitResult result) { if (this.level() instanceof ServerLevel serverLevel) { + BlockPos pos = result.getBlockPos(); + Direction face = result.getDirection(); + BlockState state = level().getBlockState(pos); + + BlockParticleOption particleData = new BlockParticleOption(ParticleTypes.BLOCK, state); + + double speed = 0.05; + double vx = face.getStepX() * speed; + double vy = face.getStepY() * speed; + double vz = face.getStepZ() * speed; + if (this.beast) { ParticleTool.sendParticle(serverLevel, ParticleTypes.END_ROD, location.x, location.y, location.z, 15, 0.1, 0.1, 0.1, 0.05, true); } else { - ParticleTool.sendParticle(serverLevel, ModParticleTypes.BULLET_HOLE.get(), location.x, location.y, location.z, 1, 0, 0, 0, 0, true); - ParticleTool.sendParticle(serverLevel, ParticleTypes.SMOKE, location.x, location.y, location.z, 3, 0, 0.1, 0, 0.01, true); + BulletDecalOption bulletDecalOption = new BulletDecalOption(result.getDirection(), result.getBlockPos()); + serverLevel.sendParticles(bulletDecalOption, location.x, location.y, location.z, 1, 0, 0, 0, 0); + + ParticleTool.sendParticle(serverLevel, ParticleTypes.SMOKE, location.x, location.y, location.z, 3, vx, vy, vz, 0.01, true); + ParticleTool.sendParticle(serverLevel, particleData, location.x, location.y, location.z, 5, vx, vy, vz, 0.1, true); this.discard(); } serverLevel.playSound(null, new BlockPos((int) location.x, (int) location.y, (int) location.z), ModSounds.LAND.get(), SoundSource.BLOCKS, 1.0F, 1.0F); diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModParticleTypes.java b/src/main/java/com/atsuishio/superbwarfare/init/ModParticleTypes.java index 26b2ef8a3..e043a1dcf 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModParticleTypes.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModParticleTypes.java @@ -1,18 +1,38 @@ package com.atsuishio.superbwarfare.init; import com.atsuishio.superbwarfare.Mod; +import com.atsuishio.superbwarfare.client.particle.BulletDecalOption; +import com.mojang.serialization.Codec; +import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleType; import net.minecraft.core.particles.SimpleParticleType; -import net.minecraft.core.registries.BuiltInRegistries; -import net.neoforged.neoforge.registries.DeferredHolder; -import net.neoforged.neoforge.registries.DeferredRegister; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; +import org.jetbrains.annotations.NotNull; public class ModParticleTypes { public static final DeferredRegister> REGISTRY = DeferredRegister.create(BuiltInRegistries.PARTICLE_TYPE, Mod.MODID); + public static final DeferredHolder, SimpleParticleType> FIRE_STAR = REGISTRY.register("fire_star", () -> new SimpleParticleType(false)); - public static final DeferredHolder, SimpleParticleType> BULLET_HOLE = REGISTRY.register("bullet_hole", () -> new SimpleParticleType(false)); + public static final DeferredHolder, ModParticleType> BULLET_DECAL = REGISTRY.register("bullet_decal", () -> new ModParticleType<>(false, BulletDecalOption.DESERIALIZER, BulletDecalOption.CODEC)); public static final DeferredHolder, SimpleParticleType> CUSTOM_CLOUD = REGISTRY.register("custom_cloud", () -> new SimpleParticleType(false)); public static final DeferredHolder, SimpleParticleType> CUSTOM_SMOKE = REGISTRY.register("custom_smoke", () -> new SimpleParticleType(false)); + + @SuppressWarnings("deprecation") + private static class ModParticleType extends ParticleType { + private final Codec codec; + + public ModParticleType(boolean overrideLimiter, ParticleOptions.Deserializer deserializer, Codec codec) { + super(overrideLimiter, deserializer); + this.codec = codec; + } + + @Override + public @NotNull Codec codec() { + return this.codec; + } + } } diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModParticles.java b/src/main/java/com/atsuishio/superbwarfare/init/ModParticles.java index d602a9630..cdcd95d78 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModParticles.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModParticles.java @@ -1,6 +1,6 @@ package com.atsuishio.superbwarfare.init; -import com.atsuishio.superbwarfare.client.particle.BulletHoleParticle; +import com.atsuishio.superbwarfare.client.particle.BulletDecalParticle; import com.atsuishio.superbwarfare.client.particle.CustomCloudParticle; import com.atsuishio.superbwarfare.client.particle.CustomSmokeParticle; import com.atsuishio.superbwarfare.client.particle.FireStarParticle; @@ -14,7 +14,7 @@ public class ModParticles { @SubscribeEvent public static void registerParticles(RegisterParticleProvidersEvent event) { event.registerSpriteSet(ModParticleTypes.FIRE_STAR.get(), FireStarParticle::provider); - event.registerSpriteSet(ModParticleTypes.BULLET_HOLE.get(), BulletHoleParticle::provider); + event.registerSpecial(ModParticleTypes.BULLET_DECAL.get(), new BulletDecalParticle.Provider()); event.registerSpriteSet(ModParticleTypes.CUSTOM_CLOUD.get(), CustomCloudParticle::provider); event.registerSpriteSet(ModParticleTypes.CUSTOM_SMOKE.get(), CustomSmokeParticle::provider); } diff --git a/src/main/resources/assets/superbwarfare/particles/bullet_decal.json b/src/main/resources/assets/superbwarfare/particles/bullet_decal.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/src/main/resources/assets/superbwarfare/particles/bullet_decal.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/main/resources/assets/superbwarfare/particles/bullet_hole.json b/src/main/resources/assets/superbwarfare/particles/bullet_hole.json deleted file mode 100644 index 14eefd54f..000000000 --- a/src/main/resources/assets/superbwarfare/particles/bullet_hole.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "textures": [ - "superbwarfare:bullet_hole" - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/superbwarfare/textures/particle/bullet_hole.png b/src/main/resources/assets/superbwarfare/textures/particle/bullet_hole.png deleted file mode 100644 index 25353b6c91d592c00c4bd899b97e42606893c3c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`TAnVBAr*1S2@mdKI;Vst086zQ_y7O^