Jump to content

[1.19.4] Ranged attack accuracy


Feroov

Recommended Posts

I have a flying mob that basically moves like a ghast, I have a custom projectile, it is a AbstractArrow type not fireball, well my accuracy is not quite there yet, on some distances, it is perfectly accurate and on some distances inaccurate, this is my current attack goal:

 

    public static class RaybeamAttackGoal extends Goal
    {
        private final Celestroid mob;
        private final Celestroid rangedAttackMob;
        @Nullable
        private LivingEntity target;
        private int attackTime = -1;
        private int seeTime;
        private final int statecheck;
        private final double attackIntervalMin, attackIntervalMax, speedModifier;
        private final float attackRadius, attackRadiusSqr;

        public RaybeamAttackGoal(Celestroid celestroid, double speedIn, double dpsIn, float rangeIn, int state)
        {
            this(celestroid, speedIn, dpsIn, dpsIn, rangeIn, state);
        }

        public RaybeamAttackGoal(Celestroid gunswine, double speedIn, double atckIntervalMin, double atckIntervalMax, float atckRadius, int state)
        {

              this.rangedAttackMob =  gunswine;
              this.mob =  gunswine;
              this.speedModifier = speedIn;
              this.attackIntervalMin = atckIntervalMin;
              this.attackIntervalMax = atckIntervalMax;
              this.attackRadius = atckRadius;
              this.attackRadiusSqr = atckRadius * atckRadius;
              this.statecheck = state;
              this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
        }


        public boolean canUse()
        {
            LivingEntity livingentity = this.mob.getTarget();
            if (livingentity != null && livingentity.isAlive())
            {
                this.target = livingentity;
                return true;
            } else  { return false; }
        }

        public boolean canContinueToUse() { return this.canUse() || !this.mob.getNavigation().isDone(); }

        public void stop() {
            this.target = null;
            this.seeTime = 0;
            this.attackTime = -1;
        }

        public boolean requiresUpdateEveryTick() { return true; }

        public void tick()
        {
            double d0 = this.mob.distanceToSqr(this.target.getX(), this.target.getY(), this.target.getZ());
            boolean flag = this.mob.getSensing().hasLineOfSight(this.target);
            if (flag) {
                ++this.seeTime;
            } else {
                this.seeTime = 0;
            }

            if (!(d0 > (double)this.attackRadiusSqr) && this.seeTime >= 5) {
                this.mob.getNavigation().stop();
            } else {
                this.mob.getNavigation().moveTo(this.target, this.speedModifier);
            }

            this.mob.getLookControl().setLookAt(this.target, 30.0F, 30.0F);
            if (--this.attackTime == 0) {
                if (!flag) {
                    return;
                }

                float f = (float)Math.sqrt(d0) / this.attackRadius;
                float f1 = Mth.clamp(f, 0.1F, 1.0F);
                this.rangedAttackMob.performRangedAttack(this.target, f1);
                this.attackTime = Mth.floor(f * (float)(this.attackIntervalMax - this.attackIntervalMin) + (float)this.attackIntervalMin);
            } else if (this.attackTime < 0) {
                this.attackTime = Mth.floor(Mth.lerp(Math.sqrt(d0) / (double)this.attackRadius, (double)this.attackIntervalMin, (double)this.attackIntervalMax));
            }
        }
    }

    public void performRangedAttack(LivingEntity livingEntity, float p_32142_)
    {
        RaygunBeam beam = new RaygunBeam(this.level, this);
        double d0 = livingEntity.getX() - this.getX();
        double d1 = livingEntity.getY(-3.3333333333333333D) - beam.getY();
        double d2 = livingEntity.getZ() - this.getZ();
        double d3 = Math.sqrt(d0 * d0 + d2 * d2);
        beam.shoot(d0, d1 + d3 * (double)0.2F, d2, 1.6F,0f);
        this.playSound(SoundEventsSTLCON.RAYGUN_SHOOT.get(), 2.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));

        this.level.addFreshEntity(beam);
    }

 

What can I do or refactor so that the accuracy is basically like a fireball of the ghast, I tried actually creating a similar way of how ghast is made, so I did create before another projectile for testing purposes, which used same functionality as a fireball, then the accuracy was perfect as I wanted but obviously the projectile wouldn't do damage to the player. Thank you so much in advance cheers

Link to comment
Share on other sites

//From AbstractSkeleton.class
//shoot() method parameters (x,y,z,velocity,inaccuracy)
abstractarrow.shoot(d0, d1 + d3 * (double)0.2F, d2, 1.6F, (float)(14 - this.level.getDifficulty().getId() * 4));
  

as you see in the last parameter its adjusting the inaccuracy of the shoot from a hardcoded number subtracted by the current world difficulty * 4. Now if you set it to 0 and  it'll be accurate and increasing the velocity makes it deal more damage and is a factor in the trajectory of the arrow. I think what you are trying to compare are two different things, the arrow first of all has drop off which compared to a fireball does not drop off and keeps its velocity constant. I have pulled the code from the Vanilla Ghast and Fireball to show you what it does compared to the Arrow

 

This is the code the Ghast uses to shoot a fireball

if (this.chargeTime == 20) {
	double d1 = 4.0D;
    Vec3 vec3 = this.ghast.getViewVector(1.0F);
    double d2 = livingentity.getX() - (this.ghast.getX() + vec3.x * 4.0D);
    double d3 = livingentity.getY(0.5D) - (0.5D + this.ghast.getY(0.5D));
    double d4 = livingentity.getZ() - (this.ghast.getZ() + vec3.z * 4.0D);
    if (!this.ghast.isSilent()) {
    	level.levelEvent((Player)null, 1016, this.ghast.blockPosition(), 0);
    }

    LargeFireball largefireball = new LargeFireball(level, this.ghast, d2, d3, d4, this.ghast.getExplosionPower());
    largefireball.setPos(this.ghast.getX() + vec3.x * 4.0D, this.ghast.getY(0.5D) + 0.5D, largefireball.getZ() + vec3.z * 4.0D);
    level.addFreshEntity(largefireball);
	this.chargeTime = -40;
}

 

 

This is the AbstractHurtingProjectile.class code that calls when its spawned in the world

public void tick() {
      Entity entity = this.getOwner();
      if (this.level.isClientSide || (entity == null || !entity.isRemoved()) && this.level.hasChunkAt(this.blockPosition())) {
         super.tick();
         if (this.shouldBurn()) {
            this.setSecondsOnFire(1);
         }

         HitResult hitresult = ProjectileUtil.getHitResult(this, this::canHitEntity);
         if (hitresult.getType() != HitResult.Type.MISS && !net.minecraftforge.event.ForgeEventFactory.onProjectileImpact(this, hitresult)) {
            this.onHit(hitresult);
         }

         this.checkInsideBlocks();
         Vec3 vec3 = this.getDeltaMovement();
         double d0 = this.getX() + vec3.x;
         double d1 = this.getY() + vec3.y;
         double d2 = this.getZ() + vec3.z;
         ProjectileUtil.rotateTowardsMovement(this, 0.2F);
         float f = this.getInertia();
         if (this.isInWater()) {
            for(int i = 0; i < 4; ++i) {
               float f1 = 0.25F;
               this.level.addParticle(ParticleTypes.BUBBLE, d0 - vec3.x * 0.25D, d1 - vec3.y * 0.25D, d2 - vec3.z * 0.25D, vec3.x, vec3.y, vec3.z);
            }

            f = 0.8F;
         }

         this.setDeltaMovement(vec3.add(this.xPower, this.yPower, this.zPower).scale((double)f));
         this.level.addParticle(this.getTrailParticle(), d0, d1 + 0.5D, d2, 0.0D, 0.0D, 0.0D);
         this.setPos(d0, d1, d2);
      } else {
         this.discard();
      }
   }

 

AbstractArrow.class tick() method, the area in where it checks if a block was hit or not

if (this.inGround && !flag) {
         if (this.lastState != blockstate && this.shouldFall()) {
            this.startFalling();
         } else if (!this.level.isClientSide) {
            this.tickDespawn();
         }

         ++this.inGroundTime;
      }

ShouldFall check and StartFalling method

private boolean shouldFall() {
      return this.inGround && this.level.noCollision((new AABB(this.position(), this.position())).inflate(0.06D));
   }

   private void startFalling() {
      this.inGround = false;
      Vec3 vec3 = this.getDeltaMovement();
      this.setDeltaMovement(vec3.multiply((double)(this.random.nextFloat() * 0.2F), (double)(this.random.nextFloat() * 0.2F), (double)(this.random.nextFloat() * 0.2F)));
      this.life = 0;
   }

as you see the arrow is more complex in physics compared to the fireball, if you want it to be just as accurate as the fireball I think you would have to override a custom extended AbstractArrow or just outright make your own version of the AbstractArrow class

Link to comment
Share on other sites

1 hour ago, sFXprt said:

You also mentioned you made a similar projectile like the Ghast does what class did you derive it from AbstractHurtingProjectile or AbstractArrow? 

 

 

Like you mentioned how I could make my own AbstractArrow, I actually went on creating my own AbstractHurtingProjectile since I think the previous one I made the testing purpose one was AbstractArrow if I recall. Everything is working as intended now so, thank you so much for your time appreciate it, and yes I had some issues regarding where it shoots, so I copied/used over ghast as you mentioned ♥

  • Like 1
Link to comment
Share on other sites

On 5/7/2023 at 3:20 AM, Feroov said:

 

 

Like you mentioned how I could make my own AbstractArrow, I actually went on creating my own AbstractHurtingProjectile since I think the previous one I made the testing purpose one was AbstractArrow if I recall. Everything is working as intended now so, thank you so much for your time appreciate it, and yes I had some issues regarding where it shoots, so I copied/used over ghast as you mentioned ♥

Nice glad to see you got it working man!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Add crash-reports with sites like https://paste.ee/ Maybe an issue with blur, essentials or cumulus_menus
    • Add the crash-report or latest.log (logs-folder) with sites like https://paste.ee/ and paste the link to it here  
    • I have a problem, I am trying to put two different effects to two different armors but when I run it only the emerald armor effect works. This is the code public class ModArmorItem extends ArmorItem{ private static final Map<ArmorMaterial, MobEffectInstance> MATERIAL_TO_EFFECT_MAP = (new ImmutableMap.Builder<ArmorMaterial, MobEffectInstance>()) .put(ModArmorMaterials.EMERALD, new MobEffectInstance(MobEffects.HERO_OF_THE_VILLAGE,200, 1,false,false, true)) .put(ModArmorMaterials.OBSIDIAN, new MobEffectInstance(MobEffects.FIRE_RESISTANCE,200, 1,false,false, true)).build(); public ModArmorItem(ArmorMaterial pMaterial, Type pType, Properties pProperties) { super(pMaterial, pType, pProperties); } @Override public void onArmorTick(ItemStack stack, Level world, Player player){ if (!world.isClientSide()) { if (hasFullSuitOfArmorOn(player)) { evaluateArmorEffects(player); } } } private void evaluateArmorEffects(Player player) { for (Map.Entry<ArmorMaterial,MobEffectInstance> entry : MATERIAL_TO_EFFECT_MAP.entrySet()){ ArmorMaterial mapArmorMaterial = entry.getKey(); MobEffectInstance mapStatusEffect = entry.getValue(); if (hasCorrectArmorOn(mapArmorMaterial, player)) { addStatusEffectForMaterial(player, mapArmorMaterial, mapStatusEffect); } } } private void addStatusEffectForMaterial(Player player, ArmorMaterial mapArmorMaterial, MobEffectInstance mapStatusEffect) { boolean hasPlayerEffect = player.hasEffect(mapStatusEffect.getEffect()); if (hasCorrectArmorOn(mapArmorMaterial, player) && !hasPlayerEffect) { player.addEffect(new MobEffectInstance(mapStatusEffect)); } } private boolean hasCorrectArmorOn(ArmorMaterial material, Player player) { for (ItemStack armorStack : player.getInventory().armor){ if (!(armorStack.getItem() instanceof ArmorItem)) { return false; } } ArmorItem helmet = ((ArmorItem)player.getInventory().getArmor(3).getItem()); ArmorItem breastplace = ((ArmorItem)player.getInventory().getArmor(2).getItem()); ArmorItem leggins = ((ArmorItem)player.getInventory().getArmor(1).getItem()); ArmorItem boots = ((ArmorItem)player.getInventory().getArmor(0).getItem()); return helmet.getMaterial() == material && breastplace.getMaterial() == material && leggins.getMaterial() == material && boots.getMaterial() == material; } private boolean hasFullSuitOfArmorOn(Player player){ ItemStack helmet = player.getInventory().getArmor(3); ItemStack breastplace = player.getInventory().getArmor(2); ItemStack leggins = player.getInventory().getArmor(1); ItemStack boots = player.getInventory().getArmor(0); return !helmet.isEmpty() && !breastplace.isEmpty() && !leggins.isEmpty() && !boots.isEmpty(); } } Also when I place two effects on the same armor, the game crashes. Here is the crash file. The code is the same, only this part is different   private static final Map<ArmorMaterial, MobEffectInstance> MATERIAL_TO_EFFECT_MAP = (new ImmutableMap.Builder<ArmorMaterial, MobEffectInstance>()) .put(ModArmorMaterials.EMERALD, new MobEffectInstance(MobEffects.HERO_OF_THE_VILLAGE,200, 1,false,false, true)) .put(ModArmorMaterials.EMERALD, new MobEffectInstance(MobEffects.FIRE_RESISTANCE,200, 1,false,false, true)).build(); I hope you guys can help me. Thanks.
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.