任务系统

任务系统

1 任务系统设计思路

(1) 玩家对任务的操作有:接受、提交、放弃任务。他们的逻辑入口函数都是UBI_CAIHuman类成员函数,主要处理逻辑都放在了UBI_CAIQuest类的成员函数里。

(a) 接受任务:UBI_CAIHuman::DoAcceptQuest()->UBI_CAIQuest::DoAccept()

(b) 提交任务:UBI_CAIHuman::DoCompleteQuest()->UBI_CAIQuest::DoComplete()

(c) 放弃任务:UBI_CAIHuman::DoAbandonQuest()->UBI_CAIQuest::DoAbandon()

 

(2) 按照任务内容,来更新任务状态。

(a) 杀怪:UBI_CAIHuman::OnQuestKillObject()->UBI_CAIQuest::DoQuestKillObject()

(b) 采集:UBI_CAIHuman::OnQuestGather()->UBI_CAIQuest::DoQuestGather()

(c) 进入区域:UBI_CAIHuman::OnQuestEnterArea()->UBI_CAIQuest::DoQuestEnterArea()

(d) 使用物品:UBI_CAIHuman::OnQuestUseItem()->UBI_CAIQuest::DoQuestUseItem()

(e) 点击面板:UBI_CAIHuman::OnQuestClickPanel()->UBI_CAIQuest::DoQuestClickPanel()

(f) 物品改变:UBI_CAIHuman::OnQuestItemCountChange()->UBI_CAIQuest::DoQuestItem _CountChange()

2任务系统关注点

(1) 任务的实现方式有两种,脚本和表格类。

因此在任务的每个逻辑流程中都实现了脚本和表格分支,它们是通过任务基本表里的任务绑定脚本ID来区分的,若ID大于0则走脚本流程,否则走表格流程。具体如下:

(a) 点击NPC显示任务选项:在Lua_DispatchEventList函数里对任务是否显示的判断做了脚本和表格的分支。

(b) 接受任务:在接受任务的入口函数UBI_CAIHuman::DoAcceptQuest()里做了脚本和表格的分支。

(c) 提交任务:在提交任务的入口函数UBI_CAIHuman::DoCompleteQuest()里做了脚本和表格的分支。

(d) 放弃任务:在放弃任务的入口函数UBI_CAIHuman::DoAbandonQuest()里做了脚本和表格的分支。

(e) 杀怪:在函数UBI_CAIHuman::OnQuestKillObject()里做了脚本和表格的分支。只要是人为杀死的怪都会走任务逻辑。表格:先遍历玩家身上的任务,再在任务基本表中找到他的杀怪触发点。脚本:调用该玩家所有已接任务脚本里的OnKillObject()接口。如果多个任务杀的是同种怪,则杀死一个这类怪就会更新所有这些相关任务参数。

(f) 采集:在函数UBI_CAIHuman::OnQuestGather()里做了脚本和表格的分支。每次采集生长点都会走任务流程。表格:先遍历玩家身上的任务,再在任务基本表中找到他的采集触发点。脚本:调用该玩家所有已接任务脚本里的OnGather ()接口。

(g) 进入区域:在函数UBI_CObjectHuman::OnEnterArea()里做了脚本和表格的分支。脚本:玩家进入绑定了脚本的区域里的时候,程序会调用到脚本函数OnEnterArea(),在里面判断该玩家是否接了该区域相关任务,如果是则调用相应的任务脚本函数。表格:玩家进入任何区域都会走进区域任务函数UBI_CAIHuman::OnQuestEnterArea(),在这里先遍历玩家身上的任务,再在任务基本表中找到他的区域触发点。

(h) 使用物品:在CSUseQuestItemHandler()函数里做了脚本和表格的分支。物品是否能使用的判断逻辑和物品的使用逻辑做了分离,这两个逻辑均做了脚本和表格的分支。

(i) 点击面板:有两种面板,一个是与NPC关联的,一个是非关联的,它们分别在CSAskNpcEventInfoHandler()函数和CSAskTriggerPanelInfoHandler()函数里做了脚本和表格的分支。表格:通过配表可实现对话以及其他一些功能,比如生成NPC,特效等。脚本:可实现对话,另外可灵活添加其它功能。复杂的对话任务最好用脚本去实现。

