Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements to Attributes #955

Merged
merged 5 commits into from Jul 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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