属性系统学习笔记

概述

持有属性物品抽象

生物属性管理器

public class CreatureGameStats implements IStatRoot  {
    protected static Logger logger = LoggerFactoty.getLogger(CreatureGameStats.class);
    /**
    *   总属性
    */
    protected Map<StatEnum, StatValue> stats;
    /**
    *  属性根节点
    */
    protected StatNode statRoot;

    protected AbstractCreature owner;
    protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    // 属性拥有者
    public CreatureGameStats(AbstractCreature owner) {
        this.owner = owner;
        this.stats = new ConcurrentHashMap<>();
        this.statRoot = StatNode.crateRoot(this);
    }

    @Override
    public void changeStats() {
        Map<StatEnum, Long> diffMap = StatUtils.diffWithNewMap(oldStats, bewStats);
        if(diffMap.isEmpty()) {
            return;
        }
        Map<StatEnum, Long> changeMap = new HashMap<>(diffMap.size());
        lock.writeLock().lock();
        try {
            // 添加
            change(childNode, diffMap, changeMap)
        } finally {
            lock.writeLock().unlock();
        }
        if(Start.debug) {
            // 抽烟检查
            checkException(childNode);
        }
        // 通知属性变化
        notifyChange(childNode, changeMap);
    }

    // 该属性,溜子类执行空间,但是注意这个方法在锁中
    protected void change(StatNode childNode, Map<StatEnum, Long> diffMap, Map<StatEnum, Long> changeMap) {
        StatUtils.mergeToMap(stats, childNode.isToBase(), diffMap, changeMap);
    }

    // 属性变化后的逻辑
    protected void notifyChange(StatNode childNode, Map<StatEnum, Long> changeStatMap) { 
        lock.readLock().lock();
        try {
            if (changeStatMap.containsKey(StatEnum.MAX_HP)) {
                // 最大血量变化
                owner.getLifeStats().changeMaxHp(stats.get(StatEnum.MAX_HP).getTotal());
            }
            if (changeStatMap.containsKey(StatEnum.MAX_MP)) {
                // 最大蓝量变化
                owner.getLifeStats().changeMaxMp(stats.get(StatEnum.MAX_MP).getTotal());
            }
            if (changeStatsMap.containsKey(StatEnum.MAX_ENERGY)) {
                // 最大内功变化
                owner.getLifeStats().changeMaxEnergy(stats.get(StatEnum.MAX_ENERGY).getTotal());
            }
            if(owner.isSpawned())  {
                // 已经出生状态下的属性同步
                syncStat(changeStatsMap);
            }
        } finally {
            lock.readLock().unlock();
        }
    }

    private void syncStat(Map<StatEnum, Long> changeStatsMap) {
        List<Stat> list = new ArrayList<>(2);
        if(changeStatsMap.containsKey(StatEnum.ATTACK_SPEED)) {
            list.add(Stat.valueOf(StateEnum.SPEED, getCurrentStat(StatEnum.SPEED)));
        }
        if(changeStatMap.containsKey(StatEnum.ATTACK_SPEED)) {
            list.add(Stat.valueOf(StatEnum.ATTACK_SPEED, getCurrentStat(StatEnum.ATTACK_SPEED)));
        }
        if(!list.isEmpty()) {
            PacketSendUtility.broadcastPacketImmediately(owner, CreatureStatChangeResp.valueOf(owner.getObjectId(), list), true);
        }
    }

    @Override
    public void removeChild(Serializable key) {
        throw new RuntimeException("stat root can not remove!");
    }

    @Override
    public void fillParentKey(List<String> list) {

    }

    // 获取属性树显示信息
    public String getPrintStatTree() {
        return JSON.toJSONString(statRoot, JsonUtils.SERIALIZE_CONFIG, StatNodeFilter.INSTANCE, JsonUtils.SERIALIZER_FEATURES);
    }
    // 获取属性树显示信息
    public StatNode getPrintStatTreeStatNode() {
        return statRoot;
    }
    // 属性来源
    public StatNode getOrCreateStatNode(StatSource statSource) {
        return statRoot.getOrCreateChild(statSource);
    }
    // 获取子节点
    public StatNode getChildNode(List<String> childKeys) {
        StatNode node = statRoot;
        lock.readLock().lock();
        try {
            for(String childKey : childKeys) {
                node = node.getOrCreateChild(childKey);
            }
        } finally {
            lock.readLock().unlock();
        }
        return node;
    }
    // 属性集合
    public Map<StatEunm, StatValue> getStatTotal() {
        return stats;
    }
    // 所有属性集合
    public Map<StatEnum, Long> getAllStat() {
        lock.readLock().lock();
        try {
            return StatUtils.toSimpleMap(stats);
        } finally {
            lock.readLock().unlock();
        }
    }

    // 清空属性
    public void clear() {
        lock.writeLock().lock();
        try {
            stats.clear();
            this.statRoot = StatNode.createRoot(this);
        } finally {
            lock.writeLock().unlock();
        }
    }

    // 指定类型的属性值
    public long getCurrentStat(StatEnum stat) {
        switch(stat) {
            case HP_CUR:
                return owener.getLifeStats().getCurrrentHp();
            case MP_CUR:
                return owner.getLifeStats().getCurrentMp();
            case ENERGY_CUR:
                return owner.getLifeStats().getCurrentEnergy();
            case HP_CUR_R :
                return owner.getLifeStats().getHpRct();
            default : {
                long value = 0;
                lock.readLock().lock();
                try {
                    if(stats.contiansKey(stat)) {
                        value = stats.get(stat).getTotal();
                    }
                } finally {
                    lock.readLock().unlock();
                }
                // 属性使用时,不能为负数
                return Math.max(value, 0);
            }
        }
    }

    // 获取万分比属性类型的小数形式,比如储存时是5000,表示概率/比值为0.5,返回值就为0.5
    public double getCurrentStatWithOdds(StatEnum stat) {
        double value = getCurrentStat(stat);
        return value / GameMathUtil.ODDS_BASE;
    }
    // 获取属性集合List,包含基础值
    public List<Stat> getStatList() {
        lock.readList().lock();
        try {
            return StatUtils.toList(stats);
        } finally {
            lock.readLock().unlock();
        }
    }
    // 增强版本,获取属性集合List,包含基础值
    public List<StatExInfo> getStatExList() {
        lock.readLock().lock();
        try {
            return StatUtils.toStatExList(stats);
        } finally {
            lock.readLock().unlock();
        }
    }

    // 获取属性集合Map
    public Map<StatEnum, Long> getStatMap() {
        lock.readLock().lock();
        try {
            return StatUtils.toSimpleMap(stats);
        } finally {
            lock.radLock().unlock();
        }
    }
    // 通过属性列表进行初始化
    public void initWithStatMap(Map<StatEnum, Long> initStats) {
        initStats.forEach( (statEnum, value) -> {
            StatValue statValue = new StarValue();
            statValue.alter(true, value);
            stats.put(statEnum, statValue);
        });
    }
    // 检测#{@link #stats} 与 statRoot. # {@link #statRoot::getStatTotal()} 是否一致
    public void checkException(StatNode childNode) {

    }

    // 创建跨服传输属性数据
    public StatNode buildStatMoedel() {
        return statRoot.buildMedel();
    }
    // 解析跨服传输的属性数据
    public void parseStatModel(StatNodeModel model) {
        StatNode.parseModelOnlyChild(statRoot, model);
    }
}

玩家属性管理器

```java
public class PlayerGameStats extends CreatureGameStats {
    // 可计算战力的总属性
    protected Map<StatEnum, StatValue> fightForceStats;
    // 用于合并玩家属性变化信息
    private Set<StatEnum> changeStats = new ConcurrentSet();
    // 记录变化的节点
    private Map<String, Map<StatEnum, Long>> changeStatsNode = new ConcurrentHashMap<>();
    // 属性拥有者
    public PlayerGameStats(AbstractCreature owner) {
        super(owner);
        this.fightForceStats = new ConcurrentHashMap<>();
    }
    @Override
    protected void change(StatNode childNode, Map<StatEnum, Long> diffMap, Map<StatEnum, Long> changeStatsMap) {
        super.change(childNode, diffMap, changeSatsMap);
        if(childNode.isToFightForce()) {
            StatUtils.mergeToMap(fightForceStats, childNode.isToBase(), diffMap, null);
        }
        FightPlayer fightPlayer = (FightPlayer) this.owner;
        if(PlayerManager.getInstace().isOnline(this.owner.getObjectId())) {
            fightPlayer.excPlayer(player -> {
                // 本服玩家属性变化,尝试抛属性变化事件到跨服
                PlayerSyncManger.self().trySubmitTransferPlayerEvent(player, G2tStatNodeChangeEvent.valueOf(childNode));
            }, transferPlayer -> {
                // 跨服玩家属性变化
            });
        }
        if(fightPlayer.hasRefreshAgingMark(CreatureAgingMark.statsChange) {
            ArrayList<String> statKeys = new ArrayList<>();
            childNode.fillParentKey(statKeys);
            // .......
        }
    }
    @Override
    protected void notifyChange(StatNode childNode, Map<StatEnum, Long> changeStatsMap) {
        super.notifyChange(childNode, changeStatsMap);
        lock.readLock().lock();
        try {
            // 记录变化的节点key
            Map<StatEnum, Long> map = this.changeStatsNode.computeIfAbsent(childNode.getFullKeyString(), s -> ConcurrentHashMap<>());
            // 记录变化的属性
            StatUtils.mergeHashMap(map, changeStatMap);
        } finally {
            lock.readLock().unlock();
        }
        changeStats.addAll(changeStatsMap.keySet());
        // 如果是玩家,延迟合并通知玩家自身属性变化
        owner.addSelfMergeDelayTaskWithMills(CreatureUpdateType.UPDATE_STAT, 200, () -> notifyPlayerChange(StatEnum.SPEED, StatEnum.ATTACK_SPEED));
    }

    // 获取所有可对战力加成的属性
    public Map<StatEnum, Long> getAllFightForceStat() {
        lock.readLock().lock();
        try {
            return StatUtils.toSimpleMap(fighrForceStats);
        } finally {
            lock.readLock().unlock();
        }
    }

    // 获取可加成战斗的属性值
    public long getFightForceStat(StatEnum statEnum) {
        lock.readLock().lock();
        try {
            StatValue statValue = fightForceStats.get(statEnum);
            retrun statValue == null ? 0 : statValue.getTotal();
        } finally {
            lock.readLock().unlock();
        }
    }

    // 通知玩家属性变化情况
    public void notifyPlayerChange(StatEnum... igonre) {
        if(changeStats.isEmpty()) {
            return;
        }
        Set<StatEnum> ignoreSet = new HashSet<>();
        if(ignore != null) {
            ignoreSet.addAll(Arrays.asList(ignore));
        }
        lock.readLock().lock();
        try {
            // 通知属性变化细信息
            List<Stat> changeStats = new ArrayList<>(this.changeStats.size());
            for(StatEnum statEnum : this.changeStats) {
                if(!igonreSet.contains(statEnum)) {
                    changeStats.add(Stat.valueOf(statEnum, getCurrentStat(statEnum)));
                }
                if(!changeStats.isEmpty()) {
                    CreatureStatChangeResp resp = CreatureChangeResp.valueOf(owner.getObjectId(), changeStats);
                    PacketSendUtility.sendPacket(owner, resp);
                }
                // 抛出属性变化事件
                ((FightPlayer) owner).submitEvent(PlayerStatChangeEvent.valueOf(new HashMap<>(this.changeStatsNode)));

                // 清除属性变化事件
                this.changeStatsNode.clear();
                this.changeStats.clear();
            } finally {
                lock.readLock().unlock();
            }
        }
    }

    @Override
    public StatNodeModel buildStatModel() {
        StatNodeModel statNodeModel = statRoot.buildModel();
        notifyPlayerChange();
        return statNodeMoedel;
    }
}

属性的抽象

属性值

public class StatValue {
    private static final int RATIO_DENOMINATOR = 10000;
    // 总值
    private long total;
    // 基础值
    private long base;
    // 额外加成固定值,不算入基础值
    private long addtional;
    // 额外加成比例
    private int ratio;

    // 合并
    public static StatValue merge(StatValue s1, StatValue s2) {
        StatValue statValue = new StatValue();
        statValue.base = s1.base + s2.base;
        statValue.ratio = s1.ratio + s2.ratiio;
        statValue.calTotal();
        return statValue;
    }

    // 获取总值
    public long getTotal() {
        return total;
    }

    // 除去不算入基础值的总值
    public long getTotalWithoutAdditional() {
        return total - additional;
    }
    // 重置
    public void clear() {
        this.total = base = additional = 0;
    }
    // 增减属性
    public long alter(boolean toBase, long alter) {
        if(toBase) {
            this.base += alter;
        } else {
            this.addtional += alter;
        }
        return calTotal();
    }
    // 修改属性加成比例
    public long aleterRatio(long alterRatio) {
        ratio += alterRatio;
        return calTotal();
    }

    // 计算总值
    private long calTotal() {
        totaol = base + addtional + base * ratio / RATIO_DENOMINATOR;
        return total;
    }

    // 获取基础值
    public long getBase() {
        return base;
    }

    // 重写Object几个基础方法

}

战斗属性定义

@Desc("战斗属性定义")
public enum StatEnum implements EnumReadable {

    @Desc("最大气血")
    MAX_HP(0,"最大气血"),
    @Desc("最大蓝量")
    MAX_HP(1,"最大蓝量"),

    @Desc("内功值加成比例")
    ENERGY_R(124,"内功值加成比例", MAX_ENERGY),
    @Desc("精灵速度加成比例")
    SPIRIT_SPEED_R(503,"精灵速度加成比例"),
    @Desc("血量万分比伤害")
    HP_HURT_R(504, "血量万分比伤害"),

    private final int value;

    // 属性名称
    private final String name;

    // 如果该数组不为null
    // 表示是按比例加成
    // 数组内容为加成的基本属性列表
    private final StatEnum[] ratioBase;

    StatEnum(int value, String name, StatEnum... ratioBase) {
        this.value = value;
        this.name = name;
        this.ratioBase = ratioBase;
    }

    // ....Object基础方法


    public String getName() {
        return name;
    }

    // 是否为比例加成类型
    public boolean isRatio() {
        return ratioBase != null &&
        ratioBase.length > 0;
    }

    // 比例加成基础属性列表
    public StatEnum[] getRatioBase() {
        return ratioBase;
    }

}

属性跟节点接口

public interface IStatRoot {

    // 属性发生变更
    void changeStats(StatNode lastNode, StatNode childNode, Map<StatEnum, Long> oldStats, Map<StatEnum, Long> newStats);

    // 移出属性节点,不推荐使用者直接使用该方法移出节点,请使用clear方法
    void removeChild(Serializable childKey);

    // 移出属性节点,不推荐使用者直接使用该方法移出节点,请使用clear方法
    void removeChild(Serializable childKey);
    // 自顶向下
    void fillParentKey(List<String> list);

}


属性效果集合

public class StatNode implements IStatRoot {
    // 当前属性节点的key
    @JSONField(serialize = false)
    private String selfKey;

    // 属性改变接口
    @JSONField(serialize = false)
    private IStatRoot statRoot;
    // 当前节点属性列表
    @JSONField(serializeUsing = StatEnumMapSerializer.class)
    private Map<StateEnum, Long> stats;
    // 属性子节点
    private final ConcueentHashMap<String, StatNode> childMap = new ConcurrentHashMap<>(0);
    // 万分比加成节点
    private final ConcurrnetHashMap<String, RctNode> rctNodeMap = new ConcurrentHashMap<>(0);
    // 当前节点万分比加成属性列表
    @JSONField(serialzeUsing = StateEnumMapSerializer.class)
    private Map<StatEnum, Long> rctStats;

    // 该节点属性是否算入战斗力
    private boolean toFightForce = true;
    // 该节点属性是否计入基本值
    private boolean toBase = true;
    // 是否已经被移除
    private boolean remove;

    // 节点锁,同一个父节点都会持有同一把锁
    @JSONField(serialize = false) 
    private ReentrantReadWriteLock lock;
    StatNode() {

    }
    // 创建属性节点
    private boolean StatNode createRoot(IStatRoot statRoot) {
        StatNode vo = new StatNode();
        vo.selfKey = "root";
        vo.statRoot = statRoot;
        vo.lock = new ReentrantReadWriteLock();
        return vo;
    }

    // 构建key值,构建为String类型,是为了方便跨服传输
    private String buildKey(Object key, boolean check) {
        if(check) {
            checkKey() {
                checkey(key.getClass());
            }
            return key.toString();
        }
    }

    private void checkKey(Class keyClass) {
        if(String.class == keyClass) {
            // 允许为字符串
            return;
        }
        if(Enum.class.isAssignableFrom(keyClass)) {
            // 允许key为枚举
            return;
        }
        if(ClassUtil.isPrimitiveOrWrapper(keyClass)) {
            // 允许key值为基础类型或者基本类型的包装类型
            return;
        }
        throw new RuntimeException("statNode key must be String/Enum/Primitive/PrimitiveWrapper, keyClass is" + keyClass);
    }

    @Override
    public void changeStats(StatNode lastNode, StatNode childNode childNode, Map<StatEnum, Long> oldStats, Map<StatEunm, Long>
    newStats) {
        if(!childMap.containsKey(lastNode.selfKey)) {
            throw new RuntimeException(String.format("lastNode %s remove form %s before", lastNode, this));
        }
        // 基础属性节点改变
        statRoot.changeStats(this, childNode, oldStats, newStats);
        // 刷新万分比加成属性
        refreshRctNode();
    }

    // 移除属性加成的子节点
    @Override
    public void removeChild(Serializable k) {
        String key = buildKey(k, false);
        lock.writeLock().lock();
        try {
            // 移除该子节点
            StatNode childNode = childMap.get(key);
            if(chilfNode == null) {
                return;
            }
            chilfNode.clear();
            childMap.remove(key);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public void fillParentKey(List<String> list) {
        if(statRoot != null) {
            statRoot.fillParentKey(list);
        }
    }

    // 获取包含全路径的key集合
    public String getFullKeyString() {
        ArrayList<String> keys = new ArrayList<>();
        fillParentKey(keys);
        return keys.toString();
    }

    // 重置该节点的属性值
    public void replace(StatEnum statEnum, long value) {
        Map<StatEnum, Long> stats = new HashMap<>(1);
        stats.put(statEnum, value);
        replace(stats);
    }

    // 重置该节点的属性值
    public void replace(Map<StatEnum, Long> newStats) {
        lock.writeLock().lock();
        try {
            Map<StatEnum, Long> oldStats = this.stats;
            if(newStats == null) {
                this.stats = null;
            } else {
                this.stats = StatUtils.cloneStaeMap(newStats);
            }
            statRoot.changeStats(this, this, oldStats, this.stats);
        } finally  {
            lock.writeLock().unlock();
        }
        // 当前节点属性变化后,刷新万分比加成
        refreshRctNode();
    }
    // 清除当前节点的数据
    public void clear() {
        lock.writeLock().lock();
        try {
            remove = true;
            Map<StatEnum, Long> oldStats = this.stats;
            this.stats = null;
            statRoot.changeStats(this, this, oldStats, null);
            childMap.clear(); 
            // 当前节点被清除,清除万分比加成
            refershRctNode();
            rctNodeMap.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }
    // 获取该节点的总属性加成,包含万分比加成属性,包含不加入战斗力的属性
    @JSONField(serialize = false)
    public Map<StatEnum, StatValue> getTotal() {
        return getTotal(true);
    }

    // 获取该节点的总属性加成
    Map<StatEnum, StatValue> getTotal(boolean includeRct) {
        lock.readLock().lock();
        try {
            Map<StatEnum, StatValue> totalMap = StatUtils.toMap(this, includeRct);
            for (StatNode statNode : childMap.values()) {
                StatUtils.mergoToMap(totalMap, statNode.getTotal());
            }
            return;
        } finally {
            lock.readLock().unlock();
        }
    }

    // 重置/创建一个属性节点,如果节点已经存在,则重置该节点的属性,如果该节点不存在,则创建该节点
    // 默认该节点的所有属性,包括下属节点,算入战斗力
    public StatNode getOrCreateChild(Serializable k) {
        return getOrCreateChild(k, true);
    }

    // 重置/创建一个属性节点,如果节点已存在,则重置该节点的属性,如果该节点不存在,则创建该节点
    // 如果当前节点已经设置为不计算入战斗力,则创建出的子节点也为不计算战斗力
    public StatNode getOrCreateChild(Serializable k, boolean toFightForce) {
        String key = buildKey(k, true);
        StatNode statNode = childMap.get(key);
        if(statNode == null) {
            statNode = childMap.computeIfAbsent( key,o -> {
                StatNode node = new StatNode();
                node.selfKey = key;
                node.toFightForce = toFightForce && this.toFightForce;

                // 记录父节点
                node.statRoot = this;
                // 继承属性锁
                node.lock = lock;
                return node;
            });
        }
        return statNode;
    }

    // 快速设置指定节点的属性
    public StatNode fastSet(Serializable k, StatEnum statEnum, long value) {
        Map<StatEnum,Long> stats = new HashMap<>(1);
        stats.put(statEnum, value);
        return fastSet(k, stats);
    }

    // 获取子节点
    public StatNode getChild(Serializable k) {
        String key = buildKey(k, false);
        return childMap.get(key);
    }
    // 是否拥有指定节点
    public boolean hashChild(Serializable k) {
        String key = buildKey(k, false);
        return childMap.containsKey(key);
    }

    // 当前节点的key
    @JSONField(serialize = false)
    public String getKey() {
        return selfKey;
    }

    // 该节点的所有属性,包括下属节点,是否算入战斗力
    public boolean isToFightForce() {
        return toFightForce;
    }
    // 获取当前节点属性,安全起见不要暴露给外部调用,避免被直接修改
    Map<StatEnum, Long> getStats() {
        return stats;
    }
    // 获取当前节点加成的万分比属性,安全弃考吗不要暴露给外部调用,避免被直接修改
    Map<StatEnum, Long> getRctStats() {
        return rctStats;
    }
    // 只读,获取当前节点加成的万分比属性
    public Map<StatEnum, Long> getRctStatsOnlyRead() {
        return rctStats;
    }
    // 标记该加成不计入战斗力
    public void notToFightForce() {
        this.toFightForce = false;
    }
    // 标记该加成不计入基础值
    public void notToBase() {
        this.toBase = false;
    }
    // 该节点的所有属性,包括下属节点,是否算入基础值
    public boolean isToBase() {
        return toBase;
    }
    // 是否已经被移除
    public boolean isRemove() {
        return remove;
    }

    @Override
    public String toString() {
        return statRoot.toString() + "-" + selfKey + "-";
    }

    // 获取或创建一个万分比加成节点
    public RctNode getOrCreateRctNode(Serializable k) {
        String key = buildKey(k, true);
        RctNode rctNode = rctNodeMap.get(key);
        if(rctNode == null) {
            rctNode = rctNodeMap.computeIfAbsent(key,o -> RctNode.valueOf(this));
        }
        return rctNode;
    }
    // 移出万分比加成节点
    public void removeRctNode(Serializable k) {
        String key = buildKey(k, false);
        lock.writeLock().lock();
        try {
            // 移出该万分比加成节点
            RctNode rctNode = rctNodeMap.remove(key);
            if(rctNode == null) {
                return;
            }
            refreshRctNode();
        } finally {
            lock.writeLock().unlock();
        }
    }

    // 刷新万分比加成的属性
    public void refreshRctNode() {
        if(MapUtils.imEmpty(rctStats) && rctNodeMap.isEmpty()) {
            // 历史没有加成,现在也没有加成
            return;
        }
        lock.writeLock().lock();
        try {
            Map<StatEnum, StatValue> baseTotal = getToal(false);
             Map<StatEnum, StatValue> newRctStats = new HashMap<>(baseTotal.size());
             rctNodeMap.values().forEach(rctNode -> StatUtils.mergeMap(newRctStats, rctNode.calRctAdd(baseTatal)));
        } finally {
            lock.writeLcok().unlock();
        }
    }

    // 所有子节点,慎用
    public ConcurrentHashMap<String, StatNdode> getChildMap() {
        return childMap;
    }

    // 构建跨服传输数据
    public StatNodeModel buildModel() {
        StatNodeModel model = StatNodeModel.valueOf(selfKey, toFightForce, toBase);
        if(this.rctMap != null) {
            // 当前节点属性
            List<Stat> stats = StatUtils.toListWithInt(this.stats);
            model.setStats(stats);
        }
        if(!childMap.isEmpty()) {
            // 子节点
            List<StatNode> childList = new ArrayList<>(childMap.size());
            for(StatNode node : childMap.values()) {
                childList.add(node.buildModel());
            }
            model.setChildNodeList(childList);
        }
        if(!rctNodeMap.isEmpty()) {
            // 万分比节点
            List<StatRctNodeMedel> rctList = new ArrayList<>(rctNodeMap.size());
            for(StatNode node : childMap.values()) {
                childList.add(node.buildModel());
            }
            model.setChildNodeList(childList);
        }
        if(!rctNodeMap.isEmpty()) {
            // 万分比节点
            List<StatRctNodeModel> rctList = new ArrayList<>(rctNodeMap.size());
            for(RctNode node : rctNodeMap.values()) {
                rctList.add(node.buildModel());
            }
            model.setRctNodeList(rctList);
        }
        return model;
    }

    // 解析跨服传输的属性
    public static void parseModel(StatNode root, StatNodeModel model ) {
        StatNode node = root.getOrCreateChild(model.getKey());
        parseModelOnlyChild(node, model);
    }

    // 解析跨服传输的属性
    public static void parseModelOnlyChild() {
        node.toFightForce = model.isToFightForce();
        node.toBase = model.isToBae();
        if(model.getStats() != null) {
            // 当前节点属性
            Map<StatEnum, Long> statMap = StatUtil.toMap2(model.getStats());
            node.replace(statMap);
        }
        if(model.getChildNodeList() != null) {
            // 子节点
            model.getChildNodeList().forEach(childModel -> parseModel(node, childModel));
        }
        if(model.getRctNodeList() != null) {
            // 万分比节点
            model.getRctNodeList().forEach(rctModel -> RctNode.parseModel(node, rctModel));
        }
    }

}

属性树信息

public class StatNodeModel() {
    // 当前属性节点的Key
    @Protobuf(description = "属性节点key")
    private String key;
    @Protobuf(description = "该节点属性是否算入战斗力")
    private boolean toFightForce;
    @Protobuf(descrption = "该节点属性是否计入基础值")
    private boolean toBase;

    @Protobuf(description = "属性")
    private List<Stat> stats;
    @Protobuf(description = "属性子节点")
    private List<StatNodeModel> childNodeList;
    @Protobuf(description = "属性子节点")
    private List<StatRctNodeModel> rctNodeList;

    public static StatNodeModel valueOf(String key, boolean toFightForce, boolean toBase) {
        StatNodeModel model = new StatNodeModel();
        model.key = key;
        model.toFightForce = toFightForce;
        model.toBase = toBase;
        return model;
    }
    // ......

}

属性来源


@Desc("属性来源")
public enum StatSource {
    @Desc("等级属性")
    level("等级属性"),
    @Desc("装备")
    equip("装备"),
    @Desc("装备基础属性")
    equip_base("装备基础属性"),
    ;

    // 节点中文名
    private String name;

    StatSource(String name) {
        this.name = nam;
    }

    public static String replaceSourceName(String statTree) {
        List<StatSource> statSources = Arrays.asList(StatSource.values());
        statSources.sort((o1, o2) -> o2.getName().length() - o1.getName().length());
        for(StatSource value : statSources) {
            String replaceKey = value.name();
            statTree = statTree.replace(replaceKey, value.getName());
        }
        return statTree;
    }

}

属性处理工具

public class StatUtils {
    // 属性比例基数
    public static final STAT_PATE = 10000;

    // 属性列表取负
    public static List<Stat> negative(List<Stat> stats) {
        if(stats == null) {
            return null;
        }
        List<stat> negativeStats = new ArrayList<>(stats.size());
        stats.forEach(stat -> negativeStats.add(Stat.valueOf(stat.getType(), -stat.getValue())));
        return negativeStats;
    }

    // 是否包含该节点的万分比属性
    public static Map<StatEnum, StatValue> toMap(StatNode staNode, boolean ) {
        Map<StatEnum, StatValue> map = new HashMap<>();
        // 基础属性
        Map<StatEnum, Long> stats = statNode.getStats();
        if (stats != null) {
            mergeToMap(map, statNode.isToBase(), stats, null);
        }
        if(includeRct) {
            // 万分比加成属性
            Map<StatEnum, Long> rctStats = statNode.getRctStats();
            if(rctStats != null) {
                mergeToMap(map, statNode.isToBase(), rctStats, null);
            }
        }
        return map;
    }

    // 属性map -> 属性简单map
    public static List<Stat> toListWithLong(Map<StatEnum, Long> map) {
        if(map == null) {
            return new ArrayList<>(0);
        }
        List<Stat> list = new ArrayList<>(0);
        map.forEach((key, value) -> list.add(Stat.valueOf(key, value)));
        return list;
    }

    // 属性map ->属性Stat List
    public satatic List<Stat> toListWithInt(Map<StatEnum, Long> map) {
        if(map == null) {
            return new ArrayList<>(0);
        }
        List<Stat> list = new ArrayList<>(map.size());
        map.forEach((key, value) -> list.add(Stat.valueOf(key, value)));
        return list;
    }

    // 属性map -> 属性Stat List
    public static List<Stat> toList(Map<StatEnum, StatValue> map) {
        if(map == null) {
            return new ArrayList<>(0);
        }
        List<Stat> list = new ArrayList<>(map.size());
        map.forEach((key, value) -> list.add(Stat.valueOf(key, value.getToatal())));
        return list;
    }

    // 包含基础值
        public static List<Stat> toStatExList(Map<StatEnum, StatValue> map) {
        if(map == null) {
            return new ArrayList<>(0);
        }
        List<Stat> list = new ArrayList<>(map.size());
        map.forEach((key, value) -> list.add(Stat.valueOf(key, value.getToatal(), value.getBase())));
        return list;
    }

    // 合并属性List到属性Map中,采用加法
    public static void mergeToMap(Map<StatEnum, StatValue> map, boolean isToBase, Map<StatEnum, Long> stats, Map<StatEnum, Long> changeMap) {
        mergeToMap(map, isToBase, stats, 1, changeMap);
    }

    // 合并属性List到属性Map中,采用减法
    public static void diffFromMap(Map<StatEnum, StatValue> map, boolean isToBase, Map<StatEnum, Long> stats, Map<StatEnum, Long> changeMap) {
        mergeToMap(map, isToBase, stats, -1, changeMap);
    }

    private static void mergeToMap(Map<StatEnum, StatValue> map, boolean isToBase, Map<StatEnum, Long> stats, int sign, Map<StatEnum, Long> changeMap) {
        if(stats == null) {
            return;
        }
        stats.forEach((statEnum, value) -> {
            value *= sign;
            // 数值加成
            StatValue statValue = getOrCreateStatValue(map, statEnum);
            long oldValue = statValue.getTotal();
            long newValue = statValue.alter(isToBase, value);
            if (changeMap != null) {
                changeMap.merge(statEnum, newValue - oldValue, Long::sum);
            }
            if(statEnum.isRatio()) {
                // 按比例加成
                for(StatEnum anEnum : statEnum.getRatioBase()) {
                    statValue = getOrCreateStatValue(map, anEnum);
                    oldValue = statValue.getTotal();
                    newValue = statValue.alterRatio(value);
                    if(changeMap != null) {
                        changeMap.merge(anEnum, newValue -oldValue, Long::sum);
                    }
                }
            }
        });
    }

    public static StatValue getOrCreateStatValue(Map<StatEnum, StatValue> map, StatEnum type) {
        StatValue statValue = map.get(type);
        if(statValue == null) {
            statValue = new SataValue();
            map.put(type, statValue);
        }
        return statValue;
    }

    // 合并2个map的值
    public static void mergeToMap(Map<StatEnum, StatValue> targetMap, Map<StatEnum, StatValue> sourceMap) {
        sourceMap.forEach((key, value) -> targetMap.merge(key, value, StatValue::merge));
    }

    // ......

    // 合并属性fromStats到属性map中
    public static void mergeMap(Map<StatEnum, Long> toStats, List<Stat> formStats) {
        fromStats.forEach((stat -> toStats.merge(stat.getType(), stat.getValue(), (value, value2) -> value1 + value2)));
    }

    // ......

    // 便捷的设置属性方法,如果需要设置statSrc1/child2/grandchild2的属性,可以这里写
    public static StatNode fastSet(AbstractCreature creature, Map<StatEnum, Long> statMap, StatSource statSrc, Serializable... keys) {
        CreatureGameStats stats = creature.getGameStats();
        StatNode node = stats.getOrCreateStatNode(statSrc);
        for(int i=0; i<keys.length; i++) {
            node = node.getOrCrateChild(keys[i]);
        }
        node.replace(statMap);
        return node;
    }

    // 设置属性
    public static StatNode fastSet(AbstractCreature creature, Map<StatEnum> statMap, StatSource statSrc, Serializable... keys) {
        GametureGameStats stats = creature.getGameStats();
        StatNode node = stats.getOrCreateStatNode(statSrc);
        for(int i=0; i< keys.leghth; i++) {
            node = node.getOrCreateChild(keys[i]);
        }
        node.notToFightForce();
        node.replace(statMap);
        return node;
    }

    // 便捷的设置属性方法
    public static StaNode fastSet(Player player, Map<StatEnum, Long> statMap, StatSource statSrc, Serializable... keys) {
        return fasrSet(player.getFightPlayer(), statMap, statSrc, keys);
    }

    public static void setRct(AbstractCreature creature, int baseRct, Serializable rctKey, StatSource statSrc, Serializable... keys) {
        StatNode statNode = getStatNode(creature, statSrc, keys);  
        RctNode rctNode = statNode.getOrCreateRctNode(rctKey);
        rctNode.replaceBaseRct(baseRct);
    }

    public static void setRct(Player player, int baseRct, Serializable rctKey, StatSource statSrc, Serializable... keys) {
        setRct(player.getFightPlayer(), baseRct, rctKey, statSrc, keys);
    }

    // 以指定的基础属性为总值,计算传入的万分比属性增加的属性值
    public static Map<StatEnum,Long> calRctStat(Map<StatEnum, Long> baseMap, Map<StatEnum, Long> rctMap) {
        Map<StatEnum, Long> result = new HashMap<>(baseMap.size());
        Map<StatEnum, Long> rctMerge = new HashMap<>(baseMap.size());
        rctMap.forEach((statEnum, value) -> {
            if(statEnum.isRatio()) {
                // 按比例加成
                for(StatEnum anEnum : statEnum.getRatioBase()) {
                    rctMerge.merge(anEnum, value, Long::sum);
                }
            } else {
                result.merge(statEnum, value, Long::sum);
            }
        });
        rctMerge.forEach((statEnum, value) -> {
            Long total = baseMap.get(statEnum);
            if(total != null) {
                result.merge(statEnum, total * value / STAT_RATE, Long::sum);
            }
        });
        return result;
    }

}

总结

  1. 游戏中的生物拥有GametureGameStats属性,其中挂载着生物所有具体属性。玩家则是拥有PlayerGameStats

  2. GametureGameStats持有总属性stats和根节点StatRoot