diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java index ea786130..7af2545c 100644 --- a/src/main/java/org/bukkit/UnsafeValues.java +++ b/src/main/java/org/bukkit/UnsafeValues.java @@ -10,6 +10,7 @@ import org.bukkit.damage.DamageEffect; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; import org.bukkit.entity.EntityType; +import org.bukkit.entity.Villager; import org.bukkit.inventory.CreativeCategory; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -137,4 +138,10 @@ public interface UnsafeValues { @ApiStatus.Internal Biome getCustomBiome(); + + @ApiStatus.Internal + Villager.ReputationType createReputationType(String key); + + @ApiStatus.Internal + Villager.ReputationEvent createReputationEvent(String key); } diff --git a/src/main/java/org/bukkit/entity/Villager.java b/src/main/java/org/bukkit/entity/Villager.java index c48f13cc..b67a4750 100644 --- a/src/main/java/org/bukkit/entity/Villager.java +++ b/src/main/java/org/bukkit/entity/Villager.java @@ -3,6 +3,8 @@ package org.bukkit.entity; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import java.util.Locale; +import java.util.UUID; +import org.bukkit.Bukkit; import org.bukkit.Keyed; import org.bukkit.Location; import org.bukkit.NamespacedKey; @@ -118,6 +120,169 @@ public interface Villager extends AbstractVillager { @Nullable public ZombieVillager zombify(); + /** + * Gets the reputation of an entity for a given type. + * + * @param uuid the UUID of the entity whose reputation is being checked + * @param reputationType reputation type to be retrieved + * @return current reputation for the given reputation type + */ + public int getReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType); + + /** + * Gets the weighted reputation of an entity for a given type. + * + *
The total reputation of an entity is a sum of its weighted + * reputations of each type, where the reputation is multiplied by weight + * assigned to its type. + * + * @param uuid the UUID of the entity whose reputation is being checked + * @param reputationType reputation type to be retrieved + * @return current reputation for the given reputation type + * @see ReputationType#getWeight() + */ + public int getWeightedReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType); + + /** + * Gets the reputation of an entity. + * + * @param uuid the UUID of the entity whose reputation is being checked + * @return current reputation for the given reputation type + */ + public int getReputation(@NotNull UUID uuid); + + /** + * Add reputation of a given type towards a given entity. + * + *
The final value will be clamped to the maximum value supported by the + * provided reputation type. If the final value is below the reputation + * discard threshold, gossip associated with this reputation type will be + * removed. + * + *
Note: this will fire a + * {@link org.bukkit.event.entity.VillagerReputationChangeEvent}. + * + * @param uuid the UUID of the entity for whom the reputation is being + * added + * @param reputationType reputation type to be modified + * @param amount amount of reputation to add + */ + public void addReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType, int amount); + + /** + * Add reputation of a given type towards a given entity, with a specific + * change reason. + * + *
The final value will be clamped to the maximum value supported by the + * provided reputation type. If the final value is below the reputation + * discard threshold, gossip associated with this reputation type will be + * removed. + * + *
Note: this will fire a + * {@link org.bukkit.event.entity.VillagerReputationChangeEvent}. + * + * @param uuid the UUID of the entity for whom the reputation is being + * added + * @param reputationType reputation type to be modified + * @param amount amount of reputation to add + * @param changeReason reputation change reason + */ + public void addReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType, int amount, @NotNull ReputationEvent changeReason); + + /** + * Remove reputation of a given type towards a given entity. + * + *
The final value will be clamped to the maximum value supported by the + * provided reputation type. If the final value is below the reputation + * discard threshold, gossip associated with this reputation type will be + * removed. + * + *
Note: this will fire a + * {@link org.bukkit.event.entity.VillagerReputationChangeEvent}. + * + * @param uuid the UUID of the entity for whom the reputation is being + * removed + * @param reputationType reputation type to be modified + * @param amount amount of reputation to remove + */ + public void removeReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType, int amount); + + /** + * Remove reputation of a given type towards a given entity, with a + * specific change reason. + * + *
The final value will be clamped to the maximum value supported by the + * provided reputation type. If the final value is below the reputation + * discard threshold, gossip associated with this reputation type will be + * removed. + * + *
Note: this will fire a + * {@link org.bukkit.event.entity.VillagerReputationChangeEvent}. + * + * @param uuid the UUID of the entity for whom the reputation is being + * removed + * @param reputationType reputation type to be modified + * @param amount amount of reputation to remove + * @param changeReason reputation change reason + */ + public void removeReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType, int amount, @NotNull ReputationEvent changeReason); + + /** + * Set reputation of a given type towards a given entity. + * + *
The final value will be clamped to the maximum value supported by the + * provided reputation type. If the final value is below the reputation + * discard threshold, gossip associated with this reputation type will be + * removed. + * + *
Note: this will fire a + * {@link org.bukkit.event.entity.VillagerReputationChangeEvent}. + * + * @param uuid the UUID of the entity for whom the reputation is being + * added + * @param reputationType reputation type to be modified + * @param amount amount of reputation to add + */ + public void setReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType, int amount); + + /** + * Set reputation of a given type towards a given entity, with a specific + * change reason. + * + *
The final value will be clamped to the maximum value supported by the + * provided reputation type. If the final value is below the reputation + * discard threshold, gossip associated with this reputation type will be + * removed. + * + *
Note: this will fire a + * {@link org.bukkit.event.entity.VillagerReputationChangeEvent}. + * + * @param uuid the UUID of the entity for whom the reputation is being + * added + * @param reputationType reputation type to be modified + * @param amount amount of reputation to add + * @param changeReason reputation change reason + */ + public void setReputation(@NotNull UUID uuid, @NotNull ReputationType reputationType, int amount, @NotNull ReputationEvent changeReason); + + /** + * Sets the reputation decay time for this villager. + * + *
Defaults to 24000 (1 daylight cycle). + * + * @param ticks amount of ticks until the villager's reputation decays + */ + public void setGossipDecayTime(long ticks); + + /** + * Gets the reputation decay time for this villager. + * + *
Defaults to 24000 (1 daylight cycle). + * + * @return amount of ticks until the villager's reputation decays + */ + public long getGossipDecayTime(); + /** * Represents Villager type, usually corresponding to what biome they spawn * in. @@ -267,4 +432,97 @@ public interface Villager extends AbstractVillager { return Lists.newArrayList(Registry.VILLAGER_PROFESSION).toArray(new Profession[0]); } } + + /** + * Reputation type used in gossips. + */ + interface ReputationType { + + /** + * Major negative reputation. It is caused by killing a villager. + */ + ReputationType MAJOR_NEGATIVE = getReputationType("major_negative"); + /** + * Minor negative reputation. It is caused by attacking a villager. + */ + ReputationType MINOR_NEGATIVE = getReputationType("minor_negative"); + /** + * Minor positive reputation. It is caused by curing a villager. + */ + ReputationType MINOR_POSITIVE = getReputationType("minor_positive"); + /** + * Major positive reputation. It is caused by curing a villager, it is + * never shared in gossip and never decays. + */ + ReputationType MAJOR_POSITIVE = getReputationType("major_positive"); + /** + * Trading reputation. It has the same weight as minor positive + * reputation and is caused by trading with a villager. + */ + ReputationType TRADING = getReputationType("trading"); + + /** + * Get maximum reputation value of this type. + * @return maximum value of this reputation type + */ + int getMaxValue(); + + /** + * Get weight of this reputation type. + * + *
When calculating total reputation of an entity, reputation of + * each type is multiplied by its weight. + * + * @return weight assigned to this reputation type + */ + int getWeight(); + + private static ReputationType getReputationType(String key) { + return Bukkit.getUnsafe().createReputationType(key); + } + } + + /** + * Reputation change reason. + */ + interface ReputationEvent { + + /** + * A villager was cured by a player. + */ + ReputationEvent ZOMBIE_VILLAGER_CURED = getReputationEvent("zombie_villager_cured"); + /** + * A player traded with a villager. + */ + ReputationEvent TRADE = getReputationEvent("trade"); + /** + * A villager was hurt by an entity. + */ + ReputationEvent VILLAGER_HURT = getReputationEvent("villager_hurt"); + /** + * A villager was killed by an entity. + */ + ReputationEvent VILLAGER_KILLED = getReputationEvent("villager_killed"); + /** + * A villager gossiped with another villager. + */ + ReputationEvent GOSSIP = getReputationEvent("bukkit_gossip"); + /** + * Reputation decayed over time. + */ + ReputationEvent DECAY = getReputationEvent("bukkit_decay"); + /** + * Village's iron golem was killed by an entity. + */ + ReputationEvent GOLEM_KILLED = getReputationEvent("golem_killed"); + /** + * Unspecified reason. Available only by setting the reputation + * programmatically. + */ + ReputationEvent UNSPECIFIED = getReputationEvent("bukkit_unspecified"); + + private static ReputationEvent getReputationEvent(String key) { + return Bukkit.getUnsafe().createReputationEvent(key); + } + } } diff --git a/src/main/java/org/bukkit/event/entity/VillagerReputationChangeEvent.java b/src/main/java/org/bukkit/event/entity/VillagerReputationChangeEvent.java new file mode 100644 index 00000000..8e26b81b --- /dev/null +++ b/src/main/java/org/bukkit/event/entity/VillagerReputationChangeEvent.java @@ -0,0 +1,152 @@ +package org.bukkit.event.entity; + +import com.google.common.base.Preconditions; +import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Villager; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Called whenever an entity's reputation with a villager changes. + */ +public class VillagerReputationChangeEvent extends EntityEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + private final UUID targetUUID; + private final Villager.ReputationEvent reason; + private final Villager.ReputationType reputationType; + private final int oldValue; + private int newValue; + private final int maxValue; + + public VillagerReputationChangeEvent(@NotNull Villager villager, @NotNull UUID targetUUID, @NotNull Villager.ReputationEvent reason, @NotNull Villager.ReputationType reputationType, int oldValue, int newValue, int maxValue) { + super(villager); + this.targetUUID = targetUUID; + this.reason = reason; + this.reputationType = reputationType; + this.oldValue = oldValue; + this.newValue = newValue; + this.maxValue = maxValue; + } + + /** + * Get UUID of the entity for whom the reputation with a villager changes. + * + * @return UUID of the entity for whom the reputation with a villager + * changes + */ + @NotNull + public UUID getTargetUUID() { + return targetUUID; + } + + /** + * Get the Entity for whom the reputation with a villager changes. + * + * @return the Entity for whom the reputation with a villager changes, + * or {@code null} if it isn't found + */ + @Nullable + public Entity getTarget() { + return Bukkit.getEntity(targetUUID); + } + + /** + * Get the reason of this reputation change. + * + * @return the reason of this reputation change + */ + @NotNull + public Villager.ReputationEvent getReason() { + return reason; + } + + /** + * Get the type of changed reputation. + * + * @return the type of changed reputation + */ + @NotNull + public Villager.ReputationType getReputationType() { + return reputationType; + } + + /** + * Get the reputation value before the change. + * + * @return the reputation value before the change + */ + public int getOldValue() { + return oldValue; + } + + /** + * Get new reputation value after the change. + * + * @return the reputation value after the change + */ + public int getNewValue() { + return newValue; + } + + /** + * Set new reputation value for this event. + * + *
If the final value is below the reputation discard threshold, gossip + * associated with this reputation type will be removed. + * + *
The provided value must be between 0 and + * {@link VillagerReputationChangeEvent#getMaxValue()}, otherwise an + * {@link IllegalArgumentException} will be thrown. Each reputation type + * may have a different maximum value. + * + * @param newValue the reputation value after the change + * @see Villager.ReputationType#getMaxValue() + */ + public void setNewValue(int newValue) { + Preconditions.checkArgument(0 <= newValue && newValue <= maxValue, "new value (%s) must be between [0, %s]", newValue, maxValue); + this.newValue = newValue; + } + + /** + * Get maximum value for the reputation type affected by this event. + * + * @return the maximum value for the reputation type affected by this event + * @see Villager.ReputationType#getMaxValue() + */ + public int getMaxValue() { + return maxValue; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + cancelled = cancel; + } + + @NotNull + @Override + public Villager getEntity() { + return (Villager) super.getEntity(); + } + + @NotNull + @Override + public HandlerList getHandlers() { + return handlers; + } + + @NotNull + public static HandlerList getHandlerList() { + return handlers; + } +}