添加M18烟雾弹

This commit is contained in:
Light_Quanta 2025-07-07 14:02:08 +08:00
parent 2b466a0036
commit 2643a97445
No known key found for this signature in database
GPG key ID: 11A39A1B8C890959
10 changed files with 343 additions and 12 deletions

View file

@ -0,0 +1,26 @@
package com.atsuishio.superbwarfare.client.model.entity;
import com.atsuishio.superbwarfare.Mod;
import com.atsuishio.superbwarfare.entity.projectile.M18SmokeGrenadeEntity;
import net.minecraft.resources.ResourceLocation;
import software.bernie.geckolib.model.GeoModel;
public class M18SmokeGrenadeEntityModel extends GeoModel<M18SmokeGrenadeEntity> {
@Override
public ResourceLocation getAnimationResource(M18SmokeGrenadeEntity entity) {
return null;
}
// TODO 添加烟雾手雷资源文件
@Override
public ResourceLocation getModelResource(M18SmokeGrenadeEntity entity) {
return Mod.loc("geo/rgo_grenade.geo.json");
}
@Override
public ResourceLocation getTextureResource(M18SmokeGrenadeEntity entity) {
return Mod.loc("textures/item/rgo_grenade.png");
}
}

View file

@ -0,0 +1,48 @@
package com.atsuishio.superbwarfare.client.renderer.entity;
import com.atsuishio.superbwarfare.client.model.entity.M18SmokeGrenadeEntityModel;
import com.atsuishio.superbwarfare.entity.projectile.M18SmokeGrenadeEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import org.jetbrains.annotations.NotNull;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class M18SmokeGrenadeRenderer extends GeoEntityRenderer<M18SmokeGrenadeEntity> {
public M18SmokeGrenadeRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new M18SmokeGrenadeEntityModel());
}
@Override
public RenderType getRenderType(M18SmokeGrenadeEntity animatable, ResourceLocation texture, MultiBufferSource bufferSource, float partialTick) {
return RenderType.entityTranslucent(getTextureLocation(animatable));
}
@Override
public void preRender(PoseStack poseStack, M18SmokeGrenadeEntity entity, BakedGeoModel model, MultiBufferSource bufferSource, VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int color) {
float scale = 1f;
this.scaleHeight = scale;
this.scaleWidth = scale;
super.preRender(poseStack, entity, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, color);
}
@Override
public void render(M18SmokeGrenadeEntity entityIn, float entityYaw, float partialTicks, PoseStack poseStack, @NotNull MultiBufferSource bufferIn, int packedLightIn) {
poseStack.pushPose();
poseStack.mulPose(Axis.YP.rotationDegrees(Mth.lerp(partialTicks, entityIn.yRotO, entityIn.getYRot()) - 90));
poseStack.mulPose(Axis.ZP.rotationDegrees(90 + Mth.lerp(partialTicks, entityIn.xRotO, entityIn.getXRot())));
super.render(entityIn, entityYaw, partialTicks, poseStack, bufferIn, packedLightIn);
poseStack.popPose();
}
@Override
protected float getDeathMaxRotation(M18SmokeGrenadeEntity entityLivingBaseIn) {
return 0.0F;
}
}

View file

