Jump to content

[SOLVED-ish][1.7.x]Why is last attacker for EntityCow always null?


jabelar

Recommended Posts

I was just trying to use the LivingDropsEvent to check last attacker to affect some drops, and started by testing on killing a cow.  However, I noticed that the getLastAttacker() always returned null.  To speed up my testing I tried a similar thing on LivingHurtEvent and still the same.  Now I know the LivingHurtEvent has a DamageSource field and that works fine.  But the LivingDropsEvent doesn't seem to have that.  And I'm curious why the last attacker is null for the cow.

 

In my event handler I have simply:

    @SubscribeEvent(priority=EventPriority.NORMAL, receiveCanceled=true)
    public void onEvent(LivingHurtEvent event)
    {
    	// DEBUG
    	System.out.println("Entity hurt, entity = "+event.entityLiving.toString());
    	if (event.entityLiving.getLastAttacker() != null)
    	{
        	System.out.println("Last attacker was "+event.entityLiving.getLastAttacker().toString());
    	}
        else
    	{
        	System.out.println("Last attacker was null");
    	}

 

Then I run it and attack some cows.  The console output happily shows this event being triggered indicating the cow entity at the location I'm attacking, but it always says the last attacker was null, even after multiple attacks (I thought perhaps on first attack it might not be set yet).

 

If I switch to checking event.source, it correctly finds that the player was attacking.  But again I want to detect last attacker in a different event that doesn't have source field, plus I'm curious about the issue.

 

Last attacker always null: what's up with that?

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

For some reason that field is not used inside of EntityLivingBase, but may be elsewhere through the use of EntityLivingBase#setLastAttacker.

 

However, the revenge target IS set, for whatever reason:

 

// in EntityLivingBase#attackEntityFrom:
Entity entity = par1DamageSource.getEntity();

                if (entity != null)
                {
                    if (entity instanceof EntityLivingBase)
                    {
                        this.setRevengeTarget((EntityLivingBase)entity);
                    }

                    if (entity instanceof EntityPlayer)
                    {
                        this.recentlyHit = 100;
                        this.attackingPlayer = (EntityPlayer)entity;
                    }
                    else if (entity instanceof EntityWolf)
                    {
                        EntityWolf entitywolf = (EntityWolf)entity;

                        if (entitywolf.isTamed())
                        {
                            this.recentlyHit = 100;
                            this.attackingPlayer = null;
                        }
                    }
                }

 

 

Try using that field instead and see if you get better results.

Link to comment
Share on other sites

Thanks coolalias for tracing it.  Yeah it seems dumb that it isn't overridden in every entity.  Probably only used in mobs that have retaliation AI or something.

 

I guess I can create an extended property to EntityAnimal to create my own tracking of last attacker, but seems like bit of a pain. Or maybe I can set it in event handler for living hurt. Have to consider this further...

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

You should be able just to use the EntityLivingBase#entityLivingToAttack field directly, well, through the getAITarget() method anyway (which simply returns that field). This field is set for every EntityLivingBase that doesn't completely override attackEntityFrom (i.e. by not calling 'super.attackEntityFrom'), so it should work even for passive mobs without you having to do any extra work. Should. With Minecraft, sometimes it can be difficult to know until you try ;)

Link to comment
Share on other sites

You should be able just to use the EntityLivingBase#entityLivingToAttack field directly, well, through the getAITarget() method anyway (which simply returns that field). This field is set for every EntityLivingBase that doesn't completely override attackEntityFrom (i.e. by not calling 'super.attackEntityFrom'), so it should work even for passive mobs without you having to do any extra work. Should. With Minecraft, sometimes it can be difficult to know until you try ;)

 

I'm not sure this is the right field.  This is field to attack, but I want the field of the last attacker (i.e. what attacked it last).  Also, entityLivingToAttack field is private, although I guess you're saying I can access it by virtue of being passed through the public method getAITarget().  But again I don't want the attack target, I want the entity that last attacked.

 

I think I can just set lastAttacker field myself.  I'm thinking that in LivingHurtEvent I can check if it is null, and if not I can set it to the damage source entity.  I'm going to try that and will report back.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

I'm not sure this is the right field.  This is field to attack, but I want the field of the last attacker (i.e. what attacked it last).  Also, entityLivingToAttack field is private, although I guess you're saying I can access it by virtue of being passed through the public method getAITarget().  But again I don't want the attack target, I want the entity that last attacked.

 

I think I can just set lastAttacker field myself.  I'm thinking that in LivingHurtEvent I can check if it is null, and if not I can set it to the damage source entity.  I'm going to try that and will report back.

That's just it, though: for passive mobs that never have an attack target, the 'entity to attack' should be the one that attacked them last (as seen in the attackEntityFrom method), from which they are now running away, assuming that there is no further logic in the sub-classes that nullifies that assumption.

 

Getting that field from getAITarget should work, but I suppose you could set the lastAttacker manually if you are really set on that specific field :P

Link to comment
Share on other sites

Use this for living drops event: event.source.getEntity().  (source is the damage source)

 

Hmmm, weird.  I thought I looked for that because I'm quite aware of it for LivingHurtEvent, but guess I missed it in LivingDropsEvent.  Yeah, that helps me do what I want.

 

I still think it is weird that getLastAttacker() is useless for animal entities, but oh well -- part of the fun of modding is building up knowledge of these peculiarities.  One thing I've definitely learned is you can't just trust the name of a method, but you really have to trace back its source to ensure it is doing what you want.  Another example that really screwed me up for a while is that there is a method in EntityLivingBase called isClientWorld() that mistakenly returns true for server world -- that took me a while to debug ...  I like hunting for useful methods, but there are some that are definitely misnamed.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

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



×
×
  • Create New...

Important Information

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