Jump to content

[1.7.10] Multipart Mob Issues


ChaKha

Recommended Posts

Hello,

 

I've been making a few posts recently on the forums about small, random problems that have to do with a multipart mob I'm attempting to create. However, I've run into so many issues making it that I think my problem is that I don't fully understand how to make a multipart entity. I'm already fully aware of how to completely make a single entity (thanks to some Jabelar tutorials :P). So, here's what I'm hoping someone can provide: Is there any sort of tutorial or guidance on making a multipart entity besides studying the enderdragon code (as I've already done that)? I apologize if I'm simply missing some obvious facts, but I need help.

 

Thanks for reading at least!

Link to comment
Share on other sites

It is better to post or at least explain what you've tried when you're asking for help.

 

But here is some advice.

 

First off, the models for an entity are already multi-part (like it has head and body and legs and arms). So when someone asks about making a multi-part entity, what they mean is that they want a complex hitbox. Because as you probably know, the hitboxes for a single entity are quite limited. The hitbox for a single entity must be a rectangular box and the X and Z dimensions have to be the same and the hitbox size is limited by pathfinding algorithms. So I'll assume that what you want to do is have a complex multi-part hitbox for your entity.

 

Anyway, the way I would do it is actually make the "entity" made up of multiple actual entities. One part (like the body) could be considered the parent entity and could have the full entity model, and the rest of the entities would be invisible and will follow along. This means that whenever you construct the parent entity part, it will additionally create the other parts (in the correct relative positions). To create the parent/child relationship between the entities, you'll need a list in the parent entity that keeps track of the instances of the child entities, and each child entity should have a field that contains the parent entity. Then you'll have information in each part about the other parts, which you'll need for your logic of movement and damage.

 

Then, all you need to do is:

1) move everything together. You'd do this by moving the parent entity part and the onUpdate() method for that entity should also automatically update the other parts' positions (and maybe rotations).

2) decide how you want to handle damage. You might just want to have the damage in the parent entity reduced when any child entity is hit. Or depending on the mob, maybe you want each part to get damaged separately. In the first case, you'll have to handle the case when the child parts are hit and instead inflict the damage on the associated parent.

3) if parent part dies, make sure to kill of the child parts.

 

The only other tricky thing is pathfinding. There is a reason that the Ender Dragon is flying and also destroys blocks -- if it did not then the path finding would be very difficult -- imagine a complex entity trying to move around in a cave or forest. To make multi-part entity work with pathfinding is a tough coding challenge, so it is best if you can "cheat" -- like maybe allow it to move anywhere the parent part can go -- i.e. only do pathfinding on the parent part and don't worry if the rest of the parts overlap into blocks.

 

That's pretty much it! Hope it helps.

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

Link to comment
Share on other sites

It is better to post or at least explain what you've tried when you're asking for help.

Alright. So, all I have really tried so far is copy/editing code from the enderdragon and some other mods that included complex hitbox entities. If you want to see exactly what I've done, then here's the very incomplete entity class:

public class EntityNWPlantBossTendril extends EntityLiving implements IEntityMultiPart {

public double targetX;
    public double targetY;
    public double targetZ;
    private static int MAX_HEALTH = 50;
    private static float ARMOR_MULTIPLIER = 2F;
public Entity[] partArray;
public EntityDragonPart tendrilBase;
public EntityDragonPart tendrilPart1;
public EntityDragonPart tendrilPart2;
public EntityDragonPart tendrilPart3;
public EntityDragonPart tendrilPart4;
public EntityDragonPart tendrilPart5;
public EntityDragonPart tendrilPart6;
public EntityDragonPart tendrilPart7;
public EntityDragonPart tendrilPart8;
public EntityDragonPart tendrilPart9;
public EntityDragonPart tendrilPart10;
public EntityDragonPart tendrilPart11;
public EntityDragonPart tendrilPart12;
public EntityDragonPart tendrilPart13;
public EntityDragonPart tendrilPart14;
    public EntityNWPlantBoss master;

public EntityNWPlantBossTendril(World world) {
	super(world);
	partArray = (new Entity[] {
			tendrilBase = new EntityDragonPart(this, "base", 4F, 4F), tendrilPart1 = new EntityDragonPart(this, "tendrilPart1", 10.0F, 10.0F), tendrilPart2 = new EntityDragonPart(this, "tendrilPart2", 10.0F, 10.0F), tendrilPart3 = new EntityDragonPart(this, "tendrilPart3", 10.0F, 10.0F), tendrilPart4 = new EntityDragonPart(this, "tendrilPart4", 10.0F, 10.0F), tendrilPart5 = new EntityDragonPart(this, "tendrilPart5", 10.0F, 10.0F), tendrilPart6 = new EntityDragonPart(this, "tendrilPart6", 10.0F, 10.0F), tendrilPart7 = new EntityDragonPart(this, "tendrilPart7", 10.0F, 10.0F), tendrilPart8 = new EntityDragonPart(this, "tendrilPart8", 10.0F, 10.0F), tendrilPart9 = new EntityDragonPart(this, "tendrilPart9", 10.0F, 10.0F), tendrilPart10 = new EntityDragonPart(this, "tendrilPart10", 10.0F, 10.0F), tendrilPart11 = new EntityDragonPart(this, "tendrilPart11", 10.0F, 10.0F), tendrilPart12 = new EntityDragonPart(this, "tendrilPart12", 10.0F, 10.0F), tendrilPart13 = new EntityDragonPart(this, "tendrilPart13", 10.0F, 10.0F), tendrilPart14 = new EntityDragonPart(this, "tendrilPart14", 10.0F, 10.0F)
	});
	setSize(2.125F, 15.0F);
	ignoreFrustumCheck = true;
	experienceValue = 20;//WIP
}

public EntityNWPlantBossTendril(World world, double x, double y, double z) {
        this(world);
        setPosition(x, y, z);
    }

protected void applyEntityAttributes() {
	super.applyEntityAttributes();
	getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setBaseValue(1.0D);
	getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(MAX_HEALTH);
	getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.0D);
}

public void onLivingUpdate() {
	//WIP
    }

public EnumCreatureAttribute getCreatureAttribute() {
	return EnumCreatureAttribute.UNDEFINED;
}

protected void entityInit() {
        super.entityInit();
    }

public void writeToNBT(NBTTagCompound nbttagcompound) {
	super.writeToNBT(nbttagcompound);
}

public void readFromNBT(NBTTagCompound nbttagcompound) {
	super.readFromNBT(nbttagcompound);
}

public boolean attackEntityFromPart(EntityDragonPart dragonPart, DamageSource dmgSrc, float f0) {
	int armoredDamage = Math.round(f0 / ARMOR_MULTIPLIER);
	double range = calculateRange(dmgSrc);
	if (range > 14D)
            return false;
	else
            return superAttackFrom(dmgSrc, armoredDamage);
}

protected boolean superAttackFrom(DamageSource dmgSrc, float f0) {
        return super.attackEntityFrom(dmgSrc, f0);
    }

protected double calculateRange(DamageSource dmgSrc) {
        double range = -1D;
        if (dmgSrc.getEntity() != null) {
            range = getDistanceSqToEntity(dmgSrc.getEntity());
        }
        if (dmgSrc.getSourceOfDamage() != null) {
            range = getDistanceSqToEntity(dmgSrc.getSourceOfDamage());
        }
        return range;
    }

public boolean attackEntityFrom(DamageSource dmgSrc, float f0) {
        return false;
    }

public Entity[] getParts() {
        return partArray;
    }

public boolean isEntityInvulnerable() {
	return false;
}

public boolean canDespawn() {
	return false;
}

public void knockBack(Entity entity1, float f, double d2, double d3) {}

protected String getLivingSound() {
        return "mob.enderdragon.growl";//Test sounds
    }

protected String getHurtSound() {
        return "mob.enderdragon.hit";
    }

protected String getDeathSound() {
	return "mob.enderdragon.death";
}

protected float getSoundVolume() {
        return 2.0F;
    }

public void onDeath(DamageSource dmgSrc) {
        super.onDeath(dmgSrc);
    }

protected void dropFewItems(boolean par1, int par2) {
	//WIP
}

protected boolean isAIEnabled() {
	return false;
}

public boolean canBeCollidedWith() {
        return true;
    }

protected void onDeathUpdate() {
	//Death anim stuff here
}

public World func_82194_d() {
	return worldObj;
}
}

 

My first issue that I've come across is that whenever I attempt to attack my entity, it doesn't take damage. I realize I set the attackEntityFrom to return false, but I want to be able to tell which hitbox of the entity was hit (I assumed attackEntityFromPart would do the trick). How can I fix this?

 

