属性系统学习笔记
概述
持有属性物品抽象
生物属性管理器
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;
}
}
总结
游戏中的生物拥有
GametureGameStats
属性,其中挂载着生物所有具体属性。玩家则是拥有PlayerGameStats
GametureGameStats
持有总属性stats
和根节点StatRoot