Jump to content

Recommended Posts

Posted

Hi everyone,

 

I am working on a  tameable mob that uses a stats container class and a stats calculation class to determine its stats. Currently I have the tameable bit down I use a custom system and the stats are generated and stored per world where each instance of my mob has a unique version of the stats due to my implementation of a "genes" factor.

 

Given this structure, I was wondering how could I use it to alter the entities properties. If you look at the read/write EntityFromNBT methods contained in my entity class I want to use the stats that have been saved for each instance of my mob to alter the mobs movement speed, armour value, hp, exp, and also want to implement custom stuff like stamina bars for my mob (I already know how to add a stamina bar to a player using IExtendedPlayerProperties). Is there a way to use my stats system to affect the mobs "minecraft stats" so that movement speed, armour value, hp, exp etc are set to my values? To start off I am just trying to make my calculated HP be the actual mobs HP at the moment. Do I have to use IExtendedEntityProperties or is there another way I could implement this since my stats are already being saved to NBT per each instance of the mob?

 

My Entity Class

 

 

public class EntityMyMob extends EntityAnimal {

//IDs for DataWatcher
private static final int ownerDataID = 25;
private static final int hpDataID = 26;
private static final int levelDataID = 27;


public int registryID;

//initialize stats variable so that we can create a unique instance of the MyMobStats class for each MyMob object
public MyMobStats stats = new MyMobStats();
//private boolean baseStatsSaved = false;

public EntityMyMob(World world) {
	/**
	 * gets the texture, sets hitbox size, movement speed, and gets the AI's, get stats for each mob here
	 */
	super(world);
	this.setSize(0.9F, 0.9F); // hitbox size?
	setupAI();
	if(!world.isRemote){
		this.addBaseStatsInformation(this.getEntityData());
	}
}

public int getRegistryID(){
	return MyMobRegistry.getRegistryID(this.getClass());
}

protected void entityInit() {
	super.entityInit();
	this.dataWatcher.addObject(ownerDataID, "");
	this.dataWatcher.addObject(hpDataID, 100);
	this.dataWatcher.addObject(levelDataID, 20);
}

// set up AI tasks
protected void setupAI()
{	
	this.getNavigator().setAvoidsWater(true);
	clearAITasks(); // clear any tasks assigned in super classes
	double speed = 0.5F;
	//this.tasks.addTask(0, new EntityAISwimming(this));
	this.tasks.addTask(1, new EntityAIMyMobWander(this, speed));
	this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityPlayer.class, 12.0F));
	this.tasks.addTask(3, new EntityAIPanic(this, speed)); // speed is the second parameter
	this.tasks.addTask(4, new EntityAILookIdle(this));
	this.tasks.addTask(5, new EntityAIWatchClosest(this, EntityMyMob.class, 6.0F));
}
// clears AI Task list in super class
protected void clearAITasks()
{
	tasks.taskEntries.clear();
	targetTasks.taskEntries.clear();
}

// Returns true if the newer Entity AI code should be run
@Override
public boolean isAIEnabled() {
	return true;
}

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


@Override
public String getCustomNameTag(){
	if(this.getOwnerName().length() > 0){
		return this.getOwnerName() + "'s " + this.getName();
	}
	return "";
}

//Add a textbox on top of mob if it has a ownerr
public boolean hasCustomNameTag(){
	return this.getOwnerName().length() > 0;
	//return true;
}

@Override
public boolean getAlwaysRenderNameTag(){
	return this.dataWatcher.getWatchableObjectByte(11) == 1;
}

public void setNameTagVisible(boolean visible){
	this.dataWatcher.updateObject(11, (byte)(visible ? 1 : 0));
}

public String getName(){
	return this.getEntityString();
}

public String getOwnerName(){
	return this.dataWatcher.getWatchableObjectString(this.ownerDataID);
}

public EntityLivingBase getOwner(){
	return this.worldObj.getPlayerEntityByName(this.getOwnerName());
}

public void setOwner(String ownerName){
	this.dataWatcher.updateObject(this.ownerDataID,ownerName);
}

public int getHP(){
	return stats.hp;
	//return this.dataWatcher.getWatchableObjectInt(this.hpDataID);
}

public void setHP(int hp){
	stats.hp = hp;
	//this.dataWatcher.updateObject(this.hpDataID, hp);
}


public int getLevel(){
	return this.dataWatcher.getWatchableObjectInt(this.levelDataID);
}

public void setLevel(int level){
	this.dataWatcher.updateObject(this.levelDataID, level);
}

@Override
public void readEntityFromNBT(NBTTagCompound nbtCompound)
{   
	super.readEntityFromNBT(nbtCompound);
	nbtCompound.getString("MobName");
	stats.hp = nbtCompound.getInteger("BaseHP");
	stats.att = nbtCompound.getInteger("BaseAtt");
	stats.def = nbtCompound.getInteger("BaseDef");
	stats.spAtt = nbtCompound.getInteger("BaseSpAtt");
	stats.spDef = nbtCompound.getInteger("BaseSpDef");
	stats.spd = nbtCompound.getInteger("BaseSpd");
}

@Override
public void writeEntityToNBT(NBTTagCompound nbtCompound)
{ 
	super.writeEntityToNBT(nbtCompound);
	//nbtCompound.setBoolean("baseStatsSaved",baseStatsSaved);
	nbtCompound.setString("MobName", EntityList.getEntityString(this));
	nbtCompound.setInteger("BaseHP",stats.hp);
	nbtCompound.setInteger("BaseAtt",stats.att);
	nbtCompound.setInteger("BaseDef",stats.def);
	nbtCompound.setInteger("BaseSpAtt",stats.spAtt);
	nbtCompound.setInteger("BaseSpDef",stats.spDef);
	nbtCompound.setInteger("BaseSpd",stats.spd);
}

