Skip to content

Commit

Permalink
Enhancements to Attributes (#955)
Browse files Browse the repository at this point in the history
  • Loading branch information
aki-ks authored and mastercoms committed Jul 20, 2018
1 parent 49189d9 commit b961db3
Show file tree
Hide file tree
Showing 5 changed files with 401 additions and 75 deletions.
252 changes: 211 additions & 41 deletions src/main/java/net/glowstone/entity/AttributeManager.java
Expand Up @@ -4,15 +4,18 @@
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.glowstone.net.GlowSession;
import net.glowstone.net.message.play.entity.EntityPropertyMessage;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.attribute.AttributeModifier;

/**
* Manages the attributes described at https://minecraft.gamepedia.com/Attribute
Expand Down Expand Up @@ -69,14 +72,24 @@ public void sendMessages(GlowSession session) {
needsUpdate = false;
}

/**
* Get the property for a certain {@link Key}.
*
* @param key the kind of property to get
* @return the property or {@code null}
*/
public Property getProperty(Key key) {
return properties.get(key.toString());
}

/**
* Updates a property and removes all modifiers.
*
* @param key the property to update
* @param value the new value
*/
public void setProperty(Key key, double value) {
setProperty(key.toString(), Math.max(key.min, Math.min(value, key.max)), null);
setProperty(key, Math.max(key.min, Math.min(value, key.max)), null);
}

/**
Expand All @@ -86,30 +99,24 @@ public void setProperty(Key key, double value) {
* @param value the new base value
* @param modifiers the new and retained modifiers, or {@code null} to remove all modifiers
*/
public void setProperty(String key, double value, List<Modifier> modifiers) {
if (properties.containsKey(key)) {
properties.get(key).value = value;
properties.get(key).modifiers = modifiers;
} else {
properties.put(key,
new Property(value, modifiers == null ? Collections.emptyList() : modifiers));
public void setProperty(Key key, double value, Collection<AttributeModifier> modifiers) {
if (modifiers == null) {
modifiers = Collections.emptyList();
}

properties.put(key.toString(), new Property(key, value, modifiers));
needsUpdate = true;
}

/**
* Returns the base value of the given property.
* Returns the base value of the given property with all modifiers applied.
*
* @param key the property to look up
* @return the property's base value, or its default value if it's not set
*/
public double getPropertyValue(Key key) {
if (properties.containsKey(key.toString())) {
return properties.get(key.toString()).value;
}

return key.def;
Property property = properties.get(key.toString());
return property == null ? key.getDef() : property.getValue();
}

/**
Expand All @@ -122,25 +129,65 @@ public Map<String, Property> getAllProperties() {

@RequiredArgsConstructor
public enum Key {
KEY_MAX_HEALTH("generic.maxHealth", 20, 1024.0),
KEY_FOLLOW_RANGE("generic.followRange", 32, 2048),
KEY_KNOCKBACK_RESISTANCE("generic.knockbackResistance", 0, 1),
KEY_MOVEMENT_SPEED("generic.movementSpeed", 0.699999988079071, 1024.0),
KEY_ATTACK_DAMAGE("generic.attackDamage", 2, 2048.0),
KEY_ATTACK_SPEED("generic.attackSpeed", 4.0, 1024.0),
KEY_ARMOR("generic.armor", 0.0, 30.0),
KEY_ARMOR_TOUGHNESS("generic.armorToughness", 0.0, 20.0),
KEY_LUCK("generic.luck", 0, -1024, 1024),
KEY_FLYING_SPEED("generic.flyingSpeed", 0.4, 1024),
KEY_HORSE_JUMP_STRENGTH("horse.jumpStrength", 0.7, 2),
KEY_ZOMBIE_SPAWN_REINFORCEMENTS("zombie.spawnReinforcements", 0, 1);
KEY_MAX_HEALTH("generic.maxHealth", Attribute.GENERIC_MAX_HEALTH, 20, 1024.0),
KEY_FOLLOW_RANGE("generic.followRange", Attribute.GENERIC_FOLLOW_RANGE, 32, 2048),
KEY_KNOCKBACK_RESISTANCE("generic.knockbackResistance",
Attribute.GENERIC_KNOCKBACK_RESISTANCE, 0, 1),
KEY_MOVEMENT_SPEED("generic.movementSpeed",
Attribute.GENERIC_MOVEMENT_SPEED, 0.699999988079071, 1024.0),
KEY_ATTACK_DAMAGE("generic.attackDamage", Attribute.GENERIC_ATTACK_DAMAGE, 2, 2048.0),
KEY_ATTACK_SPEED("generic.attackSpeed", Attribute.GENERIC_ATTACK_SPEED, 4.0, 1024.0),
KEY_ARMOR("generic.armor", Attribute.GENERIC_ARMOR, 0.0, 30.0),
KEY_ARMOR_TOUGHNESS("generic.armorToughness", Attribute.GENERIC_ARMOR_TOUGHNESS, 0.0, 20.0),
KEY_LUCK("generic.luck", Attribute.GENERIC_LUCK, 0, -1024, 1024),
KEY_FLYING_SPEED("generic.flyingSpeed", Attribute.GENERIC_FLYING_SPEED, 0.4, 1024),
KEY_HORSE_JUMP_STRENGTH("horse.jumpStrength", Attribute.HORSE_JUMP_STRENGTH, 0.7, 2),
KEY_ZOMBIE_SPAWN_REINFORCEMENTS("zombie.spawnReinforcements",
Attribute.ZOMBIE_SPAWN_REINFORCEMENTS, 0, 1);

/**
* Get a {@link Key} by its {@link Key#name attribute name}.
*
* @param name of the attribute to return
* @return the attribute with the specified name or {@code null}
*/
public static Key fromName(String name) {
for (Key key : values()) {
if (key.name.equals(name)) {
return key;
}
}

return null;
}

/**
* Get the {@link Key} for the corresponding {@link Attribute}.
*
* @param attribute attribute from Bukkit api
* @return key corresponding to the attribute or {@code null}
*/
public static Key fromAttribute(Attribute attribute) {
for (Key key : values()) {
if (key.getAttribute() == attribute) {
return key;
}
}

return null;
}

/**
* Attribute name from https://minecraft.gamepedia.com/Attribute
*/
private final String name;

/**
* Bukkit {@link Attribute} corresponding to this key.
*/
@Getter
private final Attribute attribute;

/**
* Default attribute value.
*/
Expand All @@ -164,8 +211,8 @@ public enum Key {
* @param def default value
* @param max maximum value
*/
Key(String name, double def, double max) {
this(name, def, 0, max);
Key(String name, Attribute attribute, double def, double max) {
this(name, attribute, def, 0, max);
}

@Override
Expand All @@ -175,20 +222,143 @@ public String toString() {
}

@AllArgsConstructor
public static class Property {

public class Property implements AttributeInstance {
@Getter
private Key key;
private double value;
@Getter
private List<Modifier> modifiers;
}
private Map<UUID, AttributeModifier> modifiers;

@Data
public static class Modifier {
private double cachedValue;
private boolean isCacheUpToDate = false;

private final String name;
private final UUID uuid;
private final double amount;
private final byte operation;
/**
* Create a new property instance.
*
* @param key of the property
* @param value of the property
* @param modifiers of the property
*/
public Property(Key key, double value, Collection<AttributeModifier> modifiers) {
this.key = key;
this.value = value;
this.modifiers = modifiers.stream()
.collect(Collectors.toMap(AttributeModifier::getUniqueId, Function.identity()));
}

@Override
public Attribute getAttribute() {
return key.getAttribute();
}

@Override
public double getDefaultValue() {
return key.getDef();
}

/**
* Get the value before modifiers have been applied.
*
* @return base value
*/
@Override
public double getBaseValue() {
return value;
}

/**
* Set the base value on which modifiers are applied.
*
* @param value new base value
*/
@Override
public void setBaseValue(double value) {
this.value = value;
onMutation();
}

/**
* Add a modifier to this property.
*
* <p>
* Attributes with the same uuid will be overridden according to
* https://minecraft.gamepedia.com/Attribute#Attributes
* </p>
*
* @param attributeModifier to add to this property
*/
@Override
public void addModifier(AttributeModifier attributeModifier) {
this.modifiers.put(attributeModifier.getUniqueId(), attributeModifier);
onMutation();
}

/**
* Remove an attribute from this property.
*
* @param attributeModifier to remove from this property
*/
@Override
public void removeModifier(AttributeModifier attributeModifier) {
this.modifiers.remove(attributeModifier.getUniqueId());
onMutation();
}

private void onMutation() {
this.isCacheUpToDate = false;
AttributeManager.this.needsUpdate = true;
}

/**
* Get value of this property after all modifiers have been applied.
*
* @return the resulting attribute value
*/
@Override
public double getValue() {
if (!isCacheUpToDate) {
cachedValue = computeValue();
isCacheUpToDate = true;
}

return cachedValue;
}

private double computeValue() {
double result = getBaseValue();

for (AttributeModifier modifier : modifiers.values()) {
if (modifier.getOperation() == AttributeModifier.Operation.ADD_NUMBER) {
result += modifier.getAmount();
}
}

double multiplier = 1.0;
for (AttributeModifier modifier : modifiers.values()) {
if (modifier.getOperation() == AttributeModifier.Operation.ADD_SCALAR) {
multiplier += modifier.getAmount();
}
}
result *= multiplier;

for (AttributeModifier modifier : modifiers.values()) {
if (modifier.getOperation() == AttributeModifier.Operation.MULTIPLY_SCALAR_1) {
result *= 1.0 + modifier.getAmount();
}
}

result = Math.max(result, key.min);
result = Math.min(result, key.max);
return result;
}

/**
* Get all modifiers assigned to this property.
*
* @return the modifiers of this property
*/
public Collection<AttributeModifier> getModifiers() {
return modifiers.values();
}
}

}
3 changes: 1 addition & 2 deletions src/main/java/net/glowstone/entity/GlowLivingEntity.java
Expand Up @@ -1251,8 +1251,7 @@ public void playAnimation(EntityAnimation animation) {

@Override
public AttributeInstance getAttribute(Attribute attribute) {
// todo: 1.11
return null;
return getAttributeManager().getProperty(Key.fromAttribute(attribute));
}

@Override
Expand Down

0 comments on commit b961db3

Please sign in to comment.