I also have a question: does each sub-entity of the mob have to be my own custom part entity, or can they just be dragonParts?

 

You'd do this by moving the parent entity part and the onUpdate() method

Should I be using onUpdate() or onLivingUpdate()?

 

Finally, here is a picture of the mob (WIP). Each box is intended to have its own hitbox, including the parent entity on the bottom.

Link to comment
Share on other sites

Okay, so you can see that Entity Dragon pretty much takes the approach I mentioned -- when you construct the parent part, it creates an array of child parts which get the parent part passed to them.

 

A few comments:

- I don't think you need all those fields for each part. You already have an array of parts, so you shouldn't need a field for tendrilBase1, tendrilBase2, etc. I know that Ender Dragon does do that, but it isn't really needed -- you can reference tendrilBase3 with tendrilBase[3] and there is a string which can give the part a nice name if you need it.

- When you construct the parts, you have them all set the same size (hitbox size). Is that what you want? Your picture makes it look like they'll get smaller and smaller. In Ender Dragon, it set's the hitboxes later but you can set them right when you construct them in the array of parts.

 

Anyway, I think the main problem I see though is: where do you set the position of the parts? When you create them they'll be at 0, 0, 0 so you need to set their position. The Ender Dragon does this in the onLivingUpdate() method where you can see it has a for loop that goes through all the parts and updates their position.

 

Regarding using onUpdate() or onLivingUpdate(), there are lots of methods that are called every tick and that is the main thing that matters. onLivingUpdate() makes good logical sense, so use that.

 

By the way, you probably already know, but by pressing F3+B you can see all the entity bounding boxes. This would be useful while testing a multi-part entity.

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

Link to comment
Share on other sites

Why are you trying to make a multipart entity?  Hitboxes?

Correct.

Anyway, I think the main problem I see though is: where do you set the position of the parts? When you create them they'll be at 0, 0, 0 so you need to set their position. The Ender Dragon does this in the onLivingUpdate() method where you can see it has a for loop that goes through all the parts and updates their position.

Do you mean something like this:

public void onLivingUpdate() {
	int prevHeight = 0;
	for (int i = 0; i < partArray.length; i++) {
		partArray[i].setPosition(0, ((30 - (2 * i)) / 16) + prevHeight, 0);
		prevHeight = ((30 - (2 * i)) / 16) + prevHeight;
	}
    }

I ran the game with that bit of code in, but I still can't hit it correctly. Is there a way I could see the hitboxes so I can position them better, or is that not the issue?

Link to comment
Share on other sites

Is there a way I could see the hitboxes so I can position them better?

By the way, you probably already know, but by pressing F3+B you can see all the entity bounding boxes. This would be useful while testing a multi-part entity.

bounding boxes = hit boxes

Link to comment
Share on other sites

partArray[i].setPosition(0, ((30 - (2 * i)) / 16) + prevHeight, 0);

You set the position relative to 0,0,0. You need to set the relative to the main entity's coordinates (posX,posY,posZ).

Don't PM me with questions. They will be ignored! Make a thread on the appropriate board for support.

 

1.12 -> 1.13 primer by williewillus.

 

1.7.10 and older versions of Minecraft are no longer supported due to it's age! Update to the latest version for support.

 

http://www.howoldisminecraft1710.today/

Link to comment
Share on other sites

Alright, so I changed the code to:

double prevHeight = posY;
	for (int i = 0; i < partArray.length; i++) {
		partArray[i].setPosition(posX, ((30 - (2 * i)) / 16) + prevHeight, posZ);
		prevHeight = ((30 - (2 * i)) / 16) + prevHeight;
	}

However, nothing appears to have changed. Did I do it wrong?

Link to comment
Share on other sites

For debugging you should learn to use console statements. Just add System.out.println() type statements at each point in your code to print out stuff that helps you see what it is actually doing. So I would suggest printing out the positions in the loop that creates the parts.

 

It might also be helpful to have print statements inside the parts' code. Instead of using the built-in EntityDragonPart class, maybe copy that to your own class. Then you can do things like override methods and add print statements, or you can have it print out the position in the onUpdate() method, etc. You'll have more control with your own class.

 

Anyway, if you print out information that shows what your code is doing, you should be able to quickly figure out what is going wrong.

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.