@ -0,0 +1,147 @@
package com.atsuishio.superbwarfare.entity.projectile;
import com.atsuishio.superbwarfare.init.ModEntities;
import com.atsuishio.superbwarfare.init.ModItems;
import com.atsuishio.superbwarfare.init.ModSounds;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BellBlock;
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.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.util.GeckoLibUtil;
public class M18SmokeGrenadeEntity extends FastThrowableProjectile implements GeoEntity {
private int fuse = 80;
private int count = 8;
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
public M18SmokeGrenadeEntity(EntityType<? extends M18SmokeGrenadeEntity> type, Level world) {
super(type, world);
this.noCulling = true;
}
public M18SmokeGrenadeEntity(EntityType<? extends M18SmokeGrenadeEntity> type, double x, double y, double z, Level world) {
super(type, x, y, z, world);
this.noCulling = true;
}
public M18SmokeGrenadeEntity(LivingEntity entity, Level level, int fuse) {
super(ModEntities.M18_SMOKE_GRENADE.get(), entity, level);
this.noCulling = true;
this.fuse = fuse;
}
@Override
public void addAdditionalSaveData(@NotNull CompoundTag pCompound) {
super.addAdditionalSaveData(pCompound);
pCompound.putFloat("Fuse", this.fuse);
pCompound.putInt("Count", this.count);
}
@Override
public void readAdditionalSaveData(@NotNull CompoundTag pCompound) {
super.readAdditionalSaveData(pCompound);
if (pCompound.contains("Fuse")) {
this.fuse = pCompound.getInt("Fuse");
}
if (pCompound.contains("Count")) {
this.count = Mth.clamp(pCompound.getInt("Count"), 1, 64);
}
}
@Override
protected @NotNull Item getDefaultItem() {
return ModItems.RGO_GRENADE.get();
}
@Override
public boolean shouldRenderAtSqrDistance(double pDistance) {
return true;
}
@Override
protected void onHit(@NotNull HitResult result) {
if (level().isClientSide) return;
switch (result.getType()) {
case BLOCK:
BlockHitResult blockResult = (BlockHitResult) result;
BlockPos resultPos = blockResult.getBlockPos();
BlockState state = this.level().getBlockState(resultPos);
if (state.getBlock() instanceof BellBlock bell) {
bell.attemptToRing(this.level(), resultPos, blockResult.getDirection());
}
releaseSmoke();
break;
case ENTITY:
EntityHitResult entityResult = (EntityHitResult) result;
Entity entity = entityResult.getEntity();
if (this.getOwner() != null
&& this.getOwner().getVehicle() != null
&& entity == this.getOwner().getVehicle()
|| entity == this.getOwner()
) return;
releaseSmoke();
break;
}
}
@Override
public void tick() {
super.tick();
--this.fuse;
if (this.fuse <= 0) {
this.discard();
releaseSmoke();
}
}
// TODO 优化烟雾效果
public void releaseSmoke() {
var vec3 = new Vec3(1, 1, 0);
for (int i = 0; i < this.count; i++) {
var decoy = new SmokeDecoyEntity(this.level());
decoy.setPos(this.getX(), this.getY() + getBbHeight(), this.getZ());
decoy.decoyShoot(this, vec3.yRot(i * (360f / this.count) * Mth.DEG_TO_RAD), 3, 2);
this.level().addFreshEntity(decoy);
}
this.level().playSound(null, this, ModSounds.DECOY_FIRE.get(), this.getSoundSource(), 1, 1);
this.discard();
}
@Override
protected double getDefaultGravity() {
return 0.07F;
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar data) {
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.cache;
}
}

View file

@ -14,7 +14,6 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@ -25,7 +24,6 @@ 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.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.network.PacketDistributor;
import org.jetbrains.annotations.NotNull;
import software.bernie.geckolib.animatable.GeoEntity;
@ -155,16 +153,6 @@ public class RgoGrenadeEntity extends FastThrowableProjectile implements GeoEnti
return this.cache;
}
public void droneShoot(Entity drone) {
Vec3 vec3 = (new Vec3(0.2 * drone.getDeltaMovement().x, 0.2 * drone.getDeltaMovement().y, 0.2 * drone.getDeltaMovement().z));
this.setDeltaMovement(vec3);
double d0 = vec3.horizontalDistance();
this.setYRot((float) (Mth.atan2(vec3.x, vec3.z) * (double) (180F / (float) Math.PI)));
this.setXRot((float) (Mth.atan2(vec3.y, d0) * (double) (180F / (float) Math.PI)));
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
}
@Override
public void setDamage(float damage) {
}

View file

@ -76,6 +76,8 @@ public class ModEntities {
EntityType.Builder.<HandGrenadeEntity>of(HandGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.3f, 0.3f));
public static final DeferredHolder<EntityType<?>, EntityType<RgoGrenadeEntity>> RGO_GRENADE = register("rgo_grenade",
EntityType.Builder.<RgoGrenadeEntity>of(RgoGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.3f, 0.3f));
public static final DeferredHolder<EntityType<?>, EntityType<M18SmokeGrenadeEntity>> M18_SMOKE_GRENADE = register("m18_smoke_grenade",
EntityType.Builder.<M18SmokeGrenadeEntity>of(M18SmokeGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.3f, 0.3f));
public static final DeferredHolder<EntityType<?>, EntityType<JavelinMissileEntity>> JAVELIN_MISSILE = register("javelin_missile",
EntityType.Builder.<JavelinMissileEntity>of(JavelinMissileEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.5f, 0.5f));
public static final DeferredHolder<EntityType<?>, EntityType<Agm65Entity>> AGM_65 = register("agm_65",

View file

@ -29,6 +29,7 @@ public class ModEntityRenderers {
event.registerEntityRenderer(ModEntities.DRONE.get(), DroneRenderer::new);
event.registerEntityRenderer(ModEntities.HAND_GRENADE.get(), HandGrenadeRenderer::new);
event.registerEntityRenderer(ModEntities.RGO_GRENADE.get(), RgoGrenadeRenderer::new);
event.registerEntityRenderer(ModEntities.M18_SMOKE_GRENADE.get(), M18SmokeGrenadeRenderer::new);
event.registerEntityRenderer(ModEntities.MLE_1934.get(), Mle1934Renderer::new);
event.registerEntityRenderer(ModEntities.JAVELIN_MISSILE.get(), JavelinMissileRenderer::new);
event.registerEntityRenderer(ModEntities.LASER.get(), LaserEntityRenderer::new);

View file

@ -113,6 +113,7 @@ public class ModItems {
public static final DeferredHolder<Item, Item> AP_5_INCHES = AMMO.register("ap_5_inches", () -> new CannonShellItem(new Item.Properties().rarity(Rarity.RARE)));
public static final DeferredHolder<Item, HandGrenade> HAND_GRENADE = AMMO.register("hand_grenade", HandGrenade::new);
public static final DeferredHolder<Item, RgoGrenade> RGO_GRENADE = AMMO.register("rgo_grenade", RgoGrenade::new);
public static final DeferredHolder<Item, M18SmokeGrenade> M18_SMOKE_GRENADE = AMMO.register("m18_smoke_grenade", M18SmokeGrenade::new);
public static final DeferredHolder<Item, ClaymoreMine> CLAYMORE_MINE = AMMO.register("claymore_mine", ClaymoreMine::new);
public static final DeferredHolder<Item, Tm62Item> TM_62 = AMMO.register("tm_62", Tm62Item::new);
public static final DeferredHolder<Item, C4BombItem> C4_BOMB = AMMO.register("c4_bomb", C4BombItem::new);
@ -349,6 +350,7 @@ public class ModItems {
DispenserBlock.registerBehavior(ROCKET_70.get(), new Rocket70Item.Rocket70DispenseBehavior());
DispenserBlock.registerBehavior(MEDIUM_AERIAL_BOMB.get(), new MediumAerialBombItem.MediumAerialBombDispenseBehavior());
DispenserBlock.registerBehavior(RGO_GRENADE.get(), new RgoGrenade.RgoGrenadeDispenserBehavior());
DispenserBlock.registerBehavior(M18_SMOKE_GRENADE.get(), new M18SmokeGrenade.SmokeGrenadeDispenserBehavior());
DispenserBlock.registerBehavior(TM_62.get(), new Tm62Item.Tm62DispenseBehavior());
}

View file

@ -0,0 +1,115 @@
package com.atsuishio.superbwarfare.item;
import com.atsuishio.superbwarfare.entity.projectile.M18SmokeGrenadeEntity;
import com.atsuishio.superbwarfare.init.ModEntities;
import com.atsuishio.superbwarfare.init.ModItems;
import com.atsuishio.superbwarfare.init.ModSounds;
import net.minecraft.core.Direction;
import net.minecraft.core.Position;
import net.minecraft.core.dispenser.BlockSource;
import net.minecraft.core.dispenser.ProjectileDispenseBehavior;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.item.*;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import javax.annotation.ParametersAreNonnullByDefault;
// TODO 物品图标合成配方等资源
public class M18SmokeGrenade extends Item implements ProjectileItem {
public M18SmokeGrenade() {
super(new Properties().rarity(Rarity.UNCOMMON));
}
@Override
@ParametersAreNonnullByDefault
public @NotNull InteractionResultHolder<ItemStack> use(Level worldIn, Player playerIn, InteractionHand handIn) {
ItemStack stack = playerIn.getItemInHand(handIn);
playerIn.startUsingItem(handIn);
if (playerIn instanceof ServerPlayer serverPlayer) {
serverPlayer.level().playSound(null, serverPlayer.getOnPos(), ModSounds.GRENADE_PULL.get(), SoundSource.PLAYERS, 1, 1);
}
return InteractionResultHolder.consume(stack);
}
@Override
public @NotNull UseAnim getUseAnimation(@NotNull ItemStack stack) {
return UseAnim.SPEAR;
}
@Override
@ParametersAreNonnullByDefault
public void releaseUsing(ItemStack stack, Level worldIn, LivingEntity entityLiving, int timeLeft) {
if (!worldIn.isClientSide && entityLiving instanceof Player player) {
int usingTime = this.getUseDuration(stack, player) - timeLeft;
if (usingTime > 3) {
player.getCooldowns().addCooldown(stack.getItem(), 20);
float power = Math.min(usingTime / 8f, 1.8f);
M18SmokeGrenadeEntity grenade = new M18SmokeGrenadeEntity(player, worldIn, 80 - usingTime);
grenade.shootFromRotation(player, player.getXRot(), player.getYRot(), 0, power, 0);
worldIn.addFreshEntity(grenade);
if (player instanceof ServerPlayer serverPlayer) {
serverPlayer.level().playSound(null, serverPlayer.getOnPos(), ModSounds.GRENADE_THROW.get(), SoundSource.PLAYERS, 1, 1);
}
if (!player.isCreative()) {
stack.shrink(1);
}
}
}
}
@Override
@ParametersAreNonnullByDefault
public @NotNull ItemStack finishUsingItem(ItemStack pStack, Level pLevel, LivingEntity pLivingEntity) {
if (!pLevel.isClientSide) {
var grenade = new M18SmokeGrenadeEntity(pLivingEntity, pLevel, 100);
grenade.setPos(pLivingEntity.position());
grenade.releaseSmoke();
if (pLivingEntity instanceof Player player) {
player.getCooldowns().addCooldown(pStack.getItem(), 20);
}
if (pLivingEntity instanceof Player player && !player.isCreative()) {
pStack.shrink(1);
}
}
return super.finishUsingItem(pStack, pLevel, pLivingEntity);
}
@Override
@ParametersAreNonnullByDefault
public int getUseDuration(ItemStack stack, LivingEntity entity) {
return 80;
}
public static class SmokeGrenadeDispenserBehavior extends ProjectileDispenseBehavior {
public SmokeGrenadeDispenserBehavior() {
super(ModItems.M18_SMOKE_GRENADE.get());
}
@Override
protected void playSound(BlockSource blockSource) {
blockSource.level().playSound(null, blockSource.pos(), ModSounds.GRENADE_THROW.get(), SoundSource.BLOCKS, 1.0F, 1.0F);
}
}
@Override
@ParametersAreNonnullByDefault
public @NotNull Projectile asProjectile(Level level, Position pos, ItemStack stack, Direction direction) {
return new M18SmokeGrenadeEntity(ModEntities.M18_SMOKE_GRENADE.get(), pos.x(), pos.y(), pos.z(), level);
}
}

View file

@ -240,6 +240,7 @@
"item.superbwarfare.track": "Track",
"item.superbwarfare.hand_grenade": "Hand Grenade",
"item.superbwarfare.rgo_grenade": "RGO Grenade",
"item.superbwarfare.m18_smoke_grenade": "M18 Smoke Grenade",
"item.superbwarfare.transcript": "Transcript",
"des.superbwarfare.transcript": "Recent 10 shooting records:",
"des.superbwarfare.transcript.score": "Score: ",

View file

@ -240,6 +240,7 @@
"item.superbwarfare.track": "履带",
"item.superbwarfare.hand_grenade": "M67手榴弹",
"item.superbwarfare.rgo_grenade": "RGO手榴弹",
"item.superbwarfare.m18_smoke_grenade": "M18烟雾弹",
"item.superbwarfare.transcript": "成绩单",
"des.superbwarfare.transcript": "最近10次的射击成绩",
"des.superbwarfare.transcript.score": "环数:",