public void addBaseStatsInformation(NBTTagCompound nbtCompound){

	String MobName = getName();
	CalcMyMobStats statCalculator = CalcMyMobStats.getInstance();
	statCalculator.generateRandomGenes(stats);
	stats.hp = statCalculator.getBaseHP(MobName) + stats.Genes_HP;
	stats.att = statCalculator.getBaseAttack(MobName) + stats.Genes_Att;
	stats.def = statCalculator.getBaseDefense(MobName) + stats.Genes_Def;
	stats.spAtt = statCalculator.getBaseSpecialAttack(MobName) + stats.Genes_SpAtt;
	stats.spDef = statCalculator.getBaseSpecialDefense(MobName) + stats.Genes_SpDef;
	stats.spd = statCalculator.getBaseSpeed(MobName) +  + stats.Genes_Spd;
}


@Override
public EntityMyMob createChild(EntityAgeable entityageable) {
	return new EntityMyMob(this.worldObj);
}

@Override
/**
 * handles entity death timer, experience orb and particle creation and I think it also despawns the entity after its death animation is played
 */
protected void onDeathUpdate()
{
	++this.deathTime;	//important. Do not remove. Used elsewhere in death animations
	//this.setDead(); //<- commenting this line out results in any mob that dies to fall on its side to the ground and not despawn
}

@Override
public void onDeath(DamageSource p_70645_1_){ 
	//This is called when an entities hp reaches 0, we need to add stuff here to control what happens when a mob's hp reaches 0
}

}

 

 

 

My Stats Container class

 

 

//container that holds stats of a mob
public class MyMobStats {
//TODO: need a way to serialize this structure into a string
//and restore it!
public int registryID;

//current stats
public int level;

public int hp, currentHp;
public int att, currentAtt;
public int def, currentDef;
public int spAtt, currentSpAtt;
public int spDef, currentSpDef;
public int spd, currentSpd;

//random number to be added to base stats to induce mob genetics where some mobs will be stronger than others. These should be initialized when the mob is created, and should
//never change.
public int Genes_HP;
public int Genes_Att;
public int Genes_Def;
public int Genes_SpAtt;
public int Genes_SpDef;
public int Genes_Spd;

//total experience gained from fighting
public int exp;
	}

 

 

 

Posted

I wouldn't call few fields packed into object a "system".

Just to make sure you know:

IExtendedEntityProperies is for actual EXTENDED properties, not basic properties. Unless you want to have all mobs have stats (or you just want to have neat code) you wont be using IEEP to code your OWN mobs.

 

As long as mob is your class you can put all stuff in there.

 

As to how vanilla handles attributes:

SharedMonsterAttributes is a value assigned to each Entity.class (living) and defines ALL basic stuff for all mobs of this type.

Then there are AttributeModifiers.

With those you can pretty much do something like this:

Use update() method, get entity.stats.extraHp and add Modifier for current tick for hp to be BaseHP+YourExtraHp.

 

Mobs don't have stamina, you would have to make your new AI that will use it.

Everything that vanilla has can be manipulated with modifiers.

 

I'd go even further and nuse WorlTickEvent.PRE to make calculations in pre-tick. But that is a wider-look at topic.

1.7.10 is no longer supported by forge, you are on your own.

Posted

Do you recommend then to create my own way to determine each of my mobs actual stats for example Spd by altering fields in the Entity class such as motionX, motionY, motionZ. Atm this value is just a calculated value that is stored in NBT data for each entity and when a player tames the mob the item used to tame the mob stores the instance of the mob and lets the player release and recapture the mob at a whim where the stats are saved preserved throughout this process. I guess my question is how would I alter the actual HP of the mob that was assigned by vanilla to equal the value that I calculated (to replace the default HP value that vanilla gives my mob (i think it is 20 but i am not sure). Currently my stats.hp doesnt link with the vanilla hp so all instances of my mob have the same hp when I hit them they die with the same number of hits and Im not sure how this works in Vanilla

 

 

Posted

The entity classes have writeEntityToNBT() and readEntityToNBT() methods.  So for your own custom entities you can use those to store custom per-entity information.

 

IExtendedEntityProperties works similarly, but is really more useful for adding properties to vanilla enitities since you already have the above methods available in your own custom entities.

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

Posted

I just literally told you :P

 

Base value of health is defined FOR ALL mobs that are using given class by SharedMonsterAttribute.maxHealth (might not be exact name).

 

You could actually change it, but I don't recommend it (using modifiers to add +x value is better since it auto-synchronizes)

 

    public void setEntityHealthPastMax(EntityLivingBase entity, float amount)
    {
        entity.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(amount);
        entity.setHealth(amount);
        instance.sendHealthPacket(entity, amount);
    }

 

sendHealthPacket is my method to update client-side data about entitys health.

 

Note that this custom health needs to be stored SEPARATELY and saved as other value than vanilla health and then loaded after mob is constructed. Basically you need to get your health value and set vanilla to yours everytime you want your entity to have not-basic one defined by parent class. That also requres you to send packet to client (every damn time).

 

I am not saying that this is too hard for you, but certainly coding advanced tracking systems is hard.

1.7.10 is no longer supported by forge, you are on your own.

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

    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
  • Topics

  • Who's Online (See full list)

    • There are no registered users currently online
×
×
  • Create New...

Important Information

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