0

TLDR: I pass data into the instantiation of a mob. Part of that instantiation proccess involves instantiating AI for the mob. I want the data I pass into the mob to be passed into the AI for the mob. This is difficult because of a forced Super() call among other things.

The story: I am creating my own version of Monsters inside a game.

I have coded myself pathFinders (AI goals that entities use to navigate the world). Then I took an existing mob (in this case a cow) and erased its normal AI and replaced it with my own: PathFinderGoalRpgMeleeAttack.

I would like to create new instances of my cow (i.e. RpgCow rpgCow = new rpgCow(damage,knockback)).

The issue is that when I instantiate a new mob (which extends another class remember) I am forced to call super(). The super method calls method r() (which I override to add my own AI). Since the super() calls r(), the values of damage and knockback are still 0.0 and 0.0 (default java values for double) since damage and knockback have not had a chance to be set yet.

Any thoughts on how I can get around this and insert the data I pass into the cow creation into the PathFinder creation?

I have heard abstraction or reflection might be a good thing to look into, but just reading documentation on the two subjects, neither seems like it would help me.

EDIT, cause I guess I wasn't that clear: The issue is that the values of Damage and Knockback are still 0 when I pass them into the PathFinder, which means later on when I do this.getDamage in my pathFinder, regardless of what values I passed into the mob on creation the damage and knockback will always be 0.

Code:

public class RpgCow extends EntityCow
{
    private double damage;
    private double knockback;

    public RpgCow(World world, double damage, double knockback)
    {
        super(world);
        this.damage = damage;
        this.knockback = knockback;
        this.bukkitEntity = new CraftRpgCow(this.world.getServer(), this);
    }

    @Override
    protected void r()
    {
        this.goalSelector.a(3,new PathFinderGoalRpgMeleeAttack(this,damage,knockback));
    }

    private static class CraftRpgCow extends CraftCow
    {

        private CraftRpgCow(CraftServer server, EntityCow parent)
        {
            super(server, parent);
        }

    }
}
SirLich
  • 79
  • 1
  • 12
  • 2
    Ah, the good old [overridable method call in constructor anti-pattern](https://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors). Not a lot you can do if you cannot change `EntityCow`. – Boris the Spider Nov 30 '17 at 19:38
  • 1
    @RAZ_Muh_Taz how do you propose calling `set`? If this happens in the superclass _constructor_ there is nothing you can do first. – Boris the Spider Nov 30 '17 at 19:39
  • 1
    Well, if `r()` has already been called then it's not going to do much. That's my point. – Boris the Spider Nov 30 '17 at 19:42
  • @RAZ_Muh_Taz it should not be assumed Damage and Knockback exist in EntityCow. I used those as examples, but there are other places I want to use this method. For example an AI goal WanderInsideThisArea would require passing in a Location. – SirLich Nov 30 '17 at 19:48
  • @BoristheSpider Do you think it would be possible to get the "parent" Entity from a PathFinder? I could just store the information in the Mob itself, then have the PathFinders call something like getParent.getDamage? – SirLich Nov 30 '17 at 19:49
  • I would say, and this is always a good option in any case - prefer composition over inheritance. Is there some `interface` you can `implements` rather than extending from that `class`? – Boris the Spider Dec 01 '17 at 07:31

1 Answers1

1

If you make the method definition of r() abstract in the super class, then any call of r() will use the instance's implementation of it, even it it is being called with in the super class's constructor.

This is probably a better solution than calling r() in the constructor of the child class (RpgCow)after the super() call, which, depending on your implementation, should overwrite the `cow.goalselector' that was set in your super() call.

This will require you to have the super class be abstract, but as you are programming a game with entities/mobs, having an abstract base class is a functionality you probably want anyways.

EDIT: Another way to go about this would to be to pass the AI you want into the constructor of the super class and into r(), and if it's null, do the base class's functionality, else do assign the new AI. Interfaces would be a handy way to polymorphise the AI objects.

  • Thanks for your answer! I guess the issue here is I don't have access to the super class. Since I am working with a 3rd party API, only things I am personally adding can be easily edited. Back to the drawing board... would reflection be any help? – SirLich Dec 01 '17 at 05:59
  • In that case, it looks like your best approach would be to overwrite what the super class instantiates the AI to be using your own constructor. As for reflection, it should probably be the last thing you try, since Java's static typing system isn't really designed to support it unless the object conforms to a known interface. However, using Type Introspection (instanceof) is a viable way of calling methods on an object when you don't necessarilly know it's typing. – Friedrich Kaiser Dec 03 '17 at 02:12