(j) 物品改变:在UBI_CAIHuman::OnQuestItemCountChange()函数里做了脚本和表格的分支。表格:先遍历玩家身上的任务,再在任务基本表中找到他的物品改变触发点。脚本:调用该玩家所有已接任务脚本里的OnItemChanged ()接口。

 

(2) 脚本触发点和效率优化。

(a) NPC对话框里是否显示任务:OnEnumerate(),比如判断前序任务是否做完,判断玩家等级是否达到接受任务的要求等。

(b) 接受任务:OnAccept(),将任务挂靠在玩家身上写死在了C++代码里,脚本只需关注接受任务后的一些特殊需求,比如切相位,给物品等。

(c) 放弃任务:OnAbandon(),删除玩家身上的任务也写死在了C++代码里,脚本只需关注放弃任务后的一些特殊需求,比如删物品,删buff等。

(d) 提交任务:OnSubmit()。给玩家的普通奖励写死在了C++代码里,脚本只需关注提交任务后的特殊需求,比如给buff,给技能等。

(e) 杀怪:OnKillObject(),怪物被玩家杀死之后只要玩家身上有已接的脚本类任务,就会走到这些任务的这个接口里,当然只要这些任务的目标不是杀这个怪,那么只是走进去而已,不会有别的逻辑。效率上是否能再优化呢?

(f) 采集:OnGather(),玩家采集完生长点之后只要玩家身上有已接的脚本类任务,就会走到这些任务的这个接口里。同上,只要这些任务的目标不是采集这个生长点,那么也只是走进去而已,不会有别的逻辑。采集事件不多,个人感觉效率上没有必要再去优化。如果需要优化的话,可以通过将生长点绑定脚本,然后调用该脚本来实现。

(g) 进入区域:Area:OnEnterArea()->Quest:OnEnterArea(),玩家进入绑定了脚本的区域后,调用该区域脚本里的Area:OnEnterArea()接口,在该接口里再调用所有与该区域相关的脚本类任务的Quest:OnEnterArea()接口。这个效率应该还行,没什么可优化的。

(h) 使用物品:CanUseQuestItem()OnQuestUseItem(),玩家在使用任务物品时先通过CanUseQuestItem()接口来判断是否能使用,比如使用位置,场景是否匹配等,然后再通过OnQuestUseItem()接口来做一些使用完物品的逻辑。目前实现有问题,需要修改。需要将任务物品绑定脚本来实现。

(i) 点击面板:即对话任务,OnDialog()OnDialogRequest(),目前能满足策划的对话任务的所有需求,效率上没什么可优化的了。

(j) 物品改变:OnItemChanged()背包里添加和删除物品都会走到这,影响服务器效率。优化的方式就是通过绑定物品与脚本的方式来做。

(k) 上线:OnOnLine(),主要针对计时类或者一些特殊任务的上线处理。每个脚本任务都会有这个接口,所以玩家每次上线只要身上挂了任务,就会走到这里,不过一般没什么处理逻辑。

(l) 下线:OnOffLine(),同上。

(m) 其它:策划有时候会有一些特殊任务需求,比如切相位触发任务完成,穿装备触发任务完成等,我们目前的做法都是尽量让它只触发到某个特定脚本,而不是去走一遍玩家身上挂靠的所有任务的相关接口,以节省服务器执行时间。

(3) 网络包流量。

任务对白,任务奖励数据客户端和服务器都存了一份,因此服务端发任务相关包的时候无需加上这些数据,节省了包流量。客户端所存的任务数据只做显示用,不做逻辑处理,所有任务逻辑都在服务端做。

3天龙任务系统流程

(1) NPC身上的任务的挂接

表格任务,需要先把任务挂接到NPC身上;脚本任务,一般不需要事先挂接好,任务脚本号(即事件ID列表)会写在NPC的Obj脚本中,执行函数时,会轮询那些事件ID列表中的脚本号,执行NPC的Event脚本。

(2) 客户端点击NPC弹出对话面板

à Client: 点击Npc时候,发CGCharDefaultEvent包。

CGCharDefaultEvent msg;

msg.setObjID(idTargetObj);

CNetManager::GetMe()->SendPacket(&msg );

à Server: 根据Npc的ObjID号,PushCommand。

pHuman->GetHumanAI()->PushCommand_DefaultEvent(idTarget),此函数里面做了:

pCharacter->getScene()->GetLuaInterface()->ExeScript_DDD(

              idScript,

              DEF_EVENT_ENTRY_FUNC_NAME, //脚本进入函数 "OnDefaultEvent"

              (INT)pCharacter->getScene()->SceneID(),

              (INT)pCharacter->GetID(),

              (INT)pNPC->GetID()) ;

NPC的Obj脚本函数OnDefaultEvent里面一般是:

BeginEvent(sceneId)

local PlayerName=GetName(sceneId,selfId)

AddText(sceneId,"你们宋人都是我的俘虏,都要听我的")

for i, eventId in x022008_g_eventList do ---遍历事件列表中的Event脚本号

CallScriptFunction(eventId, "OnEnumerate",sceneId,selfId, targetId)

End

EndEvent(sceneId)

DispatchEventList(sceneId,selfId,targetId)

Npc的Event脚本中的OnEnumerate函数里,也是类似一些AddNumText、AddText函数,他们被BeginEvent…EndEvent…DispatchEventList包了起来,其作用是:

BeginEvent是把一块任务缓冲MisBuf清空,为传输做好准备;

typedef struct_MisBuf

{

    enum

    {

           MISSIONBUFLEN= 64,

           MISSIONCHARBUFLEN= MISSIONBUFLEN * 4 * 8,//因为客户端的bug,所以放大buf长度

    };

    typedefstruct _MisItemBuf

    {

           //这个buf存储了发送给客户端的字符信息串,客户端需要注意字符解析问题

           CHARbuf[MISSIONCHARBUFLEN];

           enumBufType

           {

                  BufType_Int,

                  BufType_Str,

                  BufType_IntStr,

                  BufType_Money,

                  BufType_Item,

                  BufType_RandItem,

                  BufType_RadioItem,

                  BufType_Skill

           }mType

           INTm_BufUse;

           VOIDSetBufUse(INTNewBufUse){

                  m_BufUse += NewBufUse;

           }

           INTGetBufUse(void){

                  return m_BufUse;

           }

           INTGetBufMaxSize(){

                  return MISSIONCHARBUFLEN;

           }

           INTAddBuf(VOID*vpNewBuf, INTnNewBufLen){

                  if (m_BufUse+nNewBufLen > MISSIONCHARBUFLEN)

                  {

                         return 0;

                  }

                  memcpy(buf+m_BufUse, (VOID*)(vpNewBuf), nNewBufLen);

                  m_BufUse += nNewBufLen;

                  return m_BufUse;

           }

    }MisItemBuf[MISSIONBUFLEN];

    MisItemBufBuff;

    INTmUseNum;

}MisBuf;

而DispatchEventList则是解析这块MisBuf,根据每个缓冲块的类型(缓冲块内容是在AddText,AddNumText时加进去的),填充到一个ScriptParam_EventList包中:

ScriptParam_EventList paramEventList;

       paramEventList.Reset();

paramEventList.m_idNPC = targetId;

paramEventList.AddItem(&itemEvent ); //填充

之后发给客户端

       Packets::GCScriptCommand Msg;

       Msg.SetEventListResponseCmd( &paramEventList );

       Player* pPlayer = pHuman->GetPlayer();

       pPlayer->SendPacket( &Msg ) ;

该包定义如下:

struct ScriptParam_EventList

{

       ObjID_t                       m_idNPC;

       BYTE                          m_yItemCount;

       ScriptEventItem            m_seiItem[MAX_EVENT_LIST_ITEM_COUNT];

       VOID AddItem( const ScriptEventItem*pItem ){

              if ( m_yItemCount< MAX_EVENT_LIST_ITEM_COUNT )

              {

                     m_seiItem[m_yItemCount]= *pItem;

                     m_yItemCount++;

              }

       }

};

VOID SetEventListResponseCmd(const ScriptParam_EventList*pEventList ){

                     m_nCmdID =SCRIPT_COMMAND_EVENT_LIST_RESPONSE;

                     m_paramEnentList =*pEventList;

              }

à Client: 解析Server发过来的这块MisBuf,生成NPC对话框。

GCScriptCommandHandler中:

CUIDataPool*pDataPool = (CUIDataPool*)(CGameProcedure::s_pDataPool);

SCommand_DPCcmdTemp;

cmdTemp.m_wID= DPC_SCRIPT_COMMAND;

cmdTemp.m_anParam[0]= pPacket->getCmdID();

