gamecloud
Version:
game logic server over cloud
819 lines (737 loc) • 27.6 kB
JavaScript
let facade = require('../../Facade')
let {SkillType, em_Effect_Name, em_Effect_Comm, em_EffectCalcType, mapOfTechCalcType} = facade.const
let {SkillStateEnum,conditionEnum, ProfessionType,SkillNameEnum,EmitType,OperationType,StatusOfActionExecute,PeriodTypeEnum,NotifyEnum,AttrChangedType,BattleBuffEnum,AttackMode,EnhanceMode,ActionTypeEnum,ActionExecuteResult} = require('./enum')
let EventEmitter = require('events').EventEmitter; //事件管理
let EffectManager = require('../comm/EffectManager');
let EffectObject= require('../comm/EffectObject');
let {OperationItem, BattleParam,BattleOperation,BaseBattleParam,ActionResultInfo} = require('./util')
/**
* 五行相克系数表
*/
const natureConfig = [
[1, 1, 1, 1, 1.5],
[1, 1, 1.5, 0.5, 1],
[1, 0.5, 1, 1.5, 1],
[1, 1.5, 0.5, 1, 1],
[1.5, 1, 1, 1, 1],
];
/**
* 英雄属性集合,用于向客户端传递数据集
*/
class HeroAttributes
{
constructor($a,$d,$dm,$h){
this.a = $a; //攻击力
this.d = $d; //生命值
this.dm = $dm; //最大生命值
this.h = $h; //士气值
}
}
/**
* 战场中的英雄对象
*/
class BattleHero extends EventEmitter
{
/**
* 构造函数
* @param {*} $id 英雄编号
* @param {*} $level 英雄等级
*/
constructor($id, $level){
super();
/**
* 全局执行记录
*/
this.globalRecord = {};
/**
* 单回合内执行记录
*/
this.localRecord = {};
/// <summary>
/// 归属用户 BattleUser
/// </summary>
this._user = null;
/// <summary>
/// 本回合内是否已行动
/// </summary>
this.Action = false;
/// <summary>
/// 座位号
/// </summary>
this.SiteNo = 0;
/**
* 前置动画句柄, 该英雄的所有后续动作,必须在该动画播放完毕后播放
*/
this.PreCondition = 0;
/**
* 英雄的出生帧
*/
this.BornCondition = 0;
/**
* 最新动画句柄,为其他英雄的锁帧函数提供默认输入
*/
this.CurCondition = 0;
/**
* 伤害描述对象 BattleOperation
*/
this.HurtObject = null;
/// <summary>
/// 实时战斗参数,受各类Buff影响
/// </summary>
this._BattleParam = new BattleParam(this);
/// <summary>
/// 英雄ID
/// </summary>
this.id = $id;
/**
* 英雄等级
*/
this.level = $level;
/// <summary>
/// 英雄名称
/// </summary>
this.name = "";
/**
* 技能列表 Dictionary<SkillType, ActionInfo>
*/
this.actionList = {};
this.registerEvents();
//光环管理
this.effectMgr = new EffectManager(); //置放于英雄自身的光环效果
this.totalEffectMgr = new EffectManager(); //合并全局和自身光环后的最终效果
}
/**
* @return {BattleParam}
*/
get BattleParam(){
return this._BattleParam;
}
set BattleParam(val){
this._BattleParam = val;
}
/**
* 新增技能
* @param {*} params type, level
*/
addSkill(...params){
let $item = new ActionInfo(params[0], params[1], 0);
this.actionList[$item.type] = $item;
}
/**
* @return {BattleUser}
*/
get user(){
return this._user;
}
/**
* @param {BattleUser} val
* @return {BattleUser}
*/
set user(val){
this._user = val;
}
/**
* 注册非技能类事件句柄
*/
registerEvents(){
//用户回合切换事件
this.on(EmitType.CountChange, (...$params) =>{
if(this.BattleParam.Defense > 0){
this.Action = false; //复位存活英雄的行动标记
}
else{
if(!this.checkBuff(BattleBuffEnum.Dead)){ //阵亡超过2回合的英雄离场
this.lockFrame(this.user.room.GlobalFrame).emit(EmitType.Disappear);
}
else{
this.BattleParam.BattleBuff.Decrease(BattleBuffEnum.Dead);
}
}
});
this.on(EmitType.QuSan, ()=>{
this.removeBuff(BattleBuffEnum.UnabledAction);
this.removeBuff(BattleBuffEnum.Confused);
this.removeBuff(BattleBuffEnum.Dazed);
this.removeBuff(BattleBuffEnum.Poisoned);
this.removeBuff(BattleBuffEnum.Fire);
});
this.on(EmitType.Alive, ()=>{
//移除濒死标志
this.removeBuff(BattleBuffEnum.BeDead);
//将生命值补满
this.BattleParam.Defense = this.BattleParam.DefenseOfMax;
//通知客户端发生了复活事件
this.record(OperationType.Alive);
});
//英雄离场事件
this.on(EmitType.Disappear, (...$params) => {
this.removeEffectGlobal(); //清除全局光环
//下发离场通知,强制锁定到全局帧
this.record(OperationType.Disappear);
this.user.HeroList[this.SiteNo] = null;
});
//濒死事件,发起求救
this.on(EmitType.BeDead, (...$params) => {
if(this.BattleParam.Defense <= 0){
this.BattleParam.Defense = 1;//避免生命为0提前退出求救流程, 临时添加1点生命
}
this.act(SkillType.BeDead, $params[0]);
});
//英雄死亡事件
this.on(EmitType.Dead, (...$params) => {
this.removeBuff(BattleBuffEnum.BeDead);//消除濒死标记
this.addBuff(BattleBuffEnum.Dead,1); //添加死亡标志,持续1回合
this.record(OperationType.Dead); //向客户端发送阵亡通知
});
this.on(EmitType.SkillReady, (...$params) => {
let ft = $params[0];
if(!!this.globalRecord[ft]){
this.globalRecord[ft] += 1;
}
else{
this.globalRecord[ft] = 1;
}
if(!!this.localRecord[ft]){
this.localRecord[ft] += 1;
}
else{
this.localRecord[ft] = 1;
}
this.record(OperationType.Skill, {state:SkillStateEnum.Ready, type:ft});//自动添加技能发动记录(默认阶段0)
});
}
/**
* 英雄基本属性值:攻/血/血上限/气
* @return {HeroAttributes}
*/
get Attributes(){
return new HeroAttributes(
this.BattleParam.Attack,
this.BattleParam.Defense,
this.BattleParam.DefenseOfMax,
this.BattleParam.HonorValue,
);
}
/**
* 自身光环效果的计算公式,可直接返回effectMgr,也可根据底层层级汇总后返回。
* @return {EffectManager}
*/
effect(){
return this.effectMgr;
}
/**
* 当前的技能效果
* @return {EffectManager}
*/
get Effect(){
let st = [
this.user,
this
];
if(st.reduce((sofar, item)=>{
if(!item){
return sofar;
}
else{
return sofar || item.effect().GetEffectChanged()
}
}, false)){
st.reduce((sofar, item)=>{
return sofar.Add(item.effect());
}, this.totalEffectMgr.Clear());
}
return this.totalEffectMgr;
}
/**
* 向自身添加光环效果
* @param {*} $type
* @param {*} $value
*/
addEffectLocal($type, $value){
let $eo = new EffectObject($type, $value);
this.effectMgr.AddItem($eo);
this.record(OperationType.Effect, {type:$eo.type, value:$eo.value});
}
/**
* 向全局添加光环效果
* @param {*} $type
* @param {*} $value
*/
addEffectGlobal($type, $value){
$value = this.valueOfLevel($type, $value);
if(!this.localEffectCache){
this.localEffectCache = {};
}
let $eo = new EffectObject($type, $value);
this.user.effectMgr.AddItem($eo);
if(!!this.localEffectCache[$type]){
this.localEffectCache[$type] += $value;
}
else{
this.localEffectCache[$type] = $value;
}
this.record(OperationType.Effect, {global:true, type:$eo.type, value:$eo.value});
}
/**
* 光环类效果随等级提升的计算公式
* @param {*} $type 光环效果类型
* @param {*} $value 光环效果初始值
*/
valueOfLevel($type, $value){
switch($type){
case em_Effect_Comm.HAttack:
$value = $value * (1 + this.level);
break;
case em_Effect_Comm.Sacrifice:
$value = $value * (1 + this.level);
break;
default:
$value = $value + ((this.level / 20)|0);
break;
}
return $value;
}
/**
* 清除全局光环中和自己有关的光环
*/
removeEffectGlobal(){
if(!!this.localEffectCache){
Object.keys(this.localEffectCache).map(key=>{
if(!!this.user.effectMgr.effectList[key]){
this.user.effectMgr.effectList[key].value -= this.localEffectCache[key];
if(this.user.effectMgr.effectList[key].value == 0){//清除数值为零的效果条目
delete this.user.effectMgr.effectList[key];
}
}
});
this.localEffectCache = {};
}
}
/**
* 增加Combo值直至最大值
*/
ComboAdd(){
this.user.Combo = Math.min(this.user.user.player.core.ConfigMgr.ComboMax, this.user.Combo+1);
this.record(OperationType.Combo, {value:this.user.Combo});
}
/**
* 返回Combo是否已满的判断,如Combo已满,将其复位
* @return {Boolean}
*/
get ComboReady(){
if(this.user.Combo >= this.user.user.player.core.ConfigMgr.ComboMax){
this.user.Combo = 0;
this.record(OperationType.ComboReady, {});
return true;
}
return false;
}
/**
* 属性:英雄标识数组
* @return {Array}
*/
get Index(){
return [this.user.id, this.id, this.SiteNo];
}
/**
* 设置新的前置动画句柄
* @param {*} _con
* @return {BattleHero}
*/
lockFrame(_con) {
if(this.PreCondition < _con){
this.PreCondition = _con;
}
return this;
}
/**
* 设置并锁定到出生帧
* @param {*} _con
* @return {BattleHero}
*/
lockBornFrame(_con){
this.BornCondition = _con;
this.lockFrame(_con);
return this;
}
/**
* 添加英雄行为记录,攻击、防御
* @param {*} $operType
* @param {*} $params
* @return 本次记录的唯一句柄
*/
record($operType, $params = {}){
this.CurCondition = this.user.room.GetEventHandle(); //自动记录最新帧
$params.uid = this.user.id; //用户编号
$params.hid = this.id; //英雄编号
$params.site = this.SiteNo; //英雄站位
$params.sInfo = this.Index;
let rec = new OperationItem($operType, $params);
rec.PreCondition = this.PreCondition;
rec.EventIndex = this.CurCondition;
this.user.room.OperationList.push(rec);
return rec.EventIndex;
}
/**
* 发动一个技能
* @param {*} $fid 技能编号
* @param {BattleHero} $source 触发该技能的事件的主体
*
* @note 目前系统设定的所有技能中,前55个技能的技能编号和技能类型保持一致,因此在系统内部,可以通过为act函数传递技能类型的方式,发动对应的技能
*/
act($fid, $source=null){
//2018.2.1 技能调整为必然发动,技能效果随等级提升,概率问题放入技能逻辑内部判断。
//let $lv = this.GetActionLevel($fid);
//if ($lv == -1 || (Math.random()*100 | 0) <= (33 + $lv * 2)) {//执行判定:先天技能(必定发动,如普攻等),或者后天技能经概率判定发动
try{
let si = this.user.user.player.core.ConfigMgr.skill($fid);
let $ret = new si.builder($fid);
$ret.hero = this;
$ret.ori = $source;
if ($ret.CanExecute.apply($ret)) {
if(!!this.actionList[$fid]){
this.actionList[$fid].mature = this.user.CountVersion; //重新开始累计成熟度/CD
}
this.user.room.emit(EmitType.Action, $ret);
}
}
catch(e)
{
console.error(e);
}
//}
}
/**
* 判断技能是否成熟
* @param {*} $type 技能类型
* @return {Boolean}
*/
isMature($type){
return this.user.CountVersion >= this.actionList[$type].mature + this.Effect.CalcFinallyValue(em_Effect_Comm.Fee, this.user.user.player.core.ConfigMgr.skill($type).addon.fee);
}
/**
* 发起通告
* @param {*} $notifyEnum 通告的类型
* @param {BattleHero} $src 通告源
*/
Notify($notifyEnum, $src = null){
//仅测试
//this.record(OperationType.Notify, {type:$notifyEnum, ori: !!$src ? $src.Index : null});
//选取适配通告类型的、成熟了的技能
let list = this.actionByNotify($notifyEnum);
//region 2018.2.1 将原先只发动费用最高的技能的模式,调整为所有技能并发
// if(list.length >= 1){
// // 选取其中费用最高的技能中的随意一个发动
// let maxFee = list.reduce((sofar,cur)=>{
// if(sofar < this.user.user.player.core.ConfigMgr.skill(cur).addon.fee){
// sofar = this.user.user.player.core.ConfigMgr.skill(cur).addon.fee;
// }
// return sofar;
// },0);
// let $af = list.reduce((sofar,cur)=>{
// if(this.user.user.player.core.ConfigMgr.skill(cur).addon.fee == maxFee){
// sofar.push(cur);
// }
// return sofar;
// },[]).randomElement()[0];
// this.act($af, $src);
// }
list.map(cur=>{
this.act(cur, $src);
});
//end region 调整结束
}
/**
* 检测Buff有效性
*/
checkBuff($status){
return this.BattleParam.BattleBuff.Check($status);
}
/**
* 添加新的Buff,持续一定回合
* @param {*} buff Buff类型
* @param {*} count 持续回合数
*/
addBuff(buff, count=1){
let $old = this.BattleParam.BattleBuff.LeftCount(buff);
this.BattleParam.BattleBuff.Increase(buff, count);
let $new = this.BattleParam.BattleBuff.LeftCount(buff);
if($old != $new){
this.record(OperationType.BuffChanged, {type:buff, count:$new});
}
}
/**
* 移除Buff
* @param {*} buff Buff类型
*/
removeBuff(buff){
if(this.BattleParam.BattleBuff.LeftCount(buff)!=0){
this.BattleParam.BattleBuff.UnSet(buff);
this.record(OperationType.BuffChanged, {type:buff, count:this.BattleParam.BattleBuff.LeftCount(buff)});
}
}
get X()
{
return (this.SiteNo / 3) | 0;
}
get Y()
{
return this.SiteNo % 3;
}
/**
* 获取指定技能的等级
* @param {*}
*/
GetActionLevel($ft){
if (!!this.actionList[$ft]){
return this.actionList[$ft].level;
}
return -1;
}
/**
* 选取对指定触发类事件类型感兴趣的技能
* @param {*} $nt 指定的触发类事件类型
* @return {[]}
*/
actionByNotify($nt){
return Object.keys(this.actionList).reduce((sofar, cur)=>{
if(this.user.user.player.core.ConfigMgr.skill(cur).Notify.Check($nt) && this.isMature(cur)){
sofar.push(cur);
}
return sofar;
}, []);
}
isAnti($h2){
return (this.curNature == 4 && $h2.checkBuff(BattleBuffEnum.AntiFire))
|| (this.curNature == 2 && $h2.checkBuff(BattleBuffEnum.AntiWood))
|| (this.curNature == 3 && $h2.checkBuff(BattleBuffEnum.AntiWater))
}
getAnti($h2){
if((this.curNature == 4 && $h2.checkBuff(BattleBuffEnum.AntiFire)) ){
return AttrChangedType.AntiFire;
}
else if((this.curNature == 2 && $h2.checkBuff(BattleBuffEnum.AntiWood)) ){
return AttrChangedType.AntiWood;
}
else if((this.curNature == 3 && $h2.checkBuff(BattleBuffEnum.AntiWater)) ){
return AttrChangedType.AntiWater;
}
return AttrChangedType.Miss;
}
/**
* 攻击造成的伤害
* @param {BattleHero} $h2
* @param {*} $aMode
* @param {*} $HurtValuePercent
* @return {BattleOperation}
*/
calcDamageValue($h2, $aMode, $HurtValuePercent) {
let $lostLife = new BattleOperation();
if ($aMode == AttackMode.Counter || $aMode == AttackMode.Confusion){
$lostLife.DamageValue = this.BattleParam.Attack;
$lostLife.Type = AttrChangedType.Damage;
}
else if ($aMode == AttackMode.Blood){
$lostLife.DamageValue = this.BattleParam.DefenseOfMax;
$lostLife.Type = AttrChangedType.Damage;
}
else if(this.isAnti($h2)){
$lostLife.DamageValue = 0;
$lostLife.Type = this.getAnti($h2);
}
else{
let $_Attack = this.BattleParam.Attack;
let $_Tough = $h2.BattleParam.Tough;
let $_Miss = $h2.BattleParam.Miss;
let $_Parry = $h2.BattleParam.Parry;
//圆桌概率
let $BangValue = Math.min(Math.max(this.BattleParam.Bang - $_Tough, 0), 100);
let $CatchValue = Math.min($BangValue + Math.max(this.BattleParam.Catch - $_Miss, 0), 100);
let $ParryValue = Math.min($CatchValue + Math.max($_Parry - this.BattleParam.Wreck, 0), 100);
let $BigPoint = (Math.random() * 100) | 0;
if (this.checkBuff(BattleBuffEnum.Bang)/*英雄处于必暴状态*/ || $BigPoint <= $BangValue){ //发生了暴击
//计算暴击强度
$lostLife.DamageValue = (this.BattleParam.Attack * BattleParam.Intensity / 100.0) | 0;
$lostLife.Type = AttrChangedType.Bang;
}
else if ($BigPoint <= $CatchValue){ //发生了普通击中
$lostLife.DamageValue = this.BattleParam.Attack;
$lostLife.Type = AttrChangedType.Damage;
}
else if ($BigPoint <= $ParryValue){ //发生了格挡
if(!this.checkBuff(BattleBuffEnum.Sharp)){
$lostLife.DamageValue = (this.BattleParam.Attack * 0.5) | 0;
$lostLife.Type = AttrChangedType.Parry;
}
else{//破甲无视格挡
$lostLife.DamageValue = this.BattleParam.Attack;
$lostLife.Type = AttrChangedType.Damage;
}
}
else{ //发生了闪避
$lostLife.DamageValue = 0;
$lostLife.Type = AttrChangedType.Miss;
}
}
if (this.checkBuff(BattleBuffEnum.JiAng)){
$lostLife.DamageValue *= 3;
}
$lostLife.DamageValue = this.Effect.CalcFinallyValue(em_Effect_Comm.HAttack, ($lostLife.DamageValue * $HurtValuePercent) | 0);
//石头皮肤
if ($h2.checkBuff(BattleBuffEnum.Stone)){
if(!this.checkBuff(BattleBuffEnum.Sharp)){//破甲无视石头皮肤
$lostLife.DamageValue = 50;
}
}
$lostLife.DamageValue = Math.min($lostLife.DamageValue, $h2.BattleParam.Defense);
//计算五行相克系数
$lostLife.DamageValue *= this.getNature($h2);
$lostLife.attackMode = $aMode;
$lostLife.src = this;
$lostLife.dst = $h2;
$h2.HurtObject = $lostLife;
this.HurtObject = $lostLife;
if ($h2.checkBuff(BattleBuffEnum.Illusion)) {
//是幻象,遭受攻击立即死亡
$h2.BattleParam.Defense = 0;
}
else {
$h2.BattleParam.Defense = ($h2.BattleParam.Defense - $lostLife.DamageValue) | 0;
}
return $lostLife;
}
/**
* 计算五行相克系数:
* 首先确定攻防英雄各自的五行属性,确定克制系数,金土互克,木水火循环相克,
* 克制: * 1.5,统计各自阵营中同属性的英雄个数之和A,B=(A+2)/3,最终系数为 * 1.5 * B
* 被克制 / 2,统计各自阵营中同属性的英雄个数之和A,B=(A+2)/3,最终系数为 / 2 / B
*
* @param {*} $hero 敌对英雄
*/
getNature($hero){
let $A = Object.values(this.user.HeroList).filter(h=> !!h && h.nature == this.curNature).length +
Object.values($hero.user.HeroList).filter(h=> !!h && h.nature == $hero.nature).length;
let $B = ($A + 2) / 3;
let $C = natureConfig[Math.max(0,this.curNature-1)][Math.max(0, $hero.nature-1)];
if($C > 1){
return $C * $B;
}
else if($C < 1){
return $C / $B;
}
else {
return $C;
}
}
/**
* 是否处于虚弱状态
* @return {Boolean}
*/
isWeak(){
return this.checkBuff(BattleBuffEnum.Poisoned)
|| this.checkBuff(BattleBuffEnum.Fire)
|| this.checkBuff(BattleBuffEnum.Confused)
|| this.checkBuff(BattleBuffEnum.UnabledAction)
|| this.checkBuff(BattleBuffEnum.Dazed) ;
}
/**
* 克隆一位英雄
* @param {BattleUser} $simUsesr 接收克隆英雄的玩家
* @return {BattleHero}
*/
CloneHero($simUser){
if(!$simUser){
$simUser = this.user;
}
let $_SiteNo = $simUser.getTargetLocation(this.user.user.player.core.ConfigMgr.PetList()[this.id].loc);
if ($_SiteNo == -1){
return null;
}
let $ho = new BattleHero(this.id, this.level);
$ho.user = $simUser;
$ho.SiteNo = $_SiteNo;
$ho.name = this.name;
$ho.profession = this.profession;//职业
$ho.nature = this.nature; //五行属性,金木水火土 12345
$ho.combo = this.combo; //Combo设定
$ho.defaultAttackMode = this.defaultAttackMode;
$ho.BattleParam.current.clone(this.BattleParam.current);
$ho.BattleParam.Ori_Attack = this.BattleParam.Ori_Attack;
$ho.BattleParam.Ori_Defense = this.BattleParam.Ori_Defense;
for(let key in this.actionList){
$ho.addSkill(this.actionList[key].type, this.actionList[key].level);
}
return $ho;
}
/**
* 静态创建英雄对象
* @param {BattleUser} $battleUser 拥有英雄的玩家
* @param {int} $_SiteNo 英雄站位
* @param {Pet} $hi 携带英雄配置信息的对象
* @param {int} $level 设定英雄的等级
* @param {int} $star 设定英雄的星级
* @param {Array} $skill 设定英雄的附加技能,用于主角英雄的创建
* @return {BattleHero}
*/
static CreateInstance($battleUser, $_SiteNo, $hi, $level, $star, $skill=[]){
let $ho = new BattleHero($hi.id, $level);
$ho.user = $battleUser; //拥有者
$ho.SiteNo = $_SiteNo; //站位
$ho.level = $level; //等级
$ho.name = $hi.name; //名称
$ho.profession = $hi.profession;//职业
$ho.nature = $hi.nature; //五行属性,金木水火土 12345
$ho.BattleParam.Ori_Attack = parseInt($hi.attack); //攻击
$ho.BattleParam.Ori_Defense = parseInt($hi.defense); //物理防御
$ho.combo = this.user.user.player.core.ConfigMgr.ProfessionList()[$hi.profession].combo; //Combo设定
$ho.defaultAttackMode = this.user.user.player.core.ConfigMgr.ProfessionList()[$hi.profession].attackMode;
$ho.BattleParam.current.read(...this.user.user.player.core.ConfigMgr.ProfessionList()[$hi.profession].BP).multi((1+0.05*$level)); //荣誉值集合 - 使用配置表进行初始化
//默认添加攻击、求生、混乱攻击技能,等级传入-1表示必然发动
$ho.addSkill(SkillType.Attack, -1);
$ho.addSkill(SkillType.BeDead, -1);
$ho.addSkill(SkillType.ConfuseAttack, -1);
//根据英雄技能列表添加相关技能
$hi['ActionList'][$star-1].map($ai=>{
let $itemInfo = $ai.split(',');
let $it = $ai, $lv = 0;
if ($itemInfo.length >= 2){
$lv = $itemInfo[1]; //使用配置串中携带的等级信息
}
else{
$lv = $level; //让技能等级取英雄等级
}
if (!!this.user.user.player.core.ConfigMgr.skill($it) && !$ho.actionList[$it]){
$ho.addSkill($it, $lv);
}
});
//添加外部注入的技能,仅用于主角英雄的创建过程中
$skill.map(it=>{
if (!!this.user.user.player.core.ConfigMgr.skill(it[0]) && !$ho.actionList[it[0]]){
$ho.addSkill(it[0], it[1]);
}
});
return $ho;
}
}
/**
* 携带战斗技能动态信息的数据集
*/
class ActionInfo
{
/**
* 构造函数
* @param {*} $type 类型
* @param {*} $level 等级
* @param {*} $mature CD
*/
constructor($type, $level, $mature){
this.type = $type;
this.level = $level;
this.mature = $mature;
}
}
exports = module.exports = {
HeroAttributes:HeroAttributes,
BattleHero:BattleHero,
ActionInfo:ActionInfo,
}