Files
mir_server/server/LogicServer/entity/Npc.cpp

506 lines
13 KiB
C++
Raw Normal View History

2025-01-09 17:45:40 +08:00
#include "StdAfx.h"
#include "Npc.h"
#include "../LogicEngine.h"
#include <iostream>
#include <fstream>
using namespace std;
using namespace wylib::stream;
// 此宏开关用于控制是否使用缓存的Lua二进制字节码加载以提高NPC创建速度
// 关闭此宏就直接加载脚本文件
#define USE_LUA_BIN_CODE
CNpc::CNpc()
:Inherited()
{
m_pConfig = NULL;
m_bScriptInited = false;
m_Script.SetEntity(this);
m_idleTime = 0;
}
void CNpc::InitNPCAi()
{
//if (m_pConfig && m_pConfig->nAITypeId > 0)
//{
// m_pAI = GetGlobalLogicEngine()->GetAiMgr().CreateAi(m_pConfig->nAITypeId, (CAnimal*)this);
//}
}
void CNpc::LogicRun(TICKCOUNT nCurrentTime)
{
if(IsInited() == false) return ;
//DECLARE_FUN_TIME_PROF()
Inherited::LogicRun(nCurrentTime); // (NPC不执行怪物的这些逻辑检测)
//检查NPC调用idle函数
if (m_RunIdleTimer.Check(nCurrentTime))
{
DECLARE_TIME_PROF("NPC::Call_Idle")
if (m_idleTime == 0)
{
//设置下次调用idle函数的时间为90秒到180之内。
int nInterval = 120000;
if (m_pConfig)
{
nInterval = m_pConfig->nIdleInterval;
nInterval = __max(nInterval, 15000); // 最小不能低于15s避免执行太频繁
}
m_RunIdleTimer.SetNextHitTimeFromNow(nInterval + (nCurrentTime%10) * 1000);
//m_RunIdleTimer.SetNextHitTimeFromNow(90000 + (nCurrentTime%90) * 1000);
}
else
{
m_RunIdleTimer.SetNextHitTimeFromNow(m_idleTime);
}
//if (m_pConfig)
//{
// Say(mssNear, m_pConfig->sSay);
//}
/*static LPCSTR sIdleFnName = "idleTalk";
if (m_Script.FunctionExists(sIdleFnName))
{
CScriptValueList vl;
m_Script.Call(sIdleFnName, vl, vl, 0);
}*/
//检查脚本内存回收时间
if (m_ScriptGCTimer.Check(nCurrentTime))
{
//设置下次进行脚本内存回收的时间为180秒到270秒之内。
//在一个随机的时间范围内进行垃圾回收可以避免所有NPC在同一个主循环中进行脚本垃圾回收而导致的性能集中损失
//对脚本进行垃圾回收是十分低效的。
DECLARE_TIME_PROF("NPC::scriptGC")
m_ScriptGCTimer.SetNextHitTimeFromNow(180000 + (nCurrentTime%90) * 1000);
m_Script.gc();
}
}
}
void CNpc::ProcessEntityMsg(const CEntityMsg &msg)
{
CEntity *pSender;
if(msg.bIsUsed ==false)
{
OutputMsg(rmError,"name=%s,msgid=%d,ProcessEntityMsg is invalid",GetEntityName(),(int)msg.nMsg);
return ;
}
switch(msg.nMsg)
{
case CEntityMsg::emGetQuestState:
if (msg.nSender.GetType() == enActor)
{
pSender = GetEntityFromHandle(msg.nSender);
// if (pSender && pSender->IsInited()) ((CActor*)pSender)->GetQuestSystem()->SendNpcQuestState(this);
}
break;
default:
Inherited::ProcessEntityMsg(msg);
break;
}
}
void CNpc::ResetScript()
{
#ifdef USE_LUA_BIN_CODE
// 采取先编译的方式
//m_Script.resetBinScript(m_pConfig->packet);
#else
//m_Script.setScript(m_pConfig->pNpcScriptText, &m_pConfig->LRList);
#endif
// 清空所有的实体消息和实体回调
ClearAllEntityMsg();
ClearScriptCallback();
}
bool CNpc::LoadScript(LPCSTR sPath,bool boReload)
{
DECLARE_TIME_PROF("CNpc::LoadScript");
LPCSTR sScript = NULL;
if(sPath ==NULL)
{
OutputMsg(rmError, _T("unable to load Script file npc=%s"), GetEntityName());
return false;
}
if (strcmp(sPath, "") == 0)
{
return true;
}
//从文件加载脚本
CMemoryStream ms;
if (ms.loadFromFile(sPath) <= 0)
{
OutputMsg(rmError, _T("unable to load Script file %s"), sPath);
return false;
}
//对脚本进行预处理
CCustomLuaPreProcessor pp;
GetLogicServer()->GetVSPDefine().RegisteToPreprocessor(pp);
//增加语言包已被加载的标记,语言包已经作为独立的配置读取了,这里注册宏后可以加快脚本读取
if (!pp.getMacro("_LANGUAGE_INCLUDED_"))
pp.addMacro("_LANGUAGE_INCLUDED_");
try
{
sScript = pp.parse((LPCSTR)ms.getMemory(),sPath);
}
catch (RefString &s)
{
OutputMsg(rmError, _T("%s config parse error: %s\r\n"),sPath, s.rawStr());
return false;
}
if ( !sScript )
{
OutputMsg(rmError, _T("unable to pre-process Script file,npc %s"), GetEntityName() );
return false;
}
wylib::misc::CRefObject<CCustomLuaPreProcessor::CLineRangeList> LRList;
INT_PTR nLRCount = pp.getLineRangeData(NULL, 0);
if (nLRCount > 0)
{
LRList = new wylib::misc::CRefObjectImpl<CCustomLuaPreProcessor::CLineRangeList>();
LRList.raw_ptr()->reserve(nLRCount);
pp.getLineRangeData(*LRList.raw_ptr(), nLRCount);
LRList.raw_ptr()->trunc(nLRCount);
}
if(!m_Script.setScript(sScript, &LRList))
{
FILE* fp = fopen("scripterror.txt", "wb");
if (fp)
{
fputs(sScript, fp);
fclose(fp);
}
return false;
}
return true;
}
bool CNpc::Call( LPCSTR sFunName)
{
if (sFunName == NULL || sFunName[0] == 0) return false;
CScriptValueList va;
CHAR sFnArgs[1024];
sFnArgs[0]=0;
FuncParamProcess(sFunName,sFnArgs,sizeof(sFnArgs),va);
//调用脚本函数期待返回1个值
return ( m_Script.Call(sFnArgs, va, va, 1) );
}
bool CNpc::CallModule(LPCSTR sModuleName,LPCSTR sFunName)
{
if (sFunName == NULL || sFunName[0] == 0) return false;
CScriptValueList va;
CHAR sFnArgs[1024];
sFnArgs[0]=0;
FuncParamProcess(sFunName,sFnArgs,sizeof(sFnArgs),va);
//调用脚本函数期待返回1个值
return ( m_Script.CallModule(sModuleName,sFnArgs, va, va, 1) );
}
CNpc * CNpc::GetNpcPtr(char * sSceneName,char * sNpcName)
{
if(sSceneName ==NULL || sNpcName ==NULL) return NULL;
CNpc *pNpc =NULL;
//刷新系统NPC
if (!_stricmp(sSceneName, "SYS"))
{
//刷新全局功能NPC
if (!_stricmp(sNpcName, "FUNCTION"))
{
pNpc = GetGlobalLogicEngine()->GetGlobalNpc();
}
else if (!_stricmp(sNpcName, "MONSTER"))
{
pNpc = GetGlobalLogicEngine()->GetMonFuncNpc();
}
}
else
{
//场景是否存在
CFuBen * pFb =NULL;
CScene * pScene =NULL;
if(GetGlobalLogicEngine()->GetFuBenMgr()->GetFbPtrBySceneName(sSceneName,pFb,pScene) ==false || pScene ==NULL)
{
OutputMsg(rmError,_T("func [%s] scene=%s not exist"),__FUNCTION__,sSceneName);
return NULL;
}
pNpc = pScene->GetNpc(sNpcName);
}
if(pNpc ==NULL)
{
OutputMsg(rmError,_T("func [%s] scene=%s NPC=%s not exist"),__FUNCTION__,sSceneName,sNpcName);
}
return pNpc;
}
void CNpc::FuncParamProcess(LPCSTR sFnArgs, OUT CHAR * pFunc, INT_PTR nOutBuffSize,CScriptValueList & va, bool boAddFuncParam )
{
//_asncpytA(pFunc, sPtr);
char * sArgPos, *sPtr ;
strncpy(pFunc,sFnArgs,nOutBuffSize);
sArgPos= (char *)strchr(pFunc, ',');
sPtr = pFunc;
if (NULL == sArgPos)
{// 没参数
if (boAddFuncParam)
{
va << (LPCTSTR)pFunc;
}
return;
}
sPtr = sArgPos + 1;
*sArgPos = 0;//讲第一个','位置的字符改写为0使得sFnArgs再第一个','处终止sFnArgs即是函数名称
if (boAddFuncParam)
{
va << (LPCTSTR)pFunc;
}
//解出所有以','分隔的参数
while (va.count() < CScriptValueList::MaxValueCount)
{
sArgPos = strchr(sPtr, ',');
if (!sArgPos)
{
if (*sPtr)
va << sPtr;
break;
}
*sArgPos = 0;//
va << sPtr;
//将处理指针调整到次','的后面继续搜索
sPtr = sArgPos + 1;
}
}
// void CNpc::Talk(CActor *pActor, LPCSTR sFnName)
// {
// if (pActor == NULL) return;
// if (sFnName == NULL || sFnName[0] == 0)
// return;
// //OutputMsg(rmTip,_T("NPC Talk:%s"),sFnName);
// CScriptValueList va;
// CHAR sFnArgs[1024];
// sFnArgs[0]=0;
// //向参数表中写入玩家对象
// va << pActor;
// FuncParamProcess(sFnName,sFnArgs,sizeof(sFnArgs),va);
// //尝试将函数名称与参数进行分割,"function,arg1,arg2"
// /*
// _asncpytA(sFnArgs, sFnName);
// sPtr = sFnArgs;
// sArgPos = strchr(sPtr, ',');
// if (sArgPos)
// {
// sPtr = sArgPos + 1;
// *sArgPos = 0;//讲第一个','位置的字符改写为0使得sFnArgs再第一个','处终止sFnArgs即是函数名称
// //解出所有以','分隔的参数
// while (va.count() < CScriptValueList::MaxValueCount)
// {
// sArgPos = strchr(sPtr, ',');
// if (!sArgPos)
// {
// if (*sPtr)
// va << sPtr;
// break;
// }
// *sArgPos = 0;//再相爱
// va << sPtr;
// //将处理指针调整到次','的后面继续搜索
// sPtr = sArgPos + 1;
// }
// }
// */
// Talk(pActor,sFnArgs,va);
// }
void CNpc::OnNpcCaller(LPCSTR sFnName, int nNpcId)
{
CScriptValueList paramList, retParamList;
CHAR sFnArgs[1024];
sFnArgs[0]=0;
if (nNpcId < 0)
{
nNpcId = GetId();
}
paramList << nNpcId;
FuncParamProcess(sFnName, sFnArgs, sizeof(sFnArgs), paramList, true);
if(!GetScript().Call("OnNpcTimer", paramList, retParamList,0))
{
OutputMsg(rmError,_T("npc[%d,%d] func [%s] function =%s,call error"),GetId(), nNpcId,__FUNCTION__, sFnName);
}
}
// void CNpc::CallScript(CActor * pActor, LPCSTR sFnName)
// {
// CScriptValueList paramList, retList;
// CHAR sFnArgs[1024];
// sFnArgs[0]=0;
// paramList << pActor << GetId();
// FuncParamProcess(sFnName, sFnArgs, sizeof(sFnArgs), paramList, true);
// if(!GetGlobalLogicEngine()->GetScriptNpc()->GetScript().Call("OnNpcEvent", paramList, retList, 0))
// {
// const RefString &s = GetGlobalLogicEngine()->GetScriptNpc()->GetScript().getLastErrorDesc();
// if (paramList.count() >= 3)
// {
// CScriptValue& sValue = paramList[2];
// if (sValue.getType() == CScriptValue::vString)
// {
// pActor->SendOldTipmsgFormatWithId(tpOnNpcEventMsg,ttDialog,(LPCTSTR)s,(LPCTSTR)sValue);
// }
// }
// else
// {
// pActor->SendTipmsg((LPCTSTR)s,ttDialog);
// }
// }
// }
// void CNpc::Talk(CActor * pActor,LPCSTR sFnName,CScriptValueList& va)
// {
// if (pActor == NULL) return;
// INT_PTR nCampId = GetCampId(); //自身的阵营ID
// if(GetAttriFlag().DenySee)
// {
// CloseDialog(pActor);
// return ;
// }
// //不同的阵营的NPC别的阵营不能使用
// if(nCampId && nCampId != pActor->GetCampId())
// {
// /*
// CActorPacket ap;
// CDataPacket &pack = pActor->AllocPacket(ap);
// pack << (BYTE)enDefaultEntitySystemID;
// pack << (BYTE)sNpcTalk;
// pack << (BYTE)1;
// pack << m_hEntityHandler;
// LPCTSTR pStr= (LPCSTR)GetLogicServer()->GetDataProvider()->GetOldTipmsgConfig().GetTipmsg(tpCampNpcFail);
// if(pStr)
// {
// pack << pStr;
// }
// else
// {
// pack << "";
// }
// ap.flush();
// */
// return;
// }
// CScriptValueList vr;
// //调用脚本函数期待返回1个值
// if ( m_Script.Call(sFnName, va, vr, 1) )
// {
// //如果有返回值
// if ( vr.count() > 0 )
// {
// //取出第一个返回值并判断是否是字符串,如果是字符串则向客户端发送此数据
// CScriptValue &val = vr[0];
// if ( val.getType() == CScriptValue::vString )
// {
// //向客户端发送NPC对话内容的数据包
// CActorPacket ap;
// CDataPacket &pack = pActor->AllocPacket(ap);
// pack << (BYTE)enDefaultEntitySystemID;
// pack << (BYTE)sNpcTalk;
// pack << (BYTE)1;
// pack << m_hEntityHandler;
// pack << (LPCSTR)val;
// ap.flush();
// //OutputMsg(rmTip,_T("Npc Talk result:%s"),(LPCSTR)val);
// }
// }
// }
// else
// {
// //脚本错误,将以模态对话框的形式呈现给客户端
// const RefString &s = m_Script.getLastErrorDesc();
// pActor->SendTipmsg((LPCSTR)s,ttDialog);
// OutputMsg(rmError, _T("NPCTalk(%s), id=%d Failed..."), GetEntityName(),GetId());
// }
// }
VOID CNpc::CloseDialog(CActor *pActor)
{
CActorPacket ap;
CDataPacket &pack = pActor->AllocPacket(ap);
pack << (BYTE)enDefaultEntitySystemID;
pack << (BYTE)sNpcTalk;
pack << (BYTE)0;
pack << m_hEntityHandler;
ap.flush();
}
void CNpc::NPCTalk()
{
CScriptValueList paramList, retList;
paramList << this;
if (!m_Script.Call("NPCTalk", paramList, retList, 0))
{
OutputMsg(rmError, _T("NPCTalk(%s) Failed..."), GetEntityName());
}
}
void CNpc::ReloadAllNpc()
{
// LPCTSTR scriptFile[] = {CLogicEngine::szGlobalFuncScriptFile,CLogicEngine::szMonsterFuncScriptFile,CLogicEngine::szQuestNpcFile,CLogicEngine::szItemNpcFile};
// CNpc* pNpc[] = {GetGlobalLogicEngine()->GetGlobalNpc(),GetGlobalLogicEngine()->GetMonFuncNpc(),GetGlobalLogicEngine()->GetScriptNpc(),GetGlobalLogicEngine()->GetItemNpc()};
// for (int i=0; i <ArrayCount(scriptFile); i++)
// {
// pNpc[i]->GetScript().LoadScript(scriptFile[i]);
// }
}
void CNpc::CheckAllScript(char* sParam)
{
LPCTSTR scriptFile[] = {CLogicEngine::szGlobalFuncScriptFile,CLogicEngine::szMonsterFuncScriptFile,CLogicEngine::szQuestNpcFile,CLogicEngine::szItemNpcFile};
LPCTSTR scriptName[] = {"function","monster","script","item"};
char* pErrStr = NULL;
for (int i = 0; i <ArrayCount(scriptName); i++)
{
if (strncmp(sParam,scriptName[i],strlen(scriptName[i]))==0)
{
sParam+=strlen(scriptName[i]); *sParam=0; pErrStr = ++sParam;
m_Script.CheckScript(scriptFile[i], pErrStr);
return;
}
}
int nOkCount = 0;
for (int i=0; i <ArrayCount(scriptFile); i++)
{
if (m_Script.CheckScript(scriptFile[i],pErrStr))
{
nOkCount ++;
}
}
if(nOkCount==ArrayCount(scriptFile))
{
OutputMsg(rmTip,"脚本正常");
}
else
{
OutputMsg(rmTip,"注:本命令只作检查修复错误脚本文件之后需要用rsf命令重新加载或者重启");
}
}