和战斗相关的模块有mod_battle.erl
当玩家进程和怪物进程被创建的时候都会通过mod_battle:start_link()创建一个战斗进程。
该战斗进程的state,用于保存玩家上次出手或者使用技能的时机,用于cd的判断
-record(state, {
last_attack_time=0, % 上次出手时间
last_skill_time = [] % [{技能id,上次时间}]
}).
开始战斗的时候,生成攻击方和防守方的#battle_status记录实例,战斗中的伤害通过这两个记录实例来计算
战斗存在三种情况:玩家打怪,怪打玩家,玩家打玩家
战斗的大致流程:
攻击方指定防守方Id,指定使用的技能Id,向服务器发送攻击请求(20001,20003)
进入攻击方的战斗进程内,通过ETS获取防守方信息,进行攻击距离,cd,技能配置等检查
检查通过,计算双方持续效果buff,计算技能buff,计算伤害
更新持续buff列表,给防守方玩家(怪物)进程发送消息(Hp,Mp,坐标变换),更新双方状态,回写ETS,给客户端广播战斗结果
代码流程:
mod_battle:battle(Pid, Data) -> 发到战斗进程处理
start([Aer,Der,SkillId,State]) -> 初始化双方#battle_status
attack([Aer1,Der1,SkillId],[Aer,State]) -> 判断攻击是否有效,保存新的攻击者状态
skill(Aer,Der,SkillId,SkillLv,State) -> 判断技能cd,距离,群攻单攻
double_active_skill(Aer,Der,SkillData,State) 群体技能
/single_active_skill(Aer,Der,SKillData,State) -> 单体技能
cale_aer/der_last_effect/4 计算自身持续buff
+ cale_active_effect/6 计算技能buff
+ cale_hurt/2 计算伤害
具体战斗流程:
先进行出手频率检查:Time - State#state.last_attack_time >= Aer#player_status.att_speed
出手频率检查通过,进入start([Aer, Der, SkillId, State]).
%%开启一个战斗服务
start([Aer, Der, SkillId, State])->
Aer1 = init_data(Aer),
Der1 = init_data(Der),
attack([Aer1, Der1, SkillId], [Aer, State]).
将#player_status或者#ets_mon转换为#battle_status,传入attack([Aer, Der, SkillId], [AerInit, State])
attack([Aer, Der, SkillId], [AerInit, State])
获取玩家技能详细数据,在skill(Aer, Der, SkillId, SkillLv, State)中使用技能
技能使用结果分为两种情况:使用成功和使用失败
skill(Aer, Der, SkillId, Lv, State)
使用主动技能,进行如下几项判断:
技能配置是否存在:获取#ets_skill
技能cd是否已达到:State#state{last_skill_time = [{SkillId, Time}]
玩家MP是否足够:#ets_skill.data内的{mp_out, MpOut}
攻击距离是否足够:#ets_skill.attarea
主动还是被动技能,被动技能则使用失败:#ets_skill.type
判断是群攻还是单攻:#ets_skill.mod,分别调用double/single_active_skill/2
single_acvite_skill(Aer, Der, SkillData, State)
单体攻击
计算攻击方原有加成buff:buff:cale_aer_last_effect(Aer#battle_status.batle_status, Aer, [], NowTime)
其中#batle_status.battle_status为[{K,V,T}]的列表结构,K为加成的属性名称,V为加成属性的值,T为加成过期时间,与当前时间比较,如果没过期则给#battle_status计算加成,如果过期了则把该加成去掉,最后返回一个#battle_status
防守方加成buff计算同理
之后计算技能加成:cale_active_effect(Data , Aer1, Der1, Aer#battle_status.battle_status, Der#battle_status.battle_status, Time)
其中Data为技能效果列表,在#ets_skill.data中,格式为{K,V}
除了直接加成数值的效果,有一些值得注意的:
drug:加毒,需要定时给防守一方的战斗进程发送消息,造成持续掉血效果
shield:法盾,持续一定效果,给防御方的#battle_status.battle_status加效果
last_def_del:持续减防,带有概率,在加持续效果之前先进行随机
通过随机数值判断是否打退,若打退则返还新的防守方坐标[X, Y]
计算伤害:将攻击方和防守方的二级属性传入cale_hurt,返还[Hpb,Mpb,Hurt,Status]
伤害计算公式:
命中:Hit = (0.25 + Hita / (Hita + Dodgeb) * 1.3)
暴击:Crit = Crita/(Crita + Tenb)
基本伤害:Att = (Atta*Atta) div (Atta + Defb)
最终伤害:没暴击trunc(Att/3),暴击trunc(Att*(1+Critical)/3)
给防御方发送战斗结果
防御方是怪物:在怪物进程内接收,修改#ets_mon的Hp,Mp,X,Y并写回?ETS_MON,触发人物加经验,任务杀怪,副本杀怪,怪物掉落事件
防御方是人物:在玩家进程内接收,修改#player_status,如果玩家死亡则触发死亡事件
生成战斗结果:
战斗成功:调用send_msg,发送20001协议,将攻击者新状态写回
战斗失败:调用battle_fail,发送20005协议
群攻的情况:double_active_skill(Aer,Der,SkillData,State)
先计算攻击方的持续buff效果和技能buff
获取攻击者附近的人物和怪:get_user_for_battle和get_mon_for_battle
从?ETS_ONLINE和?ETS_MON中取
其他计算防御方的buff,计算伤害同单攻
使用辅助技能:use_assist_skill([Aer,Der,SkillId,State])
玩家是否有该技能
技能配置是否存在
判断技能cd:#ets_skill.cd
判断MP:从#ets_skill.data中取{mp_out, MpOut}
判断释放目标:#ets_skill.obj,或者传入的Der={}
以自己为目标:
判断单攻还是群攻:#ets_skill.mod
单攻:single_assist_skill(Aer,Der,SkillData) 作用于自己
判断是否持续性类型:#ets_skill.lastime > 0
持续性类型:cale_assist_last_effect,并将新的持续效果发送给玩家进程并写回,发送20006协议
一次性使用类型:cale_assist_one_effect,目前只有加血,将血量写回玩家进程,发送20006协议
群攻:double_assist_skill(Aer,_Der,SkillData),这里是作用于队伍成员,从?ETS_ONLINE中找,其他同单攻
以他人为目标:先用check_attarea/2判断攻击距离,之后同上
修改完毕的#player_status.battle_status,通过'BATTLE_STATUS'发送回玩家进程并写回