Give ranged attack to Monster

Discussion in 'Plugin Development' started by Scipione, Jan 14, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    Scipione

    Hey all,

    i'm currently adding some more monsters to my server .
    But it seems i'm not able to get any other ranged attack ones. If i give a zombie for example a bow, it will come close and punch you with it.
    May someone give me a hint what to do to get a zombie shoot ?
    Would be nice :) Thanks in advance
     
  2. Offline

    MinecraftSped I steal plugins as my own

    It's not in the Zombies code to shoot. To my knowledge the only ranged mobs are Skeletons, Blazes, Ghasts and I think Witches...
     
  3. Offline

    Scipione

    Thanks Adamki, i'll look into that.
    Do you also have a link for the default mob behaviour code of skeletons or something similar ?
    because even i override the class i'm sadly still missing the code for ranged attacks (or KI changes) :/
     
  4. Sorry, I can't help you with that I've never messed around with controlling mobs.

    I'm sure if you have a look around the forums you may be able to find help. It makes it difficult when Notch obfuscates his code :/.
     
  5. Offline

    Scipione

    well i try some different searches thanks :) , but i already searched quite alot before creating this thread..

    I have not found some really useful information ..
    i'm currently trying to let the zombie shoot arrows on the EntityTargetEvent
    but for some strange reason i get:
    Could not pass event EntityTargetEvent to
    java.lang.ClassCastException: org.bukkit.craftbukkit.v1_4_6.entity.CraftExperienceOrb cannot be cast to org.bukkit.entity.LivingEntity

    i need to cast to LivingEntity to be able to get launchProjectile .. but i'm also pondering what MC does with XP-Orbs on the EntityTargetEvent oO
    if someone has some informations about the "shooting KI" of the skeleton.class please let me know :) Thanks

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 30, 2016
  6. Offline

    stirante

    Maybe orb is targetting player and "follow" him. Every ranged mob have to implement IRangedAttackMob (note that this interface name is from decompiled source using MCP, so in bukkit it can be diffrent). I think i could write class for zombie which shoots arrows, but i won't promise anything :).
     
  7. Offline

    Scipione

    would be great if you could do and share :)
    thanks in advance
     
  8. Offline

    stirante

    Code:
    package com.stirante.Testificate;
    
    import java.lang.reflect.Field;
    
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.craftbukkit.v1_4_6.CraftWorld;
    import org.bukkit.craftbukkit.v1_4_6.util.UnsafeList;
    import org.bukkit.entity.Zombie;
    import org.bukkit.potion.Potion;
    
    import net.minecraft.server.v1_4_6.Enchantment;
    import net.minecraft.server.v1_4_6.EnchantmentManager;
    import net.minecraft.server.v1_4_6.EntityArrow;
    import net.minecraft.server.v1_4_6.EntityHuman;
    import net.minecraft.server.v1_4_6.EntityLiving;
    import net.minecraft.server.v1_4_6.EntityPotion;
    import net.minecraft.server.v1_4_6.EntityVillager;
    import net.minecraft.server.v1_4_6.EntityZombie;
    import net.minecraft.server.v1_4_6.IRangedEntity;
    import net.minecraft.server.v1_4_6.MathHelper;
    import net.minecraft.server.v1_4_6.PathfinderGoalArrowAttack;
    import net.minecraft.server.v1_4_6.PathfinderGoalBreakDoor;
    import net.minecraft.server.v1_4_6.PathfinderGoalFleeSun;
    import net.minecraft.server.v1_4_6.PathfinderGoalFloat;
    import net.minecraft.server.v1_4_6.PathfinderGoalHurtByTarget;
    import net.minecraft.server.v1_4_6.PathfinderGoalLookAtPlayer;
    import net.minecraft.server.v1_4_6.PathfinderGoalMeleeAttack;
    import net.minecraft.server.v1_4_6.PathfinderGoalMoveThroughVillage;
    import net.minecraft.server.v1_4_6.PathfinderGoalMoveTowardsRestriction;
    import net.minecraft.server.v1_4_6.PathfinderGoalNearestAttackableTarget;
    import net.minecraft.server.v1_4_6.PathfinderGoalRandomLookaround;
    import net.minecraft.server.v1_4_6.PathfinderGoalRandomStroll;
    import net.minecraft.server.v1_4_6.PathfinderGoalRestrictSun;
    import net.minecraft.server.v1_4_6.PathfinderGoalSelector;
    import net.minecraft.server.v1_4_6.World;
    
    public class SmarterZombie extends EntityZombie implements IRangedEntity {
        private final String MELEE = "melee";
        private final String RANGED = "ranged";
        private String ai = MELEE; 
        
        public SmarterZombie(World world) {
            super(world);
            clearGoalSelector(goalSelector);
            addStandardGoals();
            this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityHuman.class, this.bH, false));
            this.goalSelector.a(5, new PathfinderGoalMeleeAttack(this, EntityVillager.class, this.bH, true));
            
            this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false));
            this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityHuman.class, 16.0F, 0, true));
            this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget(this, EntityVillager.class, 16.0F, 0, false));
        }
    
        @Override
        public void d(EntityLiving entityliving) {
            if (getEquipment(0).id == Material.BOW.getId()){
                EntityArrow entityarrow = new EntityArrow(this.world, this, entityliving, 1.6F, 12.0F);
                int i = EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_DAMAGE.id, this.bD());
                int j = EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_KNOCKBACK.id, this.bD());
                
                if (i > 0) {
                    entityarrow.b(entityarrow.c() + (double) i * 0.5D + 0.5D);
                }
                
                if (j > 0) {
                    entityarrow.a(j);
                }
                
                if (EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_FIRE.id, this.bD()) > 0) {
                    entityarrow.setOnFire(100);
                }
                
                this.makeSound("random.bow", 1.0F, 1.0F / (this.aB().nextFloat() * 0.4F + 0.8F));
                this.world.addEntity(entityarrow);
            }
            else if (getEquipment(0).id == Material.POTION.getId() && Potion.fromDamage(getEquipment(0).getData()).isSplash()){
                EntityPotion entitypotion = new EntityPotion(this.world, this, getEquipment(0));
    
                entitypotion.pitch -= -20.0F;
                double d0 = entityliving.locX + entityliving.motX - this.locX;
                double d1 = entityliving.locY + (double) entityliving.getHeadHeight() - 1.100000023841858D - this.locY;
                double d2 = entityliving.locZ + entityliving.motZ - this.locZ;
                float f = MathHelper.sqrt(d0 * d0 + d2 * d2);
                entitypotion.shoot(d0, d1 + (double) (f * 0.2F), d2, 0.75F, 8.0F);
                this.world.addEntity(entitypotion);
            }
        }
        
        @Override
        public void c(){
            super.c();
            if (ai.equals(RANGED) && getEquipment(0) != null && (getEquipment(0).id != Material.POTION.getId() || getEquipment(0).id != Material.BOW.getId())){
                ai = MELEE;
                clearGoalSelector(goalSelector);
                addStandardGoals();
                this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityHuman.class, this.bH, false));
                this.goalSelector.a(5, new PathfinderGoalMeleeAttack(this, EntityVillager.class, this.bH, true));
            }
            else if (ai.equals(MELEE) && getEquipment(0) != null && (getEquipment(0).id == Material.POTION.getId() || getEquipment(0).id == Material.BOW.getId())){
                if (getEquipment(0).id == Material.POTION.getId() && !Potion.fromDamage(getEquipment(0).getData()).isSplash()) return ;
                ai = RANGED;
                addStandardGoals();
                this.goalSelector.a(4, new PathfinderGoalArrowAttack(this, 0.25F, 60, 10.0F));
            }
        }
        
        @SuppressWarnings("rawtypes")
        public static void clearGoalSelector(PathfinderGoalSelector selector){
            try {
                Field a = PathfinderGoalSelector.class.getDeclaredField("a");
                Field b = PathfinderGoalSelector.class.getDeclaredField("b");
                a.setAccessible(true);
                b.setAccessible(true);
                ((UnsafeList)a.get(selector)).clear();
                ((UnsafeList)b.get(selector)).clear();
            }
            catch (Throwable e) {
                e.printStackTrace();
            }
        }
        
        public void addStandardGoals(){
            this.goalSelector.a(0, new PathfinderGoalFloat(this));
            this.goalSelector.a(1, new PathfinderGoalBreakDoor(this));
            this.goalSelector.a(2, new PathfinderGoalRestrictSun(this));
            this.goalSelector.a(3, new PathfinderGoalFleeSun(this, this.bH));
            this.goalSelector.a(4, new PathfinderGoalMoveTowardsRestriction(this, this.bH));
            this.goalSelector.a(5, new PathfinderGoalMoveThroughVillage(this, this.bH, false));
            this.goalSelector.a(6, new PathfinderGoalRandomStroll(this, this.bH));
            this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
            this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this));
        }
        public static Zombie spawn(Location location){
            SmarterZombie zombie = new SmarterZombie(((CraftWorld)location.getWorld()).getHandle());
            Zombie bukkitZombie = (Zombie) zombie.getBukkitEntity();
            bukkitZombie.teleport(location);
            return bukkitZombie;
        }
    }
    Here is untested code. To spawn zombie just use SmarterZombie.spawn(location). If you set his weapon to bow or splash potion he will use it. He have witch, skeleton and zombie AI. Version for dumb xD :"Give him a bow, he will shoot arrows. Give him a splash potion, he will throw it at enemy. Give him sword or anything else, he will attack him just like he used to."
     
  9. Offline

    Scipione

    Thanks alot, i will test this code the next days :)

    so.. i tried to test it this way:
    in my onEnable() i put this code:
    Code:
    try{
             
                @SuppressWarnings("rawtypes")
                Class[] args = new Class[3];
                args[0] = Class.class;
                args[1] = String.class;
                args[2] = int.class;
     
                Method a = net.minecraft.server.v1_4_6.EntityTypes.class.getDeclaredMethod("a", args);
                a.setAccessible(true);
                a.invoke(a, SmarterZombie.class, "Zombie", 54);
            }catch (Exception e){
                System.out.println("SmarterZombie.class not loaded!");
                e.printStackTrace();
                this.setEnabled(false);
            }
    
    on CreatureSpawnEvent i put this code:
    Code:
    Entity entity = event.getEntity();
    Location location = event.getLocation();
                EntityType creatureType = entity.getType();
                World world = location.getWorld();
                net.minecraft.server.v1_4_6.World mcWorld = ((CraftWorld) world).getHandle();
                net.minecraft.server.v1_4_6.Entity mcEntity = (((CraftEntity) entity).getHandle());
       
                if (creatureType == EntityType.ZOMBIE && mcEntity instanceof SmarterZombie == false){
                    SmarterZombie smarterZombie = new SmarterZombie(mcWorld);
                    smarterZombie.setPosition(location.getX(), location.getY(), location.getZ());
                    mcWorld.removeEntity((net.minecraft.server.v1_4_6.EntityZombie) mcEntity);
                    mcWorld.addEntity(smarterZombie, SpawnReason.CUSTOM);
     
                }
     
                return;
            }
    Zombies are spawning with Bows without any error, but not shooting, still attacking in melee

    i also tried:
    Code:
     public void c(){
    super.c();
    if(getEquipment(0) != null){
    if(getEquipment(0).id == Material.BOW.getId()){
    ai=RANGED;
    }
    if(getEquipment(0).id == Material.POTION.getId()){
    ai=RANGED;
    }}
    if (ai.equals(RANGED) && getEquipment(0) != null && (getEquipment(0).id != Material.POTION.getId() || getEquipment(0).id != Material.BOW.getId())){
    ai = MELEE;
    clearGoalSelector(goalSelector);
    addStandardGoals();
    this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityHuman.class, this.bH, false));
    this.goalSelector.a(5, new PathfinderGoalMeleeAttack(this, EntityVillager.class, this.bH, true));
    }
    else if (ai.equals(MELEE) && getEquipment(0) != null && (getEquipment(0).id == Material.POTION.getId() || getEquipment(0).id == Material.BOW.getId())){
    if (getEquipment(0).id == Material.POTION.getId() && !Potion.fromDamage(getEquipment(0).getData()).isSplash()) return ;
    ai = RANGED;
    addStandardGoals();
    this.goalSelector.a(4, new PathfinderGoalArrowAttack(this, 0.25F, 60, 10.0F));
    }
    }
    adding those lines in, but sadly no changes :/

    alright, i got it !
    Code:
    public void c(){
            super.c();
            if(getEquipment(0) != null){
               
                if (getEquipment(0).id == Material.POTION.getId() || getEquipment(0).id == Material.BOW.getId()){
                    if (getEquipment(0).id == Material.POTION.getId() && !Potion.fromDamage(getEquipment(0).getData()).isSplash()) return ;
                    ai = RANGED;
                    addStandardGoals();
                    this.goalSelector.a(4, new PathfinderGoalArrowAttack(this, 0.25F, 60, 10.0F));
                }
            }
            else{
                ai = MELEE;
                clearGoalSelector(goalSelector);
                addStandardGoals();
                this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityHuman.class, this.bH, false));
                this.goalSelector.a(5, new PathfinderGoalMeleeAttack(this, EntityVillager.class, this.bH, true));
     
            }
        }
    That way it works. Many thanks again for your superior help :)

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 30, 2016
  10. Offline

    stirante

    It should work but now the problem is you are clearing AI and adding it every 1/20 of second. Better check what AI already has zombie. Thats why i created ai variable.
     
  11. Offline

    Scipione

    well problem on your code was, the zombie didn't get into the ai=RANGED if ( i added a system.out there to debug)
    hmm...

    would be very nice when you might give a suggestion how to handle it properly now.
    to make it a bit more clear:

    Code:
    public void c(){
            super.c();
            if (ai.equals(RANGED) && getEquipment(0) != null && (getEquipment(0).id != Material.POTION.getId() || getEquipment(0).id != Material.BOW.getId())){
                ai = MELEE;
                clearGoalSelector(goalSelector);
                addStandardGoals();
                this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityHuman.class, this.bH, false));
                this.goalSelector.a(5, new PathfinderGoalMeleeAttack(this, EntityVillager.class, this.bH, true));
            }
            else if (ai.equals(MELEE) && getEquipment(0) != null && (getEquipment(0).id == Material.POTION.getId() || getEquipment(0).id == Material.BOW.getId())){
                if (getEquipment(0).id == Material.POTION.getId() && !Potion.fromDamage(getEquipment(0).getData()).isSplash()) return ;
                ai = RANGED;
                addStandardGoals();
                this.goalSelector.a(4, new PathfinderGoalArrowAttack(this, 0.25F, 60, 10.0F));
            }
        }
    there a zombie with bow never got into the "else if" clause.
    Or is ist enough to change "else" into "else if" in "my code" ?

    something is really not working :/
    i now change the code to:
    Code:
    public void c(){
     
            super.c();
     
            String AI = MELEE;
     
     
     
            if(getEquipment(0) != null){
     
     
     
                if (getEquipment(0).id == Material.POTION.getId() || getEquipment(0).id == Material.BOW.getId()){
     
                    if (getEquipment(0).id == Material.POTION.getId() && !Potion.fromDamage(getEquipment(0).getData()).isSplash()) return ;
     
                    ai = RANGED;
     
                    addStandardGoals();
     
                    this.goalSelector.a(4, new PathfinderGoalArrowAttack(this, 0.25F, 60, 10.0F));
     
                }
     
            }
     
            else if(ai != RANGED){
     
                ai = MELEE;
     
                clearGoalSelector(goalSelector);
     
                addStandardGoals();
     
                this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, EntityHuman.class, this.bH, false));
     
                this.goalSelector.a(5, new PathfinderGoalMeleeAttack(this, EntityVillager.class, this.bH, true));
     
     
     
            }
     
     
     
        }
    But the Monster only walk normal the first few seconds, then the are half freezed and only moving a block per second.
    i already did a lil bench with nolagg examine, but couldn't find any lag or plugin related issues..

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 30, 2016
  12. Offline

    stirante

    Method you are editing is executed every tick. In this method you wrote that entity will set AI to MELEE every tick and then if he have potion or bow he will set AI to RANGED. I don't even know what you were thinking about when you wrote this. Btw try to copy original code and replace ai.equals... with equalsIgnoreCase.
     
  13. Offline

    Scipione

    well sry i'm just trying to find some way .. that's all... i clearly could determine that the original problem that zombies don't shoot with the bow the problem was that the if cause isn't reached, so i did some testing (because i just didn't know what to do instead ..)
    i'll try with your orig code and IgnoreCase next

    Nope .. i'm sorry again ... they don't shoot .. i have tried with equalsignorecase and a second test with setting "melee" etc.. within quotation marks

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 30, 2016
  14. Offline

    stirante

    Delete "
    if (getEquipment(0).id == Material.POTION.getId() && !Potion.fromDamage(getEquipment(0).getData()).isSplash()) return ;
    ". If it's still not working try to do methods for setting ai of zombie. This way you would do zombie.setAI(MELEE) and set item in his hand to what you want to bow or potion.
     
  15. Offline

    Scipione

    just for me to understand better what your class is doing.
    you initially set ai to melee ..
    where does a bowman gehts "ranged" ?
    if i do a system.out.prinln of "ai" i only get "melee"
     
  16. Offline

    stirante

    If you want function that sets attack mode to ranged do sth like that:
    clearGoalSelector(goalSelector);
    addStandardGoals();
    this.goalSelector.a(4, new PathfinderGoalArrowAttack(this, 0.25F, 60, 10.0F));
     
  17. Offline

    Scipione

    Ok i found another Problem ...
    i did a printout of the getEquipment(0).id
    an a printout of Material.BOW.getId()
    Die zombie is definitely holding a bow, but the first printout gives me the id of a iron sword
     
  18. Offline

    stirante

    Honestly, I have no idea what is wrong. I only gave code which should enable that. Maybe just use your version of my class which was working. But this can cause mega lag.
     
  19. Offline

    Scipione

    well thanks anyways .. i will continue testing .. :)
     
Thread Status:
Not open for further replies.

Share This Page