Skip to content

Commit

Permalink
Fixed sheep wool color and sheared state persistence issues (PowerNuk…
Browse files Browse the repository at this point in the history
…kitX#1434)

* Fixed Sheep color and sheared state persistence issues.
* Expanded sheared and color2 into EntityComponents with respective memories.
* Fixed dispensers damaging shears when shearing did not succeed.
  • Loading branch information
WWMB authored Dec 9, 2023
1 parent 10c247c commit 386f821
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 62 deletions.
9 changes: 4 additions & 5 deletions src/main/java/cn/nukkit/dispenser/ShearsDispenseBehavior.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ public Item dispense(BlockDispenser block, BlockFace face, Item item) {
1, 1, 1)
.offset(target.x, target.y, target.z);
for (var entity : block.level.getCollidingEntities(bb)) {
if (entity instanceof EntityShearable shearable) {
shearable.shear();
item.useOn(entity);
return item.getDamage() >= item.getMaxDurability() ? null : item;
}
if (!(entity instanceof EntityShearable shearable)) { continue; }
if (!shearable.shear()) { continue; }
item.useOn(entity);
return item.getDamage() >= item.getMaxDurability() ? null : item;
}
return item;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/cn/nukkit/entity/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,6 @@ public abstract class Entity extends Location implements Metadatable {
protected final Map<Integer, Effect> effects = new ConcurrentHashMap<>();
protected final EntityMetadata dataProperties = new EntityMetadata()
.putLong(DATA_FLAGS, 0)
.putByte(DATA_COLOR, 0)
.putShort(DATA_AIR, 400)
.putShort(DATA_MAX_AIR, 400)
.putString(DATA_NAMETAG, "")
Expand Down Expand Up @@ -1306,7 +1305,8 @@ protected final void init(FullChunk chunk, CompoundTag nbt) {
}
this.scale = this.namedTag.getFloat("Scale");
this.setDataProperty(new FloatEntityData(DATA_SCALE, scale), false);
this.setDataProperty(new ByteEntityData(DATA_COLOR, 0), false);

//Color is handled via EntityColor

try {
this.chunk.addEntity(this);
Expand Down
26 changes: 23 additions & 3 deletions src/main/java/cn/nukkit/entity/EntityColor.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,38 @@
import cn.nukkit.entity.ai.memory.CoreMemoryTypes;
import cn.nukkit.utils.DyeColor;

import java.util.concurrent.ThreadLocalRandom;

@PowerNukkitXOnly
@Since("1.19.80-r3")
public interface EntityColor extends EntityComponent {
default void setColor(DyeColor color) {
getMemoryStorage().put(CoreMemoryTypes.COLOUR, Integer.valueOf(color.getWoolData()).byteValue());
getMemoryStorage().put(CoreMemoryTypes.COLOR, Integer.valueOf(color.getWoolData()).byteValue());
}

default void setColor2(DyeColor color) {
getMemoryStorage().put(CoreMemoryTypes.COLOR2, Integer.valueOf(color.getWoolData()).byteValue());
}

default DyeColor getColor() {
return DyeColor.getByWoolData(getMemoryStorage().get(CoreMemoryTypes.COLOUR).intValue());
return DyeColor.getByWoolData(getMemoryStorage().get(CoreMemoryTypes.COLOR).intValue());
}

default DyeColor getColor2() {
return DyeColor.getByWoolData(getMemoryStorage().get(CoreMemoryTypes.COLOR2).intValue());
}

default boolean hasColor() {
return getMemoryStorage().notEmpty(CoreMemoryTypes.COLOUR);
return getMemoryStorage().notEmpty(CoreMemoryTypes.COLOR);
}
default boolean hasColor2() {
return getMemoryStorage().notEmpty(CoreMemoryTypes.COLOR2);
}

default DyeColor getRandomColor() {
ThreadLocalRandom random = ThreadLocalRandom.current();
DyeColor[] colors = DyeColor.values();
int c = random.nextInt(colors.length);
return colors[c];
}
}
23 changes: 20 additions & 3 deletions src/main/java/cn/nukkit/entity/EntityShearable.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,34 @@

import cn.nukkit.api.PowerNukkitXOnly;
import cn.nukkit.api.Since;
import cn.nukkit.entity.ai.memory.CoreMemoryTypes;

/**
* 实体可剪切<p/>
* 例如羊就可被剪羊毛<p/>
* 若作用于此实体的物品的isShears()为true,则将会调用此方法
* <br>
* Entities that can be sheared. Stores value with {@link CoreMemoryTypes#IS_SHEARED}
*/
@PowerNukkitXOnly
@Since("1.19.60-r1")
public interface EntityShearable {
public interface EntityShearable extends EntityComponent {
/**
* @return 此次操作是否有效。若有效,将会减少物品耐久
* @return 此次操作是否有效。若有效,将会减少物品耐久 true if shearing succeeded.
*/
boolean shear();
default boolean shear() {
if (this.isSheared() || (this.asEntity() instanceof EntityAgeable age && age.isBaby())) {
return false;
}
this.setIsSheared(true);
return true;
}

default boolean isSheared() {
return getMemoryStorage().get(CoreMemoryTypes.IS_SHEARED);
}

default void setIsSheared(boolean isSheared) {
getMemoryStorage().put(CoreMemoryTypes.IS_SHEARED, isSheared);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ public boolean execute(EntityIntelligent entity) {
}
}
if (entity instanceof EntitySheep sheep) {
if (sheep.sheared) {
if (sheep.isSheared()) {
sheep.growWool();
return false;
}
if (sheep.isBaby())
if (sheep.isBaby()) //TODO: Accelerated growth instead of instant growth
sheep.setBaby(false);
}
return false;
Expand Down
32 changes: 28 additions & 4 deletions src/main/java/cn/nukkit/entity/ai/memory/CoreMemoryTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -245,17 +245,41 @@ public interface CoreMemoryTypes {
})
);
/**
* 代表实体的颜色,和{@link Entity#DATA_COLOUR}绑定
* <p>
* 代表实体的颜色,和{@link Entity#DATA_COLOR}绑定
* <br>
* 例如狼的项圈
* <br>
* Wolf collar, Cat collar, Sheep wool, Tropical Fish base color
*/
MemoryType<Byte> COLOUR = new MemoryType<Byte>("minecraft:colour")
MemoryType<Byte> COLOR = new MemoryType<Byte>("minecraft:color")
.withCodec(new NumberMemoryCodec<Byte>("Color")
.onInit((data, entity) -> {
if (data != null) {
entity.setDataProperty(new ByteEntityData(Entity.DATA_COLOUR, data));
entity.setDataProperty(new ByteEntityData(Entity.DATA_COLOR, data));
}
})
);
/**
* 代表实体的颜第二色,和{@link Entity#DATA_COLOR_2}绑定
* <br>
* Tropical Fish secondary color
*/
MemoryType<Byte> COLOR2 = new MemoryType<Byte>("minecraft:color2")
.withCodec(new NumberMemoryCodec<Byte>("Color2")
.onInit((data, entity) -> {
if (data != null) {
entity.setDataProperty(new ByteEntityData(Entity.DATA_COLOR_2, data));
}
})
);
/**
* 和{@link Entity#DATA_FLAG_SHEARED}绑定
* <p>
* Sheep, Snow Golem
*/
MemoryType<Boolean> IS_SHEARED= new MemoryType<>("minecraft:is_sheared", false)
.withCodec(new BooleanMemoryCodec("Sheared")
.onInit((data, entity) -> entity.setDataFlag(Entity.DATA_FLAGS, Entity.DATA_FLAG_SHEARED, data))
);
// endregion
}
68 changes: 25 additions & 43 deletions src/main/java/cn/nukkit/entity/passive/EntitySheep.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import cn.nukkit.api.PowerNukkitOnly;
import cn.nukkit.api.Since;
import cn.nukkit.block.Block;
import cn.nukkit.entity.EntityColor;
import cn.nukkit.entity.EntityShearable;
import cn.nukkit.entity.EntityWalkable;
import cn.nukkit.entity.ai.behavior.Behavior;
Expand All @@ -22,7 +23,6 @@
import cn.nukkit.entity.ai.route.posevaluator.WalkingPosEvaluator;
import cn.nukkit.entity.ai.sensor.NearestFeedingPlayerSensor;
import cn.nukkit.entity.ai.sensor.NearestPlayerSensor;
import cn.nukkit.entity.data.ByteEntityData;
import cn.nukkit.event.entity.EntityDamageByEntityEvent;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemDye;
Expand All @@ -40,11 +40,9 @@
/**
* @author BeYkeRYkt (Nukkit Project)
*/
public class EntitySheep extends EntityAnimal implements EntityWalkable, EntityShearable {
public class EntitySheep extends EntityAnimal implements EntityWalkable, EntityShearable, EntityColor {

public static final int NETWORK_ID = 13;
public boolean sheared = false;
public int color = 0;

public EntitySheep(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
Expand Down Expand Up @@ -129,26 +127,15 @@ public void initEntity() {
super.initEntity();

if (!this.namedTag.contains("Color")) {
this.setColor(randomColor());
} else {
this.setColor(this.namedTag.getByte("Color"));
this.setColor(this.getRandomColor());
}

if (!this.namedTag.contains("Sheared")) {
this.namedTag.putByte("Sheared", 0);
} else {
this.sheared = this.namedTag.getBoolean("Sheared");
}

this.setDataFlag(DATA_FLAGS, DATA_FLAG_SHEARED, this.sheared);
//Color loaded by memory if it exists
//Sheared loaded by memory if it exists
}

@Override
public void saveNBT() {
super.saveNBT();

this.namedTag.putByte("Color", this.color);
this.namedTag.putBoolean("Sheared", this.sheared);
}

@Override
Expand All @@ -158,63 +145,58 @@ public boolean onInteract(Player player, Item item, Vector3 clickedPos) {
}

if (item instanceof ItemDye) {
this.setColor(((ItemDye) item).getDyeColor().getWoolData());
this.setColor(((ItemDye) item).getDyeColor());
return true;
}

return item.isShears() && shear();
}

public boolean shear() {
if (sheared || this.isBaby()) {
return false;
}
if(!EntityShearable.super.shear()) { return false; }

this.sheared = true;
this.setDataFlag(DATA_FLAGS, DATA_FLAG_SHEARED, true);

this.level.dropItem(this, Item.get(Item.WOOL, getColor(), ThreadLocalRandom.current().nextInt(2) + 1));
this.level.dropItem(this, Item.get(Item.WOOL, getColor().getWoolData(),
ThreadLocalRandom.current().nextInt(2) + 1));

level.addSound(this, Sound.MOB_SHEEP_SHEAR);
level.getVibrationManager().callVibrationEvent(new VibrationEvent(this, this.clone(), VibrationType.SHEAR));
return true;
}

public void growWool() {
this.setDataFlag(DATA_FLAGS, DATA_FLAG_SHEARED, false);
this.sheared = false;
this.setIsSheared(false);
}

@Override
public Item[] getDrops() {
if (this.lastDamageCause instanceof EntityDamageByEntityEvent) {
return new Item[]{Item.get(((this.isOnFire()) ? Item.COOKED_MUTTON : Item.RAW_MUTTON)), Item.get(Item.WOOL, getColor(), 1)};
return new Item[]{
Item.get(((this.isOnFire()) ? Item.COOKED_MUTTON : Item.RAW_MUTTON)), //TODO: looting
Item.get(Item.WOOL, getColor().getWoolData(), 1)
};
}
return Item.EMPTY_ARRAY;
}

public int getColor() {
return namedTag.getByte("Color");
}

public void setColor(int color) {
this.color = color;
this.setDataProperty(new ByteEntityData(DATA_COLOUR, color));
this.namedTag.putByte("Color", this.color);
}

private int randomColor() {
public DyeColor getRandomColor() {
ThreadLocalRandom random = ThreadLocalRandom.current();
//TODO: Improve random item selection and make use of it here
//White: 81836
//Black: 5000
//Light Gray: 5000
//Gray: 5000
//Brown: 3000 currently missing
//Pink: 164
double rand = random.nextDouble(1, 100);

if (rand <= 0.164) {
return DyeColor.PINK.getWoolData();
return DyeColor.PINK;
}

if (rand <= 15) {
return random.nextBoolean() ? DyeColor.BLACK.getWoolData() : random.nextBoolean() ? DyeColor.GRAY.getWoolData() : DyeColor.LIGHT_GRAY.getWoolData();
return random.nextBoolean() ? DyeColor.BLACK : random.nextBoolean() ? DyeColor.GRAY : DyeColor.LIGHT_GRAY;
}

return DyeColor.WHITE.getWoolData();
return DyeColor.WHITE;
}
}

0 comments on commit 386f821

Please sign in to comment.