cmdTemp.m_apParam[1]= pPacket->getBuf();

pDataPool->OnCommand_(&cmdTemp );

// DPC_SCRIPT_COMMAND是脚本指令

// D0       :      script command id

// P1           :      param list

// #define   DPC_SCRIPT_COMMAND    (6)

上面的OnCommand_ ( const SCommand_DPC*pCmd )中主要做了:

switch ( pCmd->m_wID)

{

       case DPC_SCRIPT_COMMAND: //客户端自己填充的

              {

                     INT nCmdID  = pCmd->m_anParam[0]; //服务器设置过来的

                     VOID *pBuf   = pCmd->m_apParam[1];

                     switch ( nCmdID )

                     {

                     case SCRIPT_COMMAND_EVENT_LIST_RESPONSE: // 事件列表返回

                            *m_pEventList = *((ScriptParam_EventList*)(pBuf));

                            OnEventListResponse();

                            break;

                     case SCRIPT_COMMAND_MISSION_RESPONSE// 打开任务信息

                            *m_pMissionInfo = *((ScriptParam_MissionInfo*)(pBuf));

                            OnMissionInfoResponse();

                            break;

                            //任务需求信息(完成任务的文字描述,完成需要的物品,奖励的物品)

                     case SCRIPT_COMMAND_MISSION_DEMAND_RESPONSE:// 任务需求的查询返回

                            *m_pMissionDemandInfo = *((ScriptParam_MissionDemandInfo*)(pBuf));

                            OnMissionDemandInfoResponse();

                            break;

                            //任务继续信息(包含了奖励信息,在点击continue之后再去显示)

                     case SCRIPT_COMMAND_MISSION_CONTINUE_RESPONSE:// 任务的继续按钮事件返回

                            *m_pMissionContinueInfo = *((ScriptParam_MissionContinueInfo*)(pBuf));

                            OnMissionContinueInfoResponse();

                            break;

                            //任务完成情况提示信息

                     case SCRIPT_COMMAND_MISSION_TIPS:// 任务提示

                            *m_pMissionTips = *((ScriptParam_MissionTips*)(pBuf));

                            OnMissionTips();

                            break;

                            //技能学习信息

                     case SCRIPT_COMMAND_SKILL_STUDY:// 技能信息

                            m_pSkillStudy->Reset();

                            *m_pSkillStudy = *((ScriptParam_SkillStudy*)(pBuf));

                            OnSkillStudy();

                            break;

                     default:

                            break;

                     }

              }

              break;

       case DPC_UPDATE_MISSION_LIST://名字似乎不再合适了

              {

                     UINT dwObjID = pCmd->m_adwParam[0];

                     UINT dwModifyFlags= pCmd->m_adwParam[1];

                     _OWN_MISSION *paMissionBuf = (_OWN_MISSION*)(pCmd->m_apParam[2]);

                     UINT i;

                     CDetailAttrib_Player* playData= const_cast<CDetailAttrib_Player*>(CUIDataPool::GetMe()->GetMySelfDetailAttrib());

                     _OWN_MISSION *pMission;

                     for ( i = 0; i < MAX_CHAR_MISSION_NUM;i++ )

                     {

                            if ( dwModifyFlags& (0x00000001 << i) )

                            {

                                   pMission = &(playData->m_listMission.m_aMission[i]);

                                   memcpy( (void*)pMission, &paMissionBuf[i], sizeof(_OWN_MISSION) );

                                   CGAskMissionDesc msg;

                                   msg.setMissionIndex(pMission->m_idMission);

                                   CNetManager::GetMe()->SendPacket( &msg);

                            }

                     }

              }

              break;

       case DPC_UPDATE_MISSION_ADD:

              {

                     _OWN_MISSION *pMission       = (_OWN_MISSION*)(pCmd->m_apParam[0]);

                     m_pPlayerData->AddMission(pMission );

                     CGAskMissionDesc msg;

                     msg.setMissionIndex(pMission->m_idMission);

                     CNetManager::GetMe()->SendPacket( &msg);

                     rcResult = RC_OK;

              }

              break;

       case DPC_UPDATE_MISSION_REMOVE:

              {

                     MissionID_t idMission= (MissionID_t)(pCmd->m_adwParam[0]);

                     m_pPlayerData->RemoveMission(idMission );

                     rcResult = RC_OK;

              }

              break;

这时候就会执行OnEventListResponse,里面会调用:

CEventSystem::GetMe()->PushEvent(GE_QUEST_EVENTLIST, pNPC? pNPC->GetID() : -1);

客户端会执行相应的脚本:

--=========================================================

-- 事件处理

--=========================================================

functionQuest_OnEvent(event)

       local objCared = tonumber(arg0);

       --第一次和npc对话,得到npc所能激活的操作

       if(event == "QUEST_EVENTLIST") then

              --关心NPC

              BeginCareObject_Quest(objCared)

              this:Show();

              QuestGreeting_Desc:ClearAllElement();

              Quest_EventListUpdate();

 

       --在接任务时,看到的任务信息

       elseif(event == "QUEST_INFO")then

              --关心NPC

              BeginCareObject_Quest(objCared)

              this:Show();

              QuestGreeting_Desc:ClearAllElement();

              Quest_QuestInfoUpdate()

 

       --接受任务后,再次和npc对话,所得到的任务需求信息,(任务完成)

       elseif(event =="QUEST_CONTINUE_DONE") then

              QuestGreeting_Desc:ClearAllElement();

              Quest_MissionContinueUpdate(1);

 

       --接受任务后,再次和npc对话,所得到的任务需求信息,(任务未完成)

       elseif(event =="QUEST_CONTINUE_NOTDONE") then

              QuestGreeting_Desc:ClearAllElement();

              Quest_MissionContinueUpdate(0);

              --关心NPC

              BeginCareObject_Quest(objCared)

 

       --点击“继续之后”,奖品选择界面

       elseif(event =="QUEST_AFTER_CONTINUE") then

              --关心NPC

              this:Show();

              QuestGreeting_Desc:ClearAllElement();

              Quest_MissionRewardUpdate();

end

这里的Quest_EventListUpdate负责显示任务列表:

for i=1,nEventListNum do

       local strType,strState,strScriptId,strExtra,strTemp= DataPool:GetNPCEventList_Item(i-1);

       if(strType == "text") then

              QuestGreeting_Desc:AddTextElement(strTemp);

       elseif(strType == "id") then

              strTemp = strTemp .. "#"

              strTemp = strTemp .. strScriptId

              strTemp = strTemp .. ","

              strTemp = strTemp .. strExtra

              strTemp = strTemp .. "$"

              strTemp = strTemp .. strState

              if(tonumber(strState) == 1 ortonumber(strState) == -1) then

                     canacceptArr[k] = strTemp;

                     k = k+1;

              elseif(tonumber(strState) == 2)then

                     cansubmitArr[j] = strTemp;

                     j = j+1;

              else

                     QuestGreeting_Desc:AddOptionElement(strTemp);

              end

       end

end

上面就是根据具体类型(是普通文本还是按钮(按钮上绑定脚本号))添加到NPC对话面板中去。ComplexWindow::AddOptionElement(strTmep)对strTmep按照"#"、","、"$"解析完毕后,内部又调用了:

void FalagardComplexWindow::AddChildElement_Option(String strText,StringstrExtra1,String strExtra2, String strText3, FontBase* font)

{

      ChildElement_Option* pNewChild = new ChildElement_Option;//动态创建窗口

       pNewChild->d_Type= CT_Option;

       pNewChild->d_Text= strText;

       if(font) pNewChild->d_Font= font;

       else pNewChild->d_Font = getFont();

 

       char szTempName[32];

       int nIndex = 0;

       for(int i=0; i<(int)d_ChildVector.size(); i++)

       {

              if(d_ChildVector[i]->d_Name.substr(0, 10) == (utf8*)"__option__")

              {

                     nIndex++;

              }

       }

_snprintf(szTempName, 32, "__option__%02d#%d,%d", nIndex,atoi(strExtra1.c_str()),atoi(strExtra2.c_str()));

       pNewChild->d_Name= (utf8*)szTempName; //作为窗口名字

       pNewChild->d_State= atoi(strText3.c_str());

       switch( pNewChild->d_State)

       {

              case 1:

                     pNewChild->d_Button= static_cast<FalagardButton*>(WindowManager::getSingleton().createWindow((utf8*)"TLBB_QuestButton_1", getName() + szTempName));//

                     break;

              case 2:

                     pNewChild->d_Button= static_cast<FalagardButton*>(WindowManager::getSingleton().createWindow((utf8*)"TLBB_QuestButton_2", getName() + szTempName));//

                     break;

              default:

                     pNewChild->d_Button= static_cast<FalagardButton*>(WindowManager::getSingleton().createWindow((utf8*)"TLBB_QuestButton_1", getName() + szTempName));//

                     break;

              }

       pNewChild->d_Button->setText(" "+strText);

       pNewChild->d_Button->show();

pNewChild->d_Button->subscribeEvent(PushButton::EventClicked, //注册事件响应函数Event::Subscriber(&FalagardComplexWindow::handleOptionClicked, this));

       //设为子窗口

       d_ParentWindow->addChildWindow(pNewChild->d_Button);

       d_ChildVector.push_back(pNewChild);

performWindowLayout();

}

所以点击对话框面板上的按钮时,就会调用:

bool FalagardComplexWindow::handleOptionClicked(constEventArgs& e)

{

       WindowEventArgs arg( ((constWindowEventArgs&) e).window);

       fireEvent(EventOptionClicked, arg,EventNamespace);

       return true;

}

/*************************************************************************

       Fire / Trigger anevent

*************************************************************************/

voidEventSet::fireEvent(const String& name, EventArgs& args, constString& eventNamespace)

{

    // handle globalevents

   GlobalEventSet::getSingleton().fireEvent(name, args, eventNamespace);

    // handle localevent

    fireEvent_impl(name, args);

}

voidEventSet::fireEvent_impl(const String& name, EventArgs& args)

{

    // find eventobject

    Event* ev = getEventObject(name);

    // fire the event if present and set is notmuted

    if ((ev != 0) && !d_muted)

        (*ev)(args);

}

除了AddChildElement_Option,还有AddChildElement_ItemAddChildElement_ActionAddChildElement_HyperLinkAddChildElement_Money等函数。

(3) 客户端点击按钮选项

à Client:客户端点击对话面板中按钮,就会执行:

FalagardComplexWindow::handleOptionClicked,最终执行(*ev)(args),见上面分析。

即:boolScriptFunctor::operator()(const EventArgs& e) const

{

       ScriptModule* scriptModule = System::getSingleton().getScriptingModule();

       if (scriptModule)

       {

              return scriptModule->executeScriptedEventHandler(scriptFunctionName,e);

       }

}

天龙里已经对虚的加以下面实现:

bool CGameUIScript::executeScriptedEventHandler(const String&strHandle, const EventArgs& e)

{

       const WindowEventArgs& eWindow =(const WindowEventArgs&)e;

       Window* pWindow = eWindow.window;

       g_theUIEventArg.m_pEventArg = &e;

       do

       {

              if(!pWindow) break;

              void* pUserData =pWindow->getUserData();

              if(pUserData)

              {

                     ((CUIWindowItem*)pUserData)->FireUIEvent(strHandle.c_str(), eWindow.window);

                     break;

              }

              …

}

VOID CUIWindowItem::FireUIEvent(LPCTSTR szEventHandle, CEGUI::Window*pWindow)

{

       //设置当前全局参数arg0为当前选择的窗口名字

       g_pScriptSys->GetLuaState()->GetGlobals().SetString("arg0",pWindow->getName().c_str());

       //即:执行脚本函数:QuestOption_Clicked

m_pScriptEnv->DoString(szEventHandle);

}

那么上面的scriptFunctionName是什么时候被赋值成QuestOption_Clicked的呢?

是在解析XML文件时候被赋值的:

// handle event subscription element

else if(element == EventElement)

{

String eventName(attributes.getValueAsString(EventNameAttribute));

    String functionName(attributes.getValueAsString(EventFunctionAttribute));

       // attempt tosubscribe property on window

       try

       {

              if (!d_stack.empty())

              {

           d_stack.back()->subscribeEvent(eventName,ScriptFunctor(functionName));

              }

       }

… …

}

上面的FalagardComplexWindow类型控件:QuestGreeting_Desc已经注册了Click事件的响应函数QuestOption_Clicked,这样上面的functionName就是QuestOption_Clicked啦。

--=========================================================

-- 选择一个任务

--=========================================================

function QuestOption_Clicked()

       -- arg0的格式是

       -- QuestGreeting_option_03#211207,0

       pos1,pos2 =string.find(arg0,"#");

       pos3,pos4 =string.find(arg0,",");

       local strOptionID = -1;

       local strOptionExtra1 = string.sub(arg0,pos2+1,pos3-1 );

       local strOptionExtra2 = string.sub(arg0,pos4+1);

QuestFrameOptionClicked(tonumber(strOptionID),tonumber(strOptionExtra1),tonumber(strOptionExtra2));

end

QuestFrameOptionClicked函数里面会调用SendSelectEvent(nIndex,nExIndex1,nExIndex2); SendSelectEvent会根据其参数去客户端的事件列表中查询,返回对应的事件,然后发包:

CGEventRequestmsg;

msg.setExIndex( pItem->m_index);

msg.setScriptID(pItem->m_idScript );

msg.setNPCID(m_pEventList->m_idNPC );

CNetManager::GetMe()->SendPacket(&msg );

à Server:根据NpcID,脚本号,还有按钮索引,PushCommand。

ObjID_t idObj = pHuman->GetID() ;

       ObjID_t idNPC = pPacket->getNPCID(); // NPC ID

       ScriptID_t idScript =pPacket->getScriptID(); //脚本号

       INT exIndex = pPacket->getExIndex(); //按钮索引

       pHuman->GetHumanAI()->PushCommand_EventRequest(idNPC, idScript,exIndex),此函数中主要调用了:

       pCharacter->getScene()->GetLuaInterface()->OnEventRequest(

              idScript, pCharacter->GetID(), pNPC->GetID(),idEvent, exIndex ) ;

其中就会调用到脚本函数,例如:

--**********************************

--事件列表选中一项(NPCObj脚本)

--**********************************

functionx002045_OnEventRequest(sceneId, selfId, targetId, eventId )

       if GetNumText()==0   then

              ifIsHaveMission(sceneId,selfId,4021) > 0 then

                     BeginEvent(sceneId)

                     AddText(sceneId,"你有漕运货舱在身,我们驿站不能为你提供传送服务。");

                     EndEvent(sceneId)

                     DispatchEventList(sceneId,selfId,targetId)

              else 

                     CallScriptFunction((400900),"TransferFunc",sceneId, selfId, 17,94,149)

              end

       else

              for i, findId inx002045_g_eventList do ---遍历NPCEvent脚本

                     if eventId == findId then

                            CallScriptFunction(eventId, "OnDefaultEvent",sceneId, selfId, targetId )

                            return

                     end

              end

       end

end

(4) 点击“接受任务”按钮

à Client:Lua_QuestFrameAcceptClicked

VOIDCUIDataPool::SendAcceptEvent()

{

       CGMissionAccept msg;

       msg.setScriptID(m_pMissionInfo->m_idScript );

       msg.setNPCID( m_pMissionInfo->m_idNPC);

       CNetManager::GetMe()->SendPacket(&msg );

}

4永生任务系统流程

(1) 客户端点击NPC

à Client:

  UBI_CCSDefaultDialogPacket

à Server:

  CSDefaultDialogHandler -> 调用脚本

   --sceneId表示场景号,selfid玩家,targetIdNPC

function x000001_OnDefaultEvent(sceneId, selfHighId, selfLowId, targeHighId, targeLowId )

       BeginAddEvent(sceneId)

        AddEvent(sceneId, -1, 2, string4)

        AddEvent(sceneId, -1, 13, string5)

       EndAddEvent(sceneId)

       DispatchEventList( sceneId, selfHighId,selfLowId, targeHighId, targeLowId )

       return 1

  end

  其中DispatchEventList中又会发包:SCNPCEventListPacket

à Client:

  SCNPCEventListExecute,

 pNPCDialogData->SetEventList(pSCNPCEventListPacket->GetNPCEvent(i),i);

 pEventSystem->PushEvent(GAME_EVENT_SHOW_NPCDIALOG, vParam);

(2) 客户端点击按钮选项

客户端点击按钮后执行:UBI_CNPCDialogData::ClickEventButton。

(a) 打孔镶嵌等

à Client:

  CSAskNpcEvent

à Server:

  SCNpcEventInfo

à Client:

  SCNpcEventInfoExecute

 

(b) 接受任务等

à Client:

  CSAskNpcEvent

à Server:

  NPCQuestData

à Client:

  NPCQuestDataExecute

 pEventSystem->PushEvent(GAME_EVENT_SHOW_QUEST, vParam);

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值