诛仙的攻击过程

来源:互联网 发布:嵌入式linux开发 编辑:程序博客网 时间:2024/06/10 15:01

普通攻击

人发出的攻击,客户端会发送给服务器C2S::NORMAL_ATTACK命令,然后new一个session_normal_attack,在其中调用gactive_imp::NormalAttack;怪发出的攻击,在session_npc_attack中调用gactive_imp::NormalAttack。
然后调用gactive_imp::DoAttack,DoAttack是虚函数,有不同的实现。
人和怪的DoAttack都会形成一个attack_msg,经过_filters.EF_TranslateSendAttack的处理后发给被攻击者。不同的是,人还会进入战斗状态,并通知宠物和召唤兽进行攻击,并按概率减少武器的魂力 (装备的魂力是在gplayer_imp::AdjustDamage中减少的) 。
然后攻击消息GM_MSG_ATTACK就发到了被攻击者那里,此时强制攻击、红名、白名之类的还没有判断。

对方收到攻击消息后,会判断阵营,经_filters.EF_TranslateRecvAttack处理,最后调用HandleAttackMsg。

普通技能

人发出的攻击,客户端会发送给服务器C2S::CAST_SKILL命令,对辰皇进行了特殊处理,对连续技也进行了特殊处理。

连续技:
GNET::SkillWrapper::GetPreCastSkillId返回参数表示的技能id的前提技能,这是一个静态成员函数,系统把连续技的前提技能存在一个static map里,如果当前技能有前提技能,查看当前的session是不是前提技能的session。

辰皇:
如果处于元无间状态,释放特定技能,由灵使用spirit_session_skill来释放该技能,spirit_session_skill的特殊之处在于它的GetExclusiveMask返回0,表示在有spirit_session_skill时,可以添加其他session,而session_skill的GetExclusiveMask返回~(SS_MASK_MOVE),表示在有spirit_session_skill时,只能添加move session。

下面进入session_skill:
session_skill::StartSession调用GNET::SkillWrapper::StartSkill,返回第一击的时间,如果小于50毫秒则是瞬发技能, 调用GNET::SkillWrapper::Run;否则,进入session定时机制,第一击结束后,调用GNET::SkillWrapper::Run,若后面还有段数,不断定时调用GNET::SkillWrapper::Run。

如果是蓄力技能,蓄力结束会调用session_skill::RestartSession,调用GNET::SkillWrapper::Continue,并把蓄力的tick传给它。

在SkillWrapper::StartSkill中,create技能,会检查skill->Condition();和冷却时间,检查pray距离,执行Skill::FirstRun,destroy技能。

反正最后会调用:

bool Run(const SkillStub::State *state){    state->Calculate(this);    return true;}

player.perform=x;会被解析为skill->GetPlayer()->SetPerform(x);
在bool PlayerWrapper::SetPerform(int inform)中:
如果攻击范围是自己,先执行StateAttack,再执行BlessMe,再执行CalcCost扣蓝,直接返回。
如果攻击范围不是自己,先执行BlessMe。

如果是攻击技能,形成一个attack_msg:

    object.FillAttackMsg(XID(-1,-1), msg, 0);     msg.crit_rate    += (int)(1000*skill->GetCrit());    msg.crit_factor  += skill->GetCrithurt();    msg.skill_attack_rate  += skill->GetSkillaccu();

object.Attack(*target,msg,0);
根据攻击范围不同选择调用:
object.RegionAttack1
object.RegionAttack2
object.RegionAttack3

如果是祝福或诅咒,形成一个enchant_msg,然后调用:
object.Enchant
object.RegionEnchant1
object.RegionEnchant2
object.RegionEnchant3

然后计算消耗CalcCost();

object_interface::Attack很简单,直接发送GM_MSG_ATTACK消息。
object_interface::Enchant很简单,直接发送GM_MSG_ENCHANT消息。
Region类的则会收集周围的目标,发消息出去,对人数的控制是在gmatrix::SendMultiMessage里实现的。
GetCoverage返回最多的攻击人数。
这里有个坑,收集目标的时候是收集所有active的东西,而对怪无效、对人无效是在被攻击那里判断的,这就导致对怪无效的群攻技能有时候打不到足够多的人。

然后发消息到了被攻击者,如果SetPerform参数不为0,还会调用一个PlayerWrapper::DoPerform,这里处理了技能的冷却。

被攻击者

普攻和攻击技能通过GM_MSG_ATTACK消息发送,祝福和诅咒技能通过GM_MSG_ENCHANT消息发送。前者会调用HandleAttackMsg,后者会调用HandleEnhantMsg。

在gactive_imp::HandleAttackMsg中:
首先判断一些攻击限制和阵营

计算命中和基础伤害(gactive_imp::AttackJudgement):
如果是物理攻击,命中的计算如下:
int rate = (普攻命中 + 180)/3 - (普攻躲闪 - 55)/2;
if(rate < 40) rate = 40;
if(rate > 99) rate = 99;
技能攻击(技能命中是面板显示值减去100后再乘10,技能躲闪是面板显示值的10倍):
Rand(0,1000) < 1000+技能命中-技能躲闪
每次命中概率其实就是技能命中-命中躲闪

基础伤害=rand(damage_low, damage_high)-防御,最小为0
damage_low和damage_high在攻击者那里计算,对于人:damage_low是ratio*最低攻击+plus,damage_high是ratio*最高攻击+plus

如果受攻击的是怪,加仇恨
如果受攻击的是人,进入战斗状态,通知宠物和召唤兽反击,如果是辰皇,用光灵反击

处理反弹

被隐身的人攻击,爆率加50%

得到基础伤害和伤害倍数后,让filter通过EF_AdjustDamage处理一下

float damage = dmg.damage * dmg.adjust + attack->spec_damage + dmg.damage_no_crit;
spec_damage是附加伤害,不计算防御;damage_no_crit是不出暴的伤害,计算防御,是技能编辑器里的plus2

然后计算百分比减免伤害
计算人对人对怪的伤害百分比增减

计算造化的增减

固定减少伤害的判定

再加上一个fixed_damage(无视防御和减免,好像没有用)

最后得到了int_damage

另外还有一个神圣力伤害dt_damage

通过DoDamage扣血,在DoDamage里filter通过EF_BeforeDamage还处理了一下,但是客户端显示的还是未处理前的伤害

filter通过EF_BeAttacked处理一下

把伤害发给客户端显示

struct attack_msg中有

struct{    int skill;    int level;} attached_skill;

如果技能有对被攻击者的效果,会在DoPerform里对其进行赋值,在HandleAttackMsg的后面会调用_skill.Attack,这里边会执行StateAttack。

0 0
原创粉丝点击