战斗系统重构后-buff系统学习记录
目录
- 我们项目技能系统的一些概念
- 表分析
- 向目标发送一个效果,主要用于非技能模块的效果发送
我们项目技能系统的一些概念
一.普攻:
1.可以点好几下,每一下都是一个子技能(也就是技能段)。
2.一个子技能有多个打击点,也就是点一下普攻,当前子技能有多个动作,
每个动作都可能做索敌判断。
3.蓄力技能(需要长按才能释放),例如:玄冥教普攻,先点击一下普攻按钮,客户端上发
601,这事长按普攻按钮一定时间,客户端上发609蓄力普攻技能(好几个打击点)。
接着再按下普攻,客户端上发610蓄力派生技能。
二.派发技能效果:
效果属性:时长、增益、减益,配置是否存库(有些buff存库,例如:30分钟内增加攻击力)
效果参数:每个效果参数不同
效果逻辑:逻辑代码(开放可视化重复利用云图编辑)
三.技能效果类:
AI实现
瞬发
持续
位移
带条件
...
表分析
1.由于之前的buff表列数过多,将buff表拆分
1.buff基础表SkillImpactBase 主要是条件,类似如下:
1.死后是否保留该效果(0否、1是)
2.是否增益效果
0:其他
1:增益类
2:负面类
3.跨场景是否消失(0否、1是)
4.下线是否消失(0否、1是)
5.开始移动时是否消失(0否、1是)
各种一大堆条件
2.SkillImpactParam参数表,由于特定效果的参数列非常多,故单独拆分出一张表:
状态影响效果产生的几率也是配在这里。
向目标发送一个效果,主要用于非技能模块的效果发送
1.计算派发概率
1.无成长
// 是否是无成长的效果
DictSkillImpactParamData dictSkillImpactParamData = impactToNoGrowthImpactMap.get(impactId);
if(dictSkillImpactParamData != null)
{
return dictSkillImpactParamData.getAddImpactOdds();
}
1.无成长的,直接表里配置概率(根据状态state是否大于0判断),效果产生几率SkillImpactBase表里配置参数名为addImpactOdds
2.所有的非技能模块都是无成长的,例如:
药品使用加buff
任何奖励
称号
3.注意目前陷阱派发走的是技能派发。
2.有成长
TLongIntHashMap stateLevelProbablityMap = impactOnStateLevelToProbablityMap.get(impactId);
if(CollectionUtils.isBlank(stateLevelProbablityMap))
{
return 0;
}
int noEntryValue = stateLevelProbablityMap.getNoEntryValue();
boolean hasState = false;
int rs = 0;
// 此处遍历非常非常频繁,所以不用迭代器,掏出来map的内部数组进行遍历
byte[] _state = stateLevelMap._states;
int[] _set = stateLevelMap._set;
int size = stateLevelMap.size();
for (int i = 0, iSize = _state.length; i < iSize; i++)
{
...
}
效果ID -> 技能状态 -> 状态等级 -> 概率
技能状态和状态等级会影响派发概率。
这里遍历非常频繁,所以不用迭代器遍历,直接掏出数组进行遍历。
3.属性修正概率
// 实际概率=表中对目标概率+攻击者对应ID命中属性-被攻击者对应ID抵抗属性
targetProb = targetProb + enchanceValue - declineValue;
SkillImpactBase表里会配置,属性修正,例如:派生中毒添加 217号属性会影响概率命中
4.随机概率
// 随机概率
if(targetProb < GameConstant.TEN_THOUSAND && RandomUtil.randomInt(GameConstant.TEN_THOUSAND) > targetProb)
{
return BPErrorCodeEnum.SKILL_IMPACT_PROBABLITY_FAIL;
}
如果当前概率小于10000,并且从0~10000随机取一个整数值 大于 当前概率, 那么属性派发概率失败。
这里有疑问,像任务这种其它系统派发buff, 那表里配置的概率都是超过10000吗?不然的话,有几率buff派发不上?
一般这种其它模块buff,没有特定需求需要概率派发,数值配置的概率都会大于10000,保证能派发上。
相当于,不同模块派发的buff概率类型会不同。
2.判断能否施加效果
1.普通判断
1.能否作用到死亡玩家身上
2.判断buff生效目标(玩家,npc,宠物,镜像,傀儡…)
3.判断目标细分类型
ALL(0,true,true), // 所有 (友好 √ 敌对 √ )
SELF(1,true,false), // 仅自己 (友好 √ 敌对 × )
ENEMY(2,false,true), // 敌对方 (友好 × 敌对 √ )
FRIENDLY_NO_ME(3,true,false), // 友方(不包括自己) (友好 √ 敌对 × )
FRIENDLY(4,true,false), // 友方(包括自己) (友好 √ 敌对 × )
2.大招受创绑定,不能上效果
1.代表受创的效果类型ID集合(指策划认为的播放受创动画),配置在GameCofig表里的beHitImpactTypeID列
int[] beHitImpactTypeIDs = getBeHitImpactTypeID();
if(!CollectionUtils.isBlank(beHitImpactTypeIDs))
{
for (int i = 0, iSize = beHitImpactTypeIDs.length; i < iSize; i++)
{
//index为表里配置的逻辑id索引,例如24&&30&&32&&33&&34&&35&&48
int index = beHitImpactTypeIDs[i];
if(index >= 0)
{
beHitImpactTypeSet[index] = true;
}
}
}
2.SkillImpactBase配置逻辑id参数名为logicID
3.如果当前处于大招绑定受创,那么不能上效果
// 大招受创绑定
int beBeatLockBindSender = getBeBeatLockBindSender();
if(beBeatLockBindSender > 0)
{
if(sender.getObjectID() != beBeatLockBindSender)
{
boolean flag = DictGameConfigData.isBeHitImpactType(impactData.getLogicID());
if(flag)
{
return BPErrorCodeEnum.SKILL_CANNOT_IMPCAT_BE_HIT_BIND;
}
}
}
3.击退转换
1.如果SkillImpactBase表配置的是击退效果,那么SkillImpactParam不能配置成长。
// 击退转换效果,目前写死
if(dictSkillImpactBase.getLogicID() == 24)
{
if(!paramNoGrowthImpactSet.contains(dictSkillImpactBase.getId()))
{
checkOk &= false;
BPLog.BP_LOGIC.error("【技能表加载错误】SkillImpactParam加载错误,SkillImpactBase表中配置的是击退效果,但是在SkillImpactParam出现了成长! [param-id] {} [impactID] {}",dic.getId(),impactId);
continue;
}
int transferID = dic.impactParams[10];
if (transferID > 0)
{
beatMoveTransferMap.put(dic.getId(), transferID);
}
}
如果SkillImpactBase表配置了逻辑id为24,需要将当前的效果转为对应的转换效果id(击退转换击飞)。
4.buff类的基本设计
1.抽象的技能效果基类AbstractSkillImpact
1.抽象类实现接口
/**
* 技能的效果抽象基类
* Created by wangqiang on 2017/7/19.
*/
public abstract class AbstractSkillImpact implements Reusable
{
}
效果抽象基类,实现Reusable接口,接口里有清除方法
2.主要的类成员变量,一些buff公用的特性提到抽象层面
/**
* 派发的技能相关信息
*/
protected SkillEffectInfo skillEffectInfo;
/**
* 技能效果配置
*/
protected SkillImpactConfigData configData;
/**
* 效果发送者的objectID
*/
private int senderObjectID;
/**
* 是否是自己发送给自己的
*/
private boolean isSelfSend;
/**
* 效果逻辑剩余时间
*/
protected long remainTime;
/**
* 效果总持续时间
*/
protected long remainTimeAll;
省略------------------
2.给属性归类,各属性再抽出一层抽象
1.改变属性buff的基类
/**
* 属性改变类BUFF的基类
* Created by Wang Lizhi on 2018/4/10.
*/
public abstract class AbstractSkillImpactAttrBase extends AbstractSkillImpact
- 受击位移效果的基类
/**
* 受击位移效果的基类
* Created by Wang Lizhi on 2018/12/6.
*/
public abstract class AbstractSkillImpactBeatMoveBase extends AbstractSkillImpact
3.强控类效果逻辑 基类(持续性buff)
/**
* 强控类效果逻辑 基类(持续性buff)
* Created by Wang Lizhi on 2017/8/18.
*/
public abstract class AbstractSkillImpactControlBase extends AbstractSkillImpact
主要是给相近的属性类型再抽出一层。
5.创建效果实例
1.初始化技能效果缓存
1.AbstractScene场景基类下,init()方法
sceneCache.initSkillCache(dictSceneDefine);
2.初始化缓存的基数为,指定长的对象队列长度:
1. 每条线最大承载人数 + 20 缓存基数
2. 如果缓存基数 >= 10, 那么去1 /2
3. 如果缓存基数小于1,那么缓存基数等于1
3.根据buff逻辑id,每个逻辑new一个ArrayDeque队列,加入缓存
ObjectQueue objectQueue = new ObjectQueue(baseNumber, new CreateImpact000());
skillImpactCache.add(objectQueue);
objectQueue = new ObjectQueue(baseNumber, new CreateImpact001());
skillImpactCache.add(objectQueue);
2.定长的队列设计
/**
* 定长的对象队列,设计初衷是为了技能逻辑.技能效果的对象缓存
* 如果poll的时候池内为空,增新new一个,如果add的时候池已满
* 则直接丢弃,等待垃圾回收
* 非线程安全
*/
public class ObjectQueue <T extends Reusable>
{
private ArrayDeque<T> queue;
/**
* 当前长度
*/
private int size;
/**
* 总容量
*/
private int capacity;
/**
* 对象实例化的方法
*/
private CreateTemplateInstance<T> function;
/**
* 构造函数初始化,容量和模板
* @param capacity
* @param func 所有实例都需要实现该接口,buff效果实例也需要实现该接口
*/
public ObjectQueue(int capacity, CreateTemplateInstance func)
{
queue = new ArrayDeque<>(capacity);
size = 0;
this.capacity = capacity;
function = func;
}
public ObjectQueue(T[] t)
{
this.capacity = t.length;
queue = new ArrayDeque<>(capacity);
size = 0;
for (int i = 0; i < capacity; i++)
{
size++;
queue.addLast(t[i]);
}
}
/**
* 如果队列已经为空则新new一个对象,否则弹出,队列长度减1
* @return
*/
public T poll()
{
if (size == 0)
{
if (function == null)
{
return null;
}
else
{
//队列为空时则新new一个对象
return function.createTemplateInstance();
}
}
size--;
return queue.poll();
}
/**
* T extends Reusable
*
* 添加进来的对象T实现了Reusable接口
*
* T会统一调用clear()方法,各个对象在重新加入对象池时,
*
* 自己覆写clear()方法,先进行清除操作,再添加进来
*
* 队列长度增加
* @param object
*/
public void add(T object)
{
if (object == null)
{
return;
}
if (size < capacity)
{
//调用clear()方法,各个对象自己覆写方法
object.clear();
queue.addLast(object);
size++;
}
}
}
- 定长的对象队列,设计初衷是为了技能逻辑.技能效果的对象缓存,如果poll的时候池内为空,增新new一个,如果add的时候池已满
则直接丢弃,等待垃圾回收,非线程安全。
2.添加进来时需要先清除数据。
3.根据效果逻辑id去池里拿技能效果实例
// 创建效果实例
AbstractSkillImpact skillImpact = SkillHelper.createSkillImpact(impactData.getLogicID(), sender.getScene());
if (skillImpact == null)
{
return BPErrorCodeEnum.SKILL_SKILL_IMPACT_ID_IS_NOT_EXISTED;
}
根据逻辑id,从效果池里拿出实例。
逻辑id可以理解为buff类型,是从0开始的,逻辑上抽象。
4.初始化技能效果
/**
* 根据 效果状态等级 获取 效果参数集合
* @param impactId
* @param stateLevelMap 派发该技能效果的技能的状态和等级 如果带成长的效果吧不为空或者null
* @param paramDataList 所有的技能效果的参数配置
*/
public static void getImpactParamsOnStateLevel(int impactId, TIntIntHashMap stateLevelMap, ArrayList<DictSkillImpactParamData> paramDataList)
当前效果id,有状态和状态等级,根据传进来的stateLevelMap,计算一些初始化数据,例如:当前效果持续总时长,是从低状态对应的时长,一直累加到当前状态等级对应的持续时长,
为什么需要从头开始累加?每次都计算?而不是当前状态等级对应当前持续时长?
主要初始化一些:
/**
* 技能效果的参数配置 (index为状态和状态等级处理后的索引)
*/
private ArrayList<DictSkillImpactParamData> impactParamDataArrayList;
初始化做大操作,效果id对应的状态和状态等级所有行,例如:效果id为1010,对应状态id为1,状态有1~30等级,所有行数据。
/**
* 总时长(对怪)
*/
private int durationTime;
/**
* 总时长(对玩家)
*/
private int durationTimeSpecial;
/**
* 互斥等级
*/
private int mutexLevel;
/**
* 叠加等级
*/
private int repeatLevel;
总时长,逻辑id对应所有参数配置,对怪的持续时长累加。总时长(对玩家),类似。
mutexLevel总buff互斥等级,buff互斥等级高的会覆盖低的等级。
repeatLevel状态叠加评级,作为BUFF是否能够叠加的判断,当同ID的impact所有状态之和相等时,BUFF能够叠加。
/**
* 驱散等级
*/
private int buffLevel;
buffLevel 驱散效果带有驱散等级,低驱散等级的效果无法驱散高状态等级的BUFF
// 计算次数
this.totalEffectCount = 1;
if (impactBaseData.getImpactType() == SkillImpactTypeEnum.PERSISTENT.getValue())
{
int durationTime = this.durationTime;
int intervalTime = impactBaseData.getIntervalTime();
this.totalEffectCount = durationTime / intervalTime;
this.totalEffectCount = (this.totalEffectCount <= 0 ? 1 : this.totalEffectCount);
}
效果的总生效次数
/**
* 效果实际总持续时间
*/
protected long remainTimeAll;
判断下如果接受者不是玩具那么取durationTime,如果是玩家durationTimeSpecial
/**
* 激活的剩余延迟时间
*/
protected int delayRemainTime;
效果的首次生效延迟生效时间(毫秒)
5.如果是技能派发效果初始化操作
设置技能打击点信息,技能被动主动信息,霸体信息
// 技能段的伤害系数、技能段扣除的霸体值,从这里设置进效果
if(impactData.getLogicID() == 0)
{
if(skillImpact instanceof SkillImpact000)
{
SkillImpact000 skillImpact000 = (SkillImpact000)skillImpact;
skillImpact000.setMinusEndure(senderCurrentSkillLogic.getEndureMinus());
////通过 子技能ID 和 段数获取 数据
DictSkillDataAssitData skillDataAssitData = DictSkillDataAssitData.getRecordBySubSkillAndEffect(subSkillID, senderCurrentSkillLogic.getTakeEffectCount());
if(skillDataAssitData != null)
{
int damageImpact = skillDataAssitData.getDamageImpact();
int damageRatio = skillDataAssitData.getDamageRatio();
if(damageImpact > 0 && damageRatio > 0 && damageImpact == impactData.getId())
{
skillImpact000.setExternalRatio(damageRatio);
}
}
}
else
{
BPLog.BP_FIGHT.warn("【技能效果错误】技能效果logicID为0,但是技能效果类不是 SkillImpact000 [impactId] {}",impactData.getId());
}
}
skillImpact.setHitIndex(hitIndex);
skillImpact.setEndure(isEndure);
6.补充一下,什么是段数?什么是打击点?
1.段数概念
可以简单理解为,一个技能可以连续点击多少下,例如:技能轮盘里,有些技能是可以连续点击多下,通常这种技能界面展示会与其它没有断数的技能区分出来
类似上图,单段的是一个圆圈,多断可以看到一个圆圈切分好几段代表可以连续点击四次。
那为什么设计游戏多段攻击和单段攻击?
单次伤害怕格挡,多次不怕。
单次伤害爆发高 伤害来的快。
多段伤害有一个好处就是在游戏中后期不会太怕格挡这个技能。
2.打击点概念
简单理解为,点一下技能,改技能可以释放多少下。
6.派发逻辑
/**
* 接收一个impact效果
*
* @param skillImpact
* @return
*/
public int receivedImpact(AbstractSkillImpact skillImpact)
1.收到不良效果,触发攻击事件
例如:npc受到攻击做一些状态的切换
2.判断能否上检测
受创(指播放动画被攻击的画面),绝学,轻功…不能上检测。
3.效果反弹处理
// 自己给自己上的,不反弹,不然也会出现死循环
if(skillImpact.getSenderObjectID() == character.getObjectID())
{
return false;
}
不能反弹给自己,不然,自己再反弹自己,出现死循环。
// 处理反弹事件
List<AbstractSkillImpact> listenReboundImpacts = getEventListenBuffsByEvent(BuffListenEventEnum.EVENT_TYPE_IMPACT_REBOUND.getType());
if (!CollectionUtils.isBlank(listenReboundImpacts))
{
for(int i = 0 , size = listenReboundImpacts.size(); i < size; i++)
{
int flag = listenReboundImpacts.get(i).handleEvent(BuffListenEventEnum.EVENT_TYPE_IMPACT_REBOUND,character, 0,0,0,0,0,0,0,0,skillImpact,null);
if (flag < 0)
{
return true;
}
}
}
传入反弹监听类型,迭代当前所有的反弹buff监听列表,判断是否需要处理反弹事件。
* <pre>
* 39号效果逻辑(持续性BUFF)
*
* 描述:
*
* 反弹状态
*
* 功能说明:
*
* 给目标加一个持续性的监听事件的效果,效果会根据弹射参数,将受到的效果进行反弹给周围符合条件的人或者发送者。
* (遇到反伤,返还给最初施法者。也就是说反弹出去的效果的发送者都是自己)
*
* 参数说明:
* 参数1: 反弹效果类型 : 1.正面效果 2.负面效果 3.全部效果
* 参数2: 反弹方式 : 1.反弹给施法者 2.周围敌方 3.周围友方 4.周围所有目标
39号效果,是处理反弹的
4.玩家自身是否有对该效果的抵抗
// 玩家自身是否有对该效果的抵抗
boolean resistFlag = dealImpactResist(skillImpact);
if(resistFlag)
{
//玩家对技能效果有抵抗,技能效果不被施加
return BPErrorCodeEnum.SKILL_SELF_HAS_RESIST_IMPACT;
}
如果玩家处于39号霸体,或50号状态免疫效果…, 当前要上的效果驱散等级不满足,那么效果无法派发,被免疫。
/**
* 处理效果抵抗
* @param skillImpact
* @return
*/
private boolean dealImpactResist(AbstractSkillImpact skillImpact)
{
List<AbstractSkillImpact> listenResistImpacts = getEventListenBuffsByEvent(BuffListenEventEnum.EVENT_TYPE_IS_SHOULD_RESIST_IMPACT.getType());
if (!CollectionUtils.isBlank(listenResistImpacts))
{
for(int i = 0 , size = listenResistImpacts.size(); i < size; i++)
{
SkillImpactConfigData configData = skillImpact.getConfigData();
DictSkillImpactBaseData impactBaseData = configData.getImpactBaseData();
//传入BUFF组ID(关联IDCollection表), 驱散等级
boolean resistFlag = listenResistImpacts.get(i).isResistImpact(impactBaseData.getGroupID(), configData.getBuffLevel());
if (resistFlag)
{
return true;
}
}
}
return false;
}
同样根据类型获取监听列表,传入BUFF组ID(关联IDCollection表), 驱散等级。
这里我们只看38号效果逻辑,其它同类型效果类似。
* 38号效果逻辑(持续性BUFF)
*
* 描述:
*
* 霸体效果
*
* 功能说明:
*
* 霸体效果是将身上一系列的不良技能效果移除,并且一段时间免疫这些不良效果,不能加到身上来
*
* 参数说明:
* 参数1: 霸体等级
* 参数2: 驱散免疫效果集合1
* 参数3: 驱散免疫效果集合2
* 参数4: 驱散免疫效果集合3
* 参数5: 驱散免疫效果集合4
@Override
public boolean startImpactImpl(AbstractCharacter character)
{
int[] params = configData.getImpactParams();
level = params[0];
int levelStep = configData.getImpactSteps()[0];
// 等级
if (level < 0)
{
BPLog.BP_FIGHT.error("【技能效果038派发错误】 参数 效果等级错误 [参数值] {} [impactId] {}", level, configData.getImpactBaseData().getId());
return false;
}
//注册可以作用的等级
level += getActualSkillLevel() * (levelStep / GameConstant.TEN_THOUSAND);
// 注册可以集合
for (int i = 1; i < 15; i++)
{
int idCollectionParam = params[i];
if (idCollectionParam > 0)
{
set.add(idCollectionParam);
}
}
if (CollectionUtils.isBlank(set))
{
BPLog.BP_FIGHT.error("【技能效果038派发错误】 参数 驱散集合Id都没有配置 [impactId] {}", configData.getImpactBaseData().getId());
return false;
}
// 清除逻辑
ArrayList<AbstractSkillImpact> buffList = character.getSkillModule().getBuffList();
if (CollectionUtils.isBlank(buffList))
{
return true;
}
for (int i = 0, size = buffList.size(); i < size; i++)
{
AbstractSkillImpact buff = buffList.get(i);
if(!isImpactSuit(buff,level))
{
continue;
}
toCleanList.add(buff);
}
if (!CollectionUtils.isBlank(toCleanList))
{
for (int i = 0, size = toCleanList.size(); i < size; i++)
{
toCleanList.get(i).breakImpactOnlyForCannotDispel(character);
}
toCleanList.clear();
}
return true;
}
注册可以作用的等级,注册可以清除霸体的集合。
这里作用等级计算暂时还没搞明白,为什么这么计算?
level += getActualSkillLevel() * (levelStep / GameConstant.TEN_THOUSAND);
@Override
public boolean isResistImpact(int impactGroupId, int impactLevel)
{
if(super.isResistImpact(impactGroupId, impactLevel))
{
return true;
}
// 先判断等级能减少计算量
//如果传进来要上的buff传入的驱散等级,大于霸体驱散等级,
//霸体的抵抗效果失效,该效果可以派发,返回false
if(impactLevel > level)
{
return false;
}
//如果当前要派发的效果,刚好是属于可以清除霸体集合里的buff,那么霸体的抵抗效果失效,返回false
boolean hasInCollection = false;
for(TIntIterator iterator = set.iterator(); iterator.hasNext();)
{
//技能组ID是否属于某集合ID下
boolean flag = DictIDCollectionsData.isImpactGroupIdInIdCollection(iterator.next(),impactGroupId);
if(flag)
{
hasInCollection = true;
}
}
if (!hasInCollection)
{
return false;
}
return true;
}
38号霸体默认是对当前要派发的buff有抵抗,默认返回true。
如果传进来要上的buff传入的驱散等级或者叫作用等级,大于霸体作用等级,霸体的抵抗效果失效,该效果可以派发,返回false。
如果当前要派发的效果,刚好是属于可以清除霸体集合里的buff,那么霸体的抵抗效果失效,返回false。
4.处理BUFF转移
5.处理BUFF 状态互斥,主要应用于控制状态类buff
* 处理效果控制状态互斥
* <p>
* 方法内部对控制状态的互斥关系进行了处理: 互斥(不能上) / 共存(可以上) / 顶替(顶旧BUFF)
* </p>
*
状态互斥技能不会是瞬发技能
6.往buff列表中增加一个效果
* 往buff列表中增加一个效果
* ( 添加到waittingBuffList中,下一帧tickBuff列表前,会被增加到buff列表中)
private int addSkillImpact(AbstractSkillImpact skillImpact)
{
...
if (buffSize + addOnForeachSize >= SkillCommonConstant.BUFF_LIST_MAX_SIZE)
{
// 当技能效果过大时,可能是连续多次升级导致的被动效果高级别顶低级别导致的,可以加个过滤finish状态效果的容忍
for (int i = 0,iSize = buffList.size();i < iSize; i++)
{
AbstractSkillImpact impact = buffList.get(i);
if(impact == null)
{
continue;
}
if(impact.isFinished())
{
buffSize--;
continue;
}
}
if(buffSize + addOnForeachSize >= SkillCommonConstant.BUFF_LIST_MAX_SIZE)
{
return BPErrorCodeEnum.SKILL_IMPACT_TARGET_BUFF_SIZE_MAX;
}
}
// 效果加到身上
int rs = 0;
if (buffForeachFlag)
{
// 此时正在遍历BUFF集合,先加入BUFF添加缓冲队列
addOnForeachBuffList.add(skillImpact);
}
else
{
rs = doAddSkillImpact(skillImpact);
}
}
判断效果列表是否超出上限,当前如果正在tick buff,那么添加到临时列表,下一帧合并到buff列表。
一般不会出现这种情况,预防跨线程派发buff?
添加到buff列表,添加buff事件。
到此派发逻辑就结束了。
7.ticK buff生效
1.先清一遍结束的效果
1.先清一遍结束的效果,然后执行onFinish方法,防止buff顶替的时候,新BUFF先tick,旧BUFF再执行删除导致的执行顺序错误
结束效果时要做的事:
1.解绑BUFF事件监听
2.各個BUFF结束该做的事,只有BUFF生效才会调用(每个效果执行完的回调)
3.持续性需要下掉buff
4.从buff列表中移除
buffList.remove(i);
5.放入技能池回收
scene.getSceneCache().recycleSkillImpact(impact.getConfigData().getImpactBaseData().getLogicID(), impact);
2.效果tick
1.注册事件监听
character.getSkillModule().registeToEventListenMap(this);
2.发送客户端
3.效果正式激活前各自需要做的事
4.效果触发的tick逻辑
tickTrigger(character, interval):
1.瞬发效果
立即调用toStartImpact()生效
2.持续性效果
是否是继续BUFF效果,可间隔作用BUFF
3.处理叠加(例如:持续性掉血)
5.检查是否结束
3.举个例子2号治疗效果逻辑(瞬发 + 持续性)
/**
* 效果开始生效
*
* @param character
*/
private void toStartImpact(AbstractCharacter character)
{
// 自己如果在使用绝学过程中,身上的所有impact都不生效
if (!character.isUsingJueXueStatus())
{
boolean flag = startImpact(character);
// 作用成功标志默认false,作用成功一次就算成功
this.impactEffective |= flag;
}
// 查找下一个效果需要派发的目标,只针对单体技能
// 默认为不实现
findSingleTarget();
}
每个效果调用各自的stratImpact,效果生效。
@Override
public boolean startImpactImpl(AbstractCharacter character)
{
...
// 固定治疗值
int fixedValue = impactParams[0];
int fixedValueStep = impactSteps[0];
int realFixedValue = (int) (getActualSkillLevel() * (fixedValueStep / GameConstant.TEN_THOUSAND_DOUBLE) + fixedValue);
// 附加攻击者魔法攻击(内功)的万分比治疗量
int magicAttackPercent = impactParams[1];
int magicAttackPercentStep = impactSteps[1];
int realmagicAttackPercent = magicAttackPercent + magicAttackPercentStep * getActualSkillLevel();
// 附加攻击者血上限的万分比治疗量
int senderPercent =impactParams[2];
int senderPercentStep = impactSteps[2];
int realSenderPercent = senderPercent + senderPercentStep * getActualSkillLevel();
// 附加受击者血上限的万分比治疗量
int selfPercent =impactParams[3];
int selfPercentStep = impactSteps[3];
int realSelfPercent = selfPercent + selfPercentStep * getActualSkillLevel();
// 从公式获取加血值
int hpAddtion = FormulaFacade.countHealValue(sender, character, realFixedValue, realSenderPercent, realmagicAttackPercent,realSelfPercent);
// 加血
character.getAttributeLogic().addOneAttribute(AttributeTypeEnum.HP, hpAddtion);
// 发送加血通知
doHealing(character, SkillCommonConstant.HEAL_TYPE_HP, hpAddtion, getSubSkillId(),getSkillSign(), configData.getImpactBaseData().getId());
}
加血操作
8.战斗计算公式类设计
1.战斗公式计算基础类
/**
* 战斗公式计算基础类
*/
public abstract class AbstractFormulaBase
2.子类
1.会心
/**
* 会心战斗公式
*/
public class FormulaCrit extends AbstractFormulaBase
2.元素类
/**
* 元素类战斗公式
*/
public class FormulaElement extends AbstractFormulaBase
3.治疗类
public class FormulaHeal extends AbstractFormulaBase
3.命中类
public class FormulaHit extends AbstractFormulaBase
省略。。。