Improved EntityConfig model

This commit is contained in:
kuroppoi 2022-08-21 21:51:30 +02:00
parent 5360b5fe4e
commit 0c99bf5242
2 changed files with 141 additions and 107 deletions

View file

@ -1,156 +1,202 @@
package brainwine.gameserver.entity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.annotation.Nulls;
import brainwine.gameserver.item.DamageType;
import brainwine.gameserver.item.Item;
import brainwine.gameserver.util.MapHelper;
import brainwine.gameserver.util.Vector2i;
import brainwine.gameserver.util.WeightedMap;
@JsonIgnoreProperties(ignoreUnknown = true)
public class EntityConfig {
@JsonProperty("code")
private int type;
@JacksonInject("name")
private String name;
@JsonProperty("health")
private float maxHealth = 5;
@JsonProperty("speed")
private float baseSpeed = 3;
@JsonProperty("xp")
private final String name;
private final int type;
private int experienceYield;
@JsonProperty("group")
private EntityGroup group = EntityGroup.NONE;
@JsonProperty("size")
private float maxHealth = Entity.DEFAULT_HEALTH;
private float baseSpeed = 3;
private Vector2i size = new Vector2i(1, 1);
@JsonProperty("loot")
private List<EntityLoot> loot = new ArrayList<>();
@JsonProperty("placed_loot")
private List<EntityLoot> placedLoot = new ArrayList<>();
@JsonProperty("loot_by_weapon")
private Map<Item, List<EntityLoot>> lootByWeapon = new HashMap<>();
@JsonProperty("defense")
private EntityGroup group = EntityGroup.NONE;
private WeightedMap<EntityLoot> loot = new WeightedMap<>();
private WeightedMap<EntityLoot> placedLoot = new WeightedMap<>();
private Map<Item, WeightedMap<EntityLoot>> lootByWeapon = new HashMap<>();
private Map<DamageType, Float> resistances = new HashMap<>();
@JsonProperty("weakness")
private Map<DamageType, Float> weaknesses = new HashMap<>();
@JsonProperty("components")
private Map<String, String[]> components = Collections.emptyMap();
@JsonProperty("set_attachments")
private Map<String, String> attachments = Collections.emptyMap();
@JsonProperty("behavior")
private List<Map<String, Object>> behavior = Collections.emptyList();
@JsonProperty("animations")
private List<Map<String, Object>> animations = Collections.emptyList();
@JsonProperty("slots")
private List<String> slots = Collections.emptyList();
@JsonProperty("attachments")
private List<String> possibleAttachments = Collections.emptyList();
private Map<String, String[]> components = new HashMap<>();
private Map<String, String> attachments = new HashMap<>();
private List<String> attachmentTypes = new ArrayList<>();
private List<String> slots = new ArrayList<>();
private List<String> animations = new ArrayList<>();
private List<Map<String, Object>> behavior = new ArrayList<>();
@JsonCreator
private EntityConfig() {}
private EntityConfig(@JacksonInject("name") String name,
@JsonProperty(value = "code", required = true) int type) {
this.name = name;
this.type = type;
}
@JsonCreator
public static EntityConfig fromName(String name) {
return EntityRegistry.getEntityConfig(name);
}
public int getType() {
return type;
}
@JsonValue
public String getName() {
return name;
}
public float getMaxHealth() {
return maxHealth;
}
public float getBaseSpeed() {
return baseSpeed;
public int getType() {
return type;
}
@JsonProperty("xp")
public int getExperienceYield() {
return experienceYield;
}
public EntityGroup getGroup() {
return group;
@JsonProperty("health")
public float getMaxHealth() {
return maxHealth;
}
@JsonProperty("speed")
public float getBaseSpeed() {
return baseSpeed;
}
@JsonSetter(nulls = Nulls.SKIP)
private void setSize(Vector2i size) {
this.size = size;
}
public Vector2i getSize() {
return size;
}
public List<EntityLoot> getLoot() {
@JsonSetter(nulls = Nulls.SKIP)
private void setGroup(EntityGroup group) {
this.group = group;
}
public EntityGroup getGroup() {
return group;
}
@JsonSetter(nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setLoot(List<EntityLoot> loot) {
this.loot = new WeightedMap<>(loot, EntityLoot::getFrequency);
}
public WeightedMap<EntityLoot> getLoot() {
return loot;
}
public List<EntityLoot> getPlacedLoot() {
@JsonSetter(nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setPlacedLoot(List<EntityLoot> placedLoot) {
this.placedLoot = new WeightedMap<>(placedLoot, EntityLoot::getFrequency);
}
public WeightedMap<EntityLoot> getPlacedLoot() {
return placedLoot;
}
public Map<Item, List<EntityLoot>> getLootByWeapon() {
@JsonSetter(nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setLootByWeapon(Map<Item, List<EntityLoot>> lootByWeapon) {
this.lootByWeapon = lootByWeapon.entrySet().stream()
.collect(Collectors.toMap(
Entry::getKey, entry -> new WeightedMap<EntityLoot>(entry.getValue(), EntityLoot::getFrequency)));
}
public Map<Item, WeightedMap<EntityLoot>> getLootByWeapon() {
return lootByWeapon;
}
@JsonSetter(value = "defense", nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setResistances(Map<DamageType, Float> resistances) {
this.resistances = resistances;
}
public Map<DamageType, Float> getResistances() {
return resistances;
}
@JsonSetter(value = "weakness", nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setWeaknesses(Map<DamageType, Float> weaknesses) {
this.weaknesses = weaknesses;
}
public Map<DamageType, Float> getWeaknesses() {
return weaknesses;
}
@JsonSetter(nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setComponents(Map<String, String[]> components) {
this.components = components;
}
public Map<String, String[]> getComponents() {
return components;
}
@JsonSetter(value = "set_attachments", nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setAttachments(Map<String, String> attachments) {
this.attachments = attachments;
}
public Map<String, String> getAttachments() {
return attachments;
}
public List<Map<String, Object>> getBehavior() {
return behavior;
@JsonSetter(value = "attachments", nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setAttachmentTypes(List<String> attachmentTypes) {
this.attachmentTypes = attachmentTypes;
}
public List<Map<String, Object>> getAnimations() {
return animations;
public List<String> getAttachmentTypes() {
return attachmentTypes;
}
@JsonSetter(nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setSlots(List<String> slots) {
this.slots = slots;
}
public List<String> getSlots() {
return slots;
}
public List<String> getPossibleAttachments() {
return possibleAttachments;
@JsonSetter(nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setBehavior(List<Map<String, Object>> behavior) {
this.behavior = behavior;
}
public List<Map<String, Object>> getBehavior() {
return behavior;
}
@JsonSetter(nulls = Nulls.SKIP, contentNulls = Nulls.SKIP)
private void setAnimations(List<Map<String, Object>> animations) {
this.animations = animations.stream()
.map(animation -> MapHelper.getString(animation, "name"))
.collect(Collectors.toList());
}
public List<String> getAnimations() {
return animations;
}
}

View file

@ -8,9 +8,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonIgnore;
import brainwine.gameserver.GameServer;
import brainwine.gameserver.behavior.SequenceBehavior;
@ -36,19 +33,20 @@ public class Npc extends Entity {
public static final int ATTACK_RETENTION_TIME = 2000;
public static final int ATTACK_INVINCIBLE_TIME = 333;
private final EntityConfig config;
private final Map<String, Object> properties = new HashMap<>();
private final Map<DamageType, Float> baseDefenses;
private final Map<DamageType, Float> activeDefenses = new HashMap<>();
private final Map<Player, Pair<Item, Long>> recentAttacks = new HashMap<>();
private final WeightedMap<EntityLoot> loot;
private final WeightedMap<EntityLoot> placedLoot;
private final Map<Item, WeightedMap<EntityLoot>> lootByWeapon;
private final List<String> animations;
private final SequenceBehavior behaviorTree;
private final Vector2i size;
private final String typeName;
private final float maxHealth;
private final float baseSpeed;
private final Vector2i size;
private final WeightedMap<EntityLoot> loot;
private final WeightedMap<EntityLoot> placedLoot;
private final Map<Item, WeightedMap<EntityLoot>> lootByWeapon;
private final Map<DamageType, Float> resistances;
private final Map<DamageType, Float> weaknesses;
private final List<String> animations;
private final SequenceBehavior behaviorTree;
private final Map<String, Object> properties = new HashMap<>();
private final Map<DamageType, Float> activeDefenses = new HashMap<>();
private final Map<Player, Pair<Item, Long>> recentAttacks = new HashMap<>();
private float speed;
private int moveX;
private int moveY;
@ -57,7 +55,7 @@ public class Npc extends Entity {
private Entity target;
private long lastBehavedAt = System.currentTimeMillis();
private long lastTrackedAt = System.currentTimeMillis();
public Npc(Zone zone, EntityConfig config) {
super(zone);
@ -96,37 +94,28 @@ public class Npc extends Entity {
String attachment = entry.getValue();
if(attachment != null) {
slots.put(config.getSlots().indexOf(slot), config.getPossibleAttachments().indexOf(attachment));
slots.put(config.getSlots().indexOf(slot), config.getAttachmentTypes().indexOf(attachment));
}
}
properties.put("sl", slots);
}
// TODO add setters to the config class so we can just cache the more complicated stuff
this.config = config;
this.type = config.getType();
this.typeName = config.getName();
this.type = config.getType();
this.maxHealth = config.getMaxHealth();
this.health = maxHealth;
this.baseSpeed = config.getBaseSpeed();
this.speed = baseSpeed;
this.size = config.getSize();
this.animations = config.getAnimations().stream().map(map -> MapHelper.getString(map, "name")).collect(Collectors.toList());
this.loot = config.getLoot();
this.placedLoot = config.getPlacedLoot();
this.lootByWeapon = config.getLootByWeapon();
this.resistances = config.getResistances();
this.weaknesses = config.getWeaknesses();
this.animations = config.getAnimations();
this.behaviorTree = SequenceBehavior.createBehaviorTree(this, behavior);
this.loot = new WeightedMap<>(config.getLoot(), EntityLoot::getFrequency);
this.placedLoot = new WeightedMap<>(config.getPlacedLoot(), EntityLoot::getFrequency);
this.lootByWeapon = config.getLootByWeapon().entrySet().stream()
.collect(Collectors.toMap(
Entry::getKey,
entry -> new WeightedMap<EntityLoot>(entry.getValue(), EntityLoot::getFrequency)));
// TODO should just merge defenses with weaknesses in the yml config tbh
this.baseDefenses = config.getResistances();
config.getWeaknesses().forEach((type, multiplier) -> {
baseDefenses.put(type, baseDefenses.getOrDefault(type, 0F) - multiplier);
});
health = maxHealth;
speed = baseSpeed;
}
@Override
@ -263,7 +252,6 @@ public class Npc extends Entity {
return baseDamage * (1 - getDefense(type));
}
@JsonIgnore // TODO Silly Jackson is drunk and errors trying to find a key deserializer for recentAttacks
public Collection<Pair<Item, Long>> getRecentAttacks() {
return Collections.unmodifiableCollection(recentAttacks.values());
}
@ -313,7 +301,7 @@ public class Npc extends Entity {
}
public float getBaseDefense(DamageType type) {
return baseDefenses.getOrDefault(type, 0F);
return resistances.getOrDefault(type, 0F) - weaknesses.getOrDefault(type, 0F);
}
public void setGuardBlock(int x, int y) {