技能是游戏里的重要组成部分。本文说的是实时rpg服务器的技能部分。
设计上:
技能攻击流程:
学习新的技能(到技能管理器)->技能释放->按某种范围攻击(羁绊范围或者几何范围搜索)->技能对象对防御者产生作用->添加技能状态到防御者技能状态管理器->技能状态管理器定时器运行->执行技能状态管理器中的技能状态对象->执行技能状态对象下的状态机->执行技能状态函数
数据结构
1、技能管理器:pk对象都含有技能管理器,技能管理器是自定义拓展的hashmap(sgi stl的拓展库下的__gnu_cxx::hash_multimap,需自定义哈希和等值比较函数),管理一个pk对象所有的技能对象(技能对象需要存档)。
2、技能状态管理器:pk对象都含有技能状态管理器,管理pk对象被施加的所有的技能状态对象(技能状态对象需要存档),一个技能状态含一个以上的技能状态函数(目前一个)。
技能处理主要分成两部分:技能攻击和技能状态处理。
技能攻击分为:
(1)状态技能。状态技能会加载技能状态到状态管理器。加载时运行且在技能状态定时器里运行技能状态对象(执行技能状态函数)。
(2)即时技能。
添加技能状态时会计算技能点。运行时会加载到角色属性上。
技能状态分为:
(1)可叠加状态。
(2)不可叠加状态。
对于技能攻击
技能释放时机:
(1)ai的攻击类型状态处理。
(2)客户端发来的角色施法协议处理。
范围搜索(pk对象施法按一定的规则来搜索范围)
(1)羁绊范围搜索:附近组队范围(队友或自己)
(2)技能几何范围搜索(1单2 自己圆3鼠标点圆4扇5线))
几何范围搜索参考:http://write.blog.csdn.net/postedit/26476517
对于技能状态处理
技能状态处理时机:
(1)角色循环运行技能状态管理器定时器(运行所有有效技能状态对象里的函数)。
(2)npc循环运行技能状态管理器定时器(运行所有有效技能状态对象里的函数)。
本文目录
1、技能配置
2、技能对象
3、技能管理器
4、学习新的技能
5、技能作用
(1)技能释放
(2)技能形式
(2-1)羁绊范围技能攻击
(2-2)几何范围技能攻击
(3)技能对象对防御者产生作用
(4)添加技能状态到技能状态管理器
6、范围检查
(1)技能的组队范围检查
(2)技能释放几何范围搜索
(2-1)某点为中心的圆的范围
(2-2)搜索扇形范围
7、技能状态对象
8、技能状态管理器
9、技能状态管理器定时器
10、运行技能状态对象
11、技能状态函数
本文内容:
1、技能配置
策划配置的技能配置表一行对应的一个技能配置。含技能基础属性数值。
struct SkillDataBase
{
const uint32 getUniqueID() const
{
return id;
}
uint32 id; //技能id
char job[MAX_JOB_LEN]; //职业限制
char name[MAX_NAME_LEN]; //技能名称
uint32 target; //1组队 2友方 3 敌方 4 城墙 5 自己
uint32 cd; //技能cd
uint16 range; // 1单 2自己圆 3鼠标点圆 4 扇 5 线
uint16 distance; //技能攻击距离
uint16 angle; //角度
uint16 radius; //半径
uint16 mp; //消耗mp
uint16 sp; //消耗sp
string stateid;
uint16 type; //默认类型[出身] 0 需要学习 1 需要装备的 2
uint32 ms; //角色施放该技能需要耗费的毫秒数
uint32 speed; //角色的技能基础需求速度值
uint32 attackmode; <span style="white-space:pre"> </span>//
uint16 normal; //属性对抗计算流程
uint16 combo; //触发连击的技能
};
2、技能对象
技能对象作为出生、学习的对象,可以释放并存于角色对象的技能管理器。可用于技能释放检查和当前属性扣除。
class Skill :base_object{
public:
friend class SkillManager;//技能管理器
/**
* \brief 内存分配器
*
*/
friend void constructInPlace<Skill,uint32,uint16,uint32,uint32,uint64>(Skill *,uint32,uint16,uint32,uint32,uint64);
friend void destructInPlace<Skill>(Skill *);
/**
* \brief 构造函数
* \param id 技能编号
* \param level 技能等级
* \param points 技能熟练点数
* \param coldtime 冷却时间
* \param lasttime 最后释放技能时间
*/
Skill(const uint32 _id,const uint16 _level,const uint32 _points,const uint32 _coldtime,const uint64 _lasttime);
/**
* \brief 析构函数
*/
~Skill();
public :
/**
* \brief 获取类名字
*
*/
const char *getClassName() const { return "技能"; }
/**
* \brief 技能序列化
* \param out 输出参数,缓存地址
* \return 返回缓存大小
*/
const uint32 serialize(char *out,scene_pk_object *owner);
/**
* \brief 检查攻击模式切换的时间的合法性
*
*/
bool checkAttackModeTime(scene_player *pUser,uint32 clienttime);
/**
* \brief 检查技能攻击距离
* \param pos1 位置1
* \param pos2 位置2
* \return true表示在攻击范围内
*/
bool checkRange(const nPos &pos,const nPos &pos1,uint32 adjust=0);
/**
* \brief 该技能是否需要走伤害流程
* \return 返回true需要计算伤害
*/
bool checkInjured();
/**
* \brief 获取技能剩余冷却时间
* \return 剩余冷却时间
*/
const uint32 getColdTime(scene_pk_object *owner);
/**
* \brief 检查技能需求的召唤兽
*
*/
bool checkSummon(scene_pk_object *owner);
/**
* \brief 检查技能需求的条件状态
*
*/
bool checkState(scene_pk_object *owner);
/**
* \brief 消耗技能需求的条件状态
*
*/
void consumeState(scene_pk_object *owner);
/**
* \brief 释放技能检查生命值比例
*
*/
bool checkneedhppercent(scene_pk_object *owner);
/**
* \brief 检查当前技能是否可以释放
* \return true可以释放
*
*/
bool checkColdTime(scene_pk_object *owner);
/**
* \brief 通知客户端清空冷却时间
*
*/
void notifyClearColdTime(scene_pk_object *owner);
/**
* \brief 设置冷却时间
*
*/
void setColdTime(uint32 coldtime);
//通知技能cd时间
void notifyColdTime(scene_pk_object *owner);
/* *
* \author
* \brief 获取该技能增加的效果值
* */
void getSkillValue(scene_pk_object *owner,uint32 &value,uint8 &hurtType);
/**
* \brief 释放技能扣除魔法值
* \param owner 释放者
* \return 生命值扣除是否成功
*/
bool usemp(scene_pk_object *owner);
/**
* \brief 释放技能扣除体力值
* \param owner 释放者
* \return 体力值扣除是否成功
*
*/
bool usesp(scene_pk_object *owner);
public :
/**
* \brief 技能升级表基类指针
*/
const zSkillB *base;
/**
* \brief 技能当前等级
*/
uint16 level;
/**
* \brief 技能熟练点数
*/
uint32 points;
/**
* \brief 冷却时间
*/
uint32 coldtime;
/**
* \brief 最后释放技能时间
*/
uint64 lasttime;
};
3、技能管理器
每个角色对象含一个技能管理器,包含技能的学习,升级,删除,及释放等操作。需要存档技能管理器内的技能对象。对防御者释加技能对象。
/**
* \brief 技能管理器,包括技能的学习,升级,删除,及释放等操作
*/
class SkillManager : public base_manager<index_uint32>
{
public :
typedef base_manager<index_uint32> super;
/**
* \brief 构造函数
* \param obj 技能管理器拥有者
*/
SkillManager(scene_pk_object *obj);
/**
* \brief 析构函数
*/
~SkillManager();
/**
* \brief 学习升级技能
* \param id 技能编号
* \param byGold 钻石购买技能
* \return 学习升级是否成功
*
*/
bool studySkill(const uint32 id,bool byGold = false);
/**
* \brief 技能的序列化
* \param out 输出参数,序列化缓存
* \return 返回序列化缓存大小
*/
const uint32 serialize(char *out);
/**
* \brief 技能反序列化
* \param in 需要反序列化的缓存
* \param version 版本
*
*/
void unSerialize(const SerializeBinaryMember *in, uint32 version);
/**
* \brief 技能升级列表序列化
* \param out 输出参数,序列化缓存
* \return 返回序列化缓存大小
*/
const uint32 serializeSkillUpdate(char* &out)const;
/**
* \brief 技能升级列表反序列化
* \param in 需要反序列化的缓存
* \param version 版本
*/
const uint32 unserializeSkillUpdate(const char* &in);
/**
* \brief 对对方作用技能
* \param skill 作用的技能
* \param pDef 防御者
*
*/
void putSkill(Skill *skill,scene_pk_object *pDef);
/**
* \brief 得到一个技能
* \param id 技能编号
* \return 返回技能,失败返回NULL
*
*/
Skill *getSkillByID(const uint32 id);
/**
* \brief 得到一个技能
* \param name 技能名字
* \return 返回技能,失败返回NULL
*
*/
Skill* getSkillByName(const char* name);
/**
* \brief 添加一个技能给目标
* \param id 技能编号
* \param level 技能等级
* \return 添加是否成功
*
*/
bool loadSkill(const uint32 id,const uint16 level=1);
/**
* \brief 创建一个技能
* \param base 技能基础数据
* \param points 花费技能点
* \param coldtime 冷却时间
* \return 成功,返回技能指针
* 失败,返回NULL
*/
Skill *creatorSkill(const uint32 skillid,const uint16 level = 1,const uint32 points=0 ,const uint32 coldtime=0);
/**
* \brief 随机得到一个技能
* \return 返回一个技能指针
*
*/
Skill *randomGetSkill();
/**
* \brief 卸载一个技能并通知客户端
* \param id 技能编号
*
*/
void unloadSkillNotify(const uint32 id);
/**
* \brief 卸载一个技能不通知客户端
* \param id 技能编号
*
*/
void unloadSkill(const uint32 id);
/**
* \brief 刷新所有技能给用户
*
*/
void sendAllToMe();
/**
* \brief 获取某职业已学技能点
* \param profession 职业
* \return 该职业已学技能点
*
*/
const uint16 getProfessionPoints(const uint16 profession);
/**
* \brief 获取职业
*
*/
inline const uint32 getProfession() { return _profession; }
/**
* \brief 遍历
* \param e 回调
* \return 回调是否成功
*/
template <class YourSkillEntry>
bool execEverySkill(callback<YourSkillEntry> &e)
{
return invoke_all<>(e);
}
/**
* \brief 遍历
* \param e 回调
*/
template <class DelSkillEntry>
void delEverySkill(temp_remove<DelSkillEntry> &e)
{
delete_object_if<>(e);
}
/**
* \author
* \brief 更新技能熟练点数
* \param skillid 技能ID
*/
bool updatePoints(const uint32 skillid);
/**
* \author
* \brief 升级技能
*/
bool updateSkill(const uint32 id,const uint8 flag = 0);
/**
* \author hxh
* \brief 技能升级GM命令
*/
void skillUpdateGM();
/**
* \author
* \brief 获取技能限制属性
*/
void getLimitedPoint(uint32 &strength,uint32 &agility,uint32 &power,uint32 &intell);
private :
/**
* \brief 检查并扣除技能学习升级需要的东东
* \param id 技能编号
* \param needbook 学习技能是否需要技能书,默认为需要
* \return 成功可以学习升级
*
*/
bool checkLevel(const uint32 id, bool needbook = true);
/**
* \brief 加载所有普通技能(不需要学习就会的技能)
*
*/
void loadRoleNormalSkill();
private :
/**
* \brief 管理器主人
*
*/
scene_pk_object *owner;
/**
* \brief 职业
*
*/
uint32 _profession;
public:
typedef std::map<uint32,SkillUpdate> SkillUpdateMap;
typedef SkillUpdateMap::const_iterator SkillUpdateConstIt;
typedef SkillUpdateMap::iterator SkillUpdateIt;
SkillUpdateMap skillUpdateMap;//技能升级属性存储列表
};
能pk的对象含有技能管理器
class scene_pk_object : public SceneEntry
{...
/*** \author
* \description 技能管理器
*/
SkillManager skillm;
};
4、学习新的技能
检查学习条件,根据技能的id来学习技能。
bool SkillManager::studySkill(const uint32 id,bool byGold)
{
if(owner->getType() != SceneObject_Player)
{
return false;
}
scene_player *pUser = (scene_player *)owner;
const zSkillB *base = NULL;
const SkillStudyB *skillBase = NULL;
if(byGold)
{
skillBase = (const SkillStudyB*)skillStudyBm::get_instance().getBySkillId(id);
}
else
{
skillBase = (const SkillStudyB *)skillStudyBm::get_instance().get(id);
}
if(!skillBase)
{
g_log->error("[%u,%s]技能学习配表没有该技能id=%u",pUser->id,pUser->name,id);
return false;
}
base = skillbm::get_instance().get(skillBase->skillId);
if(!base)
{
g_log->error("[%u,%s]技能配表没有该技能id=%u",pUser->id,pUser->name,skillBase->skillId);
return false;
}
Skill *skill = getSkillByID(skillBase->skillId);
if(skill)
{
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"%s技能已经学习了",skill->base->name);
return false;
}
if(skillBase->job && !((1<<pUser->roledata.job) & skillBase->job))
{
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"职业不对");
return false;
}
if(skillBase->changeJob > pUser->roledata.changeJob)
{
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"职业进阶小于%d",skillBase->changeJob);
return false;
}
if(skillBase->level && pUser->roledata.level < skillBase->level)
{
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"等级小于%d",skillBase->level);
return false;
}
if(skillBase->strength && pUser->charstate.strength < skillBase->strength)
{
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"力量小于%d",skillBase->strength);
return false;
}
if(skillBase->agility && pUser->charstate.agility < skillBase->agility)
{
//scene_chat::send_sys(pUser, "敏捷小于%d", skillBase->agility);
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"敏捷小于%d",skillBase->agility);
return false;
}
if(skillBase->power && pUser->charstate.power < skillBase->power)
{
//scene_chat::send_sys(pUser, "体力小于%d", skillBase->power);
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"体力小于%d",skillBase->power);
return false;
}
if(skillBase->intell && pUser->charstate.intell < skillBase->intell)
{
//scene_chat::send_sys(pUser, "智力小于%d", skillBase->intell);
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"智力小于%d",skillBase->intell);
return false;
}
if(byGold && !(pUser->packs.removeMoneyByType(MoneyType_Gold,skillBase->gold,pUser->id,pUser->name,"钻石学习技能",eMoneyOpType_SkillStudy)))
{
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"钻石不足%u",skillBase->gold);
return false;
}
skill = creatorSkill(base->id);
if(skill == NULL)
{
//scene_chat::send_sys(pUser, "创建技能失败");
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"创建技能失败");
return false;
}
pUser->set_save_tag(SAVE_OTHER);
BUFFER_USERCMD(MSG::stAddSkillUserCmd,send);
MSG::stSkillProperty *data = send->data;
data->dwSkillID = skillBase->skillId;
data->dwCooldownTime = 0;
data->level = skill->level;
data->points = skill->points;
send->size++;
pUser->sendmsgToMe(send, send->allsize());
//scene_chat::send_sys(pUser, "创建技能成功");
g_player_mgr.sendTipsToUser(pUser->roledata.id,TIPS_TYPE_EVENT,"创建技能成功");
pUser->CheckTaskPre( 0,TASK_FINISH_SYS,5,skillBase->skillId,0 ); //完成类任务
return true;
}
5、技能作用
(1)技能释放
释放技能
bool ScenePk::action(scene_pk_object *pAttack,const MSG::stAttackMagicUserCmd *rev, Skill *ptrskill)
{
pAttack->hvalue->reset();//清空伤害
BUFFER_USERCMD(MSG::stRetAttackMagicUserCmd,send);
if(pAttack->getType() == SceneObject_Player)
{
send->dwAttackerID = pAttack->id;
}
else
{
send->dwAttackerID = pAttack->tempid;
}
send->byEntryType = pAttack->getType();
send->wdSkillID = rev->skillID;
send->byDirect = rev->direct;
//魔法目标位置
pAttack->_xpos = rev->x;
pAttack->_ypos = rev->y;
//检测是否满足释放技能
if(!pAttack->checkSkillAction(ptrskill,rev,send->retCode))
{
pAttack->sendmsgToMe(send,send->getsize());
return false;
}
//消耗 cd
if(!pAttack->consumeSkill(ptrskill,send->retCode))
{
//释放失败
pAttack->sendmsgToMe(send,send->getsize());
return false;
}
//扣除耐久
if(pAttack->getType() == SceneObject_Player)
{
scene_player *pUser = (scene_player *)pAttack;
pUser->_cur_skill = ptrskill;
pUser->packs.epack.attackCounter();
if(ptrskill->base->id != 605201)
{
pUser->addCombo(ptrskill);
}
}
//升级技能熟练点
pAttack->skillm.updatePoints(ptrskill->base->id);
//攻击类型(几何范围方式)
switch(ptrskill->base->attackmode)
{
case MSG::SkillUseMethod_SelfUse: ///自身释放 属于没有伤害类型 只有加状态(<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">只带状态 没有伤害</span>)
{
magicAttackSelf(pAttack,rev,send, ptrskill);
}
break;
case MSG::SkillUseMethod_ToOne: //对单体释放,辅助技能,没有伤害,只加状态
{
skillStateToOne(pAttack,rev,send,ptrskill);
}
break;
case MSG::SkillUseMethod_ToMulti://对多体释放,辅助技能,没有伤害,只加状态
{
skillStateToMulti(pAttack,rev,send,ptrskill);
}
break;
case MSG::SkillUseMethod_PointAttack://对单体攻击(保存连击位置、更新分数、对防御者施加技能对象(添加技能状态)、<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">计算伤害和sd防护、发送客户端、处理伤害和反伤、处理补血</span><span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;">)</span>
{
magicAttackSingle(pAttack,rev,send,ptrskill);
}
break;
case MSG::SkillUseMethod_MultiAttack: //对多体攻击(搜索范围、计算连击伤害和魔法伤害、发送客户端、处理战斗状态、处理攻击伤害、处理反伤、处理补血)
{
magicAttackMulti(pAttack,rev,send, ptrskill);
}
break;
default:
{
send->retCode = MSG::RETURN_CODE_UNDEFINED_ATTACK_MODE;
pAttack->sendmsgToMe(send,send->getsize());
}
break;
}
return true;
}
(2)技能形式
(2-1)羁绊范围技能攻击
组队技能。 释放组队技能(一般是辅助技能)。
scene_pk_object *pAtt 施法对象
void ScenePk::magicAttackTeam(scene_pk_object *pAtt,const MSG::stAttackMagicUserCmd *rev,MSG::stRetAttackMagicUserCmd *send,Skill *ptrskill)
{
scene_player* pAttack = pAtt->getMaster();
if(!pAttack)
{
send->retCode = MSG::RETURN_CODE_PLAYER_NULL;//没有该玩家
pAtt->sendmsgToMe(send,send->getsize());
return;
}
MSG::stRetDefenceList *rdlist = NULL;
uint8 hurtType = MSG::HURT_TYPE_NULL;
//给队友加技能状态BUFF
if(pAttack->team)
{
pAttack->team->checkTeamAttckRange(pAttack,ptrskill,send);//对技能范围内的队友施法,添加技能对象作用(技能范围需要在视野范围内)
}
else//对自己施法
{
pAttack->skillm.putSkill(ptrskill,pAttack);
rdlist = (MSG::stRetDefenceList*)&send->data[send->size];
rdlist->dwDefencerID = pAttack->id;
rdlist->byEntryType = pAttack->getType();
rdlist->dwHp = 0;
rdlist->byState = 0;
rdlist->hurtType = hurtType;
send->size = 1;
}
send->retCode = MSG::RETURN_CODE_SUCCEED;
if(send->size)
{
pAttack->sendmsgToNine(send,send->getsize());//通知九屏
}else
{
pAttack->sendmsgToMe(send,send->getsize());
}
}
(2-2)几何范围技能攻击
先计算伤害和防护值,返回给客户端,再对被施法者处理战后影响(添加被战斗技能状态(buff),增减属性,红名,掉经验,掉装备,扣耐久,转换战斗模式,npc增加仇恨对象)。
技能释放几何范围方式
enum enumSkillUseMethod
{
SkillUseMethod_SelfUse = 0, /**对自己释放*/
SkillUseMethod_MultiAttack = 1, /**点对多攻击*/
SkillUseMethod_PointAttack = 2, /**点对点攻击*/
SkillUseMethod_ToOne = 3, /**点对点辅助*/
SkillUseMethod_ToMulti = 4, /**点对多辅助*/
SkillUseMethod_Num,
};
点对点的攻击(SkillUseMethod_PointAttack)。单被施法对象的攻击计算。
void ScenePk::magicAttackSingle(scene_pk_object *pAttack, const MSG::stAttackMagicUserCmd *rev, MSG::stRetAttackMagicUserCmd *send, Skill *ptrskill)
{
// 连击处理(<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">保存连击座标</span>)
if(ptrskill && pAttack && SceneObject_Player == pAttack->getType())
{
switch(ptrskill->base->range)
{
case MSG::SKILLRANGETYPE_SINGLE:/**< 单点 */
case MSG::SKILLRANGETYPE_ONESELF:/**< 自己圆 r*/
case MSG::SKILLRANGETYPE_LINE:/**< 线*/
case MSG::SKILLRANGETYPE_SECTOR:/**< 扇*/
{
((scene_player*)pAttack)->addComboAttckPos(pAttack->getPos().x, pAttack->getPos().y, ptrskill->base->id);
}
break;
case MSG::SKILLRANGETYPE_CURSOR:/**< 鼠标点圆 */
{//保存连击座标
((scene_player*)pAttack)->addComboAttckPos(rev->x, rev->y, ptrskill->base->id);
}
break;
}
}
uint8 hurtType = MSG::HURT_TYPE_NULL;
uint32 hurtSd = 0;//防御者SD伤害
bool isMiss = false;
scene_pk_object *pDefend = getPKObjectByEntryType(rev->defencerID,rev->defenderType);
if(!pDefend)
{
send->retCode = MSG::RETURN_CODE_TARGET_NULL;//防御者为空
pAttack->sendmsgToMe(send,send->getsize());
}
if(!pDefend->checkCanPKMe(pAttack))
{
return;
}
nPos comboPos;
uint32 parm1 = 0;
parm1 = getCommonSkillHurt(ptrskill);
hurtType = MSG::HURT_TYPE_NULL;
hurtSd = 0;//防御者SD伤害
pAttack->hvalue->hurt = 0;//初始化为0
pDefend->hvalue->hurt = 0;//初始化为0
//计算伤害
pAttack->hvalue->calculate(pAttack, pDefend, hurtType, parm1, pDefend->hvalue->hurt, 0, 0);
// 处理组队征服副本伤害统计
if(pAttack && pDefend && pAttack->getType() == SceneObject_Player)
{
scene_player *pUser = ((scene_player*)pAttack);
if(pUser->scene && CopySceneType_TeamConquer == pUser->scene->sceneType)
{
pUser->updateScore(pDefend->hvalue->hurt);
}
}
pAttack->hvalue->defendHurtWithSd(pAttack, pDefend, pDefend->hvalue->hurt, hurtSd);
if(hurtType == MSG::HURT_TYPE_MISS)//攻击怪物记录技能释放过程中是否闪避
{
isMiss = (pDefend->getType() == SceneObject_NPC)? true:false;//攻击怪物记录技能释放过程中是否闪避
}
if(ptrskill->base->normal)
{
send->srcx = pAttack->getPos().x;
send->srcy = pAttack->getPos().y;
if(hurtType != MSG::HURT_TYPE_MISS)
{
pAttack->skillm.putSkill(ptrskill,pAttack);
}
send->x = pAttack->getPos().x;
send->y = pAttack->getPos().y;
comboPos = pAttack->getPos();
}
else
{
send->srcx = pDefend->getPos().x;
send->srcy = pDefend->getPos().y;
if(hurtType != MSG::HURT_TYPE_MISS)
{
pAttack->skillm.putSkill(ptrskill,pDefend);
}
send->x = pDefend->getPos().x;
send->y = pDefend->getPos().y;
comboPos = pAttack->getPos();
}
pAttack->full_t_DefenceList(pDefend, (MSG::stRetDefenceList *)&send->data[send->size], pDefend->hvalue->hurt,hurtSd,hurtType);
send->size++;
MSG::stRetDefenceList *retReflect = NULL;//就算反伤
if(pDefend->hvalue->calculate_return_hurt(pAttack, pDefend, pDefend->hvalue->hurt, pAttack->hvalue->hurt))
{
uint32 returnHurtSd = 0;
pAttack->hvalue->defendHurtWithSd(pDefend, pAttack, pAttack->hvalue->hurt, returnHurtSd,true);
pAttack->full_t_DefenceList(pAttack, (MSG::stRetDefenceList *)&send->data[send->size], pAttack->hvalue->hurt,returnHurtSd,MSG::HURT_TYPE_REFLECT_DAMAGE);
retReflect = (MSG::stRetDefenceList *)&send->data[send->size];
send->size++;
}
if(send->size)
{
send->retCode = MSG::RETURN_CODE_SUCCEED;
pAttack->sendmsgToNine(send,send->getsize());
handleAttackHurt(send, pAttack);
//处理反伤害
if(retReflect)
{
pDefend->handleFightHurt(pAttack,retReflect);
}
//玩家主动攻击有一定概率获得瞬间满血
if(pAttack->getType() == SceneObject_Player && !isMiss)
{
((scene_player*)pAttack)->randomFull();
}
}
else
{
send->retCode = MSG::RETURN_CODE_TARGET_NULL;
pAttack->sendmsgToMe(send,send->getsize());
}
}
多被攻击者战斗 (SkillUseMethod_MultiAttack)。多被施法对象的战斗计算。
void ScenePk::magicAttackMulti(scene_pk_object *pAttack, const MSG::stAttackMagicUserCmd *rev, MSG::stRetAttackMagicUserCmd *send, Skill *ptrskill)
{
pAttack->reduceAttackDur();//减少装备耐久
static DefenceTargetList targetList;//被攻击者列表
targetList.clear();
send->srcx = pAttack->getPos().x;
send->srcy = pAttack->getPos().y;
send->x = rev->x;
send->y = rev->y;
bool isMiss = false;
addAttackTargetList(pAttack, rev, ptrskill, targetList);//搜索技能范围目标到被攻击者列表
ReflectorMap refMap;//反伤对象容器
if(ptrskill->base->id == 605201)
{
if(pAttack->getType() == SceneObject_Player)
{
dealMagicComboHurt(pAttack, ptrskill,targetList, send);//计算连击的伤害和防护值
}
}
else
{
dealMagicMultiHurt(pAttack, ptrskill,targetList, send, refMap, isMiss);//计算施法的伤害和防护值
}
send->retCode = MSG::RETURN_CODE_SUCCEED;
pAttack->sendmsgToNine(send,send->getsize());
if(send->size)
{
if(ptrskill->base->id != 605201)
{
handleFightState(send, pAttack, ptrskill,refMap);//加入技能状态
}
//处理伤害
handleAttackHurt(send, pAttack);
//处理反伤害
handleReflectHurt(pAttack,send,refMap);
//玩家主动攻击有一定概率获得瞬间满血
if(pAttack->getType() == SceneObject_Player && !isMiss)
{
((scene_player*)pAttack)->randomFull();
}
}
}
(3)技能对象对防御者产生作用
void SkillManager::putSkill(Skill *skill,scene_pk_object *pDefend)
{
if(!skill)
{
return;
}
//在作用对象为死亡的状态下
if(pDefend && pDefend->getEntryState() == SceneObject_Death)
{
return;
}
if(pDefend->getType() != SceneObject_NPC && pDefend->getType() != SceneObject_Player)
{
return;
}
Touch_State_Vector::const_iterator biter1 = skill->base->statelist.begin();//技能携带的技能状态
for(; biter1 != skill->base->statelist.end(); ++biter1)
{
const int &state = *biter1;
const SkillStateB *base = skillStateBm::get_instance().get(state);
if(base)
{
const Element_Value_Vector &elementlist = base->getElementListByLevel(skill->level);
for(uint32 i = 0; i < elementlist.size(); i++)
{
const ElementValue& value = elementlist[i];
if(value.id == StateValueType_AddPhysicsHurtValue ||value.id == StateValueType_AddMagicHurtValue)//技能状态的效果id
{
continue;//物理伤害和魔法伤害已经直接读取系数,这里不需状态
}
pDefend->statem->addState(value,owner,0,base->save_die,base->save_offline,base->run_offline,base->unlimited);//对防御者添加伤害状态
}
}
}
}
(4)添加技能状态到技能状态管理器
添加技能状态到技能状态管理器
bool StateManager::addState(const ElementValue& value, scene_pk_object *pAtt,uint32 ltime,bool save_die,bool save_offline,bool run_offline,bool unlimited)
{
if(!pAtt)
{
return false;
}
if(pAtt->getType() != SceneObject_Player && pAtt->getType() != SceneObject_NPC)
{
return false;
}
uint32 rate = (uint32)nMisc::randBetween(0, 100);
if(rate > value.effect)//按概率生效技能效果
{
return false;
}
StateObject *object = new StateObject(pAtt, value,save_die,save_offline,run_offline,unlimited);
if(!object)
{
return false;
}
POINT_DEBUG_INFO(info,"NP--%s-技能状态%p",_owner->name,object);
if(ltime)
{
object->run.time = ltime;
}
//计算技能影响的属性值,计算出来的结果保存在ElementValue(技能状态元素信息)的parm3中.(在运行技能状态定时器时会运行该技能状态,这里只是计算增加点数)
effectStateProperty(object);
if(!addState(object))
{
return false;
}
return true;
}
添加技能状态对象到技能状态管理器
bool StateManager::addState(StateObject *object)
{
uint32 notify = 0;
StateObject_map::iterator it = _state_map.begin();
for(;it != _state_map.end() ; ++it)
{
if((*it)->isMultiExpState())
{
continue;
}
if((*it)->run.id == object->run.id)
{
break;
}
}
//如果没有相同技能效果id的新效果, 就加入新的技能效果
if(it == _state_map.end())
{
notify |= runState(object);
object->id = this->id++;
_state_map.push_back(object);
setStateToBuf(object);
setNineStateToIndex(object);
loadIco(object);
StateManager::freshAll(_owner,notify);
return true;
}
StateObject *old = *it;
if(old->bDelete == true)
{
return true;
}
//处理技能效果id相同
//可以叠加技能效果
if(old->run.num < old->element.maxnum)//叠加次数小于最大叠加次数
{
//按照技能替换规则比较,如果newOjbect与oldObject效果id相同,且比oldObject优则返回true
if(compareState(object,old))
{
//技能效果比较较优的情况下,删除旧的技能状态并加入新的技能状态
old->run.step = MSG::STATESTEP_UNLOAD;
runState(old);
object->id = this->id++;
_state_map.push_back(object);
old->bDelete = true;
notify |= runState(object);
loadIco(object);
freshAll(_owner,notify);
return true;
}
//直接在老技能状态叠加上去 删除新的技能状态
old->run.step = MSG::STATESTEP_UNLOAD;
notify |= runState(old);
old->run.num += 1;
if(object->run.id == StateValueType_RecoveryHp || object->run.id == StateValueType_RecoveryMp)
{
old->run.time += object->run.time;//加载技能时间
old->run.num = 1;
}
else
{
old->element.parm3 += object->element.parm3;//叠加技能点
}
old->run.attacker = object->run.attacker;//施法者标识
old->run.step = MSG::STATESTEP_LOAD;
POINT_DEBUG_INFO(info,"DP--%s-技能状态%p", _owner->name,object);
SAFE_DELETE(object);
notify |= runState(old);
loadIco(old);
freshAll(_owner,notify);//刷新到前端
return true;
}
else if(old->element.maxnum == 1)//最大叠加次数为1(不能叠加技能效果)
{
按照技能替换规则比较,如果newOjbect与oldObject效果id相同,且比oldObject优则返回true
if(compareState(object,old))
{
//删除老的技能状态 加载新的技能状态
old->run.step = MSG::STATESTEP_UNLOAD;
runState(old);
object->id = this->id++;
_state_map.push_back(object);
old->bDelete = true;
notify |= runState(object);
loadIco(object);
freshAll(_owner,notify);
return true;
}
}
return false;
}
(1)技能的组队范围检查
void scene_team::checkTeamAttckRange( scene_player* pUser,Skill* pSkill, MSG::stRetAttackMagicUserCmd* pSend ,bool isFind)
{
if(pUser->getState() == SceneObject_Death)
{
return;
}
for (std::set<uint32>::iterator it = playerIdSet.begin();
it != playerIdSet.end();
++it)
{
scene_player *pOhterUser = g_player_mgr.get_player_by_id(*it);
if (pOhterUser)
{
if (pUser->scene == pOhterUser->scene)
{
if(pSkill->checkRange(pUser->getPos(), pOhterUser->getPos(), 3))
{
if (!isFind)//这个标志没有具体意义,只是为了区分ScenePk.cpp两个调用
{
MSG::stRetDefenceList *defendlist = NULL;
defendlist = (MSG::stRetDefenceList*)&pSend->data[pSend->size];
defendlist->dwDefencerID = pOhterUser->id;
defendlist->byEntryType = pOhterUser->getType();
defendlist->dwHp = 0;
defendlist->byState = 0;
defendlist->hurtType = 0;
pSend->size++;
pUser->skillm.putSkill(pSkill,pOhterUser);//对对方作用技能,添加技能状态到对方技能状态管理器
}
else
{
pUser->skillm.putSkill(pSkill,pOhterUser);
MSG::stRetDefenceList *defendlist = NULL;
defendlist = (MSG::stRetDefenceList*)&pSend->data[pSend->size];
defendlist->dwDefencerID = pOhterUser->id;
defendlist->byEntryType = pOhterUser->getType();
pSkill->getSkillValue(pOhterUser,defendlist->dwHp,defendlist->hurtType);
//defendlist->dwHp = 0;
defendlist->dwSd = 0;
defendlist->byState = 0;
//defendlist->hurtType = 0;
pSend->size++;
}
}
}
}
}
}
/**
* \author
* \description 技能释放几何范围
*/
//1单2 自己圆3鼠标点圆4扇5线
enum SkillRangeType
{
SKILLRANGETYPE_SINGLE = 1, /**< 单 */
SKILLRANGETYPE_ONESELF= 2, /**< 自己圆 r*/
SKILLRANGETYPE_CURSOR = 3, /**< 鼠标点圆 */
SKILLRANGETYPE_SECTOR = 4, /**< 扇*/
SKILLRANGETYPE_LINE = 5, /**< 线*/
SKILLRANGETYPE_MAX
};
检查被攻击者,搜索攻击范围
bool ScenePk::addAttackTargetList(scene_pk_object *pAttack, const MSG::stAttackMagicUserCmd *rev,Skill *skill, DefenceTargetList &defendList)
{
// 保存连击座标
if(skill && pAttack && SceneObject_Player == pAttack->getType())
{
switch(skill->base->range)
{
case MSG::SKILLRANGETYPE_SINGLE:
case MSG::SKILLRANGETYPE_ONESELF:
case MSG::SKILLRANGETYPE_LINE:
case MSG::SKILLRANGETYPE_SECTOR:
{//保存连击座标
((scene_player*)pAttack)->addComboAttckPos(pAttack->getPos().x, pAttack->getPos().y, skill->base->id);
}
break;
case MSG::SKILLRANGETYPE_CURSOR:
{
((scene_player*)pAttack)->addComboAttckPos(rev->x, rev->y, skill->base->id);
}
break;
}
}
uint16 flags = 0;
if(pAttack->getType() == SceneObject_Player)
{
scene_player* pAtt = (scene_player*)pAttack;
if(pAtt->getPkMode() == MSG::PKMODE_NORMAL)//普通模式只攻击npc
{
SetBit(flags,SceneObject_NPC);
}
else//其他模式攻击角色和npc
{
SetBit(flags,SceneObject_Player);
SetBit(flags,SceneObject_NPC);
}
}
else if(pAttack->getType() == SceneObject_NPC)//优化怪物攻击,不再搜索怪物
{
SetBit(flags,SceneObject_Player);
}
switch(skill->base->range)//攻击范围
{
case MSG::SKILLRANGETYPE_SINGLE: //单体攻击
{
scene_pk_object *pDefend = getPKObjectByEntryType(rev->defencerID,rev->defenderType);
if(pDefend && skill->checkRange(pAttack->getPos(),pDefend->getPos(),skill->base->distance))
{
defendList.push_back(pDefend);
}
return true;
}
break;
case MSG::SKILLRANGETYPE_ONESELF: //自己圆
{
pAttack->scene->searchRoundScreenRange(nPos(pAttack->getPos().x, pAttack->getPos().y),skill->base->radius,defendList,flags);
if(defendList.empty())
{
return false;
}
return true;
}
break;
case MSG::SKILLRANGETYPE_CURSOR: //鼠标圆
{
// pAttack->scene->searchEntryRound(nPos(rev->x, rev->y), skill->base->radius, pAttack->getCamp(), *defendList);
pAttack->scene->searchRoundScreenRange(nPos(rev->x, rev->y),skill->base->radius,defendList,flags);
if(defendList.empty())
{
return false;
}
return true;
}
break;
case MSG::SKILLRANGETYPE_SECTOR://扇形
{
//pAttack->scene->searchEntrySector(pAttack->getPos(),rev->direct, skill->base->radius, skill->base->angle, pAttack->getCamp(), *defendList);
pAttack->scene->searchSectorScreenRange(pAttack->getPos(), skill->base->radius, skill->base->angle,rev->direct, defendList,flags);
if(defendList.empty())
{
return false;
}
return true;
}
break;
case MSG::SKILLRANGETYPE_LINE://直线,鼠标点方
{
pAttack->scene->searchEntryLine(nPos(pAttack->getPos().x, pAttack->getPos().y), nPos(rev->x, rev->y),skill->base->distance, defendList,flags);
if(defendList.empty())
{
return false;
}
return true;
}
break;
default:
break;
}
return true;
}
检查范围:
(2-1)某点为中心的圆的范围
void Scene::searchRoundScreenRange(const nPos& center,const uint16 r, DefenceTargetList &targetVec,const uint16 flags)
{
SearchPlayerMonsterPosExec exec(center, r, targetVec);
const screen_vector &pv = getScreenByRange(center, r + 5);
screen_vector::const_iterator iter = pv.begin();
screen_vector::const_iterator iter_end = pv.end();
for(; iter != iter_end; ++iter)
{
if(GetBit(flags,SceneObject_Player))
{
execAllOfScreen(SceneObject_Player, *iter, exec);//遍历屏上的所有的玩家
}
if(GetBit(flags,SceneObject_NPC))
{
execAllOfScreen(SceneObject_NPC, *iter, exec);//遍历屏上的所有的npc
}
}
}
遍历屏上的对象,获取范围内对象
struct SearchPlayerMonsterPosExec : public callback<SceneEntry>
{
const nPos& _center;
const uint16& _range;
DefenceTargetList &_targetVec;
SearchPlayerMonsterPosExec(const nPos& center, const uint16& range, DefenceTargetList& targetVec)
:_center(center), _range(range), _targetVec(targetVec)
{
// _targetVec.reserve(9 * SCREEN_GRID_WIDTH * SCREEN_GRID_HEIGHT);
}
bool invoke(SceneEntry *entry)
{
if(entry == NULL)
{
return false;
}
scene_pk_object *pkEntry = NULL;
if(entry->getType() == SceneObject_NPC)
{
pkEntry = getPKObjectByEntryType(entry->tempid, SceneObject_NPC);
}
else if(entry->getType() == SceneObject_Player)
{
pkEntry = getPKObjectByEntryType(entry->id, SceneObject_Player);
}
if(!pkEntry)
{
return false;
}
uint16 distance = nGraphAlgo::getDistance(_center, pkEntry->getPos());
if(distance <= _range)
{
_targetVec.push_back(pkEntry);
return true;
}
return false;
}
};
(2-2)搜索扇形范围
void Scene::searchSectorScreenRange(const nPos& center, const uint16& range,const uint16& angle ,const uint16& dir, DefenceTargetList& targetVec, const uint16 flags)
{
if(range < 1 || angle == 180)
{
return;
}
SearchSectorPosExec exec(center, range,angle,dir ,targetVec);//检查中心点,半径、角度、方向的回调
const screen_vector &pv = getScreenByRange(center, range + 5);//获取九屏,条件是偏移5格子内
screen_vector::const_iterator iter = pv.begin();
screen_vector::const_iterator iter_end = pv.end();
for(; iter != iter_end; ++iter)
{
if(GetBit(flags,SceneObject_Player))
{
execAllOfScreen(SceneObject_Player, *iter, exec);
}
if(GetBit(flags,SceneObject_NPC))
{
execAllOfScreen(SceneObject_NPC, *iter, exec);
}
}
}
在扇形区域搜索目标,先检查最大长度,再按客户端发来的方向角度上检查技能的范围上的npc或玩家
//在扇形区域搜索目标
struct SearchSectorPosExec : public callback<SceneEntry>
{
const nPos& _center;
const uint16& _range;
const uint16& _angle;
const uint16& _dir;
DefenceTargetList &_targetVec;
int abs_x;
int abs_y;
uint16 range_pow2;
double angle_tan;//dir = up,right,down,left时使用的tan(*)值
double angle_tan_less;//dir = up_right,down_right,down_left,up_left && _angle <= 90度时使用的tan(*)
double angle_tan_more;//dir = up_right,down_right,down_left,up_left && _angle > 90度时使用的tan(*)
scene_pk_object *_exinclude;
SearchSectorPosExec(const nPos& center, const uint16& range,const uint16& angle ,const uint16& dir,DefenceTargetList& targetVec)
:_center(center), _range(range),_angle(angle), _dir(dir),_targetVec(targetVec),abs_x(0),abs_y(0)
{
range_pow2 = _range * _range;
angle_tan = tan(_angle * PI / 360);
angle_tan_less = tan((45 - _angle/2)*PI/180);
angle_tan_more = tan((_angle/2 - 45)*PI/180);
}
bool invoke(SceneEntry *entry)
{
if(entry == NULL)
{
return false;
}
scene_pk_object *pkEntry = NULL;
if(entry->getType() == SceneObject_NPC)
{
pkEntry = getPKObjectByEntryType(entry->tempid, SceneObject_NPC);
}
else if(entry->getType() == SceneObject_Player)
{
pkEntry = getPKObjectByEntryType(entry->id, SceneObject_Player);
}
if(! pkEntry)
{
return false;
}
const nPos &pos = pkEntry->getPos();
//int abs_x = 0,abs_y = 0;
abs_x = abs(_center.x - pos.x);
abs_y = abs(_center.y - pos.y);
//uint16 distance = nGraphAlgo::getDistance(_center, pos);
if((abs_x*abs_x+abs_y*abs_y) > range_pow2)
{
return false;
}
else if (abs_x == 0 && abs_y == 0)
{
_targetVec.push_back(pkEntry);
return true;
}
if(_dir % 2==0)//dir = up,right,down,left
{
//angle_tan = tan(_angle * PI / 360);
if((_dir == _DIR_UP && pos.y <= _center.y) || (_dir == _DIR_DOWN && pos.y >= _center.y))
{
if(angle_tan * abs_y - abs_x >= 0)
{
_targetVec.push_back(pkEntry);
return true;
}
}
else if((_dir == _DIR_RIGHT && pos.x >= _center.x) || (_dir == _DIR_LEFT && pos.x <= _center.x))
{
if(angle_tan * abs_x - abs_y >= 0)
{
_targetVec.push_back(pkEntry);
return true;
}
}
}
else //dir = up_right,down_right,down_left,up_left
{
if(_angle/2 <= 45)
{
//angle_tan = tan((45 - _angle/2)*PI/180);
if(abs_x >= (abs_y * angle_tan_less) && abs_y >= (abs_y * angle_tan_less))
{
if(_dir == _DIR_DOWNRIGHT && pos.x >= _center.x && pos.y >= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
else if(_dir == _DIR_UPRIGHT && pos.x >= _center.x && pos.y <= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
else if(_dir == _DIR_UPLEFT && pos.x <= _center.x && pos.y <= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
else if(_dir == _DIR_DOWNLEFT && pos.x <= _center.x && pos.y >= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
}
}
else if(_angle/2 > 45)
{
//angle_tan = tan((_angle/2 - 45)*PI/180);
if(_dir == _DIR_DOWNRIGHT)
{
if(pos.x >= _center.x && pos.y >= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.y > _center.y && abs_x <= (abs_y * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.x > _center.x && abs_y <= (abs_y * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
}
else if(_dir == _DIR_UPRIGHT)
{
if(pos.x >= _center.x && pos.y <= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.y > _center.y && abs_y <= (abs_x * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.x < _center.x && abs_x <= (abs_y * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
}
else if(_dir == _DIR_UPLEFT)
{
if(pos.x <= _center.x && pos.y <= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.x > _center.x && abs_x <= (abs_y * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.y > _center.y && abs_y <= (abs_x * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
}
else if(_dir == _DIR_DOWNLEFT)
{
if(pos.x <= _center.x && pos.y >= _center.y)
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.x > _center.x && abs_x <= (abs_y * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
else if(pos.y < _center.y && abs_x <= (abs_x * angle_tan_more))
{
_targetVec.push_back(pkEntry);
return true;
}
}
}
}
return false;
}
场景实体搜索可以参考:
http://blog.csdn.net/chenjiayi_yun/article/details/26476517
7、技能状态对象
技能状态对象
struct StateObject:public nMemoryAlloc//使用内存池
{
StateRunAttr run; /**状态属性*/
uint32 offline; /**停止时间*/
uint32 starttime; /**开始时间*/
ElementValue element; /*技能状态元素信息 **/
bool bDelete;
uint32 id;
StateObject();
StateObject(scene_pk_object *pAtt, const ElementValue& base,bool save_die,bool save_offline,bool run_offline,bool unlimited);
StateObject(StateObject &object);
~StateObject();
scene_pk_object *getAttacker();
/**
* \brief 判断需要序列化
*/
bool isNeedSerialize();
/**
* \brief 判断是非卸载状态
*/
bool isValid()const;
/**
* \brief 判断是否是卸载状态
*/
bool isWaitDel()const;
/**
* \brief 判断是否是暂停状态
*/
bool isPause()const;
/**
* \brief 是多倍
*/
bool isMultiExpState();
/**
* \brief 序列化
*/
const uint32 serialize(char *out);
/**
* \brief 反序列化
*/
const uint32 unSerialize(const char *in,const uint32 version);
};
技能状态元素
struct ElementValue
{
uint16 id;
uint32 lasttime; //持续时间
uint16 cd; //cd间隔
uint16 effect; //效果号生效概率
uint16 maxnum; //最大叠加层数
uint16 parm1; //策划配置动态参数1
uint16 parm2; //策划配置动态参数2
uint32 parm3; //增加点数,这个参数在加载技能中使用到
ElementValue():id(0),lasttime(0),cd(0),effect(0),maxnum(0),parm1(0),parm2(0),parm3(0)
{
}
ElementValue(const ElementValue &value)
{
id = value.id;
lasttime = value.lasttime;
cd = value.cd;
effect = value.effect;
maxnum = value.maxnum;
parm1 = value.parm1;
parm2 = value.parm2;
parm3 = value.parm3;
}
inline void copy(const ElementValue &value)
{
id = value.id;
lasttime = value.lasttime;
cd = value.cd;
effect = value.effect;
maxnum = value.maxnum;
parm1 = value.parm1;
parm2 = value.parm2;
parm3 = value.parm3;
}
};
状态属性
struct StateRunAttr
{
StateRunAttr()
{
bzero(this,sizeof(*this));
}
uint16 id; /**状态编号*/
uint16 level; /**状态等级*/
uint16 num; /**当前叠加的次数*/
uint32 time; /**持续时间*/
uint8 step; /**状态执行的步骤*/
uint32 attacker; /**状态投送者编号*/
uint8 type; /**状态投送者类型*/
bool pause; /**状态暂停标志*/
bool save_die; /* *死亡保留状态*/
bool save_offline; /* *下线存档状态*/
bool run_offline; /* *下线后状态继续倒计时*/
bool unlimited; //不限时间
};
8、技能状态管理器
技能状态管理器管理作为pk对象的成员而存在,管理该pk对象的所有的技能状态的运行、删除、序列化、刷新到前端。
class StateManager
{
public :
StateManager(scene_pk_object *owner);
~StateManager();
typedef vector<StateObject *> RemoveObject_vector;
public:
//刷新角色属性到前端
static void freshAll(scene_pk_object *_owner,const uint32 notify);
//序列化
const uint32 serialize(char *out);
//反序列化
void unSerialize(const SerializeBinaryMember *in,const uint32 version);
//删除所有技能状态
void deleteAll();
//删除状态
void deleteStateObject(StateObject * pObject);
//获取技能状态
StateObject *getState(const uint16 id);
//运行状态函数(装载状态、重新装载状态、定时器状态、卸载状态)( 增减属性并同步到前端)
uint32 runState(StateObject *object);
//技能状态循环(删除技能状态删除列表里的技能状态对象)
void loop();
//定时运行所有状态对象(runState)
void stateTimer();
//发送所有技能状态到前端
void loadAllIco();
//发送新技能状态到前端
void loadIco(const StateObject *object);
//发送取消技能状态到前端
void unloadIco(const StateObject *object);
/* *
* \brief 暂停状态
* */
void pauseState(StateObject *object);
/* *
* \brief 重启状态
* */
void restartState(StateObject *object);
void execEveryState(callback<StateObject> &ee);
/* *
* \brief 人物死亡状态下把受死亡影响的状态设置为待删除状态
* */
void waitToDel();
bool addState(StateObject *object);
/* *
* 添加技能状态
* */
bool addState(const ElementValue& value, scene_pk_object *pAtt, uint32 ltime=0,bool save_die=false,bool save_offline=false,bool run_offline=true,bool unlimited=false);
bool issetState(const uint16 state) const;
//初始化技能状态管理器(状态函数)
static void initall();
/**
* \brief 比较两个StateObject,根据比较规则决定返回值,
* \object1和object2必须是同种类型的StateObject,即run.id相等,否则直接返回false
* \
* \return 若返回true则newObject点数较大,否则是oldObject,返回true则oldObject 可合并到newObject
*/
bool compareState(StateObject* newObject,StateObject* oldObject);
/* *
* \brief 计算技能影响的属性值,计算出来的结果保存在ElementValue的parm3中
* */
void effectStateProperty(StateObject* object);
private :
//设置技能状态id按位设置到缓存(方便访问)
void setStateToBuf(const StateObject *object);
void clearStateToBuf(const StateObject *object);
void setNineStateToIndex(const StateObject *object);
void clearNineStateToIndex(const StateObject *object);
public :
typedef const uint32 (* Func)(scene_pk_object *,StateObject *,ElementValue *);
static std::vector<Func> funclist;
scene_pk_object *_owner;
typedef std::set<StateObject*> StateObjectDel_Set;
typedef vector<StateObject*> StateObject_map;
typedef StateObject_map::iterator StateObject_iterator;
typedef StateObject_map::const_iterator StateObject_const_iterator;
typedef std::list<uint16> zListNineState;
typedef zListNineState::iterator zListNineState_iter;
StateObject_map _state_map;
StateObjectDel_Set _state_del;
uint8 _state_buf[(MSG::StateType_Max + 7) / 8];
zListNineState _nine_state_index;
RemoveObject_vector _remove_vector;
uint32 _profession;
public:
const StateObject_map& getStateObjectVec()const;
};
9、技能状态管理器定时器
在角色循环和npc循环里会定时运行技能状态机管理器定时器。
技能状态机管理器定时器,遍历所有技能状态对象,删除失效技能状态对象,运行技能状态对象,刷新角色属性到前端。
// 技能状态机管理器定时器
void StateManager::stateTimer()
{
uint32 notify = 0;
StateObject_map::iterator iter = _state_map.begin();
StateObject_map::iterator iter_end = _state_map.end();
for(; iter != iter_end;)
{
StateObject_map::iterator ittemp = iter;
++iter;
StateObject *object = *ittemp;
if(object == NULL)
{
continue;
}
if(object->run.pause)//暂停的不执行
{
continue;
}
if(object->isWaitDel()) //技能状态对象已失效
{
notify |= removeState(object);//移除技能状态对象
continue;
}
if(object->run.num == 0)//技能状态对象的叠加数为0
{
notify |= removeState(object);
continue;
}
if(object->run.time == 0)//技能状态对象时间到时
{
notify |= removeState(object);
continue;
}
notify |= runState(object);//运行技能状态对象
if(!object->run.unlimited)//无限时间的BUFF不参加倒计时
{
object->run.time--;//技能状态对象时间减少
}
}
StateManager::freshAll(_owner,notify);//刷新属性到前端
waitToDel();//修改死亡对象技能状态对象的状态为卸载
}
//清除技能状态对象
uint32 StateManager::removeState(StateObject *object)
{
uint32 notify = 0;
if(object)
{
if(!object->run.pause)//没有暂停的状态要通知前端卸载BUFF图标
{
clearStateToBuf(object);
clearNineStateToIndex(object);
unloadIco(object);
object->run.step = MSG::STATESTEP_UNLOAD;
notify = runState(object);//运行技能状态对象
}
object->bDelete = true;//标记删除技能状态对象
}
return notify;
}
卸载死亡对象的技能状态对象
void StateManager::waitToDel()
{
if(_owner->getEntryState() == SceneObject_Death)
{
StateObject_map::iterator iter = _state_map.begin();
for(;iter != _state_map.end();)
{
StateObject *object = *iter;
if(!object->run.save_die)//死亡保存状态(如果是不需要保存的就准备卸载)
{
object->run.step = MSG::STATESTEP_UNLOAD;
}
++iter;
}
}
}
广播清除九屏状态
void StateManager::clearNineStateToIndex(const StateObject *object)
{
if(object)
{
for(zListNineState_iter iter = _nine_state_index.begin() ; iter != _nine_state_index.end(); ++iter)
{
if(*iter == object->run.id)
{
_nine_state_index.erase(iter); //需要广播九屏的技能状态id列表
MSG::stClearStateMapScreenUserCmd send;
if(_owner->getType() == SceneObject_Player)
{
send.dwTempID = _owner->id;
}
else if(_owner->getType() == SceneObject_NPC)
{
send.dwTempID = _owner->tempid;
}
send.type = _owner->getType();
send.dwState = object->run.id;
_owner->sendmsgToNine(&send,sizeof(send));//广播到九屏
return;
}
}
}
}
清除技能状态
void StateManager::clearStateToBuf(const StateObject *object)
{
if(object)
{
clear_state(_state_buf,object->run.id);
}
}
/// 清除某个技能状态(技能状态buff数组,技能状态id)
inline void clear_state(uint8 *state, uint32 stateid)
{
state[stateid/ 8] &= (0xff & (~(1 << (stateid % 8))));//按位清除
}
技能状态buff
uint8 _state_buf[(MSG::StateType_Max + 7) / 8];//按位计算技能状态是否存在
//角色循环
bool scene_player::loop()
{
...
if(_one_sec(main_logic_thread::currentTime))
{
// 运行人物状态机
{
this->statem->loop();//技能状态管理器循环(删除技能状态删除列表的对象)
this->statem->stateTimer();//运行技能状态管理器定时器
}
...
}
...
}
npc循环运行技能状态定时器
//npc循环
bool scene_npc::loop()
{
const SceneObjectState &state = getEntryState();
if(state == SceneObject_Normal)//npc正常状态
{
if (AIC)
{
if(this->base->npcType == MSG::NpcType_Gun)
{
gunAction();
}
else
{
AIC->processPhase();
normalAction();
}
}
if(_one_sec(main_logic_thread::currentTime))//1秒定时器
{
this->statem->loop();//技能状态管理器循环(删除技能状态删除列表的对象)
this->statem->stateTimer();//运行技能状态管理器定时器
}
...
}
10、运行技能状态对象
运行技能状态对象的运行,会执行运行技能状态对象的函数。
// 运行状态机 对一个技能 对应多个funclist [1:N]的关系
uint32 StateManager::runState(StateObject *object)
{
uint32 notify = 0;
if(!object)
{
return notify;
}
if(!_owner && object->run.pause)
{
return notify;
}
ElementValue& element = object->element;
//运行技能状态函数
notify |= funclist.at(element.id > MAX_ELEMENT_ID ? 0 : element.id)(_owner,object,&element);
if(object->run.step == MSG::STATESTEP_LOAD)//技能状态转换成定时器状态
{
object->run.step = MSG::STATESTEP_TIMER;
}
else if(object->run.step == MSG::STATESTEP_RELOAD)
{
object->run.step = MSG::STATESTEP_TIMER;
loadIco(object);//通知前端重新加载了该技能状态
}
return notify;
}
根据技能状态的技能元素的id来索引技能状态函数(表驱动法),运行技能状态函数。
11、技能状态函数
技能状态函数的有多个状态(状态加载STATESTEP_LOAD、状态解档加载STATESTEP_RELOAD、状态轮训STATESTEP_TIMER、状态卸载 STATESTEP_UNLOAD),在不同状态下有不同功能。
技能状态函数数量较多,以下是其中一个
/**
* \brief key = 3,add Hp
*/
const uint32 StateElementValue_3(scene_pk_object *owner,StateObject *object,ElementValue *evalue)
{
switch(object->run.step)
{
case MSG::STATESTEP_LOAD:
{
if(owner && owner->getEntryState()!=SceneObject_Death)
{
owner->addHp(evalue->parm1);
return MSG::NOTIFYTYPE_HPSD;
}
}
break;
case MSG::STATESTEP_RELOAD: //状态解档加载
{
。。。
}
break;
case MSG::STATESTEP_UNLOAD://该状态卸载后
{
。。。
}
break;
}
return MSG::NOTIFYTYPE_NULL;
}
范围搜索(pk对象施法按一定的规则来搜索范围)
(1)羁绊范围搜索:附近组队范围(队友或自己)
(2)技能几何范围搜索(1单2 自己圆3鼠标点圆4扇5线))