diff --git a/src/main/java/com/atsuishio/superbwarfare/client/model/entity/M18SmokeGrenadeEntityModel.java b/src/main/java/com/atsuishio/superbwarfare/client/model/entity/M18SmokeGrenadeEntityModel.java new file mode 100644 index 000000000..f8fdab78b --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/model/entity/M18SmokeGrenadeEntityModel.java @@ -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 { + + @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"); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/M18SmokeGrenadeRenderer.java b/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/M18SmokeGrenadeRenderer.java new file mode 100644 index 000000000..298c40524 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/M18SmokeGrenadeRenderer.java @@ -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 { + 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; + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/M18SmokeGrenadeEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/M18SmokeGrenadeEntity.java new file mode 100644 index 000000000..476fa3bf8 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/M18SmokeGrenadeEntity.java @@ -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 type, Level world) { + super(type, world); + this.noCulling = true; + } + + public M18SmokeGrenadeEntity(EntityType 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; + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/RgoGrenadeEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/RgoGrenadeEntity.java index c55b0d17c..4d938d695 100644 --- a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/RgoGrenadeEntity.java +++ b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/RgoGrenadeEntity.java @@ -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) { } diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java b/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java index 490c202c8..8ec86c10b 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java @@ -76,6 +76,8 @@ public class ModEntities { EntityType.Builder.of(HandGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.3f, 0.3f)); public static final DeferredHolder, EntityType> RGO_GRENADE = register("rgo_grenade", EntityType.Builder.of(RgoGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.3f, 0.3f)); + public static final DeferredHolder, EntityType> M18_SMOKE_GRENADE = register("m18_smoke_grenade", + EntityType.Builder.of(M18SmokeGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.3f, 0.3f)); public static final DeferredHolder, EntityType> JAVELIN_MISSILE = register("javelin_missile", EntityType.Builder.of(JavelinMissileEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(false).noSave().setTrackingRange(64).setUpdateInterval(1).sized(0.5f, 0.5f)); public static final DeferredHolder, EntityType> AGM_65 = register("agm_65", diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java b/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java index 25b73bb20..a7f1fe3db 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java @@ -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); diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java b/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java index 34021706c..cd5d16dd3 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModItems.java @@ -113,6 +113,7 @@ public class ModItems { public static final DeferredHolder AP_5_INCHES = AMMO.register("ap_5_inches", () -> new CannonShellItem(new Item.Properties().rarity(Rarity.RARE))); public static final DeferredHolder HAND_GRENADE = AMMO.register("hand_grenade", HandGrenade::new); public static final DeferredHolder RGO_GRENADE = AMMO.register("rgo_grenade", RgoGrenade::new); + public static final DeferredHolder M18_SMOKE_GRENADE = AMMO.register("m18_smoke_grenade", M18SmokeGrenade::new); public static final DeferredHolder CLAYMORE_MINE = AMMO.register("claymore_mine", ClaymoreMine::new); public static final DeferredHolder TM_62 = AMMO.register("tm_62", Tm62Item::new); public static final DeferredHolder 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()); } diff --git a/src/main/java/com/atsuishio/superbwarfare/item/M18SmokeGrenade.java b/src/main/java/com/atsuishio/superbwarfare/item/M18SmokeGrenade.java new file mode 100644 index 000000000..02fe56247 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/item/M18SmokeGrenade.java @@ -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 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); + } +} + diff --git a/src/main/resources/assets/superbwarfare/lang/en_us.json b/src/main/resources/assets/superbwarfare/lang/en_us.json index e76754863..57c0f3234 100644 --- a/src/main/resources/assets/superbwarfare/lang/en_us.json +++ b/src/main/resources/assets/superbwarfare/lang/en_us.json @@ -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: ", diff --git a/src/main/resources/assets/superbwarfare/lang/zh_cn.json b/src/main/resources/assets/superbwarfare/lang/zh_cn.json index 8aaab3c87..8fe38ba06 100644 --- a/src/main/resources/assets/superbwarfare/lang/zh_cn.json +++ b/src/main/resources/assets/superbwarfare/lang/zh_cn.json @@ -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": "环数:",