From ed9910f36584f95110fd63d12e1808c3f5968eba Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Tue, 24 Mar 2026 12:28:56 +0700 Subject: [PATCH] add rule AI --- DlgTaskTrace_clean.cpp | 1428 +++ Documentation/ChatSystem/architecture.md | 52 + Documentation/ChatSystem/chat_system.md | 63 + Documentation/ChatSystem/code_patterns.md | 21 + Documentation/ChatSystem/data_types.md | 33 + Documentation/ChatSystem/porting_rules.md | 68 + Documentation/ChatSystem/protocol.md | 31 + EC_Player_clean.cpp | 11617 ++++++++++++++++++++ ProjectSettings/QualitySettings.asset | 38 +- build_log.txt | Bin 0 -> 356942 bytes build_log2.txt | Bin 0 -> 355900 bytes build_log3.txt | Bin 0 -> 355900 bytes build_log4.txt | Bin 0 -> 355900 bytes build_log5.txt | Bin 0 -> 355900 bytes build_log6.txt | Bin 0 -> 358350 bytes build_log7.txt | Bin 0 -> 355900 bytes clean_code.py | 10 + evtbus.txt | Bin 0 -> 12312 bytes extracted_func.txt | Bin 0 -> 1064 bytes extracted_func_utf8.txt | 13 + extracted_func_v2.txt | 212 + gemini.md | 32 + login_info.txt | 0 package-lock.json | 6 + read_code.py | 25 + 25 files changed, 13639 insertions(+), 10 deletions(-) create mode 100644 DlgTaskTrace_clean.cpp create mode 100644 Documentation/ChatSystem/architecture.md create mode 100644 Documentation/ChatSystem/chat_system.md create mode 100644 Documentation/ChatSystem/code_patterns.md create mode 100644 Documentation/ChatSystem/data_types.md create mode 100644 Documentation/ChatSystem/porting_rules.md create mode 100644 Documentation/ChatSystem/protocol.md create mode 100644 EC_Player_clean.cpp create mode 100644 build_log.txt create mode 100644 build_log2.txt create mode 100644 build_log3.txt create mode 100644 build_log4.txt create mode 100644 build_log5.txt create mode 100644 build_log6.txt create mode 100644 build_log7.txt create mode 100644 clean_code.py create mode 100644 evtbus.txt create mode 100644 extracted_func.txt create mode 100644 extracted_func_utf8.txt create mode 100644 extracted_func_v2.txt create mode 100644 gemini.md create mode 100644 login_info.txt create mode 100644 package-lock.json create mode 100644 read_code.py diff --git a/DlgTaskTrace_clean.cpp b/DlgTaskTrace_clean.cpp new file mode 100644 index 0000000000..72f6cef348 --- /dev/null +++ b/DlgTaskTrace_clean.cpp @@ -0,0 +1,1428 @@ +// Filename : DlgTaskTrace.cpp +// Creator : Feng Ning +// Date : 2011/11/15 + +#pragma warning(disable:4786) + +#include "DlgTaskTrace.h" +#include "DlgTask.h" +#include "DlgAward.h" +#include "EC_Global.h" +#include "EC_Game.h" +#include "EC_GameRun.h" +#include "EC_GameSession.h" +#include "EC_GameUIMan.h" +#include "EC_World.h" +#include "EC_TaskInterface.h" +#include "TaskTemplMan.h" +#include "elementdataman.h" +#include "EC_IvtrItem.h" +#include "EC_HostPlayer.h" +#include "DlgWorldMap.h" +#include "EC_Instance.h" +#include "EC_UIConfigs.h" +#include "EC_UIHelper.h" +#include "AUIDef.h" +#include "EC_FixedMsg.h" +#define new A_DEBUG_NEW + +AUI_BEGIN_COMMAND_MAP(CDlgTaskTrace, CDlgNameLink) +AUI_ON_COMMAND("Chk_Collapse", OnCommand_SwitchCollapse) +AUI_ON_COMMAND("Rdo_Quest*", OnCommand_SwitchShowType) +AUI_ON_COMMAND("Btn_UnTrace", OnCommand_UnTraceTask) +AUI_ON_COMMAND("Btn_Map", OnCommand_Map) +AUI_ON_COMMAND("Btn_ForChat", OnCommand_Chat) +AUI_ON_COMMAND("Btn_Finish", OnCommand_FinishTask) +AUI_END_COMMAND_MAP() + +AUI_BEGIN_EVENT_MAP(CDlgTaskTrace, CDlgNameLink) +AUI_ON_EVENT("Edit_Title", WM_LBUTTONDBLCLK, OnEventDBClk_Title) +AUI_ON_EVENT("Txt_Title", WM_LBUTTONDBLCLK, OnEventDBClk_Title) +AUI_ON_EVENT("Txt_Link_Trace", WM_RBUTTONDOWN, OnEventRButton_Txt_Link_Trace) +AUI_ON_EVENT("Txt_Link_Trace", WM_RBUTTONUP, OnEventRButton_Txt_Link_Trace) +AUI_ON_EVENT("Txt_Link_Trace", WM_LBUTTONDOWN, OnEventLButton_Txt_Link_Trace) +AUI_ON_EVENT("Txt_Link_Trace", WM_LBUTTONUP, OnEventLButton_Txt_Link_Trace) +AUI_ON_EVENT("Txt_Link_Trace", WM_MOUSEWHEEL, OnMouseWheel) +AUI_END_EVENT_MAP() + +static const ACString COLOR_YELLOW = _AL("^ffcb4a"); +static const ACString COLOR_RED = _AL("^ff0000"); +static const ACString COLOR_WHITE = _AL("^ffffff"); +static const ACString INDENTATION = _AL(" "); + +////////////////////////////////////////////////////////////////////////// +// TaskNameHoverCommand +////////////////////////////////////////////////////////////////////////// +// 用于确定鼠标悬浮到哪个任务上 +class TaskNameHoverCommand : public CDlgNameLink::LinkCommand +{ +public: + virtual bool operator()(P_AUITEXTAREA_NAME_LINK pLink){ return true; }; + TaskNameHoverCommand(ACString& buffer, ACString& strName, int task_id, bool bError, bool bCanContributionFinish = false); + TaskNameHoverCommand(const TaskNameHoverCommand& rhs); + + virtual ACString GetLinkText() const; + int GetTaskID(){ return m_iTaskID; } + bool CanConbutionFinish() { return m_bCanContributionFinish; }; + +protected: + virtual void AppendText(); + virtual CDlgNameLink::LinkCommand* Clone() const; + + ACString& m_Buffer; + ACString m_TaskName; + bool m_bError; + int m_iTaskID; + bool m_bCanContributionFinish; +}; + +TaskNameHoverCommand::TaskNameHoverCommand(ACString& buffer, ACString& strName, int task_id, bool bError, bool bCanContributionFinish): +m_Buffer(buffer), +m_TaskName(strName), +m_iTaskID(task_id), +m_bError(bError), +m_bCanContributionFinish(bCanContributionFinish) +{ + +} + +TaskNameHoverCommand::TaskNameHoverCommand(const TaskNameHoverCommand& rhs): +m_Buffer(rhs.m_Buffer), +m_TaskName(rhs.m_TaskName), +m_iTaskID(rhs.m_iTaskID), +m_bError(rhs.m_bError), +m_bCanContributionFinish(rhs.m_bCanContributionFinish) +{ + +} + +ACString TaskNameHoverCommand::GetLinkText() const +{ + ACString szNameLink; + if (m_bError) + szNameLink.Format(_AL("&^%s%s&"), COLOR_RED, m_TaskName); + else + szNameLink.Format(_AL("&^%s%s&"), COLOR_YELLOW, m_TaskName); + return szNameLink; +} + +void TaskNameHoverCommand::AppendText() +{ + m_Buffer += this->GetLinkText(); +} +CDlgNameLink::LinkCommand* TaskNameHoverCommand::Clone() const +{ + return new TaskNameHoverCommand(*this); +} + + +////////////////////////////////////////////////////////////////////////// +// TraceLinkCommandCommand +////////////////////////////////////////////////////////////////////////// +typedef abase::vector ObjectTargets; + +class TraceLinkCommand : public CDlgNameLink::MoveToLinkCommand +{ +public: + TraceLinkCommand(ACString& buffer, int id, const ACString &name, int taskid = 0); + TraceLinkCommand(ACString& buffer, const ObjectTargets& targets, const ACString &name); + TraceLinkCommand(const TraceLinkCommand& rhs); + +protected: + virtual void AppendText(); + virtual CDlgNameLink::LinkCommand* Clone() const; + + ACString& m_Buffer; +}; + +TraceLinkCommand::TraceLinkCommand(ACString& buffer, int id, const ACString &name, int taskid) +:CDlgNameLink::MoveToLinkCommand(id, name, taskid) +,m_Buffer(buffer) +{} + +TraceLinkCommand::TraceLinkCommand(ACString& buffer, const ObjectTargets& targets, const ACString &name) +:CDlgNameLink::MoveToLinkCommand(targets, name) +,m_Buffer(buffer) +{ + +} + +TraceLinkCommand::TraceLinkCommand(const TraceLinkCommand& rhs) +:CDlgNameLink::MoveToLinkCommand(rhs) +,m_Buffer(rhs.m_Buffer) +{} + +CDlgNameLink::LinkCommand* TraceLinkCommand::Clone() const +{ + return new TraceLinkCommand(*this); +} + +void TraceLinkCommand::AppendText() +{ + // do nothing here + m_Buffer += this->GetLinkText(); +} + +////////////////////////////////////////////////////////////////////////// +// CDlgTaskTrace +////////////////////////////////////////////////////////////////////////// +CDlgTaskTrace::CDlgTaskTrace() +{ + m_pTxt_Desc = NULL; + m_nLastTracedTasks = -1; + m_nLastShowType = ST_NONE; + m_nTraceWorldID = -1; + m_pBtnUnTrace = NULL; + m_pBtnFinishTaskByContribution = NULL; + m_pBtnContributionTaskHelp = NULL; + m_pBtnMap = NULL; + m_pBtnChat = NULL; + m_iContributionCurrentPage = 0; + m_iContributionTotalPage = 1; + m_iIndexForContribution = 0; + m_bShowTrace = true; +} + +CDlgTaskTrace::~CDlgTaskTrace() +{ +} + +bool CDlgTaskTrace::OnInitDialog() +{ + if(!CDlgNameLink::OnInitDialog()) + return false; + + m_pTxt_Desc = dynamic_cast(GetDlgItem("Txt_Link_Trace")); + if(m_pTxt_Desc) m_pTxt_Desc->SetForceRenderScroll(false); + m_pChk_Collapse = dynamic_cast(GetDlgItem("Chk_Collapse")); + if (m_szName == "Win_QuestMinion") { + DDX_Control("Btn_UnTrace", m_pBtnUnTrace); + DDX_Control("Btn_Map", m_pBtnMap); + DDX_Control("Btn_ForChat", m_pBtnChat); + DDX_Control("Btn_Finish", m_pBtnFinishTaskByContribution); + DDX_Control("Btn_Help", m_pBtnContributionTaskHelp); + m_pBtnUnTrace->Show(false); + m_pBtnMap->Show(false); + m_pBtnChat->Show(false); + m_pBtnFinishTaskByContribution->Show(false); + m_pBtnContributionTaskHelp->Show(false); + } + + // 任务追踪界面显示默认列表 + PAUIOBJECT pObjRadio = GetDlgItem("Rdo_Quest1"); + if (pObjRadio) SetShowType(ST_TRACED); + pObjRadio = GetDlgItem("Rdo_Quest2"); + if (pObjRadio) pObjRadio->Show(CECUIConfig::Instance().GetGameUI().bEnableTitle); + return true; +} + +void CDlgTaskTrace::OnShowDialog() +{ + m_pChk_Collapse->Check(m_pTxt_Desc->IsShow()); + PAUIOBJECT pObjRadio = GetDlgItem("Rdo_Quest3"); + if (pObjRadio) { + int levelLimit = CECUIConfig::Instance().GetGameUI().nContributionTaskLevelLimit; + CECHostPlayer* pHost = GetHostPlayer(); + if (pHost && pHost->GetMaxLevelSofar() >= levelLimit) + pObjRadio->SetFlash(true); + } + +} + +void CDlgTaskTrace::OnChangeLayoutEnd(bool bAllDone) { + // 如果处于被收起状态,则对话框的大小也要相应缩小 + if (m_szName == "Win_QuestMinion" && !m_bShowTrace) { + SIZE cur = this->GetSize(); + SIZE desc = m_pTxt_Desc->GetSize(); + cur.cy -= desc.cy; + this->SetSize(cur.cx, cur.cy); + m_pChk_Collapse->Check(false); + } +} + +void CDlgTaskTrace::AppendCommand(const ACHAR* pName, int idTarget, int idTask) +{ + ACString strName = pName; + + if(!strName.IsEmpty()) + { + ACString strUniqueName = MakeNameUnique(strName); + if (!strUniqueName.IsEmpty()){ + TraceLinkCommand cmd(m_Buffer, idTarget, strUniqueName, idTask); + BindLinkCommand(m_pTxt_Desc, &strUniqueName, &cmd); + } + } +} + +void CDlgTaskTrace::ResetNameUnique(){ + m_uniqueNameCountMap.clear(); +} + +ACString CDlgTaskTrace::MakeNameUnique(const ACString &strName){ + ACString result; + if (!strName.IsEmpty()){ + ACString strTrimmedName = strName; + strTrimmedName.TrimLeft(); + strTrimmedName.TrimRight(); + if (!strTrimmedName.IsEmpty()){ + UniqueNameCountMap::iterator it = m_uniqueNameCountMap.find(strTrimmedName); + if (it == m_uniqueNameCountMap.end()){ + m_uniqueNameCountMap[strTrimmedName] = 1; + }else{ + ++ it->second; + } + result = strTrimmedName; + if (int spaceCount = m_uniqueNameCountMap[strTrimmedName]-1){ + ACString strMakeUniqueSuffix(_AL(' '), spaceCount); + result += strMakeUniqueSuffix; + } + } + } + return result; +} + +void CDlgTaskTrace::AppendCommand(int worldid, const Task_Region* pRegions, int size) +{ + ObjectTargets instCoord, tempCoord; + int cur = g_pGame->GetGameRun()->GetWorld()->GetInstanceID(); + CECInstance *pInstance = g_pGame->GetGameRun()->GetInstance(cur); + ACString strCurMap = AS2AC(pInstance->GetPath()); + // find the entrance of instance + pInstance = g_pGame->GetGameRun()->GetInstance(worldid); + ACString mapName = pInstance ? AS2AC(pInstance->GetPath()) : _AL(""); + + if(cur != worldid) + { + g_pGame->GetObjectCoord(mapName, tempCoord); + ObjectTargets::iterator iter = std::find(tempCoord.begin(), tempCoord.end(), strCurMap); + if (iter != tempCoord.end()) instCoord.push_back(*iter); + } + else if(size > 0 && pRegions) + { + for(int i=0;i 0) + { + ACString strName; + + A3DVECTOR3 vPos = instCoord[0].vPos; + strName.Format(GetStringFromTable(9393), ((int)vPos.x+4000)/10, ((int)vPos.z+5500)/10); + + TraceLinkCommand cmd(m_Buffer, instCoord, strName); + BindLinkCommand(m_pTxt_Desc, &strName, &cmd); + } +} + +void CDlgTaskTrace::AppendText(const ACString & text) +{ + m_Buffer += text; +} + +void CDlgTaskTrace::PrepareRebuildTaskTrace(){ + m_Buffer.Empty(); + ClearCommands(); + ResetNameUnique(); +} + +void CDlgTaskTrace::RefreshTaskTrace(const int* tasks, size_t size, bool withName) +{ + bool bShow = size > 0; + + // show/hide the trace dialog + if(this->IsShow() != bShow) + { + this->Show(bShow); + } + + if (!IsShow() || !m_pTxt_Desc->IsShow()) return; + + PrepareRebuildTaskTrace(); + + // get the formatted desc + for(int i=(int)size-1; i>=0; i--) + { + FormatTaskTraceDesc(tasks[i], withName, _AL("")); + } + + int lastTop = m_pTxt_Desc->GetFirstLine(); + m_pTxt_Desc->SetText(m_Buffer); + int curTop = m_pTxt_Desc->GetFirstLine(); + + if(lastTop != curTop) + { + int top = (lastTop < m_pTxt_Desc->GetLines()) ? lastTop + : max(0, m_pTxt_Desc->GetLines() - m_pTxt_Desc->GetLinesPP()); + m_pTxt_Desc->SetFirstLine(top); + } +} + +void CDlgTaskTrace::OnEventMouseMove(int x, int y, DWORD keys, const P_AUITEXTAREA_NAME_LINK pLink) +{ + if (m_szName != "Win_QuestMinion") + return; + ShowType showType = GetShowType(); + if(!pLink) GetGameUIMan()->ContinueDealMessage(); + else if (showType == ST_TRACED || showType == ST_CONTRIBUTION) { + int pos_y = pLink->rc.top - m_pAUIManager->GetRect().top - GetPos().y; + TaskNameHoverCommand* pCmd = dynamic_cast(GetLinkCommand(m_pTxt_Desc, &pLink->strName)); + if (pCmd == NULL) return; + if (showType == ST_TRACED) { + SetBtnUnTraceY(pos_y, pCmd->GetTaskID()); + } else { + CECTaskInterface* pTask = GetHostPlayer()->GetTaskInterface(); + ActiveTaskList* pActiveLst = (ActiveTaskList*)pTask->GetActiveTaskList(); + int taskID = pCmd->GetTaskID(); + ATaskTempl* pTempl = GetGame()->GetTaskTemplateMan()->GetTaskTemplByID(taskID); + if (pTempl == NULL) return; + ActiveTaskEntry* pEntry = pActiveLst->GetEntry(taskID); + if (pCmd->CanConbutionFinish() && pEntry && !pEntry->IsFinished() && pTempl->m_pFirstChild == NULL ) { + SetBtnUnTraceY(pos_y, pCmd->GetTaskID()); + } + } + } +} + +void CDlgTaskTrace::OnEventLButtonClick(int x, int y, DWORD keys, const P_AUITEXTAREA_NAME_LINK pLink) +{ + if(!pLink) GetGameUIMan()->ContinueDealMessage(); +} + +bool CDlgTaskTrace::PtInRect(const AUITEXTAREA_NAME_LINK &name, int x, int y) +{ + if (m_szName == "Win_QuestMinion"){ + A3DRECT rcExpanded = name.rc; + int right = m_pAUIManager->GetRect().left + m_pTxt_Desc->GetRect().right; + if (right > rcExpanded.right){ + rcExpanded.right = right; // 扩充到控件最右侧,方便弹出相应“关闭追踪”等控件 + } + return rcExpanded.PtInRect(x, y); + } + return CDlgNameLink::PtInRect(name, x, y); +} + +void CDlgTaskTrace::FormatTaskTraceDesc(int idTask, bool withName, const ACString& strIndentIn, bool requireActive, int index, bool bCanContributionFinish, int contribution) +{ + int j; + + ATaskTemplMan *pMan = g_pGame->GetTaskTemplateMan(); + elementdataman *pDataMan = g_pGame->GetElementDataMan(); + CECTaskInterface *pTask = GetHostPlayer()->GetTaskInterface(); + CECGameUIMan *pUIMan = GetGameUIMan(); + + bool bActiveTask = pTask->HasTask(idTask); + if (requireActive && !bActiveTask) return; + + ATaskTempl *pTemp = NULL; + if (pTemp = pMan->GetTaskTemplByID(idTask)) + { + Task_State_info tsi; + pTask->GetTaskStateInfo(idTask, &tsi, bActiveTask); + + float vMin[3] = {-6000.0f,-6000.0f,-6000.0f}; + float vMax[3] = {6000.0f,6000.0f,6000.0f}; + if(pTemp->m_ulReachSiteCnt > 0 + && is_in_zone(pTemp->m_pReachSite[0].zvMin,pTemp->m_pReachSite[0].zvMax,vMin) && is_in_zone(pTemp->m_pReachSite[0].zvMin,pTemp->m_pReachSite[0].zvMax,vMax)) + return; + + // append the name + if (withName) + { + AppendText(strIndentIn); + ACString strNum; + strNum.Format(_AL("%d"), index); + ACString strName = pTemp->GetName(); + DeleteColorStr(strName); + strName = strNum + _AL(" ") + strName; + TaskNameHoverCommand cmd(m_Buffer, strName, idTask, tsi.m_ulErrCode != 0, bCanContributionFinish); + BindLinkCommand(m_pTxt_Desc, &strName, &cmd); + if (contribution != 0) { + int colorIndex = contribution > 0 ? 11288 : 11291; + strNum.Format(_AL(" %s【%+d】"), GetStringFromTable(colorIndex), contribution); + AppendText(strNum); + } + AppendText(_AL("\r")); + } + // init the indent + ACString strIndent = withName ? strIndentIn + INDENTATION :_AL(""); + + // 任务未接时显示接任务NPC + int nDNPC = pTemp->GetDeliverNPC(); + if (!bActiveTask && nDNPC > 0) + { + DATA_TYPE dt = DT_INVALID; + NPC_ESSENCE *pNPC = (NPC_ESSENCE *)pDataMan->get_data_ptr(nDNPC, ID_SPACE_ESSENCE, dt); + if (dt == DT_NPC_ESSENCE) + { + AppendText(strIndent); + AppendText(pUIMan->GetStringFromTable(9764)); + AppendCommand(pNPC->name, nDNPC, idTask); + AppendText(_AL("\r")); + } + } + + // check whether can finish the task + int nANPC = pTemp->GetAwardNPC(); + // 当任务奖励类型比较特殊时,不要在这里返回 + bool bSpecialAwardType = pTemp->m_ulAwardType_S != enumTATNormal; + if(nANPC > 0 && pTask->CanFinishTask(idTask) && !bSpecialAwardType) + { + DATA_TYPE DataType = DT_INVALID; + NPC_ESSENCE *pNPC = + (NPC_ESSENCE *)pDataMan->get_data_ptr(nANPC, ID_SPACE_ESSENCE, DataType); + if( DataType == DT_NPC_ESSENCE ) + { + this->AppendText(strIndent); + this->AppendText(pUIMan->GetStringFromTable(9391)); + this->AppendCommand(pNPC->name, nANPC, idTask); + this->AppendText(_AL("\r")); + return; + } + } + + // append global task character + for(int i = 0; i < (int)tsi.m_TaskCharArr.size(); i++) + { + ACString strPrefix = strIndent; + strPrefix += _AL("^00FF00"); + strPrefix += tsi.m_TaskCharArr[i]; + strPrefix += _AL("\r"); + + this->AppendText(strPrefix); + } + + for( j = 0; j < MAX_ITEM_WANTED; j++ ) + { + int itemid = tsi.m_ItemsWanted[j].m_ulItemId; + if( itemid <= 0 ) + break; + + int gained = tsi.m_ItemsWanted[j].m_ulItemsGained; + int toget = tsi.m_ItemsWanted[j].m_ulItemsToGet; + + CECIvtrItem* pItem = CECIvtrItem::CreateItem(itemid, 0, 1); + if (pItem) + { + ACString strPrefix = strIndent; + if( gained < toget ) + strPrefix += COLOR_WHITE; + else + strPrefix += _AL("^00e83a"); + this->AppendText(strPrefix); + + // track the monster id which contains this item + int monid = tsi.m_ItemsWanted[j].m_ulMonsterId; + + ACString strName; + if (pTemp->m_enumMethod != enumTMKillPlayer) + { + if(monid > 0) + { + this->AppendText(pUIMan->GetStringFromTable(9388)); + this->AppendCommand(pItem->GetName(), monid); + strName.Format(pUIMan->GetStringFromTable(9389), gained, toget); + } + else + { + const MINE_ESSENCE* pMine = CDlgTask::SearchTaskMine(idTask); + if(pMine) + { + this->AppendText(pUIMan->GetStringFromTable(9388)); + this->AppendCommand(pItem->GetName(), pMine->id); + strName.Format(pUIMan->GetStringFromTable(9389), gained, toget); + } + else + { + strName.Format(pUIMan->GetStringFromTable(248), pItem->GetName(), gained, toget); + } + } + } + else + { + this->AppendText(pUIMan->GetStringFromTable(9388)); + this->AppendText(pItem->GetName()); + strName.Format(pUIMan->GetStringFromTable(9389), gained, toget); + strName += dynamic_cast(m_pAUIManager->GetDialog("Win_Quest"))->GetKillPlayerRequirements(tsi,j); + } + + this->AppendText(strName); + + delete pItem; + } + } + + for (j = 0; j < MAX_MONSTER_WANTED; j++) + { + int monid = tsi.m_MonsterWanted[j].m_ulMonsterId; + if (monid == 0) + break; + + int killed = tsi.m_MonsterWanted[j].m_ulMonstersKilled; + int tokill = tsi.m_MonsterWanted[j].m_ulMonstersToKill; + if(killed > 0 || tokill > 0 ) + { + DATA_TYPE DataType = DT_INVALID; + MONSTER_ESSENCE *pMonster = (tokill <= 0) ? NULL: + (MONSTER_ESSENCE *)pDataMan->get_data_ptr(monid, ID_SPACE_ESSENCE, DataType); + + ACString strPrefix = strIndent; + if(killed < tokill ) + strPrefix += COLOR_WHITE; + else + strPrefix += _AL("^00e83a"); + this->AppendText(strPrefix); + + ACString strName; + if( DataType == DT_MONSTER_ESSENCE ) + { + // trace the monster + this->AppendText(pUIMan->GetStringFromTable(9386)); + this->AppendCommand(pMonster->name, pMonster->id); + strName.Format(pUIMan->GetStringFromTable(9387), killed, tokill); + } + else + { + strName.Format(pUIMan->GetStringFromTable(256),killed); + } + + this->AppendText(strName); + } + } + for(int i = 0; i < MAX_PLAYER_WANTED; i++ ) + { + if (tsi.m_PlayerWanted[i].m_ulPlayersToKill == 0) + break; + if (tsi.m_ItemsWanted[i].m_ulItemId > 0) + { + continue; + } + + int killed = tsi.m_PlayerWanted[j].m_ulPlayersKilled; + int tokill = tsi.m_PlayerWanted[j].m_ulPlayersToKill; + ACString strPrefix = strIndent; + if(killed < tokill ) + strPrefix += COLOR_WHITE; + else + strPrefix += _AL("^00e83a"); + + AppendText(strPrefix); + + AppendText(GetStringFromTable(7630)); + + this->AppendText(dynamic_cast(m_pAUIManager->GetDialog("Win_Quest"))->GetKillPlayerRequirements(tsi,i)); + } + if( tsi.m_ulTimeLimit > 0 ) + { + int nSec = bActiveTask ? max(0, tsi.m_ulTimeLimit - tsi.m_ulTimePassed) : tsi.m_ulTimeLimit; + + ACString strPrefix = strIndent; + strPrefix += CDlgTask::FormatTime(nSec, pUIMan->GetStringFromTable(bActiveTask ? 246 : 245), tsi.m_ulTimeLimit); + + this->AppendText(strPrefix); + } + + if( tsi.m_ulWaitTime > 0 ) + { + int nSec = max(0, int(tsi.m_ulWaitTime) - int(tsi.m_ulTimePassed)); + + ACString strPrefix = strIndent + COLOR_WHITE; + strPrefix += CDlgTask::FormatTime(nSec, pUIMan->GetStringFromTable(199), 0); + + this->AppendText(strPrefix); + } + + if( tsi.m_ulNPCToProtect > 0 ) + { + this->AppendText(strIndent); + + DATA_TYPE DataType = DT_INVALID; + MONSTER_ESSENCE *pMonster = (MONSTER_ESSENCE *)pDataMan->get_data_ptr( + tsi.m_ulNPCToProtect, ID_SPACE_ESSENCE, DataType); + if( DataType == DT_MONSTER_ESSENCE ) + { + // trace the protected npc + this->AppendText(pUIMan->GetStringFromTable(9390)); + this->AppendCommand(pMonster->name, pMonster->id); + this->AppendText(_AL("\r")); + } + + // update protected time + int nSec = max(0, tsi.m_ulProtectTime - tsi.m_ulTimePassed); + ACString strTime = CDlgTask::FormatTime(nSec, pUIMan->GetStringFromTable(259), tsi.m_ulProtectTime); + this->AppendText(strTime); + } + + if(pTemp->m_ulReachSiteCnt > 0) + { + this->AppendText(strIndent); + this->AppendText(pUIMan->GetStringFromTable(9392)); + this->AppendCommand(pTemp->m_ulReachSiteId, pTemp->m_pReachSite, pTemp->m_ulReachSiteCnt); + this->AppendText(_AL("\r")); + } + CECHostPlayer* pHost = GetHostPlayer(); + ACString strTemp; + if (pTemp->m_ulReachLevel) + { + this->AppendText(strIndent); + strTemp.Format(pUIMan->GetStringFromTable(9396), pHost->GetBasicProps().iLevel, pTemp->m_ulReachLevel); + this->AppendText(strTemp); + this->AppendText(_AL("\r")); + } + if (pTemp->m_ulReachRealmLevel) + { + this->AppendText(strIndent); + strTemp.Format(pUIMan->GetStringFromTable(9397), + pUIMan->GetRealmName(pHost->GetRealmLevel()), pUIMan->GetRealmName(pTemp->m_ulReachRealmLevel)); + this->AppendText(strTemp); + this->AppendText(_AL("\r")); + } + if (pTemp->m_ulReachReincarnationCount) + { + this->AppendText(strIndent); + strTemp.Format(pUIMan->GetStringFromTable(9398), pHost->GetReincarnationCount(), pTemp->m_ulReachReincarnationCount); + this->AppendText(strTemp); + this->AppendText(_AL("\r")); + } + } +} + +bool CDlgTaskTrace::IsCollapsed() +{ + return !m_pChk_Collapse->IsChecked(); +} + +void CDlgTaskTrace::SwitchCollapse() +{ + // adjust the dialog size + SIZE cur = this->GetSize(); + SIZE desc = m_pTxt_Desc->GetSize(); + cur.cy += m_pTxt_Desc->IsShow() ? -desc.cy : desc.cy; + this->SetSize(cur.cx, cur.cy); + m_pTxt_Desc->Show(!m_pTxt_Desc->IsShow()); + m_bShowTrace = !m_bShowTrace; + if (GetShowType() == ST_TRACED || GetShowType() == ST_TITLE) { + m_pBtnContributionTaskHelp->Show(false); + } else { + m_pBtnContributionTaskHelp->Show(!m_pBtnContributionTaskHelp->IsShow()); + } + if (GetShowType() == ST_TRACED && m_pBtnUnTrace->GetData()) { + m_pBtnUnTrace->Show(!m_pBtnUnTrace->IsShow()); + m_pBtnMap->Show(!m_pBtnMap->IsShow()); + m_pBtnChat->Show(!m_pBtnChat->IsShow()); + } else if (GetShowType() == ST_CONTRIBUTION && m_pBtnFinishTaskByContribution->GetData()) { + m_pBtnFinishTaskByContribution->Show(!m_pBtnFinishTaskByContribution->IsShow()); + } + + PAUILABEL pTxt_Title = dynamic_cast(GetDlgItem("Txt_Title")); + if(pTxt_Title) + { + pTxt_Title->SetText(GetStringFromTable(m_pTxt_Desc->IsShow() ? 9394:9395)); + } + m_pChk_Collapse->Check(m_pTxt_Desc->IsShow()); +} + +void CDlgTaskTrace::OnEventDBClk_Title(WPARAM wParam, LPARAM lParam, AUIObject *pObj) +{ + SwitchCollapse(); +} + +void CDlgTaskTrace::OnCommand_SwitchCollapse(const char * szCommand) +{ + SwitchCollapse(); +} + +void CDlgTaskTrace::OnCommand_SwitchShowType(const char * szCommand) +{ + SetBtnUnTraceY(-1, 0); + if (IsCollapsed()){ + SwitchCollapse(); + } + if (GetShowType() == ST_CONTRIBUTION) { + int levelLimit = CECUIConfig::Instance().GetGameUI().nContributionTaskLevelLimit; + if (GetHostPlayer()->GetMaxLevelSofar() < levelLimit + && szCommand && strlen(szCommand) != 0) { + SetShowType(ST_TRACED); + ACString strText; + strText.Format(GetStringFromTable(11293), levelLimit); + GetGameUIMan()->MessageBox("", strText, MB_OK, A3DCOLORRGBA(255, 255, 255, 160)); + } + PAUIOBJECT pObjRadio = GetDlgItem("Rdo_Quest3"); + if (pObjRadio) pObjRadio->SetFlash(false); + } + m_pBtnContributionTaskHelp->Show(GetShowType() == ST_CONTRIBUTION); + GetGameUIMan()->m_pDlgTask->RefreshTaskTrace(); +} + +void CDlgTaskTrace::OnEventRButton_Txt_Link_Trace(WPARAM wParam, LPARAM lParam, AUIObject *pObj) +{ + GetGameUIMan()->ContinueDealMessage(); + this->ChangeFocus(m_pBtnUnTrace); +} + +void CDlgTaskTrace::OnEventLButton_Txt_Link_Trace(WPARAM wParam, LPARAM lParam, AUIObject *pObj) +{ + CDlgNameLink::OnEventLButtonClick_NameLink(wParam, lParam, pObj); + if (m_pTxt_Desc->GetState() != AUITEXTAREA_STATE_PRESSBAR) + this->ChangeFocus(m_pBtnUnTrace); + else if (m_pBtnUnTrace->IsShow()) + SetBtnUnTraceY(-1, 0); +} + +CDlgTaskTrace::ShowType CDlgTaskTrace::GetShowType() +{ + ShowType ret = ST_TRACED; + int nCheck = GetCheckedRadioButton(0); + switch (nCheck) + { + case 0: ret = ST_TRACED; break; + case 1: ret = ST_TITLE; break; + case 2: ret = ST_CONTRIBUTION; break; + default: ASSERT(false); + } + return ret; +} + +void CDlgTaskTrace::SetShowType(ShowType rhs) +{ + int nCheck(0); + switch (rhs) + { + case ST_TRACED: nCheck = 0; break; + case ST_TITLE: nCheck = 1; break; + case ST_CONTRIBUTION: nCheck = 2; break; + default: ASSERT(false); + } + CheckRadioButton(0, nCheck); + m_nLastShowType = rhs; +} + +void CDlgTaskTrace::RefreshTaskTrace(const int* traced, int nTraced, const int* titletasks, int num, bool withName) +{ + ShowType showType = GetShowType(); + int size(0); + const int* tasks = NULL; + switch (showType) + { + case ST_TRACED: size = nTraced; tasks = traced; break; + case ST_TITLE: size = num; tasks = titletasks; break; + } + + bool bShow = !GetGameUIMan()->IsCustomizeCharacter(); + + if(IsShow() != bShow) Show(bShow); + + int idWorld = GetGameRun()->GetWorld()->GetInstanceID(); + if (IsShow() && m_pTxt_Desc->IsShow()) + { + if (m_nTraceWorldID != idWorld || // 地图有变,追踪信息需更新 + showType == ST_TRACED || // 已接任务信息需实时更新 + showType == ST_TITLE) // 已接称号任务信息需实时更新 + { + PrepareRebuildTaskTrace(); + for (int i = size-1; i >= 0; i--) + FormatTaskTraceDesc(tasks[i], withName, _AL(""), false, size - i); + KeepScrollBarPosition(); + m_nTraceWorldID = idWorld; + } + } + + m_nLastTracedTasks = nTraced; +} + +void CDlgTaskTrace::DeleteColorStr(ACString& strName) +{ + if (strName.Left(1) == _AL("^")) { + strName.CutLeft(7); + } +} + +void CDlgTaskTrace::SetBtnUnTraceY(int pos_y, int task_id) +{ + m_pBtnUnTrace->Show(pos_y != -1 && GetShowType() == ST_TRACED); + m_pBtnUnTrace->SetPos(m_pBtnUnTrace->GetPos(true).x, pos_y); + m_pBtnUnTrace->SetData(task_id); + m_pBtnMap->Show(pos_y != -1 && GetShowType() == ST_TRACED); + m_pBtnMap->SetPos(m_pBtnMap->GetPos(true).x, pos_y); + m_pBtnMap->SetData(task_id); + m_pBtnChat->Show(pos_y != -1 && GetShowType() == ST_TRACED); + m_pBtnChat->SetPos(m_pBtnChat->GetPos(true).x, pos_y); + m_pBtnChat->SetData(task_id); + m_pBtnFinishTaskByContribution->Show(pos_y != -1 && GetShowType() == ST_CONTRIBUTION); + m_pBtnFinishTaskByContribution->SetPos(m_pBtnFinishTaskByContribution->GetPos(true).x, pos_y); + m_pBtnFinishTaskByContribution->SetData(task_id); + if (m_pBtnFinishTaskByContribution->IsShow()) StartButtonGfx("Btn_Finish", "界面\\打钩"); +} + +void CDlgTaskTrace::OnCommand_UnTraceTask(const char * szCommand) +{ + PAUIOBJECT pObj = GetDlgItem(szCommand); + if (pObj) { + int idTask = pObj->GetData(); + CECGameUIMan* pGameUI = GetGameUIMan(); + if (pGameUI) { + pGameUI->m_pDlgTask->SwitchTaskTrace(idTask); + } + } +} + +void CDlgTaskTrace::OnCommand_Map(const char * szCommand) +{ + PAUIOBJECT pObj = GetDlgItem(szCommand); + if (pObj) { + int idTask = pObj->GetData(); + if (CECUIHelper::ShowOneTaskInMap(idTask)) + GetGameUIMan()->GetMapDlgsMgr()->SwitchWorldMapShow(); + } +} + +void CDlgTaskTrace::OnCommand_Chat(const char * szCommand) +{ + PAUIOBJECT pObj = GetDlgItem(szCommand); + if (pObj) { + int idTask = pObj->GetData(); + ATaskTemplMan *pMan = g_pGame->GetTaskTemplateMan(); + elementdataman *pDataMan = g_pGame->GetElementDataMan(); + CECHostPlayer* pHost = GetHostPlayer(); + CECTaskInterface *pTask = pHost->GetTaskInterface(); + CECGameUIMan *pUIMan = GetGameUIMan(); + + bool bActiveTask = pTask->HasTask(idTask); + ACString strText; + + ATaskTempl *pTemp = NULL; + while (pTemp = pMan->GetTaskTemplByID(idTask)) + { + Task_State_info tsi; + pTask->GetTaskStateInfo(idTask, &tsi, bActiveTask); + + float vMin[3] = {-6000.0f,-6000.0f,-6000.0f}; + float vMax[3] = {6000.0f,6000.0f,6000.0f}; + + ACString strName = pTemp->GetName(); + DeleteColorStr(strName); + strText += strName; + + ACString strIndent = INDENTATION; + + int nANPC = pTemp->GetAwardNPC(); + + if(nANPC > 0 && pTask->CanFinishTask(idTask)) { + DATA_TYPE DataType = DT_INVALID; + NPC_ESSENCE *pNPC = + (NPC_ESSENCE *)pDataMan->get_data_ptr(nANPC, ID_SPACE_ESSENCE, DataType); + if( DataType == DT_NPC_ESSENCE ) { + strText += strIndent; + strText += pUIMan->GetStringFromTable(9378); + strText += pNPC->name; + strText += _AL("\r"); + break; + } + } + + // append global task character + for(int i = 0; i < (int)tsi.m_TaskCharArr.size(); i++) { + ACString strPrefix = strIndent; + strPrefix += tsi.m_TaskCharArr[i]; + strPrefix += _AL("\r"); + + strText += strPrefix; + } + + int j = 0; + for( j = 0; j < MAX_ITEM_WANTED; j++ ) { + int itemid = tsi.m_ItemsWanted[j].m_ulItemId; + if( itemid <= 0 ) + break; + + int gained = tsi.m_ItemsWanted[j].m_ulItemsGained; + int toget = tsi.m_ItemsWanted[j].m_ulItemsToGet; + + CECIvtrItem* pItem = CECIvtrItem::CreateItem(itemid, 0, 1); + if (pItem) + { + strText += strIndent; + + ACString strName; + strName.Format(pUIMan->GetStringFromTable(248), pItem->GetName(), gained, toget); + if (pTemp->m_enumMethod == enumTMKillPlayer) + strName += dynamic_cast(m_pAUIManager->GetDialog("Win_Quest"))->GetKillPlayerRequirements(tsi,j); + strText += strName; + delete pItem; + } + } + for (j = 0; j < MAX_MONSTER_WANTED; j++) { + int monid = tsi.m_MonsterWanted[j].m_ulMonsterId; + if (monid == 0) + break; + + int killed = tsi.m_MonsterWanted[j].m_ulMonstersKilled; + int tokill = tsi.m_MonsterWanted[j].m_ulMonstersToKill; + if(killed > 0 || tokill > 0 ) { + DATA_TYPE DataType = DT_INVALID; + MONSTER_ESSENCE *pMonster = (tokill <= 0) ? NULL: + (MONSTER_ESSENCE *)pDataMan->get_data_ptr(monid, ID_SPACE_ESSENCE, DataType); + + strText += strIndent; + + ACString strName; + if( DataType == DT_MONSTER_ESSENCE ) { + strText += pUIMan->GetStringFromTable(9386); + strText += pMonster->name; + strName.Format(pUIMan->GetStringFromTable(9387), killed, tokill); + } else strName.Format(pUIMan->GetStringFromTable(256),killed); + strText += strName; + } + } + for( int i = 0; i < MAX_PLAYER_WANTED; i++ ) { + if (tsi.m_PlayerWanted[i].m_ulPlayersToKill == 0) + break; + if (tsi.m_ItemsWanted[i].m_ulItemId > 0) + continue; + + int killed = tsi.m_PlayerWanted[j].m_ulPlayersKilled; + int tokill = tsi.m_PlayerWanted[j].m_ulPlayersToKill; + + strText +=strIndent; + strText += GetStringFromTable(7630); + strText += dynamic_cast(m_pAUIManager->GetDialog("Win_Quest"))->GetKillPlayerRequirements(tsi,i); + } + if( tsi.m_ulTimeLimit > 0 ) { + int nSec = bActiveTask ? max(0, tsi.m_ulTimeLimit - tsi.m_ulTimePassed) : tsi.m_ulTimeLimit; + + ACString strPrefix = strIndent; + strPrefix += CDlgTask::FormatTime(nSec, pUIMan->GetStringFromTable(bActiveTask ? 246 : 245), tsi.m_ulTimeLimit); + + strText += strPrefix; + } + + if( tsi.m_ulWaitTime > 0 ) { + int nSec = max(0, int(tsi.m_ulWaitTime) - int(tsi.m_ulTimePassed)); + + ACString strPrefix = strIndent; + strPrefix += CDlgTask::FormatTime(nSec, pUIMan->GetStringFromTable(199), 0); + + strText += strPrefix; + } + + if( tsi.m_ulNPCToProtect > 0 ) { + strText += strIndent; + + DATA_TYPE DataType = DT_INVALID; + MONSTER_ESSENCE *pMonster = (MONSTER_ESSENCE *)pDataMan->get_data_ptr( + tsi.m_ulNPCToProtect, ID_SPACE_ESSENCE, DataType); + if( DataType == DT_MONSTER_ESSENCE ) { + strText += pUIMan->GetStringFromTable(9390); + strText += pMonster->name; + strText += _AL("\r"); + } + int nSec = max(0, tsi.m_ulProtectTime - tsi.m_ulTimePassed); + ACString strTime = CDlgTask::FormatTime(nSec, pUIMan->GetStringFromTable(259), tsi.m_ulProtectTime); + strText += strTime; + } + + if(pTemp->m_ulReachSiteCnt > 0) { + strText += strIndent; + strText += pUIMan->GetStringFromTable(9379); + int cur_instance = g_pGame->GetGameRun()->GetWorld()->GetInstanceID(); + A3DVECTOR3 pos; + if (cur_instance != (int)pTemp->m_ulReachSiteId) { + ObjectTargets tempCoord; + CECInstance *pInstance = g_pGame->GetGameRun()->GetInstance(cur_instance); + ACString strCurMap = AS2AC(pInstance->GetPath()); + pInstance = g_pGame->GetGameRun()->GetInstance(pTemp->m_ulReachSiteId); + ACString mapName = pInstance ? AS2AC(pInstance->GetPath()) : _AL(""); + g_pGame->GetObjectCoord(mapName, tempCoord); + ObjectTargets::iterator iter = std::find(tempCoord.begin(), tempCoord.end(), strCurMap); + if (iter != tempCoord.end()) pos = iter->vPos; + } else { + const Task_Region& region = pTemp->m_pReachSite[0]; + pos.x = (region.zvMax.x - region.zvMin.x) / 2 + region.zvMin.x; + pos.y = (region.zvMax.y - region.zvMin.y) / 2 + region.zvMin.y; + pos.z = (region.zvMax.z - region.zvMin.z) / 2 + region.zvMin.z; + } + ACString strName; + strName.Format(GetStringFromTable(9393), ((int)pos.x+4000) / 10, ((int)pos.z + 5500) / 10); + strText += strName; + strText += _AL("\r"); + } + ACString strTemp; + if (pTemp->m_ulReachLevel) { + strText += strIndent; + strTemp.Format(pUIMan->GetStringFromTable(9396), pHost->GetBasicProps().iLevel, pTemp->m_ulReachLevel); + strText += strTemp; + strText += _AL("\r"); + } + if (pTemp->m_ulReachRealmLevel) { + strText += strIndent; + strTemp.Format(pUIMan->GetStringFromTable(9397), pUIMan->GetRealmName(pHost->GetRealmLevel()), pUIMan->GetRealmName(pTemp->m_ulReachRealmLevel)); + strText += strTemp; + strText += _AL("\r"); + } + if (pTemp->m_ulReachReincarnationCount) { + strText += strIndent; + strTemp.Format(pUIMan->GetStringFromTable(9398), pHost->GetReincarnationCount(), pTemp->m_ulReachReincarnationCount); + strText += strTemp; + strText += _AL("\r"); + } + break; + } + strText.TrimRight('\r'); + CECGameSession *pSession = GetGameSession(); + int channel = pHost->GetTeam() ? GP_CHAT_TEAM : GP_CHAT_LOCAL; + pSession->SendChatData(channel, strText, -1, -1); + pUIMan->AddChatMessage(strText, channel); + } + +} + +void CDlgTaskTrace::OnTaskCompleted(int id) +{ + if (id == (int)m_pBtnUnTrace->GetData()) + SetBtnUnTraceY(-1, 0); +} + +void CDlgTaskTrace::OnMouseWheel(WPARAM wParam, LPARAM lParam, AUIObject *pObj) +{ + SetBtnUnTraceY(-1, 0); +} + +void CDlgTaskTrace::UpdateContributionTask() +{ + if (GetShowType() != ST_CONTRIBUTION) return; + CECHostPlayer* pHost = GetHostPlayer(); + CECTaskInterface* pTask = pHost->GetTaskInterface(); + StorageTaskList* pStorageList = (StorageTaskList*)pTask->GetStorageTaskList(); + ActiveTaskList* pActiveLst = (ActiveTaskList*)pTask->GetActiveTaskList(); + ATaskTemplMan* pTaskMan = GetGame()->GetTaskTemplateMan(); + elementdataman* pElementDataMan = GetGame()->GetElementDataMan(); + + PrepareRebuildTaskTrace(); + // 显示贡献度和已消费贡献度 + ACString strText; + strText.Format(_AL("%s%s%s%d %s%s%s%d/%d"), GetStringFromTable(11289), GetStringFromTable(11280), + GetStringFromTable(11292), pHost->GetWorldContribution(), GetStringFromTable(11289), GetStringFromTable(11281), + GetStringFromTable(11292), pHost->GetWorldContributionSpend(), + pHost->HaveHealthStones() ? 9999 : TASK_WORLD_CONTRIBUTION_SPEND_PER_DAY); + AppendText(strText); + AppendText(_AL("\r")); + // + m_iIndexForContribution = 0; + if (pStorageList && pTaskMan && pElementDataMan && pActiveLst) { + int weightTaskStorageCount = 0; + for (int i = 1; i <= TASK_STORAGE_COUNT; ++i) { + const TASK_DICE_BY_WEIGHT_CONFIG* pEssence = pTaskMan->GetWeightTasksEssence(i); + if (pEssence != NULL) { + if (m_iContributionCurrentPage == weightTaskStorageCount) { + std::set TaskSetIDs, TaskIDs; + // 奖励贡献度的任务 + for (int j = 0; j < pStorageList->m_StoragesTaskSetCount[i - 1]; ++j) { + int id = pStorageList->m_Storages[i - 1][j]; + if (id == 0) break; + AddTaskSetString(id, true, i); + TaskSetIDs.insert(id); + } + for (int j = pStorageList->m_StoragesTaskSetCount[i - 1]; j < TASK_STORAGE_LEN; ++j) { + int id = pStorageList->m_Storages[i - 1][j]; + if (id == 0) break; + AddTaskString(id, true, INDENTATION, true); + TaskIDs.insert(id); + } + + AppendText(GetStringFromTable(11290)); + // 不奖励贡献度但已领取未完成的任务 + ActiveTaskEntry* aEntries = pActiveLst->m_TaskEntries; + size_t count = sizeof(pEssence->uniform_weight_list) / sizeof(pEssence->uniform_weight_list[0]); + for (int j = 0; j < pActiveLst->m_uTaskCount; ++j) { + ActiveTaskEntry& CurEntry = aEntries[j]; + if (CurEntry.IsFinished()) + continue; + int id = CurEntry.m_ID; + for (size_t k = 0; k < count; ++k) { + int taskSetID = pEssence->uniform_weight_list[k].task_list_config_id; + if (taskSetID == 0) break; + if (TaskSetIDs.find(taskSetID) == TaskSetIDs.end() && TaskListHasID(taskSetID, id)) { + AddTaskSetString(taskSetID, false, i); + TaskSetIDs.insert(taskSetID); + } + } + if (TaskIDs.find(id) == TaskIDs.end() && TaskListHasID(pEssence->other_task_list_config_id, id)) { + AddTaskString(id, false, INDENTATION, true); + TaskIDs.insert(id); + } + } + + // 其余不奖励贡献度的任务 + for (int j = 0; j < (int)count; ++j) { + int id = pEssence->uniform_weight_list[j].task_list_config_id; + if (TaskSetIDs.find(id) == TaskSetIDs.end()) + AddTaskSetString(id, false, i); + } + DATA_TYPE dt; + const TASK_LIST_CONFIG* pConfig = (TASK_LIST_CONFIG*)pElementDataMan->get_data_ptr(pEssence->other_task_list_config_id, ID_SPACE_CONFIG, dt); + if (dt == DT_TASK_LIST_CONFIG && pConfig) { + count = sizeof(pConfig->id_tasks) / sizeof(pConfig->id_tasks[0]); + for (int j = 0; j < (int)count; ++j) { + int id = pConfig->id_tasks[j]; + if (id == 0) break; + if (TaskIDs.find(id) == TaskIDs.end()) + AddTaskString(id, false, INDENTATION, true); + } + } + } + weightTaskStorageCount++; + } + } + m_iContributionTotalPage = weightTaskStorageCount; + } + KeepScrollBarPosition(); +} + +bool CDlgTaskTrace::TaskListHasID(int listID, int taskID) +{ + elementdataman* pElementDataMan = GetGame()->GetElementDataMan(); + DATA_TYPE dt; + const TASK_LIST_CONFIG* pConfig = (const TASK_LIST_CONFIG*)pElementDataMan->get_data_ptr(listID, ID_SPACE_CONFIG, dt); + if (pConfig && dt == DT_TASK_LIST_CONFIG) { + size_t count = sizeof(pConfig->id_tasks) / sizeof(pConfig->id_tasks[0]); + for (size_t i = 0; i < count; ++i) { + int id = pConfig->id_tasks[i]; + if (id == 0) break; + if (id == taskID) + return true; + } + } + return false; +} + +void CDlgTaskTrace::AddTaskSetString(unsigned int id, bool hasContributionAward, int idStorage) +{ + elementdataman* pElementDataMan = GetGame()->GetElementDataMan(); + CECTaskInterface* pTask = GetHostPlayer()->GetTaskInterface(); + ActiveTaskList* pActiveLst = (ActiveTaskList*)pTask->GetActiveTaskList(); + DATA_TYPE dt; + const TASK_LIST_CONFIG* pConfig = (const TASK_LIST_CONFIG*)pElementDataMan->get_data_ptr(id, ID_SPACE_CONFIG, dt); + const TASK_DICE_BY_WEIGHT_CONFIG* pStorage = GetGame()->GetTaskTemplateMan()->GetWeightTasksEssence(idStorage); + if (pConfig && dt == DT_TASK_LIST_CONFIG && pStorage) { + size_t count = sizeof(pStorage->uniform_weight_list) / sizeof(pStorage->uniform_weight_list[0]); + int weight = 0; + for (size_t i = 0; i < count; ++i) { + if(pStorage->uniform_weight_list[i].task_list_config_id == 0) { + ASSERT(0); + return; + } + if (pStorage->uniform_weight_list[i].task_list_config_id == id) { + weight = pStorage->uniform_weight_list[i].weight; + break; + } + } + ACString strColor; + if (hasContributionAward) { + strColor = GetStringFromTable(11286); + AppendText(strColor); + AppendText(GetStringFromTable(11284)); + } else { + weight = -weight; + strColor = GetStringFromTable(11287); + AppendText(strColor); + AppendText(GetStringFromTable(11285)); + } + + ACString strName = pConfig->name; + AppendColorText(strName, strColor); + + ActiveTaskEntry* aEntries = pActiveLst->m_TaskEntries; + for (int j = 0; j < pActiveLst->m_uTaskCount; ++j) { + ActiveTaskEntry& CurEntry = aEntries[j]; + + int taskID = CurEntry.m_ID; + if (TaskListHasID(id, taskID)) + AddTaskString(taskID, hasContributionAward, INDENTATION); + } + } +} + +void CDlgTaskTrace::AddTaskString(unsigned int id, bool hasContributionAward, const ACString& strIndentIn, bool topLevel) +{ + CECTaskInterface* pTask = GetHostPlayer()->GetTaskInterface(); + ATaskTemplMan* pTaskMan = GetGame()->GetTaskTemplateMan(); + ActiveTaskList* pActiveLst = (ActiveTaskList*)pTask->GetActiveTaskList(); + ATaskTempl* pTempl = pTaskMan->GetTaskTemplByID(id); + ActiveTaskEntry* pEntry = pActiveLst->GetEntry(id); + if (pTempl) { + int contribution = 0; + // 奖励贡献度的任务只在顶层任务显示奖励值 + if (hasContributionAward) { + contribution = pTempl->m_pParent == NULL ? pTempl->m_Award_S->m_iWorldContribution : 0; + } else { // 否则只在最底层任务显示消耗值 + contribution = pTempl->m_pFirstChild == NULL ? -pTempl->m_iWorldContribution : 0; + } + + bool bCanContributionFinish = !hasContributionAward; +// ACString strTask = pTempl->GetName(); + ACString strTask = CDlgTask::GetTaskNameWithColor(pTempl); + // 顶层显示一个任务名字 + if (topLevel) + { + int indexMark = hasContributionAward ? 11284 : 11285; + int indexColor = bCanContributionFinish ? 11287 : 11286; + AppendText(GetStringFromTable(indexColor)); + AppendText(GetStringFromTable(indexMark)); + AppendColorText(strTask, GetStringFromTable(indexColor)); + } + // 如果是奖励贡献度的任务但领取时间不是当天,则不显示 + if (hasContributionAward && pEntry) { + unsigned long ulCurTime = pTask->GetCurTime(); + unsigned long ulTaskTime = pEntry->m_ulTaskTime; + tm tmCur, tmTask; + ulCurTime -= unsigned long(TaskInterface::GetTimeZoneBias() * 60); + ulTaskTime -= unsigned long(TaskInterface::GetTimeZoneBias() * 60); + + if ((long)(ulCurTime) < 0) + ulCurTime = 0; + + if ((long)(ulTaskTime) < 0) + ulTaskTime = 0; + + tmCur = *gmtime((time_t*)&ulCurTime); + tmTask = *gmtime((time_t*)&ulTaskTime); + if (!(tmCur.tm_year == tmTask.tm_year && tmCur.tm_yday == tmTask.tm_yday)) + return; + } + if (pTask->HasTask(id)) { + FormatTaskTraceDesc(pTempl->m_ID, true, strIndentIn, false, ++m_iIndexForContribution, bCanContributionFinish, contribution); + } + ActiveTaskEntry* aEntries = pActiveLst->m_TaskEntries; + for (int j = 0; j < pActiveLst->m_uTaskCount; ++j) { + ActiveTaskEntry& CurEntry = aEntries[j]; + + int taskID = CurEntry.m_ID; + if (CurEntry.m_ParentIndex != 0xff) { + ActiveTaskEntry& ParentEntry = aEntries[CurEntry.m_ParentIndex]; + if (ParentEntry.m_ID == id) + AddTaskString(taskID, hasContributionAward, strIndentIn + INDENTATION); + } + } + } +} + +void CDlgTaskTrace::FinishTaskSpendingContribution(int taskID) +{ + CECTaskInterface *pTask = GetHostPlayer()->GetTaskInterface(); + ATaskTemplMan *pMan = GetGame()->GetTaskTemplateMan(); + ATaskTempl *pTemp = pMan->GetTaskTemplByID(taskID); + ActiveTaskList* pActiveLst = (ActiveTaskList*)pTask->GetActiveTaskList(); + ActiveTaskEntry* pEntry = pActiveLst->GetEntry(taskID); + if (pTemp && pEntry && !pEntry->IsFinished()) { + if (pTemp->m_Award_S->m_ulCandItems > 1) { + CDlgAward* pAward = dynamic_cast(m_pAUIManager->GetDialog("Win_Award")); + if (pAward) { + pAward->UpdateAwardItem(taskID, false, true); + pAward->Show(true); + } + } + else GetHostPlayer()->GetTaskInterface()->FinishTaskSpendingWorldContribution(taskID); + UpdateContributionTask(); + } +} +extern CECStringTab _task_err; +void CDlgTaskTrace::OnCommand_FinishTask(const char * szCommand) +{ + PAUIOBJECT pObj = GetDlgItem(szCommand); + if (pObj) { + int idTask = pObj->GetData(); + if (idTask) { + const ATaskTempl* pTempl = GetGame()->GetTaskTemplateMan()->GetTaskTemplByID(idTask); + if (pTempl) { + CECHostPlayer* pHost = GetHostPlayer(); + CECGameUIMan* pGameUI = GetGameUIMan(); + CECTaskInterface* pTask = pHost->GetTaskInterface(); + Task_State_info tsi; + pTask->GetTaskStateInfo(idTask, &tsi); + if (tsi.m_ulErrCode) + { + const wchar_t* szMsg = _task_err.GetWideString(tsi.m_ulErrCode); + + if (szMsg) + { + ACString strText = szMsg; + ACString strTemp; + if (tsi.m_ulErrCode == TASK_AWARD_FAIL_LEVEL_CHECK) + strTemp.Format(GetStringFromTable(7637), tsi.m_ulPremLevelMin); + else strTemp = GetStringFromTable(807); + strText += strTemp; + strText += _AL("\r"); + strText += COLOR_WHITE; + pGameUI->MessageBox("", strText, MB_OK, A3DCOLORRGBA(255, 255, 255, 160)); + return; + } + } + if (pHost->GetWorldContribution() < pTempl->m_iWorldContribution) + pGameUI->MessageBox("", pGameUI->GetStringFromTable(11282), MB_OK, A3DCOLORRGBA(255, 255, 255, 160)); + else if (!pHost->HaveHealthStones() && + pHost->GetWorldContributionSpend() + pTempl->m_iWorldContribution > TASK_WORLD_CONTRIBUTION_SPEND_PER_DAY) + pGameUI->MessageBox("", pGameUI->GetStringFromTable(11283), MB_OK, A3DCOLORRGBA(255, 255, 255, 160)); + else + FinishTaskSpendingContribution(idTask); + } + } + } +} + +void CDlgTaskTrace::AppendColorText(ACString& strTask, const ACString& strColor) +{ + ACString strText; + DeleteColorStr(strTask); + strText.Format(_AL("%s%s"), strColor, strTask); + AppendText(strText); + AppendText(_AL("\r")); +} + +void CDlgTaskTrace::KeepScrollBarPosition() +{ + // 列表类型未改变时,保持滚动条位置 + ShowType showType = GetShowType(); + if (m_nLastShowType == showType) + { + int lastTop = m_pTxt_Desc->GetFirstLine(); + m_pTxt_Desc->SetText(m_Buffer); + int curTop = m_pTxt_Desc->GetFirstLine(); + if(lastTop != curTop) + { + int top = (lastTop < m_pTxt_Desc->GetLines()) ? lastTop + : max(0, m_pTxt_Desc->GetLines() - m_pTxt_Desc->GetLinesPP()); + m_pTxt_Desc->SetFirstLine(top); + } + } + else m_pTxt_Desc->SetText(m_Buffer); + + if (showType != m_nLastShowType) + SetShowType(showType); +} + +void CDlgTaskTrace::OnTaskNew(unsigned long idTask) +{ + ATaskTemplMan* pMan = GetGame()->GetTaskTemplateMan(); + ATaskTempl* pTempl = pMan->GetTaskTemplByID(idTask); + if (pTempl && + GetShowType() != ST_CONTRIBUTION && + pTempl->GetTopTask()->CanDeliverWorldContribution(GetHostPlayer()->GetTaskInterface()) && + GetHostPlayer()->GetMaxLevelSofar() >= CECUIConfig::Instance().GetGameUI().nContributionTaskLevelLimit) { + SetShowType(ST_CONTRIBUTION); + OnCommand_SwitchShowType(NULL); + if (!IsShow()) { + CDlgTask* pDlg = dynamic_cast(m_pAUIManager->GetDialog("Win_Quest")); + if (pDlg) + pDlg->OnCommand_showtrace(NULL); + } + } +} \ No newline at end of file diff --git a/Documentation/ChatSystem/architecture.md b/Documentation/ChatSystem/architecture.md new file mode 100644 index 0000000000..eff61a5974 --- /dev/null +++ b/Documentation/ChatSystem/architecture.md @@ -0,0 +1,52 @@ +# Perfect World Port Architecture + +## Overview + +Project này port client Perfect World từ C++ sang Unity C# và xây dựng server mới bằng .NET. + +Architecture gồm 3 layer chính: + +Client Layer +Unity project chịu trách nhiệm: + +- UI +- rendering +- input +- local gameplay logic +- network client + +Server Layer + +.NET 8 server sử dụng LiteNetLib. + +Server chịu trách nhiệm: + +- authoritative game state +- player session +- snapshot replication +- chat routing + +Shared Layer + +Game.Shared chứa: + +- protocol definitions +- shared data types +- serialization logic + +## Architecture Diagram + +Unity Client +↓ +Game.Shared (protocol) +↓ +LiteNetLib transport +↓ +Game.Server + +## Main Goals + +- giữ gameplay logic từ Perfect World +- thay engine bằng Unity +- thay network stack +- modernize architecture \ No newline at end of file diff --git a/Documentation/ChatSystem/chat_system.md b/Documentation/ChatSystem/chat_system.md new file mode 100644 index 0000000000..f4a4c7eb15 --- /dev/null +++ b/Documentation/ChatSystem/chat_system.md @@ -0,0 +1,63 @@ +# Chat System + +Chat system gốc của Perfect World gồm các bước: + +Network Protocol +↓ +GameSession +↓ +GameUIMan +↓ +CECPateText +↓ +Render + +## Components + +ChatMessage + +network protocol chứa: + +channel +roleid +msg +emotion + +GameSession::OnPrtcChatMessage + +nhận packet từ network. + +GameUIMan::AddChatMessage + +thêm message vào UI. + +CECPateText + +parse text thành các item: + +TEXT +EMOTION +ITEM LINK + +## Emotion System + +Emotion được encode trong text: + +format: + +emotionSet:index + +Example: + +1:5 + +## Rendering + +CECPateText chia text thành item list. + +Item gồm: + +type +index +color +position \ No newline at end of file diff --git a/Documentation/ChatSystem/code_patterns.md b/Documentation/ChatSystem/code_patterns.md new file mode 100644 index 0000000000..ef65d892ec --- /dev/null +++ b/Documentation/ChatSystem/code_patterns.md @@ -0,0 +1,21 @@ +# Common Code Patterns + +Pattern: Player Name Lookup + +C++ + +Name2IDTable +ID2NameTable + +C# + +Dictionary +Dictionary + +Pattern: Chat Rendering + +Parse text +↓ +create item list +↓ +render UI \ No newline at end of file diff --git a/Documentation/ChatSystem/data_types.md b/Documentation/ChatSystem/data_types.md new file mode 100644 index 0000000000..ac821d53a3 --- /dev/null +++ b/Documentation/ChatSystem/data_types.md @@ -0,0 +1,33 @@ +# Data Types Mapping + +Perfect World sử dụng nhiều custom types. + +Mapping sang C#: + +ACString → string + +ACHAR → wchar_t / char + +A3DCOLOR → UnityEngine.Color hoặc Color32 + +Octets → byte[] + +pair_type → Tuple hoặc struct + +AArray → List + +DWORD → uint + +BYTE → byte + +BOOL → bool + +## Important Note + +ACString trong C++ hỗ trợ: + +- formatting +- substring +- concat + +C# string là immutable nên cần cẩn thận performance. \ No newline at end of file diff --git a/Documentation/ChatSystem/porting_rules.md b/Documentation/ChatSystem/porting_rules.md new file mode 100644 index 0000000000..43ed160f04 --- /dev/null +++ b/Documentation/ChatSystem/porting_rules.md @@ -0,0 +1,68 @@ +# C++ to C# Porting Rules + +Rule 1 + +const ACHAR* → string + +Rule 2 + +output parameters + +C++: + +int& value + +C#: + +ref int value + +Rule 3 + +pointer + +T* → reference + +Rule 4 + +array + +ACHAR buffer[1024] + +C#: + +Span hoặc string builder + +Rule 5 + +container + +AArray → List + +Rule 6 + +memory ownership + +C++ có delete + +C# dùng GC + +Rule 7 + +Chat Message Handling + +C++: +g_pGame->GetGameRun()->AddChatMessage(str, p->channel, p->srcroleid, NULL, 0, p->emotion,pItem ? pItem->Clone() : NULL, strMsgOrigion); + + +C#: +EC_Game.GetGameRun().AddChatMessage(str, p.Channel, p.Srcroleid,null, 0, p.Emotion, null, strMsgOrigion); + +Rule 8 + +Chat Bubble on Top Player + +C++: +pPlayer.SetLastSaidWords(strTemp, p.Emotion); + +C#: +EventBus.PublishChannel(p.Srcroleid, new EventChatMessageOnTopPlayer(p.Srcroleid, strTemp)); diff --git a/Documentation/ChatSystem/protocol.md b/Documentation/ChatSystem/protocol.md new file mode 100644 index 0000000000..798d82f2a1 --- /dev/null +++ b/Documentation/ChatSystem/protocol.md @@ -0,0 +1,31 @@ +# Network Protocol + +Protocol gốc sử dụng GNET. + +Structure: + +Protocol +↓ +Rpc::Data +↓ +Octets serialization + +## Example + +ChatMessage + +fields: + +channel +srcroleid +srclevel +msg +emotion + +## Serialization + +Octets chứa raw byte array. + +C# equivalent: + +byte[] \ No newline at end of file diff --git a/EC_Player_clean.cpp b/EC_Player_clean.cpp new file mode 100644 index 0000000000..bd941b193c --- /dev/null +++ b/EC_Player_clean.cpp @@ -0,0 +1,11617 @@ +/* + * FILE: EC_Player.cpp + * + * DESCRIPTION: + * + * CREATED BY: Duyuxin, 2004/9/1 + * + * HISTORY: + * + * Copyright (c) 2004 Archosaur Studio, All Rights Reserved. + */ + +#include "EC_Global.h" +#include "EC_Game.h" +#include "EC_Player.h" +#include "EC_Model.h" +#include "EC_Face.h" +#include "EC_IvtrTypes.h" +#include "EC_RTDebug.h" +#include "EC_IvtrItem.h" +#include "EC_IvtrScroll.h" +#include "EC_IvtrWeapon.h" +#include "EC_IvtrEquipMatter.h" +#include "EC_Resource.h" +#include "EC_Viewport.h" +#include "EC_Utility.h" +#include "EC_ManDecal.h" +#include "EC_Decal.h" +#include "EC_World.h" +#include "EC_GameRun.h" +#include "EC_ImageRes.h" +#include "EC_GFXCaster.h" +#include "EC_Resource.h" +#include "EC_FixedMsg.h" +#include "EC_ManAttacks.h" +#include "EC_ManPlayer.h" +#include "EC_HostPlayer.h" +#include "EC_ElsePlayer.h" +#include "EC_Team.h" +#include "EC_Faction.h" +#include "EC_Skill.h" +#include "ElementSkill.h" +#include "EC_PortraitRender.h" +#include "EC_PateText.h" +#include "EC_Configs.h" +#include "EC_GameSession.h" +#include "EC_CustomizeBound.h" +#include "EC_SceneLoader.h" +#include "FWAssemblySet.h" +#include "FWTemplate.h" +#include "EC_NPC.h" +#include "EC_ManNPC.h" +#include "EC_Sprite.h" +#include "EC_UIManager.h" +#include "EC_GameUIMan.h" +#include "EC_Goblin.h" +#include "EC_Pet.h" +#include "EC_ManMatter.h" +#include "EC_Matter.h" +#include "EC_ChangePill.h" +#include "EL_Precinct.h" +#include "EC_CountryConfig.h" +#include "EC_UIConfigs.h" +#include "EC_Optimize.h" +#include "EC_ProfConfigs.h" +#include "EC_PlayerActionController.h" +#include "EC_PlayerBodyController.h" +#include "EC_MemSimplify.h" +#include "EC_ElementDataHelper.h" + +#include "DlgChariot.h" +#include "DlgReincarnationShow.h" +#include "DlgLevel2UpgradeShow.h" +#include "EC_TaoistRank.h" + +#include "elementdataman.h" +#include "privilege.hxx" +#include "ids.hxx" + +#include "A2DSprite.h" +#include "A2DSpriteItem.h" +#include "A3DSkin.h" +#include "A3DSkinMan.h" +#include "A3DShaderMan.h" +#include "A3DSkinMesh.h" +#include "A3DShader.h" +#include "A3DSkinModel.h" +#include "A3DSkinModelAct.h" +#include "A3DSkinModelAux.h" +#include "A3DTerrainWater.h" +#include "A3DCamera.h" +#include "A3DSkeleton.h" +#include "A3DBone.h" +#include "A3DFlatCollector.h" +#include "AAssist.h" +#include "AFile.h" +#include "A3DLight.h" +#include +#include + +#include "A3DCombinedAction.h" +#include "A3DGFXFuncs.h" +#include + +#define new A_DEBUG_NEW + +#define FASHION_POWER 30.0f +#define FASHION_SPECULAR 0xff202020 + +#define ARMOR_POWER 20.0f +#define ARMOR_SPECULAR 0xffffffff + +#define WEAPON_POWER 15.0f +#define WEAPON_SPECULAR 0xff000000//0xffffffff + +#define PLAYERMODEL_GETTYPE(iShape) ((iShape & 0xff) >> 6) +#define PLAYERMODEL_GETID(iShape) (iShape & 0x3f) + +/////////////////////////////////////////////////////////////////////////// +// +// Define and Macro +// +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////// +// +// Reference to External variables and functions +// +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////// +// +// Local Types and Variables and Global variables +// +/////////////////////////////////////////////////////////////////////////// +static const char* _left_hand_weapon = "LeftHand"; +static const char* _right_hand_weapon = "RightHand"; +static const char* _wing = "Wing"; +static const char* _wing2 = "Wing2"; +const char* _cc_ride = "CC_Ride"; +static const char* _cc_ride2 = "CC_Ride2"; +const char* _hh_ride = "HH_Ride"; +const char* _hanger_ride = "Rider"; +static const char* _hanger_ride2 = "Rider2"; +static const char* _hanger_hug = "Hug"; +static const char* _hh_bind = "HH_Bind"; +static const char* _cc_bind = "CC_Bind"; + +const char* _cc_fenghuolun = "CC_feijian"; +const char* _hh_left_foot = "HH_左脚"; +const char* _hh_right_foot = "HH_右脚"; + +static const char* _hh_left_shoulder_weapon = "HH_leftsword"; // 人背上的武器挂点(左肩) +static const char* _hh_right_shoulder_weapon= "HH_rightsword"; // 人背上的武器挂点(右肩) + +static const char* _hh_left_hand_weapon = "HH_lefthandweapon"; // 人手上的武器挂点(左手) +static const char* _hh_right_hand_weapon = "HH_righthandweapon"; // 人手上的武器挂点(右手) + +static const char* _hh_qiu = "HH_qiu"; // 人手上的球挂点(右手) + +static const char* _hh_right_hand_nickle_weapon = "HH_weapon"; // 人手上胧族月镰挂点(右手) + +static const char* _cc_weapon = "CC_weapon"; // 武器上的挂点名称 +static const char* _cc_qiu = "CC_qiu"; // 球上的挂点名称 +static const char* _hh_qiu_base = "HH_base"; // 球上的特效挂点(右手) + +const char* _multiobject_effect[] = {"绿色连线.gfx","红色连线.gfx","黑色连线.gfx",}; + +static const char* _head_skin[NUM_PROFESSION*NUM_GENDER] = +{ + "Models\\Players\\形象\\武侠男\\头\\武侠男头", + "Models\\Players\\形象\\武侠女\\头\\武侠女头", + + "Models\\Players\\形象\\法师男\\头\\法师男头", + "Models\\Players\\形象\\法师女\\头\\法师女头", + + "Models\\Players\\形象\\巫师男\\头\\巫师男头", + "Models\\Players\\形象\\巫师女\\头\\巫师女头", + + "", + "Models\\Players\\形象\\妖精\\头\\妖精头", + "Models\\Players\\形象\\妖兽男\\头\\妖兽男头", + "", + "Models\\Players\\形象\\刺客男\\头\\刺客男头", + "Models\\Players\\形象\\刺客女\\头\\刺客女头", + + "Models\\Players\\形象\\羽芒男\\头\\羽芒男头", + "Models\\Players\\形象\\羽芒女\\头\\羽芒女头", + + "Models\\Players\\形象\\羽灵男\\头\\羽灵男头", + "Models\\Players\\形象\\羽灵女\\头\\羽灵女头", + + "Models\\Players\\形象\\剑灵男\\头\\剑灵男头", + "Models\\Players\\形象\\剑灵女\\头\\剑灵女头", + + "Models\\Players\\形象\\魅灵男\\头\\魅灵男头", + "Models\\Players\\形象\\魅灵女\\头\\魅灵女头", + + "Models\\Players\\形象\\夜影男\\头\\夜影男头", + "Models\\Players\\形象\\夜影女\\头\\夜影女头", + + "Models\\Players\\形象\\月仙男\\头\\月仙男头", + "Models\\Players\\形象\\月仙女\\头\\月仙女头", +}; + +static const char* _body_skin[NUM_PROFESSION*NUM_GENDER] = +{ + "Models\\Players\\形象\\武侠男\\躯干\\武侠男", + "Models\\Players\\形象\\武侠女\\躯干\\武侠女", + + "Models\\Players\\形象\\法师男\\躯干\\法师男", + "Models\\Players\\形象\\法师女\\躯干\\法师女", + + "Models\\Players\\形象\\巫师男\\躯干\\巫师男%d", + "Models\\Players\\形象\\巫师女\\躯干\\巫师女%d", + "", + "Models\\Players\\形象\\妖精\\躯干\\妖精%d", + "Models\\Players\\形象\\妖兽男\\躯干\\妖兽男%d", + "", + "Models\\Players\\形象\\刺客男\\躯干\\刺客男%d", + "Models\\Players\\形象\\刺客女\\躯干\\刺客女%d", + + "Models\\Players\\形象\\羽芒男\\躯干\\羽芒男", + "Models\\Players\\形象\\羽芒女\\躯干\\羽芒女", + + "Models\\Players\\形象\\羽灵男\\躯干\\羽灵男", + "Models\\Players\\形象\\羽灵女\\躯干\\羽灵女", + + "Models\\Players\\形象\\剑灵男\\躯干\\剑灵男", + "Models\\Players\\形象\\剑灵女\\躯干\\剑灵女", + + "Models\\Players\\形象\\魅灵男\\躯干\\魅灵男", + "Models\\Players\\形象\\魅灵女\\躯干\\魅灵女", + + "Models\\Players\\形象\\夜影男\\躯干\\夜影男", + "Models\\Players\\形象\\夜影女\\躯干\\夜影女", + + "Models\\Players\\形象\\月仙男\\躯干\\月仙男", + "Models\\Players\\形象\\月仙女\\躯干\\月仙女", +}; + +static const char* _simple_body_skin[NUM_PROFESSION*NUM_GENDER] = +{ + "Models\\Players\\形象\\武侠男\\躯干\\武侠男简化", + "Models\\Players\\形象\\武侠女\\躯干\\武侠女简化", + + "Models\\Players\\形象\\法师男\\躯干\\法师男简化", + "Models\\Players\\形象\\法师女\\躯干\\法师女简化", + + "Models\\Players\\形象\\巫师男\\躯干\\巫师男简化", + "Models\\Players\\形象\\巫师女\\躯干\\巫师女简化", + "", + "Models\\Players\\形象\\妖精\\躯干\\妖精简化", + "Models\\Players\\形象\\妖兽男\\躯干\\妖兽男简化%d", + "", + "Models\\Players\\形象\\刺客男\\躯干\\刺客男简化", + "Models\\Players\\形象\\刺客女\\躯干\\刺客女简化", + + "Models\\Players\\形象\\羽芒男\\躯干\\羽芒男简化", + "Models\\Players\\形象\\羽芒女\\躯干\\羽芒女简化", + + "Models\\Players\\形象\\羽灵男\\躯干\\羽灵男简化", + "Models\\Players\\形象\\羽灵女\\躯干\\羽灵女简化", + + "Models\\Players\\形象\\剑灵男\\躯干\\剑灵男简化", + "Models\\Players\\形象\\剑灵女\\躯干\\剑灵女简化", + + "Models\\Players\\形象\\魅灵男\\躯干\\魅灵男简化", + "Models\\Players\\形象\\魅灵女\\躯干\\魅灵女简化", + + "Models\\Players\\形象\\夜影男\\躯干\\夜影男简化", + "Models\\Players\\形象\\夜影女\\躯干\\夜影女简化", + + "Models\\Players\\形象\\月仙男\\躯干\\月仙男简化", + "Models\\Players\\形象\\月仙女\\躯干\\月仙女简化", +}; + +static const char* _equipment_skin[NUM_PROFESSION*NUM_GENDER] = +{ + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + "", + "Models\\Players\\装备\\女\\%s\\妖精%s", + "Models\\Players\\装备\\男\\%s\\妖兽%s", + "", + + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", + "Models\\Players\\装备\\男\\%s\\男通用%s", + "Models\\Players\\装备\\女\\%s\\女通用%s", +}; + +static const char* _equipment_default_skin[NUM_PROFESSION*NUM_GENDER] = +{ + "Models\\Players\\装备\\男\\%s\\武侠男%s", + "Models\\Players\\装备\\女\\%s\\武侠女%s", + "Models\\Players\\装备\\男\\%s\\法师男%s", + "Models\\Players\\装备\\女\\%s\\法师女%s", + + "Models\\Players\\装备\\男\\%s\\巫师男%s", + "Models\\Players\\装备\\女\\%s\\巫师女%s", + "", + "Models\\Players\\装备\\女\\%s\\妖精%s", + "Models\\Players\\装备\\男\\%s\\妖兽%s", + "", + + "Models\\Players\\装备\\男\\%s\\刺客男%s", + "Models\\Players\\装备\\女\\%s\\刺客女%s", + + "Models\\Players\\装备\\男\\%s\\羽芒男%s", + "Models\\Players\\装备\\女\\%s\\羽芒女%s", + "Models\\Players\\装备\\男\\%s\\羽灵男%s", + "Models\\Players\\装备\\女\\%s\\羽灵女%s", + + "Models\\Players\\装备\\男\\%s\\剑灵男%s", + "Models\\Players\\装备\\女\\%s\\剑灵女%s", + "Models\\Players\\装备\\男\\%s\\魅灵男%s", + "Models\\Players\\装备\\女\\%s\\魅灵女%s", + + "Models\\Players\\装备\\男\\%s\\夜影男%s", + "Models\\Players\\装备\\女\\%s\\夜影女%s", + "Models\\Players\\装备\\男\\%s\\月仙男%s", + "Models\\Players\\装备\\女\\%s\\月仙女%s", +}; + +static const int _skin_alpha_map[] = +{ + SKIN_SORT_DEFAULT, // SKIN_BODY_INDEX + SKIN_SORT_UPPER, // SKIN_UPPER_BODY_INDEX + SKIN_SORT_WRIST, // SKIN_WRIST_INDEX + SKIN_SORT_LOWER, // SKIN_LOWER_INDEX + SKIN_SORT_FOOT, // SKIN_FOOT_INDEX + SKIN_SORT_DEFAULT, // SKIN_HEAD_INDEX + SKIN_SORT_UPPER, // SKIN_FASHION_UPPER_BODY_INDEX + SKIN_SORT_WRIST, // SKIN_FASHION_WRIST_INDEX + SKIN_SORT_LOWER, // SKIN_FASHION_LOWER_INDEX + SKIN_SORT_FOOT, // SKIN_FASHION_FOOT_INDEX +}; + +static char strWeaponActName[512]; // Weapon action name + +static CRITICAL_SECTION l_csLoadPlayerSkin; + +static CECPlayer::PLAYER_ACTION* _default_actions = NULL; // Static Action data table +static CECPlayer::PLAYER_ACTION* _turning_actions = NULL; // Static Action data table + +static abase::hash_map _default_skill_actions; // skill actions +static PLAYER_LEVELEXP_CONFIG _player_levelup_exp; // Level up exp needed +static const int GAP_BETWEEN_NAME_TITLE = 7; +static const A3DVECTOR3 aExts[NUM_PROFESSION*NUM_GENDER] = +{ + A3DVECTOR3(0.4f, 0.9f, 0.4f), // 武侠 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 法师 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 巫师 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 妖精 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.5f, 1.05f, 0.5f), // 妖兽 + A3DVECTOR3(0.3f, 0.9f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 刺客 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 羽芒 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 羽灵 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 剑灵 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 魅灵 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 夜影 + A3DVECTOR3(0.3f, 0.85f, 0.3f), + A3DVECTOR3(0.3f, 0.9f, 0.3f), // 月仙 + A3DVECTOR3(0.3f, 0.85f, 0.3f), +}; + +const int CECPlayer::m_sciStateIDForStateAction[1] = {117}; +/////////////////////////////////////////////////////////////////////////// +// +// Local functions +// +/////////////////////////////////////////////////////////////////////////// + +static void _GenSkinPath(char* szPath, int nProfession, int nGender, const char* szSkinName) +{ + sprintf(szPath, _equipment_skin[nProfession * NUM_GENDER + nGender], szSkinName, szSkinName); +} + +static void _GenDefaultSkinPath(char* szPath, int nProfession, int nGender, const char* szSkinName) +{ + sprintf(szPath, _equipment_default_skin[nProfession * NUM_GENDER + nGender], szSkinName, szSkinName); +} + +static const char* _GenWeaponActionName(char* szAct, int nGender) +{ + sprintf(strWeaponActName, "%s_%s", szAct, (nGender==GENDER_MALE) ? "男" : "女"); + return strWeaponActName; +} + +static inline int _GetProfessionTransformModelID(int nChar, int nGender, int nModelID) +{ + int result(0); + switch (nChar){ + case PROF_HAG: // 妖精 + result = (2 == nModelID) ? RES_MOD_ORC_FOX2 : RES_MOD_ORC_FOX; + break; + case PROF_ORC: // 妖兽 + result = (2 == nModelID) ? RES_MOD_ORC_PANDER : RES_MOD_ORC_TIGER; + break; + case PROF_MONK: // 巫师 + case PROF_GHOST: // 刺客 + result = (GENDER_MALE == nGender) ? RES_MOD_SHADOW_FISH_M : RES_MOD_SHADOW_FISH_F; + break; + case PROF_YEYING: // 夜影 + result = (GENDER_MALE == nGender) ? RES_MOD_YEYING_RESHAPE_M : RES_MOD_YEYING_RESHAPE_F; + break; + case PROF_YUEXIAN: // 月仙 + result = (GENDER_MALE == nGender) ? RES_MOD_YUEXIAN_RESHAPE_M : RES_MOD_YUEXIAN_RESHAPE_F; + break; + } + return result; +} + +static bool IsTransofrmModelLikeHuman(int nChar, int nGender, int nModelID){ + bool result(false); + const int *likeHumanModelArray = NULL; + int likeHumanModelCount = 0; + switch (nChar) + { + case PROF_YEYING: + if (GENDER_MALE == nGender){ + const int LIKE_HUMAN_MODEL_COUNT = 1; + static int s_LikeHumanModel[LIKE_HUMAN_MODEL_COUNT] = {RES_MOD_YEYING_RESHAPE_M}; + likeHumanModelArray = s_LikeHumanModel; + likeHumanModelCount = LIKE_HUMAN_MODEL_COUNT; + }else{ + const int LIKE_HUMAN_MODEL_COUNT = 1; + static int s_LikeHumanModel[LIKE_HUMAN_MODEL_COUNT] = {RES_MOD_YEYING_RESHAPE_F}; + likeHumanModelArray = s_LikeHumanModel; + likeHumanModelCount = LIKE_HUMAN_MODEL_COUNT; + } + break; + case PROF_YUEXIAN: + if (GENDER_MALE == nGender){ + const int LIKE_HUMAN_MODEL_COUNT = 1; + static int s_LikeHumanModel[LIKE_HUMAN_MODEL_COUNT] = {RES_MOD_YUEXIAN_RESHAPE_M}; + likeHumanModelArray = s_LikeHumanModel; + likeHumanModelCount = LIKE_HUMAN_MODEL_COUNT; + }else{ + const int LIKE_HUMAN_MODEL_COUNT = 1; + static int s_LikeHumanModel[LIKE_HUMAN_MODEL_COUNT] = {RES_MOD_YUEXIAN_RESHAPE_F}; + likeHumanModelArray = s_LikeHumanModel; + likeHumanModelCount = LIKE_HUMAN_MODEL_COUNT; + } + break; + } + if (likeHumanModelCount > 0){ + const int *begin = likeHumanModelArray; + const int *end = likeHumanModelArray + likeHumanModelCount; + if (std::find(begin, end, _GetProfessionTransformModelID(nChar, nGender, nModelID)) != end){ + result = true; + } + } + return result; +} + +inline void _ReleaseLoadModel(EC_PLAYERLOADRESULT& Ret) +{ + if (Ret.pPlayerModel) + { + Ret.pPlayerModel->Release(); + delete Ret.pPlayerModel; + } + + if (Ret.pDummyModel) + { + Ret.pDummyModel->Release(); + delete Ret.pDummyModel; + } + + if (Ret.pFaceModel) + { + Ret.pFaceModel->Release(); + delete Ret.pFaceModel; + } + + if (Ret.pPetModel) + { + Ret.pPetModel->Release(); + delete Ret.pPetModel; + } + + for (int i = 0; i < NUM_SKIN_INDEX; i++) + { + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[i][0]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[i][1]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[i][2]); + } + + if (Ret.EquipResult.pLeftHandWeapon) + { + Ret.EquipResult.pLeftHandWeapon->Release(); + delete Ret.EquipResult.pLeftHandWeapon; + } + if (Ret.EquipResult.pRightHandWeapon) + { + Ret.EquipResult.pRightHandWeapon->Release(); + delete Ret.EquipResult.pRightHandWeapon; + } + + if (Ret.EquipResult.pWing) + { + Ret.EquipResult.pWing->Release(); + delete Ret.EquipResult.pWing; + } +} + +// Build pate faction text +static void _BuildPateFactionText(int idFaction, int iRank, ACString& strText) +{ + strText.Empty(); + + if (!idFaction) + return; + + // Reset faction name + Faction_Info* pFaction = g_pGame->GetFactionMan()->GetFaction(idFaction); + if (!pFaction) + return; + + strText = pFaction->GetName(); + + // Add faction rank + if (iRank >= _R_MASTER && iRank <= _R_POINEER) + { + strText += _AL(" "); + strText += g_pGame->GetFixedMsgTab()->GetWideString(FIXMSG_FCRANK_MASTER + iRank - _R_MASTER); + } +} +void GetFashionActionNameByID(int idEquipment, AString& strAction) +{ + if (idEquipment) + { + DATA_TYPE dt = DT_INVALID; + const void* pEquip = g_pGame->GetElementDataMan()->get_data_ptr( + idEquipment, + ID_SPACE_ESSENCE, + dt); + + if (pEquip && dt == DT_FASHION_ESSENCE) + { + const FASHION_ESSENCE *pEssence = (const FASHION_ESSENCE *)pEquip; + strAction = pEssence->wear_action; + } + } +} +bool CECPlayer::LoadPlayerEquips( + int iProf, + int iGender, + __int64 Mask, + const int* pEquips, + EquipsLoadResult& Ret, + bool bSimpleModel) +{ + memset(&Ret, 0, sizeof(Ret)); + + ShowEquipments( + iProf, + iGender, + pEquips, + Mask, + &Ret, + bSimpleModel + ); + + return true; +} + +// Load player model +bool CECPlayer::LoadPlayerModel( + int iProfession, + int iGender, + int iCustom, + const int* pEquips, + const char* szPetPath, + EC_PLAYERLOADRESULT& Ret, + bool bSimpleFace, bool bSimpleModel) +{ + static const char* aModelFiles[NUM_PROFESSION*NUM_GENDER] = + { + res_ModelFile(RES_MOD_WARRIOR_M), // 武侠 + res_ModelFile(RES_MOD_WARRIOR_F), + res_ModelFile(RES_MOD_MAGE_M), // 法师 + res_ModelFile(RES_MOD_MAGE_F), + res_ModelFile(RES_MOD_MONK_M), // 巫师 + res_ModelFile(RES_MOD_MONK_F), + res_ModelFile(RES_MOD_HAG_M), // 妖精 + res_ModelFile(RES_MOD_HAG_F), + res_ModelFile(RES_MOD_ORC_M), // 妖兽 + res_ModelFile(RES_MOD_ORC_F), + res_ModelFile(RES_MOD_GHOST_M), // 刺客 + res_ModelFile(RES_MOD_GHOST_F), + res_ModelFile(RES_MOD_ARCHOR_M), // 羽芒 + res_ModelFile(RES_MOD_ARCHOR_F), + res_ModelFile(RES_MOD_ANGEL_M), // 羽灵 + res_ModelFile(RES_MOD_ANGEL_F), + res_ModelFile(RES_MOD_JIANLING_M), // 剑灵 + res_ModelFile(RES_MOD_JIANLING_F), + res_ModelFile(RES_MOD_MEILING_M), // 魅灵 + res_ModelFile(RES_MOD_MEILING_F), + res_ModelFile(RES_MOD_YEYING_M), // 夜影 + res_ModelFile(RES_MOD_YEYING_F), + res_ModelFile(RES_MOD_YUEXIAN_M), // 月仙 + res_ModelFile(RES_MOD_YUEXIAN_F), + }; + + int iChar = iProfession * NUM_GENDER + iGender; + memset(&Ret, 0, sizeof(Ret)); + + Ret.dwValidMask |= PLAYERLOADRESULT_PLAYERMODEL | PLAYERLOADRESULT_EQUIPMODEL | PLAYERLOADRESULT_PETMODEL; + + // Load player model ---------------------------- + if (!(Ret.pPlayerModel = new CECModel)) + { + glb_ErrorOutput(ECERR_NOTENOUGHMEMORY, "CECPlayer::LoadPlayerModel", __LINE__); + _ReleaseLoadModel(Ret); + return false; + } + + // Load skeleton without skins + const char* szFile = aModelFiles[iChar]; + if (!Ret.pPlayerModel->Load(szFile, true, A3DSkinModel::LSF_NOSKIN, /*true*/false)) + { + a_LogOutput(1, "CECPlayer::LoadPlayerModel, Failed to load player model %s", szFile); + _ReleaseLoadModel(Ret); + return false; + } + + A3DSkinModel* pA3DModel = Ret.pPlayerModel->GetA3DSkinModel(); + + if (!pA3DModel) + { + _ReleaseLoadModel(Ret); + return false; + } + + pA3DModel->AddSkin(NULL, false); // body skin + pA3DModel->AddSkin(NULL, false); // upper body skin + pA3DModel->AddSkin(NULL, false); // wrist skin + pA3DModel->AddSkin(NULL, false); // lower body skin + pA3DModel->AddSkin(NULL, false); // foot skin + pA3DModel->AddSkin(NULL, false); // head skin + pA3DModel->AddSkin(NULL, false); // fashion upper body skin + pA3DModel->AddSkin(NULL, false); // fashion wrist skin + pA3DModel->AddSkin(NULL, false); // fashion lower body skin + pA3DModel->AddSkin(NULL, false); // fashion foot skin + + // load equips + __int64 EquipMask = 0; + if (pEquips) + { + for (__int64 i = 0; i < SIZE_ALL_EQUIPIVTR; i++) + EquipMask |= (pEquips[i] < 0) ? 0 : (1 << i); + } + + if(EquipMask) + { + ShowEquipments( + iProfession, + iGender, + pEquips, + EquipMask, + &Ret.EquipResult, + bSimpleModel + ); + } + + if (!LoadBodySkin(iCustom, iProfession, iGender, Ret.EquipResult.aSkins, Ret.pBodyShaders, bSimpleModel)) + { + _ReleaseLoadModel(Ret); + return false; + } + + if( bSimpleFace ) + { + Ret.pFaceModel = NULL; + } + else + { + Ret.pFaceModel = ThreadLoadFaceModel(iProfession, iGender, iCustom); + if (!Ret.pFaceModel) + { + _ReleaseLoadModel(Ret); + return false; + } + } + + if (szPetPath && szPetPath[0] && !LoadPetModel(szPetPath, &Ret.pPetModel)) + { + _ReleaseLoadModel(Ret); + return false; + } + + return true; +} + +bool CECPlayer::LoadPetModel(const char* szPetPath, CECModel** ppPetModel) +{ + CECModel*& pPetModel = *ppPetModel; + pPetModel = new CECModel(); + +// g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("armor.sdr", A3DSkinMan::SHADERREPLACE_REFLECTPREFIX); + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile((const char*)glb_ArmorReplaceShader_ReflectPrefix, A3DSkinMan::SHADERREPLACE_USERDEFINE); + if (!pPetModel->Load(szPetPath, true, A3DSkinModel::LSF_NOSKIN, true)) + { + delete pPetModel; + pPetModel = NULL; + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); + return false; + } + + pPetModel->BuildWoundActionChannel(); + + char szSkin[MAX_PATH]; + strncpy(szSkin, szPetPath, MAX_PATH); + glb_ChangeExtension(szSkin, "ski"); + A3DSkin * pPetSkin = g_pGame->LoadA3DSkin(szSkin, false); + if( !pPetSkin ) + { + pPetModel->Release(); + delete pPetModel; + pPetModel = NULL; + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); + return false; + } + pPetModel->GetA3DSkinModel()->AddSkin(pPetSkin, true); + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); + return true; +} + +bool CECPlayer::LoadDummyModel(int iShape, CECModel** ppDummyModel) +{ + CECModel* pDummyModel = NULL; + + int resID = PLAYERMODEL_GETID(iShape); + if(resID > 0) // == 0 means no model changed but logic transformed + { + pDummyModel = new CECModel; + if (!pDummyModel) + { + glb_ErrorOutput(ECERR_NOTENOUGHMEMORY, "CECPlayer::LoadDummyModel", __LINE__); + return false; + } + + const char* szFile = NULL; + if (PLAYERMODEL_GETTYPE(iShape) == PLAYERMODEL_DUMMYTYPE2) // 小动物变身 + szFile = res_ModelFileForChangeShape2(resID); + else + szFile = res_ModelFile(resID); + + if (!pDummyModel->Load(szFile, true, 0, false)) + { + delete pDummyModel; + a_LogOutput(1, "CECPlayer::LoadDummyModel, Failed to load dummy model %s", szFile); + return false; + } + } + + *ppDummyModel = pDummyModel; + return true; +} + +// Release player model +void CECPlayer::ReleasePlayerModel(EC_PLAYERLOADRESULT& Ret) +{ + if (Ret.pPlayerModel) + { + QueueECModelForRelease(Ret.pPlayerModel); + Ret.pPlayerModel = NULL; + } + + if (Ret.pDummyModel) + { + QueueECModelForRelease(Ret.pDummyModel); + Ret.pDummyModel = NULL; + } + + A3DRELEASE(Ret.pFaceModel); + A3DRELEASE(Ret.pPetModel); + A3DRELEASE(Ret.EquipResult.pLeftHandWeapon); + A3DRELEASE(Ret.EquipResult.pRightHandWeapon); + A3DRELEASE(Ret.EquipResult.pWing); + A3DRELEASE(Ret.EquipResult.pWing2); + + for (int i = 0; i < 3; i++) + { + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_BODY_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_HEAD_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_UPPER_BODY_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_WRIST_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_LOWER_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_FOOT_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_FASHION_UPPER_BODY_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_FASHION_WRIST_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_FASHION_LOWER_INDEX][i]); + g_pGame->ReleaseA3DSkin(Ret.EquipResult.aSkins[SKIN_FASHION_FOOT_INDEX][i]); + } +} + +// Get exp of specified level +int CECPlayer::GetLevelUpExp(int iLevel) +{ + return _player_levelup_exp.exp[iLevel - 1]; + //return iLevel * iLevel * 500; +} + +// Build riding pet file name +const char* CECPlayer::GetRidingPetFileName(int idPet) +{ + static const char* szDef = "Models\\NPCs\\宠物\\骑宠\\骑宠马\\骑宠马白.ecm"; + + DATA_TYPE DataType; + const void* pDataPtr = g_pGame->GetElementDataMan()->get_data_ptr(idPet, ID_SPACE_ESSENCE, DataType); + if (DataType != DT_PET_ESSENCE) + return szDef; + + const PET_ESSENCE* pData = (const PET_ESSENCE*)pDataPtr; + if (!pData->file_model[0]) + return szDef; + + return pData->file_model; +} + +// Initialize static resources +bool CECPlayer::InitStaticRes() +{ + // Element data man must has been initialized + elementdataman* pdb = g_pGame->GetElementDataMan(); + if (!pdb) + { + ASSERT(pdb); + return false; + } + + BuildActionList(); + + // Initialize level up exp table + DATA_TYPE dt; + _player_levelup_exp = *(PLAYER_LEVELEXP_CONFIG*)g_pGame->GetElementDataMan()->get_data_ptr(202, ID_SPACE_CONFIG, dt); + + // Initialize a CRITICAL_SECTION for loading player skin + InitializeCriticalSection(&l_csLoadPlayerSkin); + + return true; +} + +// Release static resources +void CECPlayer::ReleaseStaticRes() +{ + delete[] _default_actions; + _default_actions = NULL; + + delete[] _turning_actions; + _turning_actions = NULL; + + _default_skill_actions.clear(); + + DeleteCriticalSection(&l_csLoadPlayerSkin); +} + +/////////////////////////////////////////////////////////////////////////// +// +// Implement CECPlayer +// +/////////////////////////////////////////////////////////////////////////// +CECPlayer::CECPlayer(CECPlayerMan* pPlayerMan) +{ + m_iCID = OCID_PLAYER; + m_pPlayerMan = pPlayerMan; + m_pPlayerModel = NULL; + m_pFaceModel = NULL; + memset(m_pModels, 0, sizeof(m_pModels)); + memset(m_aShapeID, 0, sizeof(m_aShapeID)); + m_pLeftHandWeapon = NULL; + m_pRightHandWeapon = NULL; + m_bWeaponAttached = false; + m_pPetModel = NULL; + m_pActionController = NULL; + m_pBodyController = NULL; + m_pSprite = NULL; + m_pGoblin = NULL; + memset(m_pBodyShader, 0, sizeof(A3DShader *) * 3); + m_iMoveMode = MOVE_STAND; + m_iMoveEnv = MOVEENV_GROUND; + m_bCastShadow = false; + m_pLevelUpGFX = NULL; + m_pWaterWaveStill = NULL; + m_pWaterWaveMove = NULL; + m_pAirBubble = NULL; + m_pSwimBubble = NULL; + m_pTransformGfx = NULL; + m_pDuelStateGFX = NULL; + m_pPetCureGFX = NULL; + m_pPetCureGFXtate = -1; + memset(m_pMonsterSpiritGFX, 0, sizeof(m_pMonsterSpiritGFX)); + m_iMonsterSpiritMineID = 0; + m_stateMonsterSpirit = BALL_STATE_NONE; + m_fTouchRad = 0.3f; + m_iMoneyCnt = 0; + m_iMaxMoney = 1000; + m_bWalkRun = 1; + m_pTeam = NULL; + m_iProfession = PROF_WARRIOR; + m_iGender = GENDER_MALE; + m_bRenderName = false; + m_bRenderBar = true; + m_dwStates = 0; + m_dwStates2 = 0; + m_dwResFlags = 0; + m_uAttackType = DEFAULT_ACTION_TYPE; + m_iFashionWeaponType= DEFAULT_ACTION_TYPE; + m_bAboutToDie = false; + memset(m_aExtStates, 0, sizeof(m_aExtStates)); + memset(m_aExtStatesShown, 0, sizeof(m_aExtStatesShown)); + m_pCurSkill = NULL; + m_idCurSkillTarget = 0; + m_bFight = false; + m_iReputation = 0; + m_iShape = 0; + m_bInSanctuary = false; + m_bPetInSanctuary = false; + m_iBoothState = 0; + m_crcBooth = 0; + m_bFashionMode = false; + m_factionPVPMask = 0; + m_idFaction = 0; + m_idFRole = GNET::_R_UNMEMBER; + m_idSpouse = 0; + m_byPariahLvl = 0; + m_bRushFly = false; + m_AttachMode = enumAttachNone; + m_bHangerOn = false; + m_iBuddyId = 0; + m_idCandBuddy = 0; + m_bCandHangerOn = false; + m_pFactionDecal = NULL; + m_iBattleCamp = GP_BATTLE_CAMP_NONE; + m_dwGMFlags = 0; + m_idCurPet = 0; + m_pBoothModel = NULL; + m_iBoothModelCertificateID = -1; // 默认值为-1,以处理默认摆摊模型为0时、也需要加载默认摆摊模型的情况 + m_bBoothModelLoaded = false; + m_iBoothBarCertificateID = -1; // 默认值为-1,以处理默认摆摊模型为0时、也需要加载默认摆摊模型的情况 + m_weaponHangerPos = WEAPON_HANGER_HAND; + m_vNamePos = A3DVECTOR3(0.0f); + m_i64EquipDisabled = 0; + m_idForce = 0; + m_idCountry = 0; + m_pCountryDecal = NULL; + m_pPateCountry = NULL; + m_PlayerActions = _default_actions; // bind to default actions + m_iMemUsage = CECMemSimplify::MEMUSAGE_NORMAL; + + m_GoblinRenderCnt.SetPeriod(30000); + m_GoblinRenderCnt.Reset(true); + + m_bRenderGoblin = true; + + m_TransCnt.SetPeriod(500); + m_fDstTrans = 0.f; + m_fCurTrans = 0.f; + m_fTransDelta = 0.f; + + m_fDistToCamera = 0.0f; + + if ((m_pPateName = new CECPateText)) + m_pPateName->EnableBorder(true); + + if ((m_pPateMarry = new CECPateText)) + m_pPateMarry->EnableBorder(true); + + if ((m_pPateBooth = new CECPateText)) + m_pPateBooth->EnableBorder(true); + + if ((m_pPateFaction = new CECPateText)) + m_pPateFaction->EnableBorder(true); + + if ((m_pPateForce = new CECPateText)) + m_pPateForce->EnableBorder(true); + + if ((m_pPateTitle = new CECPateText)) + m_pPateTitle->EnableBorder(true); + + + m_pPateLastWords1 = new CECPateText; + m_pPateLastWords2 = new CECPateText; + m_pPateTeamReq = new CECPateText; + m_pBubbleTexts = new CECBubbleDecalList; + + m_nLowerEquipMethod = enumSkinShowNone; + m_nLowerFashionEquipMethod = enumSkinShowNone; + m_wingType = WINGTYPE_FLYSWORD; + + m_aabb.Center = g_vOrigin; + m_aabb.Extents.Set(0.3f, 0.9f, 0.3f); + m_aabbServer = m_aabb; + m_MoveConst.fStepHei = 0.8f; + m_MoveConst.fMinAirHei = 1.6f; + m_MoveConst.fMinWaterHei = 0.3f; + m_MoveConst.fShoreDepth = 1.6f; + m_MoveConst.fWaterSurf = 0.6f; + + memset(&m_CustomizeData, 0, sizeof(m_CustomizeData)); + m_CustomizeData.bodyID = 0; + m_CustomizeData.dwVersion = CUSTOMIZE_DATA_VERSION; + m_CustomizeData.colorBody = 0xffffffff; + m_CustomizeData.headScale = 128; + m_CustomizeData.upScale = 128; + m_CustomizeData.waistScale = 128; + m_CustomizeData.armWidth = 128; + m_CustomizeData.legWidth = 128; + m_CustomizeData.breastScale = 128; + + m_OldCustomizeData = m_CustomizeData; + m_ChgPllCustomizeData = m_CustomizeData; + m_vPortraitCamScale = 1.0f; + m_bIsChangingFace = false; + + memset(&m_PlayerInfo, 0, sizeof (m_PlayerInfo)); + memset(&m_BasicProps, 0, sizeof (m_BasicProps)); + memset(&m_ExtProps, 0, sizeof (m_ExtProps)); + memset(m_aEquips, 0xff, sizeof (m_aEquips)); + memset(&m_TeamReq, 0, sizeof (m_TeamReq)); + memset(m_aSkins, 0, sizeof (m_aSkins)); + memset(m_aCurSkins, 0, sizeof (m_aCurSkins)); + memset(&m_pvp, 0, sizeof (m_pvp)); + memset(&m_meridiansProp, 0, sizeof (m_meridiansProp)); + + m_PlayerInfo.crc_c = -1; + m_PateContent.iVisible = 0; + + m_bShowWeapon = true; + + m_stoneUpperBody = 0; + m_stoneWrist = 0; + m_stoneLowerBody = 0; + m_stoneFoot = 0; + m_stoneWeapon = 0; + m_idFullSuite = 0; + + m_stoneUpperBodyShown = 0; + m_stoneWristShown = 0; + m_stoneLowerBodyShown = 0; + m_stoneFootShown = 0; + m_stoneWeaponShown = 0; + m_idFullSuiteShown = 0; + m_TitleID = 0; + m_ReincarnationCount = 0; + m_RealmLevel = 0; + + m_strLastSayCnt.SetPeriod(20000); + m_fScaleBySkill = 1.f; + + m_SkillIDForStateAction = 0; + + // Initialize Customize Factor + InitCustomizeFactor(); + + m_idSelTarget = 0; + + //载入个性化限制 + //m_pCustomizeBound = g_CustomizeBoundMgr.GetCustomizeBound("Configs\\CustomizeBound.ini"); + //assert(m_pCustomizeBound); +} + +CECPlayer::~CECPlayer() +{ +} + +//#define _DEBUG_OUTPUT_ACTIONS + +void CECPlayer::BuildActionList() +{ +#ifdef _DEBUG_OUTPUT_ACTIONS + FILE * fpActionList = fopen("actions.txt", "wt"); +#endif + + // Load action names from file + if (!_default_actions) + { + typedef abase::hashtab PlayerActionMap; + typedef abase::hashtab PlayerSkillActionMap; + + int i; + elementdataman * dataman = g_pGame->GetElementDataMan(); + PLAYER_ACTION_INFO_CONFIG * data = NULL; + + // 1. 建立动作名称到 PLAYER_ACTION_INFO_CONFIG 数据的映射,供后续快速查询 + PlayerActionMap actionMap(100); + PlayerSkillActionMap skillActionMap(100); + { + int count = dataman->get_data_num(ID_SPACE_CONFIG); + + DATA_TYPE dt; + int id(0); + + for (i = 0; i < count; ++ i) + { + id = dataman->get_data_id(ID_SPACE_CONFIG, i, dt); + if (dt != DT_PLAYER_ACTION_INFO_CONFIG) + continue; + + data = (PLAYER_ACTION_INFO_CONFIG *) dataman->get_data_ptr(id, ID_SPACE_CONFIG, dt); + + // 建立通用动作名的映射表条目 + if (data->action_name[0] && data->action_name[0] != '0') + { + if (!actionMap.put(data->action_name, data)) + { +#ifdef _DEBUG + AString strTemp; + strTemp.Format("通用动作名重复: action_name = %s, 原id = %d, 新id = %d\n", data->action_name, (*(actionMap.find(data->action_name).value()))->id, data->id); + ::OutputDebugStringA(strTemp); +#endif + } + } + + // 建立技能动作名的映射表条目 + if (data->name[0] && data->name[0] != '0') + { + if (!skillActionMap.put(data->name, data)) + { +#ifdef _DEBUG + ACString strTemp; + strTemp.Format(_AL("技能动作名重复: name = %s, 原id = %d, 新id = %d\n"), data->name, (*(skillActionMap.find(data->name).value()))->id, data->id); + ::OutputDebugString(strTemp); +#endif + } + } + +#ifdef _DEBUG + if (!(data->action_name[0] && data->action_name[0] != '0') && + !(data->name[0] && data->name[0] != '0')) + { + AString strTemp; + strTemp.Format("动作配置表错误,名称为空: id = %d\n", data->id); + ::OutputDebugStringA(strTemp); + } +#endif + } + } + + // 2.处理通用动作 + CECStringTab actionNames; + actionNames.Init("Configs\\actions_player.txt", false); + + _default_actions = new PLAYER_ACTION[ACT_MAX]; + memset(_default_actions, 0, sizeof(PLAYER_ACTION) * ACT_MAX); + + for(i=0; i= ACT_ATTACK_1 && i <= ACT_ATTACK_4 ) + { + for(int n=0; naction_prefix, data->action_weapon_suffix[n].suffix); + fprintf(fpActionList, "%s_%s落\n", data->action_prefix, data->action_weapon_suffix[n].suffix); + } + } + else + { + for(int n=0; naction_prefix, data->action_weapon_suffix[n].suffix); + } + } +#endif + continue; + } + } + + // not found + // a_LogOutput(1, "CECPlayer::CECPlayer(), Failed to find action [%s]'s data", actionNames.GetANSIString(i)); + } + + // 建立特殊动作映射 + if(_turning_actions) delete[] _turning_actions; + _turning_actions = new PLAYER_ACTION[ACT_MAX]; + PlayerActionMap::iterator turning = actionMap.find("自身旋转"); // 查找特殊的动作 + for(i=0; i ACT_REVIVE) && // 复活 + turning != actionMap.end() ) + { + // 用旋转动作替换通用动作 + _turning_actions[i].type = (PLAYER_ACTION_TYPE) i; + _turning_actions[i].data = *turning.value(); + } + else + { + // 使用通用动作 + _turning_actions[i] = _default_actions[i]; + } + } + + // 3.处理技能动作 + unsigned int idSkill = 0; + while(true) + { + idSkill = GNET::ElementSkill::NextSkill(idSkill); + if( idSkill == 0 ) + break; + + const wchar_t *wszName = GNET::ElementSkill::GetName(idSkill); + if (wszName && wszName[0]) + { + PlayerSkillActionMap::iterator it = skillActionMap.find(wszName); + if (it != skillActionMap.end()) + { + _default_skill_actions[idSkill] = data = *it.value(); + +#ifdef _DEBUG_OUTPUT_ACTIONS + for(int n=0; naction_weapon_suffix[n].suffix[0] ) + { + fprintf(fpActionList, "%s_吟唱_%s\n", data->action_prefix, data->action_weapon_suffix[n].suffix); + fprintf(fpActionList, "%s_施放起_%s\n", data->action_prefix, data->action_weapon_suffix[n].suffix); + fprintf(fpActionList, "%s_施放落_%s\n", data->action_prefix, data->action_weapon_suffix[n].suffix); + } + } +#endif + continue; + } + } + + // not found + // a_LogOutput(1, "CECPlayer::BuildActionList(), Failed to find skill action %d!", idSkill); + } + } + +#ifdef _DEBUG_OUTPUT_ACTIONS + fclose(fpActionList); +#endif +} + +// Release object +void CECPlayer::Release() +{ + DetachBuddy(); + + // Clear extend states before model is released + ClearShowExtendStates(); + ::memset(m_aExtStates, 0, sizeof(m_aExtStates)); + m_aIconStates.clear(); + + if (m_pPateName) + { + delete m_pPateName; + m_pPateName = NULL; + } + + if (m_pPateMarry) + { + delete m_pPateMarry; + m_pPateMarry = NULL; + } + + if (m_pPateForce) + { + delete m_pPateForce; + m_pPateForce = NULL; + } + + if (m_pPateTitle) + { + delete m_pPateTitle; + m_pPateTitle = NULL; + } + + if (m_pPateLastWords1) + { + delete m_pPateLastWords1; + m_pPateLastWords1 = NULL; + } + + if (m_pPateLastWords2) + { + delete m_pPateLastWords2; + m_pPateLastWords2 = NULL; + } + + if (m_pPateTeamReq) + { + delete m_pPateTeamReq; + m_pPateTeamReq = NULL; + } + + if (m_pPateBooth) + { + delete m_pPateBooth; + m_pPateBooth = NULL; + } + + if (m_pPateFaction) + { + delete m_pPateFaction; + m_pPateFaction = NULL; + } + + if (m_pFactionDecal) + { + delete m_pFactionDecal; + m_pFactionDecal = NULL; + } + + if (m_pPateCountry) + { + delete m_pPateCountry; + m_pPateCountry = NULL; + } + if (m_pCountryDecal) + { + delete m_pCountryDecal; + m_pCountryDecal = NULL; + } + + if (m_pBubbleTexts) + { + delete m_pBubbleTexts; + m_pBubbleTexts = NULL; + } + + if (m_pLevelUpGFX) + { + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pLevelUpGFX); + m_pLevelUpGFX = NULL; + } + + if (m_pPetCureGFX) + { + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + if (pGFXExMan) + pGFXExMan->CacheReleasedGfx(m_pPetCureGFX); + m_pPetCureGFX = NULL; + m_pPetCureGFXtate = -1; + } + for (int i = 0; i < sizeof(m_pMonsterSpiritGFX) / sizeof(m_pMonsterSpiritGFX[0]); ++i) { + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + if (pGFXExMan) + pGFXExMan->CacheReleasedGfx(m_pMonsterSpiritGFX[i]); + m_pMonsterSpiritGFX[i] = NULL; + } + + if (m_pWaterWaveStill) + { + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pWaterWaveStill); + m_pWaterWaveStill = NULL; + } + + if (m_pWaterWaveMove) + { + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pWaterWaveMove); + m_pWaterWaveMove = NULL; + } + + if (m_pAirBubble) + { + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pAirBubble); + m_pAirBubble = NULL; + } + + if (m_pSwimBubble) + { + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pSwimBubble); + m_pSwimBubble = NULL; + } + + if (m_pTransformGfx) + { + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pTransformGfx); + m_pTransformGfx = NULL; + } + + if (m_pDuelStateGFX) + { + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pDuelStateGFX); + m_pDuelStateGFX = NULL; + } + + if (m_pPetModel) + { + m_pPetModel->RemoveChildModel(_hanger_ride, false); + A3DRELEASE(m_pPetModel); + } + + ClearBoothModel(); + + for (MOEffectMAP::iterator it = m_mapMOEffect.begin();it != m_mapMOEffect.end();++it) + { + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + pGFXExMan->CacheReleasedGfx(it->second); + } + + m_mapMOEffect.clear(); + + // Release face model + ReleaseFaceModel(); + + // Release player model + ReleasePlayerModel(); + + // Clear resource ready flags + SetResReadyFlag(RESFG_ALL, false); + + m_bAboutToDie = false; +} + +bool CECPlayer::LoadBodySkin( + int nBodyID, + int iProfession, + int iGender, + A3DSkin* aSkins[NUM_SKIN_INDEX][3], + A3DShader* pBodyShaders[3], + bool bSimpleModel) +{ + // body skin will be customized later, so they counld't be sharedly loaded by + // A3DSkinMan + + char szBodySkin[256]; + + // ensure the gender is valid + if(iGender != GENDER_MALE) + { + ASSERT(iGender == GENDER_FEMALE); + iGender = GENDER_FEMALE; + } + + const int nMaxBodyID = CECProfConfig::Instance().GetMaxBodyID(iProfession); + if (nMaxBodyID > 0) + { + // use the max value instead of invalid value + if(nBodyID < 0 || nBodyID > nMaxBodyID) + { +#ifndef _PROFILE_MEMORY + ASSERT(false); +#endif + nBodyID = nMaxBodyID; + } + + if( !bSimpleModel ) + sprintf(szBodySkin, _body_skin[iProfession * NUM_GENDER + iGender], nBodyID + 1); + else + sprintf(szBodySkin, _simple_body_skin[iProfession * NUM_GENDER + iGender], nBodyID + 1); + } + else + { + if( !bSimpleModel ) + strncpy(szBodySkin, _body_skin[iProfession * NUM_GENDER + iGender], 256); + else + strncpy(szBodySkin, _simple_body_skin[iProfession * NUM_GENDER + iGender], 256); + } + + if (!LoadPlayerSkin(aSkins[SKIN_BODY_INDEX], SKIN_BODY_INDEX, szBodySkin)) + { + a_LogOutput(1, "CECPlayer::LoadBodySkin, Falied to replace body skin"); + return false; + } + + int n; + for(n=0; n<3; n++) + { + A3DSkin* pBodySkin = aSkins[SKIN_BODY_INDEX][n]; + int i; + + // adjust skin's material to make it a little speculable + int nNumMaterial = pBodySkin->GetMaterialNum(); + for(i=0; iGetMaterial(i)->GetMaterialParam(); + param.Specular = 0xff3a3a3a; + param.Power = 10.0f; + pBodySkin->GetMaterial(i)->SetMaterialParam(param); + } + + A3DSkinMesh* pMesh = NULL; + for(i=0; iGetSkinMeshNum(); i++) + { + if( pBodySkin->GetSkinMesh(i)->GetTextureIndex() == 0 ) + { + // found body mesh + pMesh = pBodySkin->GetSkinMesh(i); + break; + } + } + if (!pMesh) + { + a_LogOutput(1, "CECPlayer::LoadBodySkin, Failed to set body shader"); + return false; + } + + int iTexIndex = pMesh->GetTextureIndex(); + A3DTexture* pTex = pBodySkin->GetTexture(iTexIndex); + char szTextureMap[MAX_PATH]; + strncpy(szTextureMap, pTex->GetMapFile(), MAX_PATH); + + // now load body skin shader + pBodyShaders[n] = glb_LoadBodyShader(pBodySkin, szTextureMap); + if (!pBodyShaders[n]) + a_LogOutput(1, "CECPlayer::LoadBodySkin, Failed to load body shader"); + + if (!pBodySkin->ChangeSkinTexturePtr(iTexIndex, pBodyShaders[n])) + { + a_LogOutput(1, "CECPlayer::LoadBodySkin, Failed to change skin texture"); + return false; + } + } + + // Record head skin file + char szHeadFile[256]; + if( nMaxBodyID > 0 ) + sprintf(szHeadFile, "%s%d", _head_skin[iProfession * NUM_GENDER + iGender], nBodyID + 1); + else + strcpy(szHeadFile, _head_skin[iProfession * NUM_GENDER + iGender]); + + if (!LoadPlayerSkin(aSkins[SKIN_HEAD_INDEX], SKIN_HEAD_INDEX, szHeadFile)) + { + a_LogOutput(1, "CECPlayer::LoadBodySkin, Falied to replace head skin"); + return false; + } + + return true; +} + +bool CECPlayer::LoadBodySkin(int nBodyID, bool bSimpleModel) +{ + A3DSkin* aSkins[NUM_SKIN_INDEX][3] = {0}; + A3DShader* pBodyShaders[3]; + + if (!LoadBodySkin(nBodyID, m_iProfession, m_iGender, aSkins, pBodyShaders, bSimpleModel)) + { + for (int i = 0; i < NUM_SKIN_INDEX; i++) + { + g_pGame->ReleaseA3DSkin(aSkins[i][0]); + g_pGame->ReleaseA3DSkin(aSkins[i][1]); + g_pGame->ReleaseA3DSkin(aSkins[i][2]); + } + return false; + } + + // we must remove the skin from skin model before we can release it. + ReplaceCurSkin(SKIN_BODY_INDEX, NULL); + ReplaceCurSkin(SKIN_HEAD_INDEX, NULL); + + for (int i = 0; i < 3; i++) + { + g_pGame->ReleaseA3DSkin(m_aSkins[SKIN_BODY_INDEX][i]); + g_pGame->ReleaseA3DSkin(m_aSkins[SKIN_HEAD_INDEX][i]); + + m_aSkins[SKIN_BODY_INDEX][i] = aSkins[SKIN_BODY_INDEX][i]; + m_aSkins[SKIN_HEAD_INDEX][i] = aSkins[SKIN_HEAD_INDEX][i]; + + m_pBodyShader[i] = pBodyShaders[i]; + } + + if (GetMajorModel() && !bSimpleModel) + { + GetMajorModel()->ShowSkin(SKIN_HEAD_INDEX, false); + } + + return true; +} + +bool CECPlayer::LoadPlayerSkin( + A3DSkin* aSkins[3], + int iIndex, + const char* szFile) +{ + ASSERT(iIndex >= 0 && iIndex < NUM_SKIN_INDEX); + +// #ifdef _DEBUG +// static int _reentrant = 0; +// assert(_reentrant++ == 0); +// #endif + + ACSWrapper csa(&l_csLoadPlayerSkin); + + memset(aSkins, 0, sizeof(A3DSkin*) * 3); + + // now we make skinman replace skin's texture with a shader automatically + if( iIndex >= SKIN_UPPER_BODY_INDEX && iIndex <= SKIN_FOOT_INDEX ) + { + #ifdef SKIN_BUMP_ENABLE + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("armornormalmap.sdr", A3DSkinMan::SHADERREPLACE_NORMALMAPNEEDED); + #else + // g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("armor.sdr", 0); + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile((const char*)glb_ArmorReplaceShader, A3DSkinMan::SHADERREPLACE_USERDEFINE); + #endif + } + else if( iIndex >= SKIN_FASHION_UPPER_BODY_INDEX && iIndex <= SKIN_FASHION_FOOT_INDEX ) + { + // g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("fashion.sdr", 0); + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile((const char*)glb_FashionReplaceShader, A3DSkinMan::SHADERREPLACE_USERDEFINE); + } + + if( szFile ) + { + const char * suffix1[] = {"一级", "二级", "三级"}; + const char * suffix2[] = {"", "二级", "三级"}; + const char ** pSuffixes; + + if( iIndex == SKIN_BODY_INDEX || iIndex == SKIN_HEAD_INDEX ) + pSuffixes = suffix2; + else + pSuffixes = suffix1; + + char strSkinPath[MAX_PATH]; + for(int i=0; i<3; i++) + { + sprintf(strSkinPath, "%s%s.ski", szFile, pSuffixes[i]); + + A3DSkin * pSkin = g_pGame->LoadA3DSkin(strSkinPath, true); + if (pSkin == NULL) + { + a_LogOutput(1, "CECPlayer::LoadPlayerSkin, Falied to load skin %s", strSkinPath); + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); +// #ifdef _DEBUG +// assert(--_reentrant == 0); +// #endif + return false; + } + + // now set a specific material for armors + if( iIndex >= SKIN_UPPER_BODY_INDEX && iIndex <= SKIN_FOOT_INDEX ) + { + A3DCOLOR specular = ARMOR_SPECULAR; + if( !g_pGame->GetA3DDevice()->TestPixelShaderVersion(1, 1) ) + { + specular = 0xff606060; + } + + int numMaterial = pSkin->GetMaterialNum(); + for(int j=0; jGetMaterial(j)->GetMaterialParam(); + param.Specular = specular; + param.Power = ARMOR_POWER; + pSkin->GetMaterial(j)->SetMaterialParam(param); + } + } + else if( iIndex >= SKIN_FASHION_UPPER_BODY_INDEX && iIndex <= SKIN_FASHION_FOOT_INDEX ) + { + A3DCOLOR specular = FASHION_SPECULAR; + int numMaterial = pSkin->GetMaterialNum(); + for(int j=0; jGetMaterial(j)->GetMaterialParam(); + param.Specular = specular; + param.Power = FASHION_POWER; + pSkin->GetMaterial(j)->SetMaterialParam(param); + } + } + + aSkins[i] = pSkin; + } + } + + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); + +// #ifdef _DEBUG +// assert(--_reentrant == 0); +// #endif + + return true; +} + +// Release player model +void CECPlayer::ReleasePlayerModel() +{ + if( m_pSprite ) + { + m_pSprite->Release(); + delete m_pSprite; + m_pSprite = NULL; + } + + if( m_pGoblin ) + { + m_pGoblin->Release(); + delete m_pGoblin; + m_pGoblin = NULL; + } + + // Release player skins + int i(0); + for (i=0; i < NUM_SKIN_INDEX; i++) + { + g_pGame->ReleaseA3DSkin(m_aSkins[i][0]); + g_pGame->ReleaseA3DSkin(m_aSkins[i][1]); + g_pGame->ReleaseA3DSkin(m_aSkins[i][2]); + m_aSkins[i][0] = m_aSkins[i][1] = m_aSkins[i][2] = NULL; + m_aCurSkins[i] = NULL; + } + + memset(m_pBodyShader, 0, sizeof(m_pBodyShader)); + + //memset(m_aEquips, 0xff, sizeof(m_aEquips)); + + ReleaseWeapon(); + m_pPlayerModel = NULL; + //memset(m_aShapeID, 0, sizeof(m_aShapeID)); + for(i = 0; iRelease(); + delete m_pModels[i]; + } + } + memset(m_pModels, 0, sizeof(m_pModels)); + + m_GfxRecords.clear(); + + if (m_pFaceModel) + m_pFaceModel->SetParent(NULL); + + SetResReadyFlag(RESFG_SKELETON | RESFG_SKIN, false); + + RecreateActionController(); + RecreateBodyController(); +} + +CECFace* CECPlayer::ThreadLoadFaceModel(int nCurCharacter, int nCurGender, int nFaceID) +{ + CECFace* pFaceModel; + + if (!(pFaceModel = new CECFace)) + { + glb_ErrorOutput(ECERR_NOTENOUGHMEMORY, "CECPlayer::ThreadLoadFaceModel", __LINE__); + return NULL; + } + + // load different model's according to the character and gender + bool bval; + bval = pFaceModel->Init(nCurCharacter, nCurGender, nFaceID); + if( !bval ) + { + a_LogOutput(1, "CECPlayer::ThreadLoadFaceModel(), Failed to init face model."); + delete pFaceModel; + return NULL; + } + + return pFaceModel; +} + +// Load player face model +bool CECPlayer::LoadFaceModel(int nCurCharacter, int nCurGender, int nFaceID) +{ + // Release old face model + ReleaseFaceModel(); + if (m_pFaceModel = ThreadLoadFaceModel(nCurCharacter, nCurGender, nFaceID)){ + AttachFaceModel(); + } + return m_pFaceModel != NULL; +} + +// Release player face model +void CECPlayer::ReleaseFaceModel() +{ + if (m_pFaceModel) + { + if (GetMajorModel()){ + GetMajorModel()->RemoveComboModel(m_pFaceModel->GetECModel()); + GetMajorModel()->ShowSkin(SKIN_HEAD_INDEX, true); + } + + m_pFaceModel->Release(); + delete m_pFaceModel; + m_pFaceModel = NULL; + } + +// SetResReadyFlag(RESFG_CUSTOM, false); +} + +void CECPlayer::TransformShape(int iShape, bool bLoadAtOnce/* =false */) +{ + SetShape(iShape); + a_LogOutput(1, "CECPlayer::TransformShape(iShape=%d)(iShapeType=%d,iShapeID=%d)", iShape, PLAYERMODEL_GETTYPE(iShape), PLAYERMODEL_GETID(iShape)); + + if (!GetMajorModel()) return; + + if (IsShapeChanged()) + { + // change to a dummy model, may cause an asynchronous loading + QueueLoadDummyModel(m_iShape, bLoadAtOnce); + } + else + { + // back to major model is a synchronous operation + ApplyShapeModelChange(GetMajorModel()); + } +} + +void CECPlayer::ApplyShapeModelChange(CECModel* pModel) +{ + // store and reset the attach state + int iBuddyId = m_iBuddyId; + bool bHangerOn = m_bHangerOn; + if (m_AttachMode != enumAttachNone) DetachBuddy(NULL, false); + + // logic transform but no model changed + if(!pModel) pModel = GetMajorModel(); + + if(pModel && m_pPlayerModel && m_pPlayerModel != pModel) + { + // sync the position + pModel->SetPos(m_pPlayerModel->GetPos()); + pModel->SetDirAndUp(m_pPlayerModel->GetDir(), m_pPlayerModel->GetUp()); + + // sync wing + CECModel* pWing = m_pPlayerModel->GetChildModel(_wing); + m_pPlayerModel->RemoveChildModel(_wing, false); + + CECModel* pWing2 = m_pPlayerModel->GetChildModel(_wing2); + m_pPlayerModel->RemoveChildModel(_wing2, false); + + if (m_wingType == WINGTYPE_FLYSWORD || m_wingType == WINGTYPE_WING) { + if (pWing) { + pModel->AddChildModel( + _wing, + false, + UsingWing() ? "HH_chibang" : "HH_feijian", + pWing, + UsingWing() ? "CC_chibang" : "CC_feijian"); + } + } else if (m_wingType == WINGTYPE_DOUBLEWHEEL) { + if (pWing) { + pModel->AddChildModel( + _wing, + false, + _hh_left_foot, + pWing, + _cc_fenghuolun); + } + if (pWing2) { + pModel->AddChildModel( + _wing2, + false, + _hh_right_foot, + pWing2, + _cc_fenghuolun); + } + } + ShowWing(IsFlying()); + + // sync the effect + typedef abase::hash_map::iterator GFXIter; + for(GFXIter i=m_GfxRecords.begin();i!=m_GfxRecords.end();++i) + { + const GFXRECORD& rec = i->second; + m_pPlayerModel->RemoveGfx(rec.strPath, rec.strHook); + if (!pModel->GetGfx(rec.strPath, rec.strHook)){ + pModel->PlayGfx(rec.strPath, rec.strHook, rec.fScale); + } + } + } + + // change the target + if (m_pPlayerModel != pModel){ + ClearShowExtendStates(); + if (IsMajorModel(m_pPlayerModel)){ + RemoveEquipGfx(); + } + DetachWeapon(); + m_pPlayerModel = pModel; + AttachWeapon(); + if (pModel != NULL){ + ShowExtendStates(0, m_aExtStates, OBJECT_EXT_STATE_COUNT); + } + if (IsMajorModel(m_pPlayerModel)){ + AddEquipGfx(); + } + RecreateActionController(); + RecreateBodyController(); + } + + // different ground mode + SetUseGroundNormal( ShouldUseGroundNormalForCurrentShapeModel() ); + PlayAction(GetMoveStandAction(false, IsFighting()), true); + + // restore the attach state + if (iBuddyId) + { + if( !bHangerOn ) + AttachBuddy(iBuddyId); + else + { + CECPlayer* pBuddy = m_pPlayerMan->GetPlayer(iBuddyId); + if( pBuddy ) pBuddy->AttachBuddy(m_PlayerInfo.cid); + } + } + ScaleBody(m_fScaleBySkill); + + // 战车升级光效,此时模型已记载,gfx可播放。 + CECHostPlayer* pHost = g_pGame->GetGameRun()->GetHostPlayer(); + if (IsHostPlayer() && pHost && pHost->GetBattleInfo().IsChariotWar()) + { + CChariot* pChariot = pHost->GetChariot(); + if (pChariot && pChariot->IsUpdateState()) + { + PlayGfx(res_GFXFile(RES_GFX_LEVELUP), NULL, 4.0, PLAYERMODEL_TYPEALL); + BubbleText(BUBBLE_LEVELUP, 0); + pChariot->UpdateState(0,0);// 恢复战车升级标志 + } + } +} + +void CECPlayer::AttachBuddy(int iBuddy) +{ + if (!GetMajorModel()) + return; + + if (GetGender() != GENDER_MALE) + return; + + CECPlayer* pBuddy = m_pPlayerMan->GetPlayer(iBuddy); + if (!pBuddy || pBuddy->GetGender() != GENDER_FEMALE) + return; + + CECModel* pBuddyModel = pBuddy->GetPlayerModel(); + if (!pBuddyModel) return; + + if (m_pPetModel) + { + if (!m_pPetModel->AddChildModel(_hanger_ride2, false, _hh_ride, pBuddyModel, _cc_ride2)) + return; + + A3DSkinModelHanger* pHanger = m_pPetModel->GetA3DSkinModel()->GetSkinModelHanger(_hanger_ride2); + if (pHanger) pHanger->SetOffsetMat(a3d_Translate(0, 0, 0)); + pHanger = m_pPetModel->GetA3DSkinModel()->GetSkinModelHanger(_hanger_ride); + if (pHanger) pHanger->SetOffsetMat(a3d_Translate(0, 0, -0.1f)); + + m_AttachMode = enumAttachRideOnPet; + m_bHangerOn = false; + m_iBuddyId = pBuddy->m_PlayerInfo.cid; + pBuddy->m_AttachMode = enumAttachRideOnPet; + pBuddy->m_bHangerOn = true; + pBuddy->m_iBuddyId = m_PlayerInfo.cid; + } + else if (IsShapeChanged()) + { + if (!m_pPlayerModel || !m_pPlayerModel->AddChildModel(_hanger_ride, false, _hh_ride, pBuddyModel, _cc_ride2)) + return; + + A3DSkinModelHanger* pHanger = m_pPlayerModel->GetA3DSkinModel()->GetSkinModelHanger(_hanger_ride); + if (pHanger) pHanger->SetOffsetMat(a3d_Translate(0, 0, 0.0f)); + m_AttachMode = enumAttachRideOnPlayer; + m_bHangerOn = false; + m_iBuddyId = pBuddy->m_PlayerInfo.cid; + pBuddy->m_AttachMode = enumAttachRideOnPlayer; + pBuddy->m_bHangerOn = true; + pBuddy->m_iBuddyId = m_PlayerInfo.cid; + } + else + { + if (!GetMajorModel()->AddChildModel(_hanger_hug, false, _hh_bind, pBuddyModel, _cc_bind)) + return; + + m_AttachMode = enumAttachHugPlayer; + m_bHangerOn = false; + m_iBuddyId = pBuddy->m_PlayerInfo.cid; + pBuddy->m_AttachMode = enumAttachHugPlayer; + pBuddy->m_bHangerOn = true; + pBuddy->m_iBuddyId = m_PlayerInfo.cid; + } + + m_idCandBuddy = 0; + m_bCandHangerOn = false; + pBuddy->m_idCandBuddy = 0; + pBuddy->m_bCandHangerOn = false; + pBuddy->SetPos(GetPos()); + + PlayAction(GetMoveStandAction(false, IsFighting()), true); +} + +void CECPlayer::DetachBuddy(CECPlayer* pBuddy, bool bResetData) +{ + if (m_AttachMode == enumAttachNone) + return; + + if (m_bHangerOn) + { + if (!pBuddy) pBuddy = m_pPlayerMan->GetPlayer(m_iBuddyId); + if (pBuddy) + pBuddy->DetachBuddy(this, bResetData); + else + { + assert(false); + + if( bResetData ) + { + m_AttachMode = enumAttachNone; + m_bHangerOn = false; + m_iBuddyId = 0; + } + } + } + else + { + if (!pBuddy) pBuddy = m_pPlayerMan->GetPlayer(m_iBuddyId); + + if (m_AttachMode == enumAttachRideOnPlayer) + { + if(m_pPlayerModel) m_pPlayerModel->RemoveChildModel(_hanger_ride, false); + } + else if (m_AttachMode == enumAttachHugPlayer) + { + if(m_pPlayerModel) m_pPlayerModel->RemoveChildModel(_hanger_hug, false); + } + else if (m_AttachMode == enumAttachRideOnPet) + { + if (m_pPetModel) + { + A3DSkinModelHanger* pHanger = m_pPetModel->GetA3DSkinModel()->GetSkinModelHanger(_hanger_ride); + if (pHanger) pHanger->SetOffsetMat(a3d_IdentityMatrix()); + m_pPetModel->RemoveChildModel(_hanger_ride2, false); + } + } + + if( bResetData ) + { + m_AttachMode = enumAttachNone; + m_bHangerOn = false; + m_iBuddyId = 0; + } + + if (pBuddy) + { + if( bResetData ) + { + pBuddy->m_AttachMode = enumAttachNone; + pBuddy->m_bHangerOn = false; + pBuddy->m_iBuddyId = 0; + } + + pBuddy->SetPos(pBuddy->GetPos()); + pBuddy->ChangeModelMoveDirAndUp(pBuddy->GetDir(), pBuddy->GetUp()); + pBuddy->PlayAction(GetMoveStandAction(false, IsFighting()), true); + } + PlayAction(GetMoveStandAction(IsPlayingMoveAction(), IsFighting()), true); + } +} + +// Load player's skeleton +bool CECPlayer::LoadPlayerSkeleton(bool bAtOnce) +{ + // remove disabled equipments before loading + int aEnabledEquips[SIZE_ALL_EQUIPIVTR]; + for (__int64 i=0; i < SIZE_ALL_EQUIPIVTR; i++) + { + aEnabledEquips[i] = + ((1 << i) & m_i64EquipDisabled) == 1 ? 0 : m_aEquips[i]; + } + + const char* szPetPath = NULL; + if (m_RidingPet.id) + szPetPath = GetRidingPetFileName(m_RidingPet.id); + + EC_PLAYERLOADRESULT Ret; + int aEquips[IVTRSIZE_EQUIPPACK]; + __int64 EquipMask = 0; + if (aEnabledEquips[EQUIPIVTR_WEAPON] > 0) + EquipMask |= 1 << EQUIPIVTR_WEAPON; + if (aEnabledEquips[EQUIPIVTR_FASHION_WEAPON] > 0) + EquipMask |= 1 << EQUIPIVTR_FASHION_WEAPON; + memcpy(aEquips, aEnabledEquips, sizeof(aEquips)); + DecideWeaponLoad(aEquips, EquipMask); + if (bAtOnce || !IsLoadThreadReady()) + { + // Under normal circumstances, only HostPlayer can reach here + if (!LoadPlayerModel(m_iProfession, m_iGender, m_CustomizeData.bodyID, aEquips, szPetPath, Ret,false,false)) + { + a_LogOutput(1, "CECPlayer::Init, failed to call LoadPlayerModel() !"); + return false; + } + + SetPlayerLoadedResult(Ret); + + if(IsShapeChanged() && !QueueLoadDummyModel(m_iShape, true)) + { + // ignore the dummy model loading failure + a_LogOutput(1, "CECPlayer::Init, failed to call QueueLoadDummyModel() !"); + } + } + else + { + int iType = m_iCID == OCID_HOST_NAVIGATER ? MTL_ECM_HOST_NAVIGATER : MTL_ECM_PLAYER; + QueueECModelForLoad( + iType, + m_PlayerInfo.cid, + m_dwBornStamp, + GetPos(), + szPetPath, + m_iProfession, + m_iGender, + m_CustomizeData.bodyID, + aEquips); + + if(IsShapeChanged()) + QueueLoadDummyModel(m_iShape, false); + } + + return true; +} + +bool CECPlayer::QueueLoadEquips(const int* pEquips, __int64 Mask, bool bAtOnce) +{ + if (bAtOnce || !IsLoadThreadReady()) + { + EquipsLoadResult Ret; + + if (!LoadPlayerEquips(m_iProfession, m_iGender, Mask, pEquips, Ret)) + return false; + + SetEquipsLoadedResult(Ret, true); + } + else + { + QueueECModelForLoad( + MTL_ECM_PLAYER_EQUIP, + m_PlayerInfo.cid, + m_dwBornStamp, + GetPos(), + NULL, + m_iProfession, + m_iGender, + Mask, + const_cast(pEquips)); + } + + return true; +} + +bool CECPlayer::QueueLoadPetModel(const char* szPetPath, bool bAtOnce) +{ + if (bAtOnce || !IsLoadThreadReady()) + { + CECModel* pPetModel = NULL; + + if (!LoadPetModel(szPetPath, &pPetModel) || !pPetModel) + return false; + + if (!SetPetLoadResult(pPetModel)) + { + A3DRELEASE(pPetModel); + return false; + } + } + else + { + QueueECModelForLoad(MTL_ECM_PET, m_PlayerInfo.cid, m_dwBornStamp, GetPos(), szPetPath); + } + + return true; +} + +bool CECPlayer::QueueLoadFace(bool bAtOnce) +{ + if( bAtOnce || !IsLoadThreadReady() ) + { + EC_PLAYERLOADRESULT Ret; + memset(&Ret, 0, sizeof(Ret)); + Ret.dwValidMask |= PLAYERLOADRESULT_PLAYERFACE; + Ret.pFaceModel = ThreadLoadFaceModel(m_iProfession, m_iGender, m_CustomizeData.bodyID); + if( !Ret.pFaceModel ) + return false; + + if( !SetPlayerLoadedResult(Ret) ) + { + A3DRELEASE(Ret.pFaceModel); + return false; + } + } + else + { + QueueECModelForLoad(MTL_ECM_PLAYER_FACE, m_PlayerInfo.cid, m_dwBornStamp, GetPos(), NULL, + m_iProfession, m_iGender, m_CustomizeData.bodyID); + } + + return true; +} + +bool CECPlayer::QueueLoadDummyModel(int iShape, bool bAtOnce) +{ + int iShapeType = PLAYERMODEL_GETTYPE(iShape); + int iShapeID = PLAYERMODEL_GETID(iShape); + + // ensure this function only used in dummy model loading + ASSERT(iShapeType != PLAYERMODEL_MAJOR && iShapeType < PLAYERMODEL_MAX); + + // If model already loaded, use it directly. + if(m_aShapeID[iShapeType] == iShapeID && m_pModels[iShapeType]) + { + ApplyShapeModelChange(m_pModels[iShapeType]); + return true; + } + + // try to load a new dummy model + if (bAtOnce || !IsLoadThreadReady()) + { + CECModel* pDummyModel = NULL; + + if (!LoadDummyModel(iShape, &pDummyModel) || !pDummyModel) + return false; + + if (!SetDummyLoadResult(iShape, pDummyModel)) + { + A3DRELEASE(pDummyModel); + return false; + } + } + else + { + int iType = (m_iCID == OCID_HOST_NAVIGATER) ? MTL_ECM_HOST_NAVIGATER_DUMMY:MTL_ECM_PLAYER_DUMMY; + QueueECModelForLoad( + iType, + m_PlayerInfo.cid, + m_dwBornStamp, + GetPos(), + NULL, + iShape); + } + + return true; +} + + +void CECPlayer::RideOnPet(int id, unsigned short color) +{ + m_CandPet.id = id; + m_CandPet.color = color; + m_RidingPet.id = id; + m_RidingPet.color = color; +} + +void CECPlayer::GetOffPet(bool bResetData) +{ + int iBuddyId = 0; + m_CandPet.Reset(); + + SetNamePos(A3DVECTOR3(0.0f)); + + if (m_AttachMode != enumAttachNone) + { + iBuddyId = m_iBuddyId; + DetachBuddy(NULL, bResetData); + } + + if (m_pPetModel) + { + m_pPetModel->RemoveChildModel(_hanger_ride, false); + A3DRELEASE(m_pPetModel); + } + + if( bResetData ) + m_RidingPet.Reset(); + + // Re-calculate player's AABB + CalcPlayerAABB(); + SetUseGroundNormal(ShouldUseGroundNormalForCurrentShapeModel()); + SetPos(GetPos()); + + if (iBuddyId) + AttachBuddy(iBuddyId); + else + PlayAction(GetMoveStandAction(false, IsFighting()), true); +} + +#define SET_SKIN_SHOW_MASK(index, b) \ +{ \ + dwSkinShowMask |= (1 << (index)); \ + if (b) \ + dwSkinShowFlag |= (1 << (index)); \ + else \ + dwSkinShowFlag &= ~(1 << (index)); \ +} + +inline void _get_skin_show_mask( + DWORD& dwChangeMask, + int& nLowerMethod, + DWORD& dwSkinShowMask, + DWORD& dwSkinShowFlag, + bool bFashion) +{ + + for (int i = enumSkinShowUpperBody; i <= enumSkinShowUpperLowerAndWrist; i++) + { + if ((1 << i) & dwChangeMask) + { + switch (i) + { + case enumSkinShowUpperBody: + if (bFashion) + { + SET_SKIN_SHOW_MASK(SKIN_FASHION_WRIST_INDEX, true) + SET_SKIN_SHOW_MASK(SKIN_FASHION_LOWER_INDEX, true) + + if (nLowerMethod == enumSkinShowLowerAndFoot) + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, false) + else + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, true) + } + else + { + SET_SKIN_SHOW_MASK(SKIN_WRIST_INDEX, true) + SET_SKIN_SHOW_MASK(SKIN_LOWER_INDEX, true) + + if (nLowerMethod == enumSkinShowLowerAndFoot) + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, false) + else + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, true) + } + break; + case enumSkinShowWrist: + break; + case enumSkinShowLowerBody: + nLowerMethod = enumSkinShowLowerBody; + if (bFashion) + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, true) + else + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, true) + break; + case enumSkinShowFoot: + break; + case enumSkinShowUpperAndLower: + if (bFashion) + { + SET_SKIN_SHOW_MASK(SKIN_FASHION_WRIST_INDEX, true) + SET_SKIN_SHOW_MASK(SKIN_FASHION_LOWER_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, true) + } + else + { + SET_SKIN_SHOW_MASK(SKIN_WRIST_INDEX, true) + SET_SKIN_SHOW_MASK(SKIN_LOWER_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, true) + } + break; + case enumSkinShowUpperAndWrist: + if (bFashion) + { + SET_SKIN_SHOW_MASK(SKIN_FASHION_WRIST_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_FASHION_LOWER_INDEX, true) + + if (nLowerMethod == enumSkinShowLowerAndFoot) + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, false) + else + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, true) + } + else + { + SET_SKIN_SHOW_MASK(SKIN_WRIST_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_LOWER_INDEX, true) + + if (nLowerMethod == enumSkinShowLowerAndFoot) + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, false) + else + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, true) + } + break; + case enumSkinShowLowerAndFoot: + nLowerMethod = enumSkinShowLowerAndFoot; + if (bFashion) + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, false) + else + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, false) + break; + case enumSkinShowUpperLowerAndWrist: + if (bFashion) + { + SET_SKIN_SHOW_MASK(SKIN_FASHION_WRIST_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_FASHION_LOWER_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_FASHION_FOOT_INDEX, true) + } + else + { + SET_SKIN_SHOW_MASK(SKIN_WRIST_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_LOWER_INDEX, false) + SET_SKIN_SHOW_MASK(SKIN_FOOT_INDEX, true) + } + break; + } + } + } +} + +bool CECPlayer::SetEquipsLoadedResult(EquipsLoadResult& Result, bool bUpdateAtOnce) +{ + if (!GetMajorModel()) + return false; + + A3DSkinModel* pA3DModel = GetMajorModel()->GetA3DSkinModel(); + DWORD dwSkinShowMask = 0, dwSkinShowFlag = 0; + + _get_skin_show_mask(Result.dwShowMask, m_nLowerEquipMethod, dwSkinShowMask, dwSkinShowFlag, false); + _get_skin_show_mask(Result.dwFashionShowMask, m_nLowerFashionEquipMethod, dwSkinShowMask, dwSkinShowFlag, true); + + if( !ShouldUseClothedModel() ) + { + for (int i = 0; i < NUM_SKIN_INDEX; i++) + { + if( i == SKIN_HEAD_INDEX || i == SKIN_BODY_INDEX ) + continue; + + if( Result.aSkins[i][0] ) g_pGame->ReleaseA3DSkin(Result.aSkins[i][0]); + if( Result.aSkins[i][1] ) g_pGame->ReleaseA3DSkin(Result.aSkins[i][1]); + if( Result.aSkins[i][2] ) g_pGame->ReleaseA3DSkin(Result.aSkins[i][2]); + + Result.aSkins[i][0] = Result.aSkins[i][1] = Result.aSkins[i][2] = NULL; + } + } + else + { + for (int i = 0; i < NUM_SKIN_INDEX; i++) + { + DWORD dwMask = (1 << i); + + if (dwMask & Result.dwSkinChangeMask) + { + ReplaceCurSkin(i, NULL); + + g_pGame->ReleaseA3DSkin(m_aSkins[i][0]); + g_pGame->ReleaseA3DSkin(m_aSkins[i][1]); + g_pGame->ReleaseA3DSkin(m_aSkins[i][2]); + + m_aSkins[i][0] = Result.aSkins[i][0]; + m_aSkins[i][1] = Result.aSkins[i][1]; + m_aSkins[i][2] = Result.aSkins[i][2]; + + for (int j = 0; j < 3; j++) + { + if (NULL != m_aSkins[i][j]) + m_aSkins[i][j]->SetAlphaSortWeight(_skin_alpha_map[i]); + } + } + + if (dwMask & dwSkinShowMask) + pA3DModel->ShowSkin(i, (dwMask & dwSkinShowFlag) != 0); + } + } + + if (bUpdateAtOnce) + UpdateCurSkins(); + + if (Result.bWingChanged) + { + m_wingType = Result.wingType; + m_pPlayerModel->RemoveChildModel(_wing); + m_pPlayerModel->RemoveChildModel(_wing2); + + if (m_wingType == WINGTYPE_FLYSWORD || m_wingType == WINGTYPE_WING) { + if (Result.pWing) { + m_pPlayerModel->AddChildModel( + _wing, + false, + UsingWing() ? "HH_chibang" : "HH_feijian", + Result.pWing, + UsingWing() ? "CC_chibang" : "CC_feijian"); + } + } else if (m_wingType == WINGTYPE_DOUBLEWHEEL) { + if (Result.pWing) { + m_pPlayerModel->AddChildModel( + _wing, + false, + _hh_left_foot, + Result.pWing, + _cc_fenghuolun); + } + if (Result.pWing2) { + m_pPlayerModel->AddChildModel( + _wing2, + false, + _hh_right_foot, + Result.pWing2, + _cc_fenghuolun); + } + } + + if (Result.pWing && Result.pWing->GetA3DSkinModel()) + { + Result.pWing->GetA3DSkinModel()->SetAlphaSortID(m_PlayerInfo.cid); + Result.pWing->GetA3DSkinModel()->SetAlphaSortWeight(SKIN_SORT_WING); + } + + if (Result.pWing2 && Result.pWing2->GetA3DSkinModel()) + { + Result.pWing2->GetA3DSkinModel()->SetAlphaSortID(m_PlayerInfo.cid); + Result.pWing2->GetA3DSkinModel()->SetAlphaSortWeight(SKIN_SORT_WING); + } + + if (Result.pWing || Result.pWing2) + { + // HACK: ensure weapon added after wings to avoid alpha sort bug + if(!Result.bWeaponChanged && !Result.bFashionWeaponChanged){ + DetachWeapon(); + } + } + } + + ShowWing(IsFlying()); + + if (Result.bWeaponChanged || Result.bFashionWeaponChanged) + { + if (Result.bFashionWeaponChanged) + { + m_iFashionWeaponType = Result.uFashionAttackType; + m_strLeftFashionWeapon = Result.strLeftFashionWeapon; + m_strRightFashionWeapon = Result.strRightFashionWeapon; + } + if (Result.bWeaponChanged) + { + m_uAttackType = Result.uAttackType; + m_strLeftWeapon = Result.strLeftWeapon; + m_strRightWeapon = Result.strRightWeapon; + } + SetWeaponResult(Result); + }else if (Result.bWingChanged && (Result.pWing || Result.pWing2)){ + AttachWeapon(); + } + + int idFullSuite = SearchFullSuite(); + if( idFullSuite == 0 ) + { + RemoveFullSuiteGFX(); + m_idFullSuite = 0; + } + else if( idFullSuite != m_idFullSuite ) + { + RemoveFullSuiteGFX(); + m_idFullSuite = idFullSuite; + AddFullSuiteGFX(); + } + + if( Result.stoneUpperBody != 0xff && m_stoneUpperBody != Result.stoneUpperBody ) + { + RemoveUpperBodyStones(); + m_stoneUpperBody = Result.stoneUpperBody; + AddUpperBodyStones(); + } + if( Result.stoneWrist != 0x00ff && m_stoneWrist != Result.stoneWrist ) + { + RemoveWristStones(); + m_stoneWrist = Result.stoneWrist; + AddWristStones(); + } + if( Result.stoneLowerBody != 0x00ff && m_stoneLowerBody != Result.stoneLowerBody ) + { + RemoveLowerBodyStones(); + m_stoneLowerBody = Result.stoneLowerBody; + AddLowerBodyStones(); + } + if( Result.stoneFoot != 0x00ff && m_stoneFoot != Result.stoneFoot ) + { + RemoveFootStones(); + m_stoneFoot = Result.stoneFoot; + AddFootStones(); + } + if( Result.stoneWeapon != 0x00ff ) + { + m_stoneWeaponShown = 0; + m_stoneWeapon = Result.stoneWeapon; + AddWeaponStones(); + }else if (IsShowFashionWeapon()){ + m_stoneWeapon = 0; + m_stoneWeaponShown = 0; + } + ScaleChildModel(); + return true; +} + +void CECPlayer::SetRidingPetColor(unsigned short clr) +{ + m_RidingPet.color = clr; + + A3DCOLOR clrModel; + if (!m_RidingPet.GetColor(clrModel)) + clrModel = RIDINGPET::GetDefaultColor(); + + SetRidingPetColor(m_pPetModel, clrModel); +} + +void CECPlayer::SetRidingPetColor(CECModel *pModel, A3DCOLOR clr) +{ + // 设置修改模型上的颜色 + + while (true) + { + if (!pModel) break; + + A3DSkinModel *pSkinModel = pModel->GetA3DSkinModel(); + if (!pSkinModel) break; + + for (int i = 0; i < pSkinModel->GetSkinNum(); ++ i) + { + A3DSkin *pSkin = pSkinModel->GetA3DSkin(i); + if (!pSkin) continue; + for(int idTex=0; idTexGetTextureNum(); idTex ++) + { + A3DTexture * pTex = pSkin->GetTexture(idTex); + if( pTex && pTex->IsShaderTexture() ) + { + A3DShader * pShader = (A3DShader *) pTex; + if (strstr(pShader->GetMapFile(), "rewu.sdr")) + pShader->GetGeneralProps().dwTFactor = clr; + } + } + } + + break; + } +} + + +bool CECPlayer::SetPetLoadResult(CECModel* pPetModel) +{ + ASSERT(pPetModel); + + if (!m_RidingPet.id || !GetMajorModel() || m_CandPet.id || !pPetModel) + return false; + + if (m_pPetModel) + { + RIDINGPET pet = m_RidingPet; + GetOffPet(); + m_RidingPet = pet; + } + + const A3DVECTOR3 vCurPos = GetPos(); + + m_aabbServer.Center = vCurPos + A3DVECTOR3(0.0f, m_aabbServer.Extents.y, 0.0f); + m_aabbServer.CompleteMinsMaxs(); + + m_pPetModel = pPetModel; + + // 将宠物颜色设置到模型上 + SetRidingPetColor(m_RidingPet.color); + + SetUseGroundNormal(true); + m_pPetModel->SetAffectedByParent(false); + m_pPetModel->SetPos(vCurPos); + m_pPetModel->SetDirAndUp(GetDir(), GetUp()); + + int iIndex; + A3DSkeletonHook* pHook = GetMajorModel()->GetA3DSkinModel()->GetSkeleton()->GetHook(_cc_ride, &iIndex); + if (pHook) pHook->SetFixDirFlag(true); + + int iBuddyId = m_iBuddyId; + if (m_AttachMode != enumAttachNone) + DetachBuddy(); + + m_pPetModel->AddChildModel(_hanger_ride, false, _hh_ride, GetMajorModel(), _cc_ride); + m_pPetModel->GetA3DSkinModel()->Update(0); + + if (iBuddyId) + AttachBuddy(iBuddyId); + else + PlayAction(GetMoveStandAction(false, IsFighting()), true); + + return true; +} + +// Set loaded model to player object, this function is used in multithread loading process +bool CECPlayer::SetPlayerLoadedResult(EC_PLAYERLOADRESULT& Ret) +{ + int i; + + if(!ShouldUseModel()) + return false; + + // model loading + if(Ret.dwValidMask & PLAYERLOADRESULT_PLAYERMODEL) + { + if (GetMajorModel()) + return false; + + // set the major model + m_pPlayerModel = Ret.pPlayerModel; + m_pPlayerModel->SetAffectedByParent(false); + m_pPlayerModel->GetA3DSkinModel()->SetInheritTransFlag(false); + m_pModels[PLAYERMODEL_MAJOR] = m_pPlayerModel; + m_GfxRecords.clear(); + + RecreateActionController(); + RecreateBodyController(); + } + + // dummy model loading + if(Ret.dwValidMask & PLAYERLOADRESULT_DUMMYMODEL) + { + if( !SetDummyLoadResult(Ret.iShape, Ret.pDummyModel) ) + A3DRELEASE(Ret.pDummyModel); + } + + // pet model loading + if(Ret.dwValidMask & PLAYERLOADRESULT_PETMODEL) + { + if (Ret.pPetModel && !SetPetLoadResult(Ret.pPetModel)) + A3DRELEASE(Ret.pPetModel); + } + + // player model loading + if(Ret.dwValidMask & PLAYERLOADRESULT_PLAYERMODEL) + { + if (!m_pPetModel) + { + m_pPlayerModel->SetPos(GetPos()); + m_pPlayerModel->SetDirAndUp(GetDir(), GetUp()); + m_pPlayerModel->GetA3DSkinModel()->Update(0); + SetUseGroundNormal(ShouldUseGroundNormalForCurrentShapeModel()); + // make the player stand at beginning + PlayAction(ACT_STAND, true, 0); + } + + for (i = 0; i < SIZE_ALL_EQUIPIVTR; i++) + { + if (m_aEquips[i] > 0 && IsShownEquip(i) && GetMajorModel()) + { + DWORD dwRealID = GetRealElementID(i, m_aEquips[i]); + GetMajorModel()->OnScriptChangeEquip(dwRealID, 1, false); + } + } + } + + // equips model loading + if(Ret.dwValidMask & PLAYERLOADRESULT_EQUIPMODEL) + { + bool bOnlyEquip = (0 == (Ret.dwValidMask & (~PLAYERLOADRESULT_EQUIPMODEL))); + if(!SetEquipsLoadedResult(Ret.EquipResult, bOnlyEquip) && bOnlyEquip) + { + return false; + } + } + + // player model loading + if(Ret.dwValidMask & PLAYERLOADRESULT_PLAYERMODEL) + { + ReplaceCurSkin(SKIN_BODY_INDEX, NULL); + ReplaceCurSkin(SKIN_HEAD_INDEX, NULL); + + for (i = 0; i < 3; i++) + { + g_pGame->ReleaseA3DSkin(m_aSkins[SKIN_BODY_INDEX][i]); + g_pGame->ReleaseA3DSkin(m_aSkins[SKIN_HEAD_INDEX][i]); + m_aSkins[SKIN_BODY_INDEX][i] = Ret.EquipResult.aSkins[SKIN_BODY_INDEX][i]; + m_aSkins[SKIN_HEAD_INDEX][i] = Ret.EquipResult.aSkins[SKIN_HEAD_INDEX][i]; + m_pBodyShader[i] = Ret.pBodyShaders[i]; + } + + if (ShouldUseFaceModel()){ + SetFaceModel(Ret.pFaceModel); + }else{ + SetFaceModel(NULL); + A3DRELEASE(Ret.pFaceModel); + } + + // Update some customized data + SetBodyColor(m_CustomizeData.colorBody); + UpdateBodyScales(); + + // Set model's position and orientation + m_pPlayerModel->SetPos(GetPos()); + m_pPlayerModel->SetDirAndUp(GetDir(), GetUp()); + + // Update the transparent level + SetTransparent(GetTransparentLimit()); + + // Set skin model ID, so same ID can deal with the aphla sort in same space + if (NULL != m_pPlayerModel->GetA3DSkinModel()) + { + m_pPlayerModel->GetA3DSkinModel()->SetAlphaSortID(m_PlayerInfo.cid); + m_pPlayerModel->GetA3DSkinModel()->SetAlphaSortWeight(SKIN_SORT_DEFAULT); + } + + // Force to update model once completely to avoid rendering error + m_pPlayerModel->SetAutoUpdateFlag(false); + m_pPlayerModel->Tick(1); + m_pPlayerModel->SetAutoUpdateFlag(true); + if( m_pFaceModel ) m_pFaceModel->Tick(1); + + m_dwResFlags = RESFG_ALL; + OnAllResourceReady(); + UpdateCurSkins(); + UpdateGodEvilSprite(); + + // 被CECMemSimplify重新加载模型时,通知男方重新绑定相依相偎 + if( m_iBuddyId ) + { + if( m_bHangerOn ) + { + CECPlayer* pPlayer = m_pPlayerMan->GetPlayer(m_iBuddyId); + if( pPlayer ) + pPlayer->m_idCandBuddy = m_PlayerInfo.cid; + } + else + m_idCandBuddy = m_iBuddyId; + } + } + + // 加载玩家的自定义脸数据 + if( Ret.dwValidMask & PLAYERLOADRESULT_PLAYERFACE ){ + if( !ShouldUseFaceModel() || !GetMajorModel() ){ + A3DRELEASE(Ret.pFaceModel); + }else{ + SetFaceModel(Ret.pFaceModel); + } + } + + return true; +} + +void CECPlayer::SetFaceModel(CECFace *pFaceModel){ + if (pFaceModel == m_pFaceModel){ + return; + } + ReleaseFaceModel(); + if (!pFaceModel){ + return; + } + m_pFaceModel = pFaceModel; + if (m_bIsChangingFace){ + m_pFaceModel->SetFaceData(m_CustomizeData.faceData); + }else{ + if( m_CustomizeData.faceData.idFaceTex == 0 && (m_iProfession != PROF_ORC || m_iGender != GENDER_MALE) ){ + m_CustomizeData.faceData = *m_pFaceModel->GetFaceData(); + m_pFaceModel->UpdateAll(); + }else{ + m_pFaceModel->SetFaceData(m_CustomizeData.faceData); + } + if (InFashionMode()){ + UpdateHairModel(true, m_aEquips[EQUIPIVTR_FASHION_HEAD]); + }else{ + UpdateHairModel(true, m_aEquips[EQUIPIVTR_HEAD]); + } + } + AttachFaceModel(); +} + +void CECPlayer::AttachFaceModel(){ + if (!m_pFaceModel){ + return; + } + if (m_pFaceModel->GetA3DSkinModel()){ + m_pFaceModel->GetA3DSkinModel()->SetAlphaSortID(m_PlayerInfo.cid); + m_pFaceModel->GetA3DSkinModel()->SetAlphaSortWeight(SKIN_SORT_HEAD); + } + if (GetMajorModel()){ + m_pFaceModel->SetParent(GetMajorModel()->GetA3DSkinModel()); + GetMajorModel()->ShowSkin(SKIN_HEAD_INDEX, false); + if (GetMajorModel()->FindComboModel(m_pFaceModel->GetECModel()) < 0){ + GetMajorModel()->AddComboModel(m_pFaceModel->GetECModel()); + } + } +} + +bool CECPlayer::SetDummyLoadResult(int iShape, CECModel* pModel) +{ + int iShapeType = PLAYERMODEL_GETTYPE(iShape); + int iShapeID = PLAYERMODEL_GETID(iShape); + a_LogOutput(1, "CECPlayer::SetDummyLoadResult(iShape=%d)(iShapeType=%d,iShapeID=%d)", iShape, iShapeType, iShapeID); + + if (iShapeType == PLAYERMODEL_MAJOR || + iShapeType < 0 || iShapeType >= PLAYERMODEL_MAX){ + a_LogOutput(1, "ApplyShapeModelChange invalid(iShapeType=%d, iShapeID=%d)", iShapeType, iShapeID); + ASSERT(false); + return false; + } + if (iShape != m_iShape){ // 2014-8-4:频繁变身时,资源加载落后于m_iShape变化(后者在处理协议时即修改),此时,丢弃结果 + a_LogOutput(1, "ApplyShapeModelChange ignored cause shape mismatch(m_iShape=%d, iShapeType=%d, iShapeID=%d)", m_iShape, iShapeType, iShapeID); + return false; + } + + CECModel* pOldModel = m_pModels[iShapeType]; + if(pOldModel != pModel) + { + m_pModels[iShapeType] = pModel; + m_aShapeID[iShapeType] = iShapeID; + if(pModel) pModel->SetAffectedByParent(false); + } + + ApplyShapeModelChange(pModel); + + if (pOldModel && pOldModel != pModel && pOldModel != GetPlayerModel()){ + if (IsLoadThreadReady()){ + QueueECModelForRelease(pOldModel); + }else{ + pOldModel->Release(); + delete pOldModel; + } + } + + return true; +} + +bool CECPlayer::UpdateGodEvilSprite() +{ + // 简单模型不显示小精灵 + if( !ShouldUseClothedModel()) + return false; + + // first determine new sprite id. + CECSprite::SPRITE_ID idSprite = CECSprite::SPRITE_NULL; + if( GetBasicProps().iLevel2 >= 20 ) + { + switch(GetBasicProps().iLevel2) + { + case 20: + idSprite = CECSprite::SPRITE_GOD1; + break; + case 21: + idSprite = CECSprite::SPRITE_GOD2; + break; + case 22: + idSprite = CECSprite::SPRITE_GOD3; + break; + + case 30: + idSprite = CECSprite::SPRITE_EVIL1; + break; + case 31: + idSprite = CECSprite::SPRITE_EVIL2; + break; + case 32: + idSprite = CECSprite::SPRITE_EVIL3; + break; + } + } + + if( m_pSprite && idSprite != m_pSprite->GetSpriteID() ) + { + // first try to release old ones. + if( m_pSprite ) + { + m_pSprite->Release(); + delete m_pSprite; + m_pSprite = NULL; + } + } + + if( NULL == m_pSprite && idSprite != CECSprite::SPRITE_NULL ) + { + m_pSprite = new CECSprite(); + if( !m_pSprite->Init(idSprite, this) ) + { + m_pSprite->Release(); + delete m_pSprite; + m_pSprite = NULL; + a_LogOutput(1, "CECPlayer::UpdateGodEvilSprite(), failed to load sprite!"); + } + else + m_pSprite->SetPos(GetPos() + A3DVECTOR3(0.0f, 2.5f, 0.0f)); + } + + return true; +} + +bool CECPlayer::UpdateGoblin() +{ + + return true; +} + +// Render goblin or sprite +void CECPlayer::RenderGoblinOrSprite(CECViewport* pViewport) +{ + CECHostPlayer* pHost = g_pGame->GetGameRun()->GetHostPlayer(); + if (pHost && pHost->GetBattleInfo().IsChariotWar()){ + return; // 战车战场里不显示精灵 + } + + if(m_pGoblin && m_pSprite) + { + if( IsFighting()) + { + m_pGoblin->SetTransparent(0.0f); + m_pGoblin->Render(pViewport); + } + else + { + if(m_bRenderGoblin) + m_pGoblin->Render(pViewport); + else + m_pSprite->Render(pViewport); + } + } + else if(m_pGoblin) + { + m_GoblinRenderCnt.Reset(); + m_pGoblin->SetTransparent(0.0f); + m_pGoblin->Render(pViewport); + } + else if(m_pSprite) + { + m_GoblinRenderCnt.Reset(); + m_pSprite->SetTransparent(0.0f); + m_pSprite->Render(pViewport); + } +} + +inline void _fade_out_gfx(A3DGFXEx*& pGfx, float fDeltaTime) +{ + if (!pGfx) return; + + if (pGfx->GetState() != ST_STOP) + { + float fAlpha = pGfx->GetAlpha(); + fAlpha -= fDeltaTime; + + if (fAlpha <= 0) + { + pGfx->Stop(); + // g_pGame->GetA3DGFXExMan()->CacheReleasedGfx(pGfx); + g_pGame->GetGFXCaster()->ReleaseGFXEx(pGfx); + pGfx = NULL; + } + else + pGfx->SetAlpha(fAlpha); + } + else + { + // g_pGame->GetA3DGFXExMan()->CacheReleasedGfx(pGfx); + g_pGame->GetGFXCaster()->ReleaseGFXEx(pGfx); + pGfx = NULL; + } +} + +// Tick routine +bool CECPlayer::Tick(DWORD dwDeltaTime) +{ + CECObject::Tick(dwDeltaTime); + if (m_pBodyController){ + m_pBodyController->Tick(dwDeltaTime); + } + + DWORD dwRealTime = g_pGame->GetRealTickTime(); + + UpdateCurSkins(); + UpdateBoothModel(); + UpdateBoothBar(); + + // Update last said words + if (m_pPateLastWords1 && m_pPateLastWords1->GetItemNum()) + { + if (m_strLastSayCnt.IncCounter(dwDeltaTime)) + { + // Clear string + m_strLastSayCnt.Reset(); + m_pPateLastWords1->Clear(); + m_pPateLastWords2->Clear(); + } + else + { + m_pPateLastWords1->Tick(dwDeltaTime); + m_pPateLastWords2->Tick(dwDeltaTime); + } + } + + A3DTerrainWater* pWater = g_pGame->GetGameRun()->GetWorld()->GetTerrainWater(); + float fDeltaTime = dwDeltaTime / 1000.0f; + + A3DVECTOR3 vPos = GetPos(); + float fWaterHei = pWater->GetWaterHeight(vPos); + float fGrndHei = g_pGame->GetGameRun()->GetWorld()->GetTerrainHeight(vPos); + float fWaterOff = vPos.y - fWaterHei; + + if (fWaterHei > fGrndHei + .3f && + ((fWaterOff > -10.f && fWaterOff <= 0) || (fWaterOff > 0 && fWaterOff < 10.0f && IsFlying()))) + { + A3DMATRIX4 mat; + + if (pWater->IsUnderWater(g_pGame->GetViewport()->GetA3DCamera()->GetPos())) + vPos.y = fWaterHei - .01f; + else + vPos.y = fWaterHei + .01f; + + A3DVECTOR3 vDir = GetDir(); + vDir.y = 0; + vDir.Normalize(); + mat = a3d_TransformMatrix(vDir, g_vAxisY, vPos); + + if (IsPlayerMoving()) + { + if (!m_pWaterWaveMove) + m_pWaterWaveMove = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_WATER_WAVE_MOVE)); + + if (m_pWaterWaveMove) + { + if (m_pWaterWaveMove->GetState() == ST_STOP + || m_pWaterWaveMove->GetAlpha() < 1.0f) + { + m_pWaterWaveMove->SetAlpha(1.0f); + float fSpeed = GetFlySpeed() / GetSwimSpeed(); + if (fSpeed > 2.0f) fSpeed = 2.0f; + if (IsFlying()) m_pWaterWaveMove->SetPlaySpeed(fSpeed); + else m_pWaterWaveMove->SetPlaySpeed(1.0f); + m_pWaterWaveMove->Start(true); + } + + m_pWaterWaveMove->SetParentTM(mat); + } + + _fade_out_gfx(m_pWaterWaveStill, fDeltaTime); + } + else + { + if (!m_pWaterWaveStill) + m_pWaterWaveStill = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_WATER_WAVE_STILL)); + + if (m_pWaterWaveStill) + { + if (m_pWaterWaveStill->GetState() == ST_STOP + || m_pWaterWaveStill->GetAlpha() < 1.0f) + { + m_pWaterWaveStill->SetAlpha(1.0f); + m_pWaterWaveStill->Start(true); + } + + m_pWaterWaveStill->SetParentTM(mat); + } + + _fade_out_gfx(m_pWaterWaveMove, fDeltaTime); + } + } + else + { + _fade_out_gfx(m_pWaterWaveStill, fDeltaTime); + _fade_out_gfx(m_pWaterWaveMove, fDeltaTime); + } + + bool bFadeAirBubble = true; + bool bFadeSwimBubble = true; + + if (!pWater->IsUnderWater(g_pGame->GetViewport()->GetA3DCamera()->GetPos())) + { + if (m_pAirBubble) + { + m_pAirBubble->Stop(); + // g_pGame->GetA3DGFXExMan()->CacheReleasedGfx(m_pAirBubble); + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pAirBubble); + m_pAirBubble = NULL; + } + + if (m_pSwimBubble) + { + m_pSwimBubble->Stop(); + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pSwimBubble); + m_pSwimBubble = NULL; + } + + bFadeAirBubble = false; + bFadeSwimBubble = false; + } + else if (m_iMoveEnv == MOVEENV_WATER) + { + if (IsPlayerMoving()) + { + if (!m_pSwimBubble) + m_pSwimBubble = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_SWIM_BUBBLE)); + + if (m_pSwimBubble) + { + if (m_pSwimBubble->GetState() == ST_STOP + || m_pSwimBubble->GetAlpha() < 1.0f) + { + m_pSwimBubble->SetAlpha(1.0f); + m_pSwimBubble->Start(true); + } + + A3DMATRIX4 mat = GetAbsoluteTM(); + A3DVECTOR3 vOff = GetPos() + GetDir() * .8f; + vOff.y += 1.1f; + mat.SetRow(3, vOff); + m_pSwimBubble->SetParentTM(mat); + } + + bFadeSwimBubble = false; + } + else + { + if (!m_pAirBubble) + m_pAirBubble = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_AIR_BUBBLE)); + + if (m_pAirBubble) + { + if (m_pAirBubble->GetState() == ST_STOP + || m_pAirBubble->GetAlpha() < 1.0f) + { + m_pAirBubble->SetAlpha(1.0f); + m_pAirBubble->Start(true); + } + + A3DMATRIX4 mat = GetAbsoluteTM(); + A3DVECTOR3 vOff = GetPos() + GetDir() * .1f; + vOff.y += 1.6f; + mat.SetRow(3, vOff); + m_pAirBubble->SetParentTM(mat); + } + + bFadeAirBubble = false; + } + } + + if (bFadeAirBubble) _fade_out_gfx(m_pAirBubble, fDeltaTime); + if (bFadeSwimBubble) _fade_out_gfx(m_pSwimBubble, fDeltaTime); + + // Update bubble text + if (m_pBubbleTexts) + m_pBubbleTexts->Tick(dwDeltaTime); + + if( m_pvp.iDuelState == DUEL_ST_INDUEL ) + { + if (!m_pDuelStateGFX) + m_pDuelStateGFX = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_INDUEL)); + + if (m_pDuelStateGFX) + { + if (m_pDuelStateGFX->GetState() == ST_STOP || m_pDuelStateGFX->GetAlpha() < 1.0f) + { + m_pDuelStateGFX->SetAlpha(1.0f); + m_pDuelStateGFX->Start(true); + } + + A3DMATRIX4 mat = GetAbsoluteTM(); + m_pDuelStateGFX->SetParentTM(mat); + } + } + else + { + if (m_pDuelStateGFX) + { + m_pDuelStateGFX->Stop(); + g_pGame->GetGFXCaster()->ReleaseGFXEx(m_pDuelStateGFX); + m_pDuelStateGFX = NULL; + } + } + + if( m_pSprite ) + m_pSprite->Tick(dwDeltaTime); + + if( m_pGoblin ) + m_pGoblin->Tick(dwDeltaTime); + + if (m_idCandBuddy) AttachBuddy(m_idCandBuddy); + + if (m_CandPet.id && GetMajorModel()) + { + const char* szPetPath = GetRidingPetFileName(m_CandPet.id); + QueueLoadPetModel(szPetPath, false); + m_CandPet.Reset(); + } + + // Update render goblin counter + if(m_pGoblin && m_pSprite) + { + if(m_GoblinRenderCnt.IncCounter(dwDeltaTime)) + { + m_GoblinRenderCnt.Reset(); + + if(m_bRenderGoblin) + m_pSprite->SetTransparent(1.0f); + else + m_pGoblin->SetTransparent(1.0f); + + m_bRenderGoblin = !m_bRenderGoblin; + } + else + { + int iRenderCnt = (int)m_GoblinRenderCnt.GetCounter(); + if(iRenderCnt < 5000 || iRenderCnt > 25000) + { + float fTransparent = iRenderCnt<5000 ? (1.0f - (float)iRenderCnt/5000.0f) : (float)(iRenderCnt - 25000)/5000.0f; + if(m_bRenderGoblin) + m_pGoblin->SetTransparent(fTransparent); + else + m_pSprite->SetTransparent(fTransparent); + } + } + } + + DoSkillStateAction(); + + return true; +} + +// Process message +bool CECPlayer::ProcessMessage(const ECMSG& Msg) +{ + return true; +} + +// Play a damaged effect +void CECPlayer::Damaged(int nDamage, DWORD dwModifier/* 0 */,int skill /* 0 */) +{ + if (nDamage == -2) + { + // this message is caused by a help skill, so don't use a wounded action here + if (dwModifier & CECAttackEvent::MOD_IMMUNE) + BubbleText(BUBBLE_IMMUNE, 0); + else if (dwModifier & CECAttackEvent::MOD_NULLITY) + BubbleText(BUBBLE_INVALIDHIT, 0); + else if (dwModifier & CECAttackEvent::MOD_DODGE_DEBUFF) + BubbleText(BUBBLE_DODGE_DEBUFF, 0); + } + else if (nDamage == -1) + { + // when else player hit this player iDamage is -1, + // Just play a wounded action + if(!OnDamaged(skill)){ + PlayAction(ACT_WOUNDED); + } + if (dwModifier & CECAttackEvent::MOD_IMMUNE) + BubbleText(BUBBLE_IMMUNE, 0); + else if (dwModifier & CECAttackEvent::MOD_NULLITY) + BubbleText(BUBBLE_INVALIDHIT, 0); + else if (dwModifier & CECAttackEvent::MOD_DODGE_DEBUFF) + BubbleText(BUBBLE_DODGE_DEBUFF, 0); + } + else + { + // Popup a damage decal + if (nDamage > 0) + { + int p1 = 0; + if (dwModifier & CECAttackEvent::MOD_CRITICAL_STRIKE) + p1 |= 0x0001; + + if (dwModifier & CECAttackEvent::MOD_RETORT) + p1 |= 0x0002; + + if(!OnDamaged(skill)) + PlayAction(ACT_WOUNDED); + + if (dwModifier & CECAttackEvent::MOD_IMMUNE) + BubbleText(BUBBLE_IMMUNE, 0); + else if (dwModifier & CECAttackEvent::MOD_REBOUND) + BubbleText(BUBBLE_REBOUND, nDamage); + else if (dwModifier & CECAttackEvent::MOD_BEAT_BACK) + BubbleText(BUBBLE_BEAT_BACK, nDamage); + else + BubbleText(BUBBLE_DAMAGE, nDamage, p1); + } + else if (dwModifier & CECAttackEvent::MOD_IMMUNE) + BubbleText(BUBBLE_IMMUNE, 0); + else if (dwModifier & CECAttackEvent::MOD_NULLITY) + BubbleText(BUBBLE_INVALIDHIT, 0); + else + BubbleText(BUBBLE_HITMISSED, 0); + } +} +bool CECPlayer::OnDamaged(int skill) +{ + CECAttacksMan* pAtkMan = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan(); + AString name1,name2; + for (int i=0;iGetSkillStateActionName(skill,m_sciStateIDForStateAction[i],name1,name2)) + { + m_SkillIDForStateAction = skill; + return true; + } + } + + return false; +} +// Play an attack effect +void CECPlayer::PlayAttackEffect(int idTarget, int idSkill, int skillLevel, int nDamage, + DWORD dwModifier, int nAttackSpeed, int* piAttackTime/* NULL */, int nSection) +{ + if (!IsAllResReady()) + return; + + if( !idSkill ) + { + int idWeapon = IsShapeChanged() ? 0 : GetWeaponID(); + + int nTimeFly = 10; + if( idWeapon ) + { + // 看看是不是远程武器 + DATA_TYPE dt; + WEAPON_ESSENCE * pWeapon = (WEAPON_ESSENCE *) g_pGame->GetElementDataMan()->get_data_ptr(idWeapon, ID_SPACE_ESSENCE, dt); + ASSERT(dt == DT_WEAPON_ESSENCE); + + if( dt == DT_WEAPON_ESSENCE && pWeapon && pWeapon->require_projectile ) + { + nTimeFly = 700; + + if( m_aEquips[EQUIPIVTR_PROJECTILE] ) + idWeapon = m_aEquips[EQUIPIVTR_PROJECTILE]; // set weapon to the projectile + } + } + + if( g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->FindAttackByAttacker(GetPlayerInfo().cid) ) + { + // signal early attack event + ClearComActFlagAllRankNodes(true); + } + + // melee attack + CECAttackEvent *pAttack = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->AddMeleeAttack( + GetPlayerInfo().cid, idTarget, idWeapon, dwModifier, nDamage, nTimeFly); + + if (pAttack) + { + if( !IsDead() && (dwModifier & CECAttackEvent::MOD_RETORT) == 0 + && (dwModifier & CECAttackEvent::MOD_ATTACK_AURA) == 0 + && PlayAttackAction(nAttackSpeed, piAttackTime, &pAttack->m_bSignaled) + && (dwModifier & CECAttackEvent::MOD_BEAT_BACK) == 0){ + }else{ + pAttack->m_bSignaled = true; + } + } + } + else + { + if( skillLevel == 0 ) + { + if( m_pCurSkill ) + skillLevel = m_pCurSkill->GetSkillLevel(); + else + skillLevel = 1; + } + + CECAttackEvent *pAttack = NULL; + + // first try to find if there is already a skill attack event in attackman + CECAttackerEvents attackerEvents = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->FindAttackByAttacker(GetPlayerInfo().cid); + if( attackerEvents ) + { + if(CECAttackEvent *pAttack = attackerEvents.Find(idSkill, nSection)) + { + // 面攻击的非第一次伤害消息 + pAttack->AddTarget(idTarget, dwModifier, nDamage); + goto EXIT; + } + else + { + attackerEvents.Signal(); + } + } + if(GNET::ElementSkill::IsGoblinSkill(idSkill) && + GNET::ElementSkill::GetType(idSkill) == 2) + { + pAttack = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->AddSkillAttack( + GetPlayerInfo().cid, GetPlayerInfo().cid, idTarget, GetWeaponID(), idSkill, skillLevel, dwModifier, nDamage); + } + else + { + // begin a skill attack + pAttack = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->AddSkillAttack( + GetPlayerInfo().cid, m_idCurSkillTarget, idTarget, GetWeaponID(), idSkill, skillLevel, dwModifier, nDamage); + } + + if (pAttack) + { + pAttack->SetSkillSection(nSection); + if( !IsDead() && (dwModifier & CECAttackEvent::MOD_RETORT) == 0 + && (dwModifier & CECAttackEvent::MOD_ATTACK_AURA) == 0 + && PlaySkillAttackAction(idSkill, nAttackSpeed,NULL,nSection,&pAttack->m_bSignaled) + && (dwModifier & CECAttackEvent::MOD_BEAT_BACK) == 0){ + }else{ + pAttack->m_bSignaled = true; + } + } + +EXIT: + // For skill attacking, time is always set to 0 + if (piAttackTime) + *piAttackTime = 0; + } +} + +// Set absolute position +void CECPlayer::SetPos(const A3DVECTOR3& vPos) +{ + CECObject::SetPos(vPos); + + m_aabb.Center = vPos + A3DVECTOR3(0.0f, m_aabb.Extents.y, 0.0f); + m_aabb.CompleteMinsMaxs(); + m_aabbServer.Center = vPos + A3DVECTOR3(0.0f, m_aabbServer.Extents.y, 0.0f); + m_aabbServer.CompleteMinsMaxs(); + + if (m_pPetModel) + m_pPetModel->SetPos(vPos); + else if(m_pPlayerModel) + { + m_pPlayerModel->SetPos(vPos); + } + + if (m_iBuddyId && !m_bHangerOn) + { + CECPlayer* pBuddy = m_pPlayerMan->GetPlayer(m_iBuddyId); + if (pBuddy) + { + pBuddy->SetPos(vPos); + + // Buddy有可能在此之前就Tick了,这里设置Pos后应再TICK一次以确保变换矩阵的正确 + if( !m_pPlayerModel && pBuddy->GetPlayerModel() ) + pBuddy->GetPlayerModel()->Tick(0); + } + } + + // test code ... + /* + if (g_pGame->GetGameRun()->GetGameState() == CECGameRun::GS_GAME) + { + float fHei = g_pGame->GetGameRun()->GetWorld()->GetTerrainHeight(vPos); + if (fHei > vPos.y + 0.001f) + { + g_pGame->RuntimeDebugInfo(0xffffffff, _AL("%f"), fHei - vPos.y); + } + } + */ +} + +// Set absolute forward and up direction +void CECPlayer::SetDirAndUp(const A3DVECTOR3& vDir, const A3DVECTOR3& vUp) +{ + CECObject::SetDirAndUp(vDir, vUp); + + A3DVECTOR3 vNewDir = GetDir(); + A3DVECTOR3 vNewUp = GetUp(); + + if (m_pPetModel) + m_pPetModel->SetDirAndUp(vNewDir, vNewUp); + else if(m_pPlayerModel) + { + m_pPlayerModel->SetDirAndUp(vNewDir, vNewUp); + } +} + +bool CECPlayer::SupportCastSkillWhenMove()const{ + return m_pActionController != NULL && m_pActionController->SupportCastSkillWhenMove(); +} + +bool CECPlayer::CanCombineWithMoveForSkill(int idSkill)const{ + return m_pActionController != NULL && m_pActionController->CanCombineWithMoveForSkill(idSkill); +} + +// 移动、停止移动时调用此函数,用于处理移动攻击中模型朝向和转向 +void CECPlayer::StartModelMove(const A3DVECTOR3& vMoveDir, const A3DVECTOR3& vHeadUp, DWORD dwSwitchDirTime){ + if (!m_pBodyController){ + if (dwSwitchDirTime > 0){ + SetDestDirAndUp(vMoveDir, vHeadUp, dwSwitchDirTime); + }else{ + SetDirAndUp(vMoveDir, vHeadUp); + } + }else{ + if (dwSwitchDirTime > 0){ + m_pBodyController->Move(vMoveDir, vHeadUp); + }else{ + m_pBodyController->MoveImmediatelyTo(vMoveDir, vHeadUp); + } + } +} +void CECPlayer::StopModelMove(const A3DVECTOR3& vMoveDir, const A3DVECTOR3& vHeadUp, DWORD dwSwitchDirTime){ + if (!m_pBodyController){ + if (dwSwitchDirTime > 0){ + SetDestDirAndUp(vMoveDir, vHeadUp, dwSwitchDirTime); + }else{ + SetDirAndUp(vMoveDir, vHeadUp); + } + }else{ + m_pBodyController->StopMove(vMoveDir, vHeadUp); + } +} +void CECPlayer::StopModelMove(){ + if (m_pBodyController){ + m_pBodyController->StopMove(); + } +} + +void CECPlayer::ChangeModelMoveDirAndUp(const A3DVECTOR3& vMoveDir, const A3DVECTOR3& vHeadUp){ + if (!m_pBodyController){ + SetDirAndUp(vMoveDir, vHeadUp); + }else{ + m_pBodyController->ChangeMoveDirAndUp(vMoveDir, vHeadUp); // 根据当前移动状态,调整移动方向或模型朝向 + } +} + +void CECPlayer::ChangeModelTargetDirAndUp(const A3DVECTOR3 &vDir, const A3DVECTOR3 &vUp){ + if (!m_pBodyController){ + SetDirAndUp(vDir, vUp); + }else{ + m_pBodyController->ChangeModelTargetDirAndUp(vDir, vUp); + } +} + +A3DVECTOR3 CECPlayer::GetModelMoveDir()const{ // 移动时、返回移动朝向,静止时、返回模型朝向 + if (!m_pBodyController){ + return GetDir(); + }else{ + return m_pBodyController->GetModelMoveDir(); + } +} + +bool CECPlayer::PlayNonSkillActionWithName(int iAction, const char* szActName, bool bRestart/* =true */, int nTransTime/* =200 */, bool bNoFx/* =false */, bool *pActFlag/* =NULL */, DWORD dwFlagMode/* =COMACT_FLAG_MODE_NONE */){ + return m_pActionController + && m_pActionController->PlayNonSkillActionWithName(iAction, szActName, bRestart, nTransTime, bNoFx, pActFlag, dwFlagMode); +} + +bool CECPlayer::QueueNonSkillActionWithName(int iAction, const char* szActName, int nTransTime/* =200 */, bool bForceStopPrevAct/* =false */, bool bNoFx/* =false */, bool bResetSpeed/* =false */, bool bResetActFlag/* =false */, bool *pNewActFlag/* =NULL */, DWORD dwNewFlagMode/* =COMACT_FLAG_MODE_NONE */){ + return m_pActionController + && m_pActionController->QueueNonSkillActionWithName(iAction, szActName, nTransTime, bForceStopPrevAct, bNoFx, bResetSpeed, bResetActFlag, pNewActFlag, dwNewFlagMode); +} + +bool CECPlayer::PlaySkillCastActionWithName(int idSkill, const char *szActName, bool bNoFX/* =false */){ + return m_pActionController + && m_pActionController->PlaySkillCastActionWithName(idSkill, szActName, bNoFX); +} + +bool CECPlayer::PlaySkillAttackActionWithName(int idSkill, const char *szActName, bool bNoFX/* =false */, bool *pActFlag/* =NULL */, DWORD dwFlagMode/* =COMACT_FLAG_MODE_NONE */){ + return m_pActionController + && m_pActionController->PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, pActFlag, dwFlagMode); +} + +bool CECPlayer::QueueSkillAttackActionWithName(int idSkill, const char *szActName, int nTransTime/* =200 */, bool bNoFX/* =false */, bool bResetSpeed/* =false */, bool bResetActFlag/* =false */, bool *pNewActFlag/* =NULL */, DWORD dwNewFlagMode/* =COMACT_FLAG_MODE_NONE */){ + return m_pActionController + && m_pActionController->QueueSkillAttackActionWithName(idSkill, szActName, nTransTime, bNoFX, bResetSpeed, bResetActFlag, pNewActFlag, dwNewFlagMode); +} + +bool CECPlayer::PlayWoundActionWithName(const char* szActName){ + return m_pActionController + && m_pActionController->PlayWoundActionWithName(szActName); +} + +void CECPlayer::ClearComActFlagAllRankNodes(bool bSignalCurrent){ + if (m_pActionController){ + m_pActionController->ClearComActFlagAllRankNodes(bSignalCurrent); + } +} + +void CECPlayer::StopChannelAction(){ + if (m_pActionController){ + m_pActionController->StopChannelAction(); + } +} + +void CECPlayer::StopSkillCastAction(){ + if (m_pActionController){ + m_pActionController->StopSkillCastAction(); + } +} + +void CECPlayer::StopSkillAttackAction(){ + if (m_pActionController){ + m_pActionController->StopSkillAttackAction(); + } +} + +bool CECPlayer::IsPlayingAction()const{ + return GetLowerBodyAction() != -1; +} + +bool CECPlayer::IsPlayingAction(int iAction)const{ + return m_pActionController ? m_pActionController->IsPlayingAction(iAction) : false; +} + +bool CECPlayer::IsPlayingCastingSkillAction()const{ + return m_pActionController ? m_pActionController->IsPlayingCastingSkillAction() : false; +} + +bool CECPlayer::IsPlayingMoveAction()const{ + return m_pActionController ? m_pActionController->IsPlayingMoveAction() : false; +} + +bool CECPlayer::IsPlayingCastingSkillAndMoveActions()const{ + return IsPlayingCastingSkillAction() && IsPlayingMoveAction(); +} + +int CECPlayer::GetLowerBodyAction()const{ + return m_pActionController ? m_pActionController->GetLowerBodyAction() : -1; +} + +bool CECPlayer::PlayAction(int iAction, bool bRestart/* true */, int iTransTime/* 200 */, bool bQueue/* false */){ + return PlayActionWithConfig(iAction, 0, bRestart, iTransTime, bQueue); +} + +bool CECPlayer::PlayActionWithConfig(int iAction, int actionConfigID, bool bRestart/* =true */, int iTransTime/* =200 */, bool bQueue/* =false */){ + if (iAction < 0 || iAction >= ACT_MAX){ + ASSERT(false); + return false; + } + if (actionConfigID > 0){ + DATA_TYPE dt(DT_INVALID); + const void *p = g_pGame->GetElementDataMan()->get_data_ptr(actionConfigID, ID_SPACE_CONFIG, dt); + if (dt == DT_PLAYER_ACTION_INFO_CONFIG){ + PLAYER_ACTION actionConfig; + actionConfig.type = (PLAYER_ACTION_TYPE)iAction; + actionConfig.data = (PLAYER_ACTION_INFO_CONFIG *)p; + return PlayActionWithConfig(iAction, actionConfig, bRestart, iTransTime, bQueue); + }else{ + ASSERT(false); + a_LogOutput(1, "CECPlayer::PlayActionWithConfig, invalid action config ID(%d)", actionConfigID); + } + } + return PlayActionWithConfig(iAction, m_PlayerActions[iAction], bRestart, iTransTime, bQueue); +} + +// Play model action by weapon and relative action index +bool CECPlayer::PlayActionWithConfig(int iAction, const PLAYER_ACTION &actionConfig, bool bRestart/* true */, int iTransTime/* 200 */, bool bQueue/* false */) +{ + if (m_pPlayerModel) + { + if (iAction != ACT_WOUNDED) + { + if (iAction == ACT_TAKEOFF){ + ShowWing(true); + }else if (iAction == ACT_LANDON){ + ShowWing(false); + } + + PLAYER_ACTION action = actionConfig; + if( !action.data ) + return false; + + if( !bQueue ) + { + // update show weapon states. + m_bShowWeapon = action.data->hide_weapon ? false : true; + } + + char szAct[512]; + int weapon_type = GetShowingWeaponType(); + if( iAction >= ACT_ATTACK_1 && iAction <= ACT_ATTACK_4 ) + { + // attack action should not be played by this function + return false; + } + else if( iAction == ACT_ATTACK_TOSS ) + { + // we use toss second part action as a common action + sprintf(szAct, "%s_%s落", action.data->action_prefix, action.data->action_weapon_suffix[weapon_type].suffix); + if( !bQueue ){ + PlayNonSkillActionWithName(iAction, szAct, bRestart, iTransTime); + }else{ + QueueNonSkillActionWithName(iAction, szAct, iTransTime, bRestart, false, false, true, NULL, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX); + } + } + else if( (iAction >= ACT_EXP_WAVE && iAction <= ACT_EXP_KISS_END) || (iAction >= ACT_EXP_FIGHT && iAction <= ACT_EXP_DANCE) ) + { + // 表情动作 + AString strFashionAction; + if (iAction == ACT_EXP_DANCE && InFashionMode()) + strFashionAction = GetFashionActionName(); + sprintf(szAct, "%s_%s" + , strFashionAction.IsEmpty() ? action.data->action_prefix : strFashionAction + , action.data->action_weapon_suffix[weapon_type].suffix); + if( !bQueue ){ + PlayNonSkillActionWithName(iAction, szAct, bRestart, iTransTime); + }else{ + QueueNonSkillActionWithName(iAction, szAct, iTransTime, false, false, false, true, NULL, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX); + } + } + else if (iAction == ACT_EXP_FASHIONWEAPON) + { + if (CanShowFashionWeapon(m_uAttackType, m_iFashionWeaponType)) + { + AString strAction; + int idEquipment = m_aEquips[EQUIPIVTR_FASHION_WEAPON] & 0x0000ffff; + GetFashionActionNameByID(idEquipment,strAction); + if (!strAction.IsEmpty()) + { //by2024 sprintf_s + sprintf(szAct, "%s_%s", strAction, action.data->action_weapon_suffix[weapon_type].suffix); + if( !bQueue ){ + PlayNonSkillActionWithName(iAction, szAct, bRestart, iTransTime); + }else{ + QueueNonSkillActionWithName(iAction, szAct, iTransTime, false, false, false, true, NULL, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX); + } + } + } + } + else + { + // 通用动作 + if (m_pPetModel) + { + if( iAction == ACT_FIGHTSTAND ) + action = m_PlayerActions[ACT_STAND]; + else if( iAction >= ACT_PICKUP && iAction <= ACT_PICKUP_MATTER ) + action = m_PlayerActions[ACT_STAND]; + + if( iAction == ACT_TWO_KISS ) + sprintf(szAct, "站立_亲吻_双骑_通用"); + else + sprintf(szAct, "%s_%s", action.data->action_prefix, "骑乘_通用"); + + const char * szPetAct = NULL; + if (iAction == ACT_STAND || iAction == ACT_FIGHTSTAND) + szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_STAND); + else if (iAction == ACT_RUN) + szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_RUN); + else if (iAction == ACT_WALK) + szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_WALK); + else if (iAction == ACT_JUMP_START) + szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_JUMP_START); + else if (iAction == ACT_JUMP_LAND) + szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_JUMP_LAND); + else if (iAction == ACT_JUMP_LOOP) + szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_JUMP_LOOP); + if( !bQueue ) + m_pPetModel->PlayActionByName(szPetAct, 1.0f, bRestart, iTransTime, true); + else + m_pPetModel->QueueAction(szPetAct, iTransTime); + // now weapon wanted. + m_bShowWeapon = false; + } + else if (m_bHangerOn) + { + if( iAction == ACT_FIGHTSTAND ) + action = m_PlayerActions[ACT_STAND]; + else if( iAction >= ACT_PICKUP && iAction <= ACT_PICKUP_MATTER ) + action = m_PlayerActions[ACT_STAND]; + + if( iAction == ACT_TWO_KISS ) + { + if (m_AttachMode == enumAttachRideOnPlayer) + sprintf(szAct, "抚摸_绑定_通用"); + else if (m_AttachMode == enumAttachHugPlayer) + sprintf(szAct, "亲吻_绑定_通用"); + else if (m_AttachMode == enumAttachRideOnPet) + sprintf(szAct, "亲吻_双骑_通用"); + } + else + { + if (m_AttachMode == enumAttachRideOnPlayer) + sprintf(szAct, "%s_%s", action.data->action_prefix, "双骑_通用"); + else if (m_AttachMode == enumAttachHugPlayer) + { + if( iAction != ACT_RUN ) + sprintf(szAct, "%s", "站立_绑定_通用"); + else + sprintf(szAct, "%s", "奔跑_绑定_通用"); + } + else if (m_AttachMode == enumAttachRideOnPet) + sprintf(szAct, "%s_%s", action.data->action_prefix, "双骑_通用"); + } + // now weapon wanted. + m_bShowWeapon = false; + } + else if (m_AttachMode == enumAttachHugPlayer || m_AttachMode == enumAttachRideOnPlayer) + { + if( iAction == ACT_FIGHTSTAND ) + action = m_PlayerActions[ACT_STAND]; + else if( iAction >= ACT_PICKUP && iAction <= ACT_PICKUP_MATTER ) + action = m_PlayerActions[ACT_STAND]; + + if( iAction == ACT_TWO_KISS ) + { + action = m_PlayerActions[GetMoveStandAction(false, false)]; + if (m_AttachMode == enumAttachHugPlayer) + sprintf(szAct, "%s_%s", action.data->action_prefix, "亲吻_绑定_通用"); + else + sprintf(szAct, "%s_%s", action.data->action_prefix, "抚摸_绑定_通用"); + } + else + sprintf(szAct, "%s_%s", action.data->action_prefix, "绑定_通用"); + // now weapon wanted. + m_bShowWeapon = false; + } + else + sprintf(szAct, "%s_%s", action.data->action_prefix, action.data->action_weapon_suffix[weapon_type].suffix); + + + CECModel* pRightHandWeapon = GetRightHandWeapon(); + if( !bQueue ) + { + PlayNonSkillActionWithName(iAction, szAct, bRestart, iTransTime); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->PlayActionByName(_GenWeaponActionName(szAct, m_iGender), 1.0f, bRestart, iTransTime, true, iAction); + } + else + { + QueueNonSkillActionWithName(iAction, szAct, iTransTime, bRestart, false, false, true, NULL, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->QueueAction(_GenWeaponActionName(szAct, m_iGender), iTransTime, iAction, bRestart ? true : false); + } + + if (m_iBuddyId) + { + if( !m_bHangerOn ) + { + CECPlayer* pBuddy = m_pPlayerMan->GetPlayer(m_iBuddyId); + if (pBuddy) + { + if( iAction == ACT_STAND || iAction == ACT_FIGHTSTAND || + iAction == ACT_RUN || iAction == ACT_WALK || iAction == ACT_JUMP_START || + iAction == ACT_JUMP_LOOP || iAction == ACT_JUMP_LAND || iAction == ACT_WOUNDED || + iAction == ACT_TWO_KISS ) + { + pBuddy->PlayAction(iAction, bRestart, iTransTime, bQueue); + } + else + pBuddy->PlayAction(ACT_STAND, bRestart, iTransTime, bQueue); + } + } + } + } + } + else // iAction == ACT_WOUNDED + { + PLAYER_ACTION action = m_PlayerActions[iAction]; + + PlayWoundActionWithName(action.data->action_name); + + if (m_pPetModel) + { + A3DSkinModel *pSkinModel = m_pPetModel->GetA3DSkinModel(); + const char* szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_WOUNDED); + if (!pSkinModel->GetAction(szPetAct)) + { + szPetAct = CECNPC::GetBaseActionName(CECNPC::ACT_WOUNDED2); + if (!pSkinModel->GetAction(szPetAct)) + szPetAct = NULL; + if (szPetAct) + pSkinModel->PlayActionByName(szPetAct, CECModel::ACTCHA_WOUND, 1, 0, false); + } + } + } + + if( !bQueue ) + { + if( !m_bShowWeapon ) + ShowWeapon(false); + else + { + // 巫师跳跃时需要隐藏法宝 + if(IsUsingMagicWeapon() && (iAction == ACT_JUMP_START || iAction == ACT_JUMP_LAND || iAction == ACT_JUMP_LOOP)) + ShowWeapon(false); + else + ShowWeapon(true); + } + + // 根据播放动作,更改武器悬挂位置 + UpdateWeaponHangerPosByAction(iAction); + } + return true; + } + else + return false; +} + +// Play skill cast action +bool CECPlayer::PlaySkillCastAction(int idSkill) +{ + if( !m_pPlayerModel ) + return false; + + char szAct[512]; + szAct[0] = '\0'; + + int weapon_type = GetShowingWeaponType(); + + const PLAYER_ACTION_INFO_CONFIG* data = _default_skill_actions[idSkill]; + + if( !data || !data->action_prefix[0] ) + { + // Check if it's a target item skill + if(GNET::ElementSkill::GetCommonCoolDown(idSkill) > 1<<4) + { + data = m_PlayerActions[ACT_USING_TARGET_ITEM].data; + + if( !data || !data->action_prefix[0] ) + return false; + } + else + return false; + } + + if( GetMoveEnv() == MOVEENV_GROUND ) + { + sprintf(szAct, "%s_吟唱_%s", data->action_prefix, data->action_weapon_suffix[weapon_type].suffix); + } + else + { + if( (/*UsingWing()*/m_wingType == WINGTYPE_WING && IsFlying()) || (GetProfession() == PROF_ANGEL) || (GetProfession() == PROF_ARCHOR) || (GetProfession() == PROF_MONK) || (GetProfession() == PROF_GHOST) ) + sprintf(szAct, "%s_空中翅膀_吟唱_%s", data->action_prefix, data->action_weapon_suffix[weapon_type].suffix); + else + sprintf(szAct, "%s_空中飞剑_吟唱_%s", data->action_prefix, data->action_weapon_suffix[weapon_type].suffix); + } + + bool bHideFX = !CECOptimize::Instance().GetGFX().CanShowCast(GetCharacterID(), GetClassID()); + if (!PlaySkillCastActionWithName(idSkill, szAct, bHideFX)){ + return false; + } + + ShowWeaponByConfig(data); + UpdateWeaponHangerPosBySkillAction(idSkill);// 根据技能播放的动作,更改武器悬挂位置(只能检查不排队动作) + return true; +} + +// Set part extend properties +void CECPlayer::SetPartExtendProps(int iPropIdx, void* pData) +{ + switch (iPropIdx) + { + case EXTPROPIDX_BASE: + + m_ExtProps.bs = *(ROLEEXTPROP_BASE*)pData; + break; + + case EXTPROPIDX_MOVE: + + m_ExtProps.mv = *(ROLEEXTPROP_MOVE*)pData; + break; + + case EXTPROPIDX_ATTACK: + + m_ExtProps.ak = *(ROLEEXTPROP_ATK*)pData; + break; + + case EXTPROPIDX_DEF: + + m_ExtProps.df = *(ROLEEXTPROP_DEF*)pData; + break; + + default: + ASSERT(0); + return; + } +} + +/* + * Add By Zhangyu, 12.22.04 + */ + +void CECPlayer::ShowEquipments( + int nProf, + int nGender, + const int* pEquipmentID, + __int64 ChangeMask, + EquipsLoadResult* pResult, + bool bSimpleModel) +{ + DATA_TYPE dt; + A3DCOLOR color; + + // keep old things. + pResult->stoneWeapon = 0x00ff; + pResult->stoneUpperBody = 0x00ff; + pResult->stoneWrist = 0x00ff; + pResult->stoneLowerBody = 0x00ff; + pResult->stoneFoot = 0x00ff; + + for (__int64 i = 0; i < SIZE_ALL_EQUIPIVTR; i++) + { + if (((1 << i) & ChangeMask)) + { + int idEquipment = pEquipmentID[i]; + if((i >= EQUIPIVTR_FASHION_BODY && i <= EQUIPIVTR_FASHION_WRIST ) || + i == EQUIPIVTR_FASHION_HEAD || i == EQUIPIVTR_FASHION_WEAPON) + { + WORD wColor = (idEquipment & 0xffff0000) >> 16; + idEquipment &= 0x0000ffff; + color = FASHION_WORDCOLOR_TO_A3DCOLOR(wColor); + } + else + { + if( i == EQUIPIVTR_WEAPON ) + { + pResult->stoneWeapon = (idEquipment & 0xffff0000) >> 16; + } + if( i == EQUIPIVTR_BODY ) + { + pResult->stoneUpperBody = (idEquipment & 0xffff0000) >> 16; + } + if( i == EQUIPIVTR_WRIST ) + { + pResult->stoneWrist = (idEquipment & 0xffff0000) >> 16; + } + if( i == EQUIPIVTR_LEG ) + { + pResult->stoneLowerBody = (idEquipment & 0xffff0000) >> 16; + } + if( i == EQUIPIVTR_FOOT ) + { + pResult->stoneFoot = (idEquipment & 0xffff0000) >> 16; + } + idEquipment &= 0x0000ffff; + } + + if (idEquipment) + { + const void* pEquip = g_pGame->GetElementDataMan()->get_data_ptr( + idEquipment, + ID_SPACE_ESSENCE, + dt); + + if (pEquip) + { + switch (dt) + { + case DT_WEAPON_ESSENCE: + ChangeWeapon(pResult, static_cast(pEquip)); + continue; + case DT_ARMOR_ESSENCE: + if( !bSimpleModel ) + { + ChangeArmor( + static_cast(pEquip), + pResult->dwShowMask, + pResult->dwSkinChangeMask, + nProf, + nGender, + pResult->aSkins); + } + continue; + case DT_WINGMANWING_ESSENCE: + pResult->wingType = WINGTYPE_WING; + ChangeWing(pResult, static_cast(pEquip)->file_model); + continue; + case DT_FLYSWORD_ESSENCE: + pResult->wingType = FlyMode2WingType(static_cast(pEquip)->fly_mode); + if (pResult->wingType != WINGTYPE_DOUBLEWHEEL) { + ChangeWing(pResult, static_cast(pEquip)->file_model); + } else { + ChangeWing(pResult, static_cast(pEquip)->file_model, + static_cast(pEquip)->file_model2); + } + continue; + case DT_FASHION_ESSENCE: + { + const FASHION_ESSENCE* pFashion = static_cast(pEquip); + if (pFashion->equip_location == enumSkinShowHand) + { + pResult->uFashionAttackType = pFashion->action_type; + pResult->bFashionWeaponChanged = true; + pResult->strLeftFashionWeapon = pFashion->file_model_left; + pResult->strRightFashionWeapon = pFashion->file_model_right; + ChangeWeapon(pResult, pFashion->file_model_left, pFashion->file_model_right); + } + else + { + if( !bSimpleModel ) + { + ChangeFashion( + pFashion, + pResult->dwFashionShowMask, + pResult->dwSkinChangeMask, + nProf, + nGender, + pResult->aSkins, + color + ); + } + } + + continue; + } + default: + continue; + } + } + else + a_LogOutput(1, "Equipment Invent: WrongEquipType: Id = %d", idEquipment); + } + + if( bSimpleModel ) // 简单模型不加载默认装备 + continue; + + if (i == EQUIPIVTR_BODY) + { + pResult->dwShowMask |= (1 << enumSkinShowUpperBody); + pResult->dwSkinChangeMask |= (1 << SKIN_UPPER_BODY_INDEX); + pResult->stoneUpperBody = 0; + ChangeDefaultUpper(pResult->aSkins[SKIN_UPPER_BODY_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_LEG) + { + pResult->dwShowMask |= (1 << enumSkinShowLowerBody); + pResult->dwSkinChangeMask |= (1 << SKIN_LOWER_INDEX); + pResult->stoneLowerBody = 0; + ChangeDefaultLower(pResult->aSkins[SKIN_LOWER_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_WRIST) + { + pResult->dwShowMask |= (1 << enumSkinShowWrist); + pResult->dwSkinChangeMask |= (1 << SKIN_WRIST_INDEX); + pResult->stoneWrist = 0; + ChangeDefaultWrist(pResult->aSkins[SKIN_WRIST_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_FOOT) + { + pResult->dwShowMask |= (1 << enumSkinShowFoot); + pResult->dwSkinChangeMask |= (1 << SKIN_FOOT_INDEX); + pResult->stoneFoot = 0; + ChangeDefaultFoot(pResult->aSkins[SKIN_FOOT_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_WEAPON) + { + pResult->bWeaponChanged = true; + pResult->stoneWeapon = 0; + pResult->uAttackType = DEFAULT_ACTION_TYPE; + } + else if (i == EQUIPIVTR_FLYSWORD) + pResult->bWingChanged = true; + else if (i == EQUIPIVTR_FASHION_BODY) + { + pResult->dwFashionShowMask |= (1 << enumSkinShowUpperBody); + pResult->dwSkinChangeMask |= (1 << SKIN_FASHION_UPPER_BODY_INDEX); + ChangeDefaultFashionUpper(pResult->aSkins[SKIN_FASHION_UPPER_BODY_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_FASHION_LEG) + { + pResult->dwFashionShowMask |= (1 << enumSkinShowLowerBody); + pResult->dwSkinChangeMask |= (1 << SKIN_FASHION_LOWER_INDEX); + ChangeDefaultFashionLower(pResult->aSkins[SKIN_FASHION_LOWER_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_FASHION_WRIST) + { + pResult->dwFashionShowMask |= (1 << enumSkinShowWrist); + pResult->dwSkinChangeMask |= (1 << SKIN_FASHION_WRIST_INDEX); + ChangeDefaultFashionWrist(pResult->aSkins[SKIN_FASHION_WRIST_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_FASHION_FOOT) + { + pResult->dwFashionShowMask |= (1 << enumSkinShowFoot); + pResult->dwSkinChangeMask |= (1 << SKIN_FASHION_FOOT_INDEX); + ChangeDefaultFashionFoot(pResult->aSkins[SKIN_FASHION_FOOT_INDEX], nProf, nGender); + } + else if (i == EQUIPIVTR_FASHION_HEAD) + { + int aaa = 1; // for test + } + else if (i == EQUIPIVTR_FASHION_WEAPON) + { + pResult->bFashionWeaponChanged = true; + pResult->uFashionAttackType = DEFAULT_ACTION_TYPE; + } + } + } +} + +void CECPlayer::ShowEquipments(const int* pEquipmentID, bool bLoadAtOnce, bool bForceLoad) +{ + if( !GetMajorModel() || !GetMajorModel()->GetA3DSkinModel()) + return; + + __int64 Mask = 0; + + for (__int64 i = 0; i < SIZE_ALL_EQUIPIVTR; i++) + { + if( bForceLoad ) + { + if( !((1 << i) & m_i64EquipDisabled) || pEquipmentID[i] == 0 ) + Mask |= (1 << i); + } + else + { + if ( pEquipmentID[i] != m_aEquips[i] ) + { + if (IsShownEquip(i)) + { + if (m_aEquips[i] > 0 ) + { + DWORD dwRealID = GetRealElementID(i, m_aEquips[i]); + GetMajorModel()->OnScriptChangeEquip(dwRealID, 0, false); + } + + if (pEquipmentID[i] > 0) + { + DWORD dwRealID = GetRealElementID(i, pEquipmentID[i]); + GetMajorModel()->OnScriptChangeEquip(dwRealID, 1, false); + } + } + + if( // not disable + !((1 << i) & m_i64EquipDisabled) + // remove equip + || pEquipmentID[i] == 0 ) + { + Mask |= (1 << i); + } + + m_aEquips[i] = pEquipmentID[i]; + } + } + } + int aEquips[IVTRSIZE_EQUIPPACK]; + memcpy(aEquips, pEquipmentID, sizeof(aEquips)); + DecideWeaponLoad(aEquips, Mask); + + QueueLoadEquips(aEquips, Mask, bLoadAtOnce); +} + +void CECPlayer::ChangeEquipDisableMask(__int64 Mask) +{ + // equipment disable will affect the player animation + + int aChangedEquip[SIZE_ALL_EQUIPIVTR]; + int aRestoreEquip[SIZE_ALL_EQUIPIVTR]; + + // backup the equipments info + ASSERT(sizeof(aRestoreEquip) == sizeof(m_aEquips)); + memcpy(aRestoreEquip, m_aEquips, sizeof(m_aEquips)); + + for (__int64 i = 0; i < SIZE_ALL_EQUIPIVTR; i++) + { + __int64 flagOld = (1 << i) & m_i64EquipDisabled; + __int64 flagCur = (1 << i) & Mask; + + if(flagCur != flagOld) + { + if(flagCur) + { + // this equipment should be disabled + aChangedEquip[i] = 0; + } + else + { + // this equipment should be restored + aChangedEquip[i] = m_aEquips[i]; + m_aEquips[i] = 0; + } + } + else + { + aChangedEquip[i] = m_aEquips[i]; + } + } + m_i64EquipDisabled = Mask; + + // remove disabled equipments + ShowEquipments(aChangedEquip, false); + // restore the equipments info + memcpy(m_aEquips, aRestoreEquip, sizeof(m_aEquips)); +} + +bool CECPlayer::ChangeWing( + EquipsLoadResult* pResult, + const char* szModeFile, + const char* szModeFile2) +{ + if (!szModeFile[0]) return false; + + pResult->bWingChanged = true; + + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile((const char*)glb_ArmorReplaceShader_ReflectPrefix, A3DSkinMan::SHADERREPLACE_USERDEFINE); + + CECModel* pWing = new CECModel; + + if (pWing->Load(szModeFile, true, A3DSkinModel::LSF_UNIQUESKIN)) + { + pWing->Show(false); + pResult->pWing = pWing; + } + else + delete pWing; + + if (szModeFile2 != NULL) { + CECModel* pRightFoot = new CECModel; + + if (pRightFoot->Load(szModeFile2, true, A3DSkinModel::LSF_UNIQUESKIN)) + { + pRightFoot->Show(false); + pResult->pWing2 = pRightFoot; + } + else + delete pRightFoot; + } + + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); + + return true; +} + +inline void _SetWeaponMaterial(A3DSkinModel* pModel) +{ + if (!pModel) return; + + int nNumSkin = pModel->GetSkinNum(); + A3DSkin * pSkin; + + for (int i = 0; i < nNumSkin; i++) + { + pSkin = pModel->GetA3DSkin(i); + + if( pSkin ) + { + int nNumMaterial = pSkin->GetMaterialNum(); + + for (int i = 0; i < nNumMaterial; i++) + { + A3DMATERIALPARAM param = pSkin->GetMaterial(i)->GetMaterialParam(); + param.Specular = WEAPON_SPECULAR; + param.Power = WEAPON_POWER; + pSkin->GetMaterial(i)->SetMaterialParam(param); + } + } + } +} + +bool CECPlayer::ChangeWeapon(EquipsLoadResult* pResult, const char* szLeft, const char* szRight) +{ + if (pResult->bFashionWeaponChanged) + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile((const char*)glb_FashionReplaceShader, A3DSkinMan::SHADERREPLACE_USERDEFINE); + else + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile((const char*)glb_ArmorReplaceShader_ReflectPrefix, A3DSkinMan::SHADERREPLACE_USERDEFINE); + + if (szLeft[0]) + { + CECModel* pWeapon = new CECModel; + + if (pWeapon->Load(szLeft, true, A3DSkinModel::LSF_UNIQUESKIN)) + { + _SetWeaponMaterial(pWeapon->GetA3DSkinModel()); + pResult->pLeftHandWeapon = pWeapon; + } + else + delete pWeapon; + } + + if (szRight[0]) + { + CECModel* pWeapon = new CECModel; + + if (pWeapon->Load(szRight, true, A3DSkinModel::LSF_UNIQUESKIN)) + { + _SetWeaponMaterial(pWeapon->GetA3DSkinModel()); + pResult->pRightHandWeapon = pWeapon; + } + else + delete pWeapon; + } + + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); + return true; +} + +void CECPlayer::ChangeWeapon(EquipsLoadResult* pResult, const WEAPON_ESSENCE* pWeapon) +{ + DATA_TYPE dt; + + const WEAPON_SUB_TYPE* pSubType = + (const WEAPON_SUB_TYPE*) g_pGame->GetElementDataMan()->get_data_ptr( + pWeapon->id_sub_type, + ID_SPACE_ESSENCE, + dt); + + if (pSubType == NULL || dt != DT_WEAPON_SUB_TYPE) + { + a_LogOutput(1, "Equipment Invent: Wrong Weapon SubType, Essence Id = %d", pWeapon->id); + return; + } + + pResult->uAttackType = pSubType->action_type; + pResult->strLeftWeapon = pWeapon->file_model_left; + pResult->strRightWeapon = pWeapon->file_model_right; + pResult->bWeaponChanged = true; + + ChangeWeapon( + pResult, + pWeapon->file_model_left, + pWeapon->file_model_right); +} + +void CECPlayer::ChangeArmor( + const ARMOR_ESSENCE* pArmor, + DWORD& dwShowMask, + DWORD& dwSkinChangeMask, + int nProf, + int nGender, + A3DSkin* pSkins[][3] + ) +{ + static const int _location_skin_map[] = + { + 0, // none + SKIN_UPPER_BODY_INDEX, // enumSkinShowUpperBody + SKIN_WRIST_INDEX, // enumSkinShowWrist + SKIN_LOWER_INDEX, // enumSkinShowLowerBody + SKIN_FOOT_INDEX, // enumSkinShowFoot + SKIN_UPPER_BODY_INDEX, // enumSkinShowUpperAndLower + SKIN_UPPER_BODY_INDEX, // enumSkinShowUpperAndWrist + SKIN_LOWER_INDEX, // enumSkinShowLowerAndFoot + SKIN_UPPER_BODY_INDEX // enumSkinShowUpperLowerAndWrist + }; + + int nLocation = pArmor->equip_location; + if (nLocation < enumSkinShowUpperBody || nLocation > enumSkinShowUpperLowerAndWrist) + return; + + char szPath[MAX_PATH]; + _GenSkinPath(szPath, nProf, nGender, pArmor->realname); + + int nSkinIndex = _location_skin_map[nLocation]; + dwShowMask |= (1 << nLocation); + dwSkinChangeMask |= (1 << nSkinIndex); + + ChangeArmor( + szPath, + pSkins[nSkinIndex], + nLocation, + false, + nProf, + nGender + ); +} + +bool CECPlayer::ChangeArmor( + const char* strSkinFile, + A3DSkin* aSkins[3], + int nLocation, + bool bDefault, + int nProf, + int nGender) +{ + switch (nLocation) + { + case enumSkinShowUpperBody: + + if (!LoadPlayerSkin(aSkins, SKIN_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowWrist: + + if (!LoadPlayerSkin(aSkins, SKIN_WRIST_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultWrist(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowLowerBody: + + if (!LoadPlayerSkin(aSkins, SKIN_LOWER_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultLower(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowFoot: + + if (!LoadPlayerSkin(aSkins, SKIN_FOOT_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFoot(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowUpperAndLower: + + if (!LoadPlayerSkin(aSkins, SKIN_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowUpperAndWrist: + + if (!LoadPlayerSkin(aSkins, SKIN_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowLowerAndFoot: + + if (!LoadPlayerSkin(aSkins, SKIN_LOWER_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultLower(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowUpperLowerAndWrist: + + if (!LoadPlayerSkin(aSkins, SKIN_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowNone: + + break; + + default: + a_LogOutput(1, "Equipment Invent: Wrong Location: %d", nLocation); + break; + } + + return true; +} + +void CECPlayer::ChangeFashion( + const FASHION_ESSENCE* pFashion, + DWORD& dwShowMask, + DWORD& dwSkinChangeMask, + int nProf, + int nGender, + A3DSkin* pSkins[][3], + A3DCOLOR color) +{ + static const int _location_skin_map[] = + { + 0, // none + SKIN_FASHION_UPPER_BODY_INDEX, // enumSkinShowUpperBody + SKIN_FASHION_WRIST_INDEX, // enumSkinShowWrist + SKIN_FASHION_LOWER_INDEX, // enumSkinShowLowerBody + SKIN_FASHION_FOOT_INDEX, // enumSkinShowFoot + SKIN_FASHION_UPPER_BODY_INDEX, // enumSkinShowUpperAndLower + SKIN_FASHION_UPPER_BODY_INDEX, // enumSkinShowUpperAndWrist + SKIN_FASHION_LOWER_INDEX, // enumSkinShowLowerAndFoot + SKIN_FASHION_UPPER_BODY_INDEX, // enumSkinShowUpperLowerAndWrist + }; + + int nLocation = pFashion->equip_location; + if (nLocation < enumSkinShowUpperBody || nLocation > enumSkinShowUpperLowerAndWrist) + return; + + char szPath[MAX_PATH]; + _GenSkinPath(szPath, nProf, nGender, pFashion->realname); + + int nSkinIndex = _location_skin_map[nLocation]; + dwShowMask |= (1 << nLocation); + dwSkinChangeMask |= (1 << nSkinIndex); + + ChangeFashion( + szPath, + pSkins[nSkinIndex], + nLocation, + false, + nProf, + nGender, + color + ); +} + +bool CECPlayer::ChangeFashion( + const char* strSkinFile, + A3DSkin* aSkins[3], + int nLocation, + bool bDefault, + int nProf, + int nGender, + A3DCOLOR color) +{ + switch (nLocation) + { + case enumSkinShowUpperBody: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowWrist: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_WRIST_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionWrist(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowLowerBody: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_LOWER_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionLower(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowFoot: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_FOOT_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionFoot(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowUpperAndLower: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowUpperAndWrist: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowLowerAndFoot: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_LOWER_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionLower(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowUpperLowerAndWrist: + + if (!LoadPlayerSkin(aSkins, SKIN_FASHION_UPPER_BODY_INDEX, strSkinFile)) + { + a_LogOutput(1, "Equipment Invent: Wrong Skin: %s", strSkinFile); + if (!bDefault) ChangeDefaultFashionUpper(aSkins, nProf, nGender); + } + + break; + + case enumSkinShowNone: + + break; + + default: + a_LogOutput(1, "Equipment Invent: Wrong Location: %d", nLocation); + break; + } + + // now get the shader and set color to it. + for(int i=0; i<3; i++) + { + A3DSkin * pSkin = aSkins[i]; + if (!pSkin) continue; + + for(int idTex=0; idTexGetTextureNum(); idTex ++) + { + A3DTexture * pTex = pSkin->GetTexture(idTex); + if( pTex && pTex->IsShaderTexture() ) + { + A3DShader * pShader = (A3DShader *) pTex; + pShader->GetGeneralProps().dwTFactor = color; + } + } + } + + return true; +} + +// play attack action +bool CECPlayer::PlayAttackAction(int nAttackSpeed, int* piAttackTime/* =NULL */, bool *pActFlag/* =NULL */) +{ + if (!m_pPlayerModel) + return false; + + int nRand = rand() % 4; + char szAct[512]; + szAct[0] = '\0'; + + int weapon_type = GetShowingWeaponType(); + + int nTime1, nTime2, iAction = ACT_ATTACK_1 + nRand; + const PLAYER_ACTION& action = m_PlayerActions[iAction]; + + if( !action.data || !action.data->action_prefix[0] ) + return false; + + ShowWeaponByConfig(action.data); + + CECModel* pRightHandWeapon = GetRightHandWeapon(); + bool bHideFX = !CECOptimize::Instance().GetGFX().CanShowAttack(GetCharacterID(), GetClassID()); + + if( GetMoveEnv() == MOVEENV_GROUND ) + { + sprintf(szAct, "%s_%s起", action.data->action_prefix, action.data->action_weapon_suffix[weapon_type].suffix); + PlayNonSkillActionWithName(iAction, szAct, true, 200, bHideFX, pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->PlayActionByName(_GenWeaponActionName(szAct, m_iGender), 1.0f, true, 200, true, iAction, bHideFX); + + nTime1 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + + sprintf(szAct, "%s_%s落", action.data->action_prefix, action.data->action_weapon_suffix[weapon_type].suffix); + QueueNonSkillActionWithName(iAction, szAct, 0, false, bHideFX); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->QueueAction(_GenWeaponActionName(szAct, m_iGender), 0, iAction, false, false, bHideFX); + + nTime2 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + } + else + { + const char *szActionMiddleName = NULL; + if( (/*UsingWing()*/m_wingType == WINGTYPE_WING && IsFlying()) || (GetProfession() == PROF_ANGEL) || (GetProfession() == PROF_ARCHOR) || (GetProfession() == PROF_MONK) || (GetProfession() == PROF_GHOST) ) + { + szActionMiddleName = "空中翅膀"; + }else{ + szActionMiddleName = "空中飞剑"; + } + sprintf(szAct, "%s_%s_%s起", action.data->action_prefix, szActionMiddleName, action.data->action_weapon_suffix[weapon_type].suffix); + PlayNonSkillActionWithName(iAction, szAct, true, 200, bHideFX, pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->PlayActionByName(_GenWeaponActionName(szAct, m_iGender), 1.0f, true, 200, true, iAction, bHideFX); + + nTime1 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + + sprintf(szAct, "%s_%s_%s落", action.data->action_prefix, szActionMiddleName, action.data->action_weapon_suffix[weapon_type].suffix); + QueueNonSkillActionWithName(iAction, szAct, 0, false, bHideFX); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->QueueAction(_GenWeaponActionName(szAct, m_iGender), 0, iAction, false, false, bHideFX); + + nTime2 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + } + + // make attack action end up with a stand to eliminate the still animation if a long delay occurs. + const PLAYER_ACTION& stand_action = m_PlayerActions[ACT_FIGHTSTAND]; + sprintf(szAct, "%s_%s", stand_action.data->action_prefix, stand_action.data->action_weapon_suffix[weapon_type].suffix); + QueueNonSkillActionWithName(ACT_FIGHTSTAND, szAct, 300, false, bHideFX, true); // 2012-1-31: 增加设置 bResetSpeed=true,避免 ACT_FIGHTSTAND 动作受攻击动作速度修改影响(m_pPlayerModel->SetPlaySpeed)而播放过快 + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->QueueAction(_GenWeaponActionName(szAct, m_iGender), 300, iAction, false, false, bHideFX, true); // 2012-1-31: 同上 + + // now we try to adjust the player's animation to meet synchronize with attack speed + if( nAttackSpeed > 0 ) + { + float vScale = (nTime1 + nTime2) / (float)nAttackSpeed; + if (vScale > 0.0f){ + m_pPlayerModel->SetPlaySpeed(vScale); + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->SetPlaySpeed(vScale); + } + } + + if (piAttackTime) + *piAttackTime = nTime1 + nTime2; + + // 根据播放动作,更改武器悬挂位置 + UpdateWeaponHangerPosByAction(iAction); + + return true; +} +static void _GetSkillSectionActionName(char* szAct,int idSkill,int nSection) +{ + if (nSection) + { + CECAttacksMan* pAtkMan = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan(); + AString suffix; + if(pAtkMan && pAtkMan->GetSkillSectionActionSuffix(idSkill,nSection,suffix)) + { + strcat(szAct,"_"); + strcat(szAct,suffix); + } + } +} +// play skill attack action +bool CECPlayer::PlaySkillAttackAction(int idSkill, int nAttackSpeed, int* piAttackTime/* =NULL */,int nSection /* = 0 */, bool *pActFlag/* =NULL */) +{ + if (!m_pPlayerModel ) + return false; + + char szAct[512]; + szAct[0] = '\0'; + + int weapon_type = GetShowingWeaponType(); + + const PLAYER_ACTION_INFO_CONFIG* data = _default_skill_actions[idSkill]; + + if( !data || !data->action_prefix[0] ) + { + // Check if it's a target item skill + if(GNET::ElementSkill::GetCommonCoolDown(idSkill) > 1<<4) + { + data = m_PlayerActions[ACT_USING_TARGET_ITEM].data; + + if( !data || !data->action_prefix[0] ) + return false; + } + else + return false; + } + + int nTime1, nTime2; + bool bInfinite = false; + A3DCombinedAction * pAct; + CECModel* pRightHandWeapon = GetRightHandWeapon(); + bool bHideFX = !CECOptimize::Instance().GetGFX().CanShowAttack(GetCharacterID(), GetClassID()); + + CECAttacksMan* pAtkMan = g_pGame->GetGameRun()->GetWorld()->GetAttacksMan(); + + if( GetMoveEnv() == MOVEENV_GROUND ) + { + sprintf(szAct, "%s_施放起_%s", data->action_prefix, data->action_weapon_suffix[weapon_type].suffix); + _GetSkillSectionActionName(szAct,idSkill,nSection); + if (!PlaySkillAttackActionWithName(idSkill, szAct, bHideFX, pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX)){ + return false; + } + + nTime1 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + pAct = m_pPlayerModel->GetComActByName(szAct); + if( pAct ) bInfinite |= pAct->IsInfinite(); + + sprintf(szAct, "%s_施放落_%s", data->action_prefix, data->action_weapon_suffix[weapon_type].suffix); + _GetSkillSectionActionName(szAct,idSkill,nSection); + QueueSkillAttackActionWithName(idSkill, szAct, 0, bHideFX); + + nTime2 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + pAct = m_pPlayerModel->GetComActByName(szAct); + if( pAct ) bInfinite |= pAct->IsInfinite(); + } + else + { + const char *szActionMiddleName = NULL; + if( (/*UsingWing()*/ m_wingType == WINGTYPE_WING && IsFlying()) || (GetProfession() == PROF_ANGEL) || (GetProfession() == PROF_ARCHOR) || (GetProfession() == PROF_MONK) || (GetProfession() == PROF_GHOST) ) + { + szActionMiddleName = "空中翅膀"; + }else{ + szActionMiddleName = "空中飞剑"; + } + + sprintf(szAct, "%s_%s_施放起_%s", data->action_prefix, szActionMiddleName, data->action_weapon_suffix[weapon_type].suffix); + _GetSkillSectionActionName(szAct,idSkill,nSection); + if (!PlaySkillAttackActionWithName(idSkill, szAct, bHideFX, pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX)){ + return false; + } + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->PlayActionByName(_GenWeaponActionName(szAct, m_iGender), 1.0f, true, 200, true, ACT_CASTSKILL, bHideFX); + + nTime1 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + pAct = m_pPlayerModel->GetComActByName(szAct); + if( pAct ) bInfinite |= pAct->IsInfinite(); + + sprintf(szAct, "%s_%s_施放落_%s", data->action_prefix, szActionMiddleName, data->action_weapon_suffix[weapon_type].suffix); + _GetSkillSectionActionName(szAct,idSkill,nSection); + QueueSkillAttackActionWithName(idSkill, szAct, 0, bHideFX); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->QueueAction(_GenWeaponActionName(szAct, m_iGender), 0, ACT_CASTSKILL, false, false, bHideFX); + + nTime2 = m_pPlayerModel->GetComActTimeSpanByName(szAct); + pAct = m_pPlayerModel->GetComActByName(szAct); + if( pAct ) bInfinite |= pAct->IsInfinite(); + } + + int nExecuteTime = GNET::ElementSkill::GetExecuteTime(idSkill, 0); + + // now we try to adjust the player's animation to meet synchronize with attack speed + if( !bInfinite ) + { + if( nExecuteTime > 0 ) + { + float vScale = (nTime1 + nTime2) / (float)nExecuteTime; + m_pPlayerModel->SetPlaySpeed(vScale * 1.2f); + + if(pRightHandWeapon && IsUsingMagicWeapon()) + pRightHandWeapon->SetPlaySpeed(vScale * 1.2f); + } + + if (piAttackTime) + *piAttackTime = nTime1 + nTime2; + } + else + { + // the action is looped, so we just return the execute determined by skill + if (piAttackTime) + *piAttackTime = nExecuteTime; + } + + ShowWeaponByConfig(data); + UpdateWeaponHangerPosBySkillAction(idSkill); + return true; +} + +// Change the color of body +void CECPlayer::SetBodyColor(A3DCOLOR color) +{ + if( GetProfession() == PROF_ORC && GetGender() == GENDER_MALE ) // 妖兽男的皮肤不调色 + m_CustomizeData.colorBody = 0xffffffff; + else + m_CustomizeData.colorBody = color; + + for(int i=0; i<3; i++) + { + if (m_pBodyShader[i]) + { + m_pBodyShader[i]->GetGeneralProps().dwTFactor = m_CustomizeData.colorBody; + } + } +} + +void CECPlayer::SetBodyHeadScale(unsigned char vScale) +{ + m_CustomizeData.headScale = vScale; + UpdateBodyScales(); +} + +void CECPlayer::SetBodyUpScale(unsigned char vScale) +{ + m_CustomizeData.upScale = vScale; + UpdateBodyScales(); +} + +void CECPlayer::SetBodyWaistScale(unsigned char vScale) +{ + m_CustomizeData.waistScale = vScale; + UpdateBodyScales(); +} + +void CECPlayer::SetBodyArmWidth(unsigned char vScale) +{ + m_CustomizeData.armWidth = vScale; + UpdateBodyScales(); +} + +void CECPlayer::SetBodyLegWidth(unsigned char vScale) +{ + m_CustomizeData.legWidth = vScale; + UpdateBodyScales(); +} + +void CECPlayer::SetBodyBreastScale(unsigned char vScale) +{ + m_CustomizeData.breastScale = vScale; + UpdateBodyScales(); +} + +void CECPlayer::UpdateBodyScales() +{ + if (!GetPlayerModel()) + return; + + A3DSkinModel * pSkinModel = GetPlayerModel()->GetA3DSkinModel(); + + if( pSkinModel ) + { + A3DVECTOR3 vecScale; + A3DSkeleton * pSkeleton = pSkinModel->GetSkeleton(); + + // head adjust + vecScale.x = 1.0f; + float fHeadScaleYZ = 1.0f; + if (GetProfession() == PROF_YEYING || GetProfession() == PROF_YUEXIAN){ + // 玩家使用大头时,Neck 不随头部缩放更易接受。老职业维持原状 + fHeadScaleYZ = TransformScaleFromIntToFloat( m_CustomizeData.upScale, m_CustomizeFactor.fScaleUpFactor, SCALE_UP_FACTOR); + }else{ + fHeadScaleYZ = TransformScaleFromIntToFloat(m_CustomizeData.headScale, m_CustomizeFactor.fScaleHeadFactor, SCALE_HEAD_FACTOR); + } + vecScale.y = vecScale.z = fHeadScaleYZ; + + A3DBone * pBoneNeck = pSkeleton->GetBone("Bip01 Neck", NULL); + if( pBoneNeck ) + { + pBoneNeck->SetScaleFactor(vecScale); + pBoneNeck->SetScaleType(A3DBone::SCALE_LOCAL_NOOFFSET); + } + + vecScale.x = vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.headScale, m_CustomizeFactor.fScaleHeadFactor, SCALE_HEAD_FACTOR); + + A3DBone * pBoneHead = pSkeleton->GetBone("Bip01 Head", NULL); + if( pBoneHead ) + { + pBoneHead->SetScaleFactor(vecScale); + pBoneHead->SetScaleType(A3DBone::SCALE_WHOLE); + } + + if( m_iGender == GENDER_FEMALE ) + { + // female's breast adjust + vecScale.x = TransformScaleFromIntToFloat( m_CustomizeData.breastScale, m_CustomizeFactor.fScaleBreastFactor, SCALE_BREAST_FACTOR); + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.breastScale,m_CustomizeFactor.fScaleBreastFactor, SCALE_BREAST_FACTOR); + + A3DBone * pBoneFBreast = pSkeleton->GetBone("Bone01", NULL); + if( pBoneFBreast ) + { + pBoneFBreast->SetScaleFactor(vecScale); + pBoneFBreast->SetScaleType(A3DBone::SCALE_LOCAL); + } + } + + // first adjust clavicles + vecScale.x = TransformScaleFromIntToFloat( m_CustomizeData.upScale, m_CustomizeFactor.fScaleUpFactor, SCALE_UP_FACTOR); + vecScale.y = vecScale.z =TransformScaleFromIntToFloat( m_CustomizeData.upScale, m_CustomizeFactor.fScaleUpFactor, SCALE_UP_FACTOR); + + A3DBone * pBoneLClavicle = pSkeleton->GetBone("Bip01 L Clavicle", NULL); + if( pBoneLClavicle ) + { + pBoneLClavicle->SetScaleFactor(vecScale); + pBoneLClavicle->SetScaleType(A3DBone::SCALE_LOCAL); + } + A3DBone * pBoneRClavicle = pSkeleton->GetBone("Bip01 R Clavicle", NULL); + if( pBoneRClavicle ) + { + pBoneRClavicle->SetScaleFactor(vecScale); + pBoneRClavicle->SetScaleType(A3DBone::SCALE_LOCAL); + } + + // then breast + vecScale.x = 1.0f; + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.upScale,m_CustomizeFactor.fScaleUpFactor, SCALE_UP_FACTOR); + + A3DBone * pBoneBreast = pSkeleton->GetBone("Bip01 Spine2", NULL); + if( pBoneBreast ) + { + pBoneBreast->SetScaleFactor(vecScale); + pBoneBreast->SetScaleType(A3DBone::SCALE_LOCAL); + } + + // chest + vecScale.x = 1.0f; + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.upScale,m_CustomizeFactor.fScaleUpFactor, SCALE_UP_FACTOR); + + A3DBone * pBoneChest = pSkeleton->GetBone("Bip01 Spine1", NULL); + if( pBoneChest ) + { + pBoneChest->SetScaleFactor(vecScale); + pBoneChest->SetScaleType(A3DBone::SCALE_LOCAL); + } + + // waist + vecScale.x = 1.0f; + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.waistScale,m_CustomizeFactor.fScaleWaistFactor, SCALE_WAIST_FACTOR); + + A3DBone * pBoneWaist = pSkeleton->GetBone("Bip01 Spine", NULL); + if( pBoneWaist ) + { + pBoneWaist->SetScaleFactor(vecScale); + pBoneWaist->SetScaleType(A3DBone::SCALE_LOCAL_NOOFFSET); + } + + // pelvis + vecScale.x = 1.0f; + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.waistScale,m_CustomizeFactor.fScaleWaistFactor, SCALE_WAIST_FACTOR); + + A3DBone * pBonePelvis = pSkeleton->GetBone("Bip01 Pelvis", NULL); + if( pBonePelvis ) + { + pBonePelvis->SetScaleFactor(vecScale); + pBonePelvis->SetScaleType(A3DBone::SCALE_LOCAL); + } + + // arm + vecScale.x = 1.0f; + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.armWidth,m_CustomizeFactor.fWidthArmFactor, WIDTH_ARM_FACTOR); + + A3DBone * pBoneLUpperArm = pSkeleton->GetBone("Bip01 L UpperArm", NULL); + if( pBoneLUpperArm ) + { + pBoneLUpperArm->SetScaleFactor(vecScale); + pBoneLUpperArm->SetScaleType(A3DBone::SCALE_LOCAL); + } + A3DBone * pBoneRUpperArm = pSkeleton->GetBone("Bip01 R UpperArm", NULL); + if( pBoneRUpperArm ) + { + pBoneRUpperArm->SetScaleFactor(vecScale); + pBoneRUpperArm->SetScaleType(A3DBone::SCALE_LOCAL); + } + A3DBone * pBoneLForeArm = pSkeleton->GetBone("Bip01 L Forearm", NULL); + if( pBoneLForeArm ) + { + pBoneLForeArm->SetScaleFactor(vecScale); + pBoneLForeArm->SetScaleType(A3DBone::SCALE_LOCAL); + } + A3DBone * pBoneRForeArm = pSkeleton->GetBone("Bip01 R Forearm", NULL); + if( pBoneRForeArm ) + { + pBoneRForeArm->SetScaleFactor(vecScale); + pBoneRForeArm->SetScaleType(A3DBone::SCALE_LOCAL); + } + + // leg + vecScale.x = 1.0f; + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.legWidth, m_CustomizeFactor.fWidthLegFactor, WIDTH_ARM_FACTOR); + + A3DBone * pBoneLThigh = pSkeleton->GetBone("Bip01 L Thigh", NULL); + if( pBoneLThigh ) + { + pBoneLThigh->SetScaleFactor(vecScale); + pBoneLThigh->SetScaleType(A3DBone::SCALE_LOCAL); + } + A3DBone * pBoneRThigh = pSkeleton->GetBone("Bip01 R Thigh", NULL); + if( pBoneRThigh ) + { + pBoneRThigh->SetScaleFactor(vecScale); + pBoneRThigh->SetScaleType(A3DBone::SCALE_LOCAL); + } + + vecScale.x = 1.0f; + vecScale.y = vecScale.z = TransformScaleFromIntToFloat( m_CustomizeData.legWidth, m_CustomizeFactor.fWidthLegFactor, WIDTH_LEG_FACTOR); + + A3DBone * pBoneLCalf = pSkeleton->GetBone("Bip01 L Calf", NULL); + if( pBoneLCalf ) + { + pBoneLCalf->SetScaleFactor(vecScale); + pBoneLCalf->SetScaleType(A3DBone::SCALE_LOCAL); + } + A3DBone * pBoneRCalf = pSkeleton->GetBone("Bip01 R Calf", NULL); + if( pBoneRCalf ) + { + pBoneRCalf->SetScaleFactor(vecScale); + pBoneRCalf->SetScaleType(A3DBone::SCALE_LOCAL); + } + ScaleBody(m_fScaleBySkill); + } +} + +//更新头发模型及贴图(与头盔模型(或时装头饰)显示相关) +void CECPlayer::UpdateHairModel(bool bUpdateAll, int iArmetID) +{ + if( (m_iProfession == PROF_ORC) || + (!m_pFaceModel || !m_pFaceModel->GetFaceData()) || + (iArmetID == -1)) + return; + + iArmetID &= 0xffff; + + // iArmetID为0时,恢复形象的默认个性化数据 + int idHairModel = m_CustomizeData.faceData.idHairModel; + int idHairTex = m_CustomizeData.faceData.idHairTex; + + if(iArmetID != 0) + { + // 根据iArmetID找到相应头盔(或时装头饰),读取对应的idHairModel和idHairTex + DATA_TYPE dt; + ARMOR_ESSENCE * pArmorHead = (ARMOR_ESSENCE *) g_pGame->GetElementDataMan()->get_data_ptr(iArmetID, ID_SPACE_ESSENCE, dt); + + if(dt != DT_ARMOR_ESSENCE) // 非头盔id,则为时装头饰id + { + FASHION_ESSENCE* pFashionHead = (FASHION_ESSENCE*)g_pGame->GetElementDataMan()->get_data_ptr(iArmetID, ID_SPACE_ESSENCE, dt); + ASSERT(dt == DT_FASHION_ESSENCE); + + if(dt == DT_FASHION_ESSENCE && pFashionHead) + { + idHairModel = pFashionHead->id_hair; + idHairTex = pFashionHead->id_hair_texture; + } + } + else if( pArmorHead ) + { + if(pArmorHead->id_hair != 0) + idHairModel = pArmorHead->id_hair; + + if(pArmorHead->id_hair_texture != 0) + idHairTex = pArmorHead->id_hair_texture; + } + } + + m_pFaceModel->GetFaceData()->idHairModel = idHairModel; + m_pFaceModel->GetFaceData()->idHairTex = idHairTex; + + if (bUpdateAll) + m_pFaceModel->UpdateAll(); +} + +bool CECPlayer::ValidateCustomizeData(int nProf, int nGender, PLAYER_CUSTOMIZEDATA &data) +{ + bool modified(false); + + static const char *s_hairModel[NUM_PROFESSION][2] = { + {"facedata\\武侠男\\预定义\\01武侠男标准1.ini", "facedata\\法师女\\预定义\\武侠女标准1.ini"}, // 武侠 + {"facedata\\武侠男\\预定义\\法师男标准1.ini", "facedata\\法师女\\预定义\\01法师女标准1.ini"}, // 法师 + {"facedata\\影族男\\预定义\\巫师男标准01.ini", "facedata\\影族女\\预定义\\巫师女标准01.ini"}, // 巫师 + {"", "facedata\\法师女\\预定义\\妖精女标准1.ini"}, // 妖精 + {"facedata\\妖兽男\\预定义\\妖兽男标准.ini", ""}, // 妖兽 + {"facedata\\影族男\\预定义\\刺客男标准01.ini", "facedata\\影族女\\预定义\\刺客女标准01.ini"}, // 刺客 + {"facedata\\武侠男\\预定义\\羽灵男标准1.ini", "facedata\\法师女\\预定义\\羽芒女标准1.ini"}, // 羽芒 + {"facedata\\武侠男\\预定义\\羽灵男标准1.ini", "facedata\\法师女\\预定义\\羽灵女标准1.ini"}, // 羽灵 + {"facedata\\灵族男\\预定义\\剑灵男标准01.ini", "facedata\\灵族女\\预定义\\剑灵女标准01.ini"}, // 剑灵 + {"facedata\\灵族男\\预定义\\魅灵男标准01.ini", "facedata\\灵族女\\预定义\\魅灵女标准01.ini"}, // 魅灵 + {"facedata\\胧族男\\预定义\\夜影男标准01.ini", "facedata\\胧族女\\预定义\\夜影女标准01.ini"}, // 夜影 + {"facedata\\胧族男\\预定义\\月仙男标准01.ini", "facedata\\胧族女\\预定义\\月仙女标准01.ini"}, // 月仙 + }; + + static int s_idHairModel[NUM_PROFESSION][2] = {0}; + static int s_idHairTex[NUM_PROFESSION][2] = {0}; + static bool s_loaded[NUM_PROFESSION][2]; + + int &defaultHairModel = s_idHairModel[nProf][nGender]; + int &defaultHairTex = s_idHairTex[nProf][nGender]; + bool &loaded = s_loaded[nProf][nGender]; + + // Check and load default hair model and tex + if (!loaded) + { + defaultHairModel = 0; + defaultHairTex = 0; + loaded = true; + + // Get from file + const char *szFile = s_hairModel[nProf][nGender]; + + if (szFile && strlen(szFile)>0) + { + AIniFile IniFile; + if(!IniFile.Open(szFile)) + { + a_LogOutput(1, "CECPlayer::ValidateCustomizeData, Failed to open file %s", szFile); + } + else + { + AString strSection = "Hair"; + defaultHairModel = IniFile.GetValueAsInt(strSection, "idHairModel", 0); + defaultHairTex = IniFile.GetValueAsInt(strSection, "idHairTex", 0); + IniFile.Close(); + } + } + } + + elementdataman* pElementDataMan = g_pGame->GetElementDataMan(); + + unsigned short &idHairModel = data.faceData.idHairModel; + unsigned short &idHairTex = data.faceData.idHairTex; + + DATA_TYPE datatype; + if (defaultHairModel && idHairModel) + { + FACE_HAIR_ESSENCE* pEssence = (FACE_HAIR_ESSENCE*)pElementDataMan->get_data_ptr(idHairModel, ID_SPACE_FACE, datatype); + if (pEssence && datatype==DT_FACE_HAIR_ESSENCE && pEssence->fashion_head_only) + { + // Hair model is invalid + a_LogOutput(1, "CECPlayer::ValidateCustomizeData, idHairModel(%d) is invalid and set to default", idHairModel); + idHairModel = defaultHairModel; + modified = true; + } + } + if (defaultHairTex && idHairTex) + { + FACE_TEXTURE_ESSENCE* pEssence = (FACE_TEXTURE_ESSENCE*)pElementDataMan->get_data_ptr(idHairTex, ID_SPACE_FACE, datatype); + if (pEssence && datatype==DT_FACE_TEXTURE_ESSENCE && pEssence->fashion_head_only) + { + // Hair tex is invalid + a_LogOutput(1, "CECPlayer::ValidateCustomizeData, idHairTex(%d) is invalid and set to default", idHairTex); + idHairTex = defaultHairTex; + modified = true; + } + } + + // only allowed the third eye in these professions + unsigned short &idThirdEye = data.faceData.idThirdEye; + if( nProf != PROF_MEILING && + nProf != PROF_JIANLING && + idThirdEye != 0) + { + a_LogOutput(1, "CECPlayer::ValidateCustomizeData, idThirdEye(%d) is invalid and set to default", idThirdEye); + idThirdEye = 0; + modified = true; + } + + return modified; +} + +// Change customize data +bool CECPlayer::ChangeCustomizeData(const PLAYER_CUSTOMIZEDATA& data, bool bApply/* true */) +{ + if( data.dwVersion >= CUSTOMIZE_DATA_VERSION_1 && + data.dwVersion <= CUSTOMIZE_DATA_VERSION && + data.faceData.scaleUp > 0.0f ) + { + if( !m_pFaceModel || !GetMajorModel() ) + bApply = false; + + if (bApply && m_CustomizeData.bodyID != data.bodyID) + { + ASSERT(ShouldUseClothedModel()); // 这里不可能是简单模型模式 + + // We should reload a new body + if (!LoadBodySkin(data.bodyID)) + return false; + + if (!LoadFaceModel(m_iProfession, m_iGender, data.bodyID)) + { + a_LogOutput(1, "CECPlayer::ChangeCustomizeData, Failed to load face model"); + return false; + } + } + + m_CustomizeData = data; + + // 某些不允许的个性化情况(如时装头巾被用来替换非时装状态下的默认头发) + ValidateCustomizeData(GetProfession(), GetGender(), m_CustomizeData); + + if (bApply) + { + if (m_pFaceModel) + m_pFaceModel->SetFaceData(m_CustomizeData.faceData); + + if(!InFashionMode()) + UpdateHairModel(true); + + SetBodyColor(data.colorBody); + UpdateBodyScales(); + } + + SetResReadyFlag(RESFG_CUSTOM, true); + return true; + } + else + { + if( m_pFaceModel ) + m_CustomizeData.faceData = *m_pFaceModel->GetFaceData(); + + SetResReadyFlag(RESFG_CUSTOM, true); + return false; + } +} + +// 更新形象的默认个性化数据 +bool CECPlayer::SaveAsDefaultCustomizeData() +{ + const CECFace::FACE_CUSTOMIZEDATA& faceData = m_CustomizeData.faceData; + + AIniFile IniFile; + char szFile[256]; + sprintf(szFile, "userdata\\character\\character%d%d.ini", m_iProfession, m_iGender); + + IniFile.Open(szFile); + + //3庭3 + AString strSection = "3Parts"; + IniFile.WriteIntValue(strSection, "scaleUp", faceData.scaleUp); + IniFile.WriteIntValue(strSection, "scaleMiddle", faceData.scaleMiddle); + IniFile.WriteIntValue(strSection, "scaleDown", faceData.scaleDown); + + //脸型融合3 + strSection = "BlendFace"; + IniFile.WriteIntValue(strSection, "idFaceShape1", faceData.idFaceShape1); + IniFile.WriteIntValue(strSection, "idFaceShape2", faceData.idFaceShape2); + IniFile.WriteIntValue(strSection, "blendFaceShape", faceData.blendFaceShape); + + //脸盘调整3 + strSection = "Face"; + IniFile.WriteIntValue(strSection, "scaleFaceH", faceData.scaleFaceH); + IniFile.WriteIntValue(strSection, "scaleFaceV", faceData.scaleFaceV); + IniFile.WriteIntValue(strSection, "idFaceTex", faceData.idFaceTex); + + //法令 + strSection = "Faling"; + IniFile.WriteIntValue(strSection, "idFalingSkin", faceData.idFalingSkin); + IniFile.WriteIntValue(strSection, "idFalingTex", faceData.idFalingTex); + + //额头5 + strSection = "Forehead"; + IniFile.WriteIntValue(strSection, "offsetForeheadH", faceData.offsetForeheadH); + IniFile.WriteIntValue(strSection, "offsetForeheadV", faceData.offsetForeheadV); + IniFile.WriteIntValue(strSection, "offsetForeheadZ", faceData.offsetForeheadZ); + IniFile.WriteIntValue(strSection, "rotateForehead", faceData.rotateForehead); + IniFile.WriteIntValue(strSection, "scaleForehead", faceData.scaleForehead); + + //颧骨5 + strSection = "YokeBone"; + IniFile.WriteIntValue(strSection, "offsetYokeBoneH", faceData.offsetYokeBoneH); + IniFile.WriteIntValue(strSection, "offsetYokeBoneV", faceData.offsetYokeBoneV); + IniFile.WriteIntValue(strSection, "offsetYokeBoneZ", faceData.offsetYokeBoneZ); + IniFile.WriteIntValue(strSection, "rotateYokeBone", faceData.rotateYokeBone); + IniFile.WriteIntValue(strSection, "scaleYokeBone", faceData.scaleYokeBone); + + //脸颊4 + strSection = "Cheek"; + IniFile.WriteIntValue(strSection, "offsetCheekH", faceData.offsetCheekH); + IniFile.WriteIntValue(strSection, "offsetCheekV", faceData.offsetCheekV); + IniFile.WriteIntValue(strSection, "offsetCheekZ", faceData.offsetCheekZ); + IniFile.WriteIntValue(strSection, "scaleCheek", faceData.scaleCheek); + + //下巴4 + strSection = "Chain"; + IniFile.WriteIntValue(strSection, "offsetChainV", faceData.offsetChainV); + IniFile.WriteIntValue(strSection, "offsetChainZ", faceData.offsetChainZ); + IniFile.WriteIntValue(strSection, "rotateChain", faceData.rotateChain); + IniFile.WriteIntValue(strSection, "scaleChainH", faceData.scaleChainH); + + //颌骨6 + strSection = "Jaw"; + IniFile.WriteIntValue(strSection, "offsetJawH", faceData.offsetJawH); + IniFile.WriteIntValue(strSection, "offsetJawV", faceData.offsetJawV); + IniFile.WriteIntValue(strSection, "offsetJawZ", faceData.offsetJawZ); + IniFile.WriteIntValue(strSection, "scaleJawSpecial", faceData.scaleJawSpecial); + IniFile.WriteIntValue(strSection, "scaleJawH", faceData.scaleJawH); + IniFile.WriteIntValue(strSection, "scaleJawV", faceData.scaleJawV); + + //眼睛18 + strSection = "Eye"; + IniFile.WriteIntValue(strSection, "idThirdEye", faceData.idThirdEye); + IniFile.WriteIntValue(strSection, "idEyeBaseTex", faceData.idEyeBaseTex); + IniFile.WriteIntValue(strSection, "idEyeHighTex", faceData.idEyeHighTex); + IniFile.WriteIntValue(strSection, "idEyeBallTex", faceData.idEyeBallTex); + IniFile.WriteIntValue(strSection, "idEyeShape", faceData.idEyeShape); + IniFile.WriteIntValue(strSection, "scaleEyeH", faceData.scaleEyeH); + IniFile.WriteIntValue(strSection, "scaleEyeV", faceData.scaleEyeV); + IniFile.WriteIntValue(strSection, "rotateEye", faceData.rotateEye); + IniFile.WriteIntValue(strSection, "offsetEyeH", faceData.offsetEyeH); + IniFile.WriteIntValue(strSection, "offsetEyeV", faceData.offsetEyeV); + IniFile.WriteIntValue(strSection, "offseteyeZ", faceData.offsetEyeZ); + IniFile.WriteIntValue(strSection, "scaleEyeBall", faceData.scaleEyeBall); + + IniFile.WriteIntValue(strSection, "scaleEyeH2", faceData.scaleEyeH2); + IniFile.WriteIntValue(strSection, "scaleEyeV2", faceData.scaleEyeV2); + IniFile.WriteIntValue(strSection, "rotateEye2", faceData.rotateEye2); + IniFile.WriteIntValue(strSection, "offsetEyeH2", faceData.offsetEyeH2); + IniFile.WriteIntValue(strSection, "offsetEyeV2", faceData.offsetEyeV2); + IniFile.WriteIntValue(strSection, "offseteyeZ2", faceData.offsetEyeZ2); + IniFile.WriteIntValue(strSection, "scaleEyeBall2", faceData.scaleEyeBall2); + + //眉毛14 + strSection = "Brow"; + IniFile.WriteIntValue(strSection, "idBrowTex", faceData.idBrowTex); + IniFile.WriteIntValue(strSection, "idBrowShape", faceData.idBrowShape); + IniFile.WriteIntValue(strSection, "scaleBrowH", faceData.scaleBrowH); + IniFile.WriteIntValue(strSection, "scaleBrowV", faceData.scaleBrowV); + IniFile.WriteIntValue(strSection, "rotateBrow", faceData.rotateBrow); + IniFile.WriteIntValue(strSection, "offsetBrowH", faceData.offsetBrowH); + IniFile.WriteIntValue(strSection, "offsetBrowV", faceData.offsetBrowV); + IniFile.WriteIntValue(strSection, "offsetBrowZ", faceData.offsetBrowZ); + + IniFile.WriteIntValue(strSection, "scaleBrowH2", faceData.scaleBrowH2); + IniFile.WriteIntValue(strSection, "scaleBrowV2", faceData.scaleBrowV2); + IniFile.WriteIntValue(strSection, "rotateBrow2", faceData.rotateBrow2); + IniFile.WriteIntValue(strSection, "offsetBrowH2", faceData.offsetBrowH2); + IniFile.WriteIntValue(strSection, "offsetBrowV2", faceData.offsetBrowV2); + IniFile.WriteIntValue(strSection, "offsetBrowZ2", faceData.offsetBrowZ2); + + //鼻子8 + strSection = "Nose"; + IniFile.WriteIntValue(strSection, "idNoseTex", faceData.idNoseTex); + IniFile.WriteIntValue(strSection, "idNoseTipShape", faceData.idNoseTipShape); + IniFile.WriteIntValue(strSection, "scaleNoseTipH", faceData.scaleNoseTipH); + IniFile.WriteIntValue(strSection, "scaleNoseTipV", faceData.scaleNoseTipV); + IniFile.WriteIntValue(strSection, "scaleNoseTipZ", faceData.scaleNoseTipZ); + IniFile.WriteIntValue(strSection, "offsetNoseTipV", faceData.offsetNoseTipV); + IniFile.WriteIntValue(strSection, "idNoseBridgeShape", faceData.idNoseBridgeShape); + IniFile.WriteIntValue(strSection, "scaleBridgeTipH", faceData.scaleBridgeTipH); + IniFile.WriteIntValue(strSection, "offsetBridgeTipZ", faceData.offsetBridgeTipZ); + + //嘴9 + strSection = "Mouth"; + IniFile.WriteIntValue (strSection, "idMouthUpLipLine", faceData.idMouthUpLipLine); + IniFile.WriteIntValue(strSection, "idMouthMidLipLine", faceData.idMouthMidLipLine); + IniFile.WriteIntValue(strSection, "idMouthDownLipLine", faceData.idMouthDownLipLine); + IniFile.WriteIntValue(strSection, "thickUpLip", faceData.thickUpLip); + IniFile.WriteIntValue(strSection, "thickDownLip", faceData.thickDownLip); + IniFile.WriteIntValue(strSection, "scaleMouthH", faceData.scaleMouthH); + IniFile.WriteIntValue(strSection, "offsetMouthV", faceData.offsetMouthV); + IniFile.WriteIntValue(strSection, "offsetMOuthZ", faceData.offsetMouthZ); + IniFile.WriteIntValue(strSection, "idMouthTex", faceData.idMouthTex); + IniFile.WriteIntValue(strSection, "offsetCornerOfMouthSpecial", faceData.offsetCornerOfMouthSpecial); + + IniFile.WriteIntValue(strSection, "scaleMouthH2", faceData.scaleMouthH2); + IniFile.WriteIntValue(strSection, "offsetCornerOfMouthSpecial2", faceData.offsetCornerOfMouthSpecial2); + + //耳朵3 + strSection = "Ear"; + IniFile.WriteIntValue(strSection, "idEarShape", faceData.idEarShape); + IniFile.WriteIntValue(strSection, "scaleEar", faceData.scaleEar); + IniFile.WriteIntValue(strSection, "offsetEarV", faceData.offsetEarV); + + //头发1 + strSection = "Hair"; + IniFile.WriteIntValue(strSection, "idHairModel", faceData.idHairModel); + IniFile.WriteIntValue(strSection, "idHairTex", faceData.idHairTex); + + //胡子2 + strSection = "Moustache"; + IniFile.WriteIntValue(strSection, "idMoustacheTex", faceData.idMoustacheTex); + IniFile.WriteIntValue(strSection, "idMoustacheSkin", faceData.idMoustacheSkin); + IniFile.WriteIntValue(strSection, "idGoateeTex", faceData.idGoateeTex); + + //各部分颜色7 + strSection = "Color"; + IniFile.WriteIntValue(strSection, "colorHair", faceData.colorHair); + IniFile.WriteIntValue(strSection, "colorFace", faceData.colorFace); + IniFile.WriteIntValue(strSection, "colorEye", faceData.colorEye); + IniFile.WriteIntValue(strSection, "colorBrow", faceData.colorBrow); + IniFile.WriteIntValue(strSection, "colorMouth", faceData.colorMouth); + IniFile.WriteIntValue(strSection, "colorEyeBall", faceData.colorEyeBall); + IniFile.WriteIntValue(strSection, "colorMoustache", faceData.colorMoustache); + + //身体参数 + strSection = "Body"; + IniFile.WriteIntValue(strSection, "bodyID", m_CustomizeData.bodyID); + IniFile.WriteIntValue(strSection, "colorBody", m_CustomizeData.colorBody); + IniFile.WriteIntValue(strSection, "headScale", m_CustomizeData.headScale); + IniFile.WriteIntValue(strSection, "upScale", m_CustomizeData.upScale); + IniFile.WriteIntValue(strSection, "waistScale", m_CustomizeData.waistScale); + IniFile.WriteIntValue(strSection, "armWidth", m_CustomizeData.armWidth); + IniFile.WriteIntValue(strSection, "legWidth", m_CustomizeData.legWidth); + IniFile.WriteIntValue(strSection, "breastScale", m_CustomizeData.breastScale); + + IniFile.Save(szFile); + IniFile.Close(); + + return true; +} + +// 载入形象的默认个性化数据 +bool CECPlayer::LoadDefaultCustomizeData(int prof, int gender, PLAYER_CUSTOMIZEDATA &data) +{ + memset(&data, 0, sizeof(data)); + data.dwVersion = CUSTOMIZE_DATA_VERSION; + data.colorBody = 0xffffffff; + data.headScale = 128; + data.upScale = 128; + data.waistScale = 128; + data.armWidth = 128; + data.legWidth = 128; + data.breastScale = 128; + + AIniFile IniFile; + char szFile[256]; + sprintf(szFile, "userdata\\character\\character%d%d.ini", prof, gender); + + if(!IniFile.Open(szFile)) + { + a_LogOutput(1, "CECPlayer::LoadDefaultCustomizeData, Failed to open file %s", szFile); + return false; + } + //3庭3 + AString strSection = "3Parts"; + data.faceData.scaleUp = IniFile.GetValueAsInt(strSection, "scaleUp", data.faceData.scaleUp); + data.faceData.scaleMiddle = IniFile.GetValueAsInt(strSection, "scaleMiddle", data.faceData.scaleMiddle); + data.faceData.scaleDown = IniFile.GetValueAsInt(strSection, "scaleDown", data.faceData.scaleDown); + + //脸型融合3 + strSection = "BlendFace"; + data.faceData.idFaceShape1 = IniFile.GetValueAsInt(strSection, "idFaceShape1", data.faceData.idFaceShape1); + data.faceData.idFaceShape2 = IniFile.GetValueAsInt(strSection, "idFaceShape2", data.faceData.idFaceShape2); + data.faceData.blendFaceShape = IniFile.GetValueAsInt(strSection, "blendFaceShape", data.faceData.blendFaceShape); + + //脸盘调整3 + strSection = "Face"; + data.faceData.scaleFaceH = IniFile.GetValueAsInt(strSection, "scaleFaceH", data.faceData.scaleFaceH); + data.faceData.scaleFaceV = IniFile.GetValueAsInt(strSection, "scaleFaceV", data.faceData.scaleFaceV); + data.faceData.idFaceTex = IniFile.GetValueAsInt(strSection, "idFaceTex", data.faceData.idFaceTex); + + //法令 + strSection = "Faling"; + data.faceData.idFalingSkin = IniFile.GetValueAsInt(strSection, "idFalingSkin", data.faceData.idFalingSkin); + data.faceData.idFalingTex = IniFile.GetValueAsInt(strSection, "idFalingTex", data.faceData.idFalingTex); + + //额头5 + strSection = "Forehead"; + data.faceData.offsetForeheadH = IniFile.GetValueAsInt(strSection, "offsetForeheadH", data.faceData.offsetForeheadH); + data.faceData.offsetForeheadV = IniFile.GetValueAsInt(strSection, "offsetForeheadV", data.faceData.offsetForeheadV); + data.faceData.offsetForeheadZ = IniFile.GetValueAsInt(strSection, "offsetForeheadZ", data.faceData.offsetForeheadZ); + data.faceData.rotateForehead = IniFile.GetValueAsInt(strSection, "rotateForehead", data.faceData.rotateForehead); + data.faceData.scaleForehead = IniFile.GetValueAsInt(strSection, "scaleForehead", data.faceData.scaleForehead); + + //颧骨5 + strSection = "YokeBone"; + data.faceData.offsetYokeBoneH = IniFile.GetValueAsInt(strSection, "offsetYokeBoneH", data.faceData.offsetYokeBoneH); + data.faceData.offsetYokeBoneV = IniFile.GetValueAsInt(strSection, "offsetYokeBoneV", data.faceData.offsetYokeBoneV); + data.faceData.offsetYokeBoneZ = IniFile.GetValueAsInt(strSection, "offsetYokeBoneZ", data.faceData.offsetYokeBoneZ); + data.faceData.rotateYokeBone = IniFile.GetValueAsInt(strSection, "rotateYokeBone", data.faceData.rotateYokeBone); + data.faceData.scaleYokeBone = IniFile.GetValueAsInt(strSection, "scaleYokeBone", data.faceData.scaleYokeBone); + + //脸颊4 + strSection = "Cheek"; + data.faceData.offsetCheekH = IniFile.GetValueAsInt(strSection, "offsetCheekH", data.faceData.offsetCheekH); + data.faceData.offsetCheekV = IniFile.GetValueAsInt(strSection, "offsetCheekV", data.faceData.offsetCheekV); + data.faceData.offsetCheekZ = IniFile.GetValueAsInt(strSection, "offsetCheekZ", data.faceData.offsetCheekZ); + data.faceData.scaleCheek = IniFile.GetValueAsInt(strSection, "scaleCheek", data.faceData.scaleCheek); + + //下巴4 + strSection = "Chain"; + data.faceData.offsetChainV = IniFile.GetValueAsInt(strSection, "offsetChainV", data.faceData.offsetChainV); + data.faceData.offsetChainZ = IniFile.GetValueAsInt(strSection, "offsetChainZ", data.faceData.offsetChainZ); + data.faceData.rotateChain = IniFile.GetValueAsInt(strSection, "rotateChain", data.faceData.rotateChain); + data.faceData.scaleChainH = IniFile.GetValueAsInt(strSection, "scaleChainH", data.faceData.scaleChainH); + + //颌骨6 + strSection = "Jaw"; + data.faceData.offsetJawH = IniFile.GetValueAsInt(strSection, "offsetJawH", data.faceData.offsetJawH); + data.faceData.offsetJawV = IniFile.GetValueAsInt(strSection, "offsetJawV", data.faceData.offsetJawV); + data.faceData.offsetJawZ = IniFile.GetValueAsInt(strSection, "offsetJawZ", data.faceData.offsetJawZ); + data.faceData.scaleJawSpecial = IniFile.GetValueAsInt(strSection, "scaleJawSpecial", data.faceData.scaleJawSpecial); + data.faceData.scaleJawH = IniFile.GetValueAsInt(strSection, "scaleJawH", data.faceData.scaleJawH); + data.faceData.scaleJawV = IniFile.GetValueAsInt(strSection, "scaleJawV", data.faceData.scaleJawV); + + //眼睛18 + strSection = "Eye"; + data.faceData.idThirdEye = IniFile.GetValueAsInt(strSection, "idThirdEye", data.faceData.idThirdEye); + data.faceData.idEyeBaseTex = IniFile.GetValueAsInt(strSection, "idEyeBaseTex", data.faceData.idEyeBaseTex); + data.faceData.idEyeHighTex = IniFile.GetValueAsInt(strSection, "idEyeHighTex", data.faceData.idEyeHighTex); + data.faceData.idEyeBallTex = IniFile.GetValueAsInt(strSection, "idEyeBallTex", data.faceData.idEyeBallTex); + data.faceData.idEyeShape = IniFile.GetValueAsInt(strSection, "idEyeShape", data.faceData.idEyeShape); + data.faceData.scaleEyeH = IniFile.GetValueAsInt(strSection, "scaleEyeH", data.faceData.scaleEyeH); + data.faceData.scaleEyeV = IniFile.GetValueAsInt(strSection, "scaleEyeV", data.faceData.scaleEyeV); + data.faceData.rotateEye = IniFile.GetValueAsInt(strSection, "rotateEye", data.faceData.rotateEye); + data.faceData.offsetEyeH = IniFile.GetValueAsInt(strSection, "offsetEyeH", data.faceData.offsetEyeH); + data.faceData.offsetEyeV = IniFile.GetValueAsInt(strSection, "offsetEyeV", data.faceData.offsetEyeV); + data.faceData.offsetEyeZ = IniFile.GetValueAsInt(strSection, "offseteyeZ", data.faceData.offsetEyeZ); + data.faceData.scaleEyeBall = IniFile.GetValueAsInt(strSection, "scaleEyeBall", data.faceData.scaleEyeBall); + + data.faceData.scaleEyeH2 = IniFile.GetValueAsInt(strSection, "scaleEyeH2", data.faceData.scaleEyeH2); + data.faceData.scaleEyeV2 = IniFile.GetValueAsInt(strSection, "scaleEyeV2", data.faceData.scaleEyeV2); + data.faceData.rotateEye2= IniFile.GetValueAsInt(strSection, "rotateEye2", data.faceData.rotateEye2); + data.faceData.offsetEyeH2 = IniFile.GetValueAsInt(strSection, "offsetEyeH2", data.faceData.offsetEyeH2); + data.faceData.offsetEyeV2 = IniFile.GetValueAsInt(strSection, "offsetEyeV2", data.faceData.offsetEyeV2); + data.faceData.offsetEyeZ2 = IniFile.GetValueAsInt(strSection, "offseteyeZ2", data.faceData.offsetEyeZ2); + data.faceData.scaleEyeBall2 = IniFile.GetValueAsInt(strSection, "scaleEyeBall2", data.faceData.scaleEyeBall2); + + //眉毛14 + strSection = "Brow"; + data.faceData.idBrowTex = IniFile.GetValueAsInt(strSection, "idBrowTex", data.faceData.idBrowTex); + data.faceData.idBrowShape = IniFile.GetValueAsInt(strSection, "idBrowShape", data.faceData.idBrowShape); + data.faceData.scaleBrowH = IniFile.GetValueAsInt(strSection, "scaleBrowH", data.faceData.scaleBrowH); + data.faceData.scaleBrowV = IniFile.GetValueAsInt(strSection, "scaleBrowV", data.faceData.scaleBrowV); + data.faceData.rotateBrow = IniFile.GetValueAsInt(strSection, "rotateBrow", data.faceData.rotateBrow); + data.faceData.offsetBrowH = IniFile.GetValueAsInt(strSection, "offsetBrowH", data.faceData.offsetBrowH); + data.faceData.offsetBrowV = IniFile.GetValueAsInt(strSection, "offsetBrowV", data.faceData.offsetBrowV); + data.faceData.offsetBrowZ = IniFile.GetValueAsInt(strSection, "offsetBrowZ", data.faceData.offsetBrowZ); + + data.faceData.scaleBrowH2 = IniFile.GetValueAsInt(strSection, "scaleBrowH2", data.faceData.scaleBrowH2); + data.faceData.scaleBrowV2 = IniFile.GetValueAsInt(strSection, "scaleBrowV2", data.faceData.scaleBrowV2); + data.faceData.rotateBrow2 = IniFile.GetValueAsInt(strSection, "rotateBrow2", data.faceData.rotateBrow2); + data.faceData.offsetBrowH2 = IniFile.GetValueAsInt(strSection, "offsetBrowH2", data.faceData.offsetBrowH2); + data.faceData.offsetBrowV2 = IniFile.GetValueAsInt(strSection, "offsetBrowV2", data.faceData.offsetBrowV2); + data.faceData.offsetBrowZ2 = IniFile.GetValueAsInt(strSection, "offsetBrowZ2", data.faceData.offsetBrowZ2); + + //鼻子9 + strSection = "Nose"; + data.faceData.idNoseTex = IniFile.GetValueAsInt(strSection, "idNoseTex", data.faceData.idNoseTex); + data.faceData.idNoseTipShape = IniFile.GetValueAsInt(strSection, "idNoseTipShape", data.faceData.idNoseTipShape); + data.faceData.scaleNoseTipH = IniFile.GetValueAsInt(strSection, "scaleNoseTipH", data.faceData.scaleNoseTipH); + data.faceData.scaleNoseTipV = IniFile.GetValueAsInt(strSection, "scaleNoseTipV", data.faceData.scaleNoseTipV); + data.faceData.scaleNoseTipZ = IniFile.GetValueAsInt(strSection, "scaleNoseTipZ", data.faceData.scaleNoseTipZ); + data.faceData.offsetNoseTipV = IniFile.GetValueAsInt(strSection, "offsetNoseTipV", data.faceData.offsetNoseTipV); + data.faceData.idNoseBridgeShape = IniFile.GetValueAsInt(strSection, "idNoseBridgeShape", data.faceData.idNoseBridgeShape); + data.faceData.scaleBridgeTipH = IniFile.GetValueAsInt(strSection, "scaleBridgeTipH", data.faceData.scaleBridgeTipH); + data.faceData.offsetBridgeTipZ = IniFile.GetValueAsInt(strSection, "OffsetBridgeTipZ", data.faceData.offsetBridgeTipZ); + + //嘴9 + strSection = "Mouth"; + data.faceData.idMouthUpLipLine = IniFile.GetValueAsInt (strSection, "idMouthUpLipLine", data.faceData.idMouthUpLipLine); + data.faceData.idMouthMidLipLine = IniFile.GetValueAsInt(strSection, "idMouthMidLipLine", data.faceData.idMouthMidLipLine); + data.faceData.idMouthDownLipLine = IniFile.GetValueAsInt(strSection, "idMouthDownLipLine", data.faceData.idMouthDownLipLine); + data.faceData.thickUpLip = IniFile.GetValueAsInt(strSection, "thickUpLip", data.faceData.thickUpLip); + data.faceData.thickDownLip = IniFile.GetValueAsInt(strSection, "thickDownLip", data.faceData.thickDownLip); + data.faceData.scaleMouthH = IniFile.GetValueAsInt(strSection, "scaleMouthH", data.faceData.scaleMouthH); + + data.faceData.offsetMouthV = IniFile.GetValueAsInt(strSection, "offsetMouthV", data.faceData.offsetMouthV); + data.faceData.offsetMouthZ = IniFile.GetValueAsInt(strSection, "offsetMOuthZ", data.faceData.offsetMouthZ); + data.faceData.idMouthTex = IniFile.GetValueAsInt(strSection, "idMouthTex", data.faceData.idMouthTex); + data.faceData.offsetCornerOfMouthSpecial = IniFile.GetValueAsInt(strSection, "offsetCornerOfMouthSpecial", data.faceData.offsetCornerOfMouthSpecial); + + data.faceData.scaleMouthH2 = IniFile.GetValueAsInt(strSection, "scaleMouthH2", data.faceData.scaleMouthH2); + data.faceData.offsetCornerOfMouthSpecial2 = IniFile.GetValueAsInt(strSection, "offsetCornerOfMouthSpecial2", data.faceData.offsetCornerOfMouthSpecial2); + + //耳朵3 + strSection = "Ear"; + data.faceData.idEarShape = IniFile.GetValueAsInt(strSection, "idEarShape", data.faceData.idEarShape); + data.faceData.scaleEar = IniFile.GetValueAsInt(strSection, "scaleEar", data.faceData.scaleEar); + data.faceData.offsetEarV = IniFile.GetValueAsInt(strSection, "offsetEarV", data.faceData.offsetEarV); + + //头发1 + strSection = "Hair"; + data.faceData.idHairModel = IniFile.GetValueAsInt(strSection, "idHairModel", data.faceData.idHairModel); + data.faceData.idHairTex = IniFile.GetValueAsInt(strSection, "idHairTex", data.faceData.idHairTex); + + //胡子2 + strSection = "Moustache"; + data.faceData.idMoustacheTex = IniFile.GetValueAsInt(strSection, "idMoustacheTex", data.faceData.idMoustacheTex); + data.faceData.idMoustacheSkin = IniFile.GetValueAsInt(strSection, "idMoustacheSkin", data.faceData.idMoustacheSkin); + data.faceData.idGoateeTex = IniFile.GetValueAsInt(strSection, "idGoateeTex", data.faceData.idGoateeTex); + + //各部分颜色7 + strSection = "Color"; + data.faceData.colorHair = IniFile.GetValueAsInt(strSection, "colorHair", data.faceData.colorHair); + data.faceData.colorFace = IniFile.GetValueAsInt(strSection, "colorFace", data.faceData.colorFace); + data.faceData.colorEye = IniFile.GetValueAsInt(strSection, "colorEye", data.faceData.colorEye); + data.faceData.colorBrow = IniFile.GetValueAsInt(strSection, "colorBrow", data.faceData.colorBrow); + data.faceData.colorMouth = IniFile.GetValueAsInt(strSection, "colorMouth", data.faceData.colorMouth); + data.faceData.colorEyeBall = IniFile.GetValueAsInt(strSection, "colorEyeBall", data.faceData.colorEyeBall); + data.faceData.colorMoustache = IniFile.GetValueAsInt(strSection, "colorMoustache", data.faceData.colorMoustache); + + // 身体参数 + strSection = "Body"; + data.bodyID = IniFile.GetValueAsInt(strSection, "bodyID", data.bodyID); + data.colorBody = IniFile.GetValueAsInt(strSection, "colorBody", data.colorBody); + data.headScale = IniFile.GetValueAsInt(strSection, "headScale", data.headScale); + data.upScale = IniFile.GetValueAsInt(strSection, "upScale", data.upScale); + data.waistScale = IniFile.GetValueAsInt(strSection, "waistScale", data.waistScale); + data.armWidth = IniFile.GetValueAsInt(strSection, "armWidth", data.armWidth); + data.legWidth = IniFile.GetValueAsInt(strSection, "legWidth", data.legWidth); + data.breastScale = IniFile.GetValueAsInt(strSection, "breastScale", data.breastScale); + + IniFile.Close(); + return true; +} + +// Store player's customize data for later restore +void CECPlayer::StoreCustomizeData() +{ + memcpy(&m_OldCustomizeData, &m_CustomizeData, sizeof(PLAYER_CUSTOMIZEDATA)); +} + +// Retore player's customize data +void CECPlayer::RestoreCustomizeData() +{ + ChangeCustomizeData(m_OldCustomizeData); +} + +// Show / hide wing +void CECPlayer::ShowWing(bool bShow) +{ + if (m_pPlayerModel) + { + CECModel* pWing = m_pPlayerModel->GetChildModel(_wing); + if (pWing) pWing->Show(bShow); + + CECModel* pWing2 = m_pPlayerModel->GetChildModel(_wing2); + if (pWing2) pWing2->Show(bShow); + } +} + +enumWingType CECPlayer::FlyMode2WingType(unsigned int flymode) { + switch (flymode){ + case 0: + return WINGTYPE_FLYSWORD; + case 1: + return WINGTYPE_WING; + case 2: + return WINGTYPE_DOUBLEWHEEL; + default: + ASSERT(false && AString().Format("unknow fly mode: %d", flymode)); + return WINGTYPE_FLYSWORD; + } +} + +// Show / hide wing +void CECPlayer::ShowWeapon(bool bShow) +{ + if (m_pPlayerModel) + { + CECModel* pLeftHandWeapon = GetLeftHandWeapon(); + if (pLeftHandWeapon) + pLeftHandWeapon->Show(bShow); + + CECModel* pRightHandWeapon = GetRightHandWeapon(); + if (pRightHandWeapon) + pRightHandWeapon->Show(bShow); + } +} + +void CECPlayer::ShowWeaponByConfig(const PLAYER_ACTION_INFO_CONFIG *p){ + m_bShowWeapon = p->hide_weapon ? false : true; + ShowWeapon(m_bShowWeapon); +} + +// Add money amount +int CECPlayer::AddMoneyAmount(int iAmount) +{ + m_iMoneyCnt += iAmount; + if (m_iMoneyCnt < 0) + { + g_pGame->RuntimeDebugInfo(RTDCOL_WARNING, _AL("Player money is negative")); + m_iMoneyCnt = 0; + } + + return m_iMoneyCnt; +} + +// Render titles / names / talks above player's header +bool CECPlayer::RenderName(CECViewport* pViewport, DWORD dwFlags) +{ + if (!FillPateContent(pViewport)) + return true; + + AUIManager* pUIMan = g_pGame->GetGameRun()->GetUIManager()->GetCurrentUIManPtr(); + float fScale = CECPateText::GetRenderScale(); + CECImageRes* pImageRes = g_pGame->GetImageRes(); + int x, y=int(m_PateContent.iCurY-20*fScale); + int cx, cy; + int title_cx(0), title_cy(0),name_cx(0), name_cy(0); + bool bTitleAfter(true); + + // Decide name color + DWORD dwNameCol = GetNameColor(); + bool bRenderName = GetShowName(); + + const TITLE_CONFIG* pTitle = GetTitleConfig(m_TitleID); + // get title size + if (pTitle) { + m_pPateTitle->GetExtent(&title_cx, &title_cy); + title_cx = int(title_cx * fScale); + title_cy = int(title_cy * fScale); + bTitleAfter = pTitle->after_name != 0; + } + + // Draw name string + if (bRenderName && m_pPateName && (dwFlags & RNF_NAME)) + { + if (g_pGame->GetPrivilege()->Has_Toggle_NameID()) + { + ACString strText; + if (g_pGame->GetConfigs()->GetShowIDFlag()) + strText.Format(_AL("%u"), m_PlayerInfo.cid); + else + strText = m_strName; + + m_pPateName->SetText(strText, false); + } + + m_pPateName->GetExtent(&cx, &cy); + name_cx = cx = int(cx * fScale); + name_cy = cy = int(cy * fScale); + + x = m_PateContent.iBaseX - ((cx + title_cx + GAP_BETWEEN_NAME_TITLE) >> 1); + // m_pPateName->Render(pViewport, x, y+2, dwNameCol, m_PateContent.z); + m_pPateName->RegisterRender(bTitleAfter ? x : x + title_cx + GAP_BETWEEN_NAME_TITLE, y+2, dwNameCol, m_PateContent.z); + + int tx = x + cx + title_cx + GAP_BETWEEN_NAME_TITLE + 4; + + // Draw team leader icon + // TODO: It's better to use m_pTeam rather host's team object, but in + // current version, else player's m_pTeam is always NULL + CECTeam* pTeam = g_pGame->GetGameRun()->GetHostPlayer()->GetTeam(); + if (pTeam) + { + int iIcon = -1; + if (pTeam->GetLeaderID() == m_PlayerInfo.cid) + iIcon = CECImageRes::IMG_TEAMLEADER; + else if (pTeam->GetMemberByID(m_PlayerInfo.cid)) + iIcon = CECImageRes::IMG_TEAMMATE; + + if (iIcon >= 0) + { + pImageRes->GetImageItemSize(iIcon, 0, &cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + pImageRes->RegisterDraw(iIcon, tx, y, 0xffffffff, 0, m_PateContent.z); + tx += cx; + } + } + + // Draw PK state flag + if (m_pvp.bInPVPCombat) + { + pImageRes->GetImageItemSize(CECImageRes::IMG_PKSTATE, 0, &cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + pImageRes->RegisterDraw(CECImageRes::IMG_PKSTATE, tx, y, 0xffffffff, 0, m_PateContent.z); + tx += cx; + } + + // Draw GM flag + if (IsGM()) + { + pImageRes->GetImageItemSize(CECImageRes::IMG_GMFLAG, 0, &cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + pImageRes->RegisterDraw(CECImageRes::IMG_GMFLAG, tx, y, 0xffffffff, 0, m_PateContent.z); + tx += cx; + } + } + // Draw title + if (bRenderName && m_pPateTitle && pTitle && (dwFlags & RNF_NAME)) + { + m_pPateTitle->Clear(); + m_pPateTitle->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + if (bTitleAfter) + x = m_PateContent.iBaseX - ((cx + name_cx + GAP_BETWEEN_NAME_TITLE) >> 1) + name_cx + GAP_BETWEEN_NAME_TITLE; + else x = m_PateContent.iBaseX - ((cx + name_cx + GAP_BETWEEN_NAME_TITLE) >> 1); + m_pPateTitle->SetText(pTitle->name, false); + m_pPateTitle->RegisterRender(x, y + 2, pTitle->color ? pTitle->color : dwNameCol, m_PateContent.z); + } + + // Does player join a faction ? + if (bRenderName && m_idFaction && m_pPateFaction && (dwFlags & RNF_NAME)) + { + // Check faction name + if (!m_pPateFaction->GetItemNum()) + { + ACString strText; + _BuildPateFactionText(m_idFaction, m_idFRole, strText); + m_pPateFaction->SetText(strText, false, false); + } + + // Draw faction icon and name + if (m_pPateFaction->GetItemNum()) + { + // Get text size + m_pPateFaction->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + + // Get icon size + int cx1=0, cy1=0; + if (m_pFactionDecal) + { + m_pFactionDecal->GetExtent(&cx1, &cy1); + cx1 = int(cx1 * fScale); + cy1 = int(cy1 * fScale); + } + + // Reset line height + int ty, lh = a_Max(cy, cy1); // Line height + + // Calculate start position + x = m_PateContent.iBaseX - ((cx + cx1) >> 1); + y -= lh + 2; + + if (m_pFactionDecal) + { + // Draw faction icon + ty = y + ((lh - cy1) >> 1); + m_pFactionDecal->SetZValue(m_PateContent.z); + m_pFactionDecal->SetScreenPos(x, ty); + m_pFactionDecal->SetScaleX(fScale); + m_pFactionDecal->SetScaleY(fScale); + m_pFactionDecal->Render(pViewport); + x += cx1 + 2; + } + + // Draw faction name + DWORD dwFactionCol = dwNameCol; + if (IsInFactionPVP()){ + dwFactionCol = NAMECOL_FACTION_PVP; + }else{ + CECFactionMan *pFMan = g_pGame->GetFactionMan(); + if (pFMan->IsFactionAlliance(GetFactionID())) + dwFactionCol = NAMECOL_ALLIANCE; + else if (pFMan->IsFactionHostile(GetFactionID())) + dwFactionCol = NAMECOL_HOSTILE; + } + ty = y + ((lh - cy) >> 1); + m_pPateFaction->RegisterRender(x, ty, dwFactionCol, m_PateContent.z); + } + } + + // Draw marry + if (bRenderName && m_pPateMarry && (dwFlags & RNF_TITLE) && GetSpouse()) + { + m_pPateMarry->Clear(); + + if( GetGender() == 0 ) + { + ACString strTitle; + const wchar_t * szSpouseName = g_pGame->GetGameRun()->GetPlayerName(GetSpouse(), false); + if( szSpouseName ) + { + strTitle.Format(g_pGame->GetItemDesc()->GetWideString(ITEMDESC_SPOUSE_MALE), szSpouseName); + m_pPateMarry->SetText(strTitle, false, false); + } + } + else + { + ACString strTitle; + const wchar_t * szSpouseName = g_pGame->GetGameRun()->GetPlayerName(GetSpouse(), false); + if( szSpouseName ) + { + strTitle.Format(g_pGame->GetItemDesc()->GetWideString(ITEMDESC_SPOUSE_FEMALE), szSpouseName); + m_pPateMarry->SetText(strTitle, false, false); + } + } + + m_pPateMarry->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + y -= cy + 2; + x = m_PateContent.iBaseX - (cx >> 1); + m_pPateMarry->RegisterRender(x, y, 0xffffffff, m_PateContent.z); + } + + // Draw Force Name + if (bRenderName && GetForce() && m_pPateForce && (dwFlags & RNF_FORCE)) + { + // search the data essence + DATA_TYPE DataType = DT_INVALID; + const FORCE_CONFIG* pConfig = + (const FORCE_CONFIG*)g_pGame->GetElementDataMan()->get_data_ptr(GetForce(), ID_SPACE_CONFIG, DataType); + if (DataType == DT_FORCE_CONFIG && pConfig) + { + m_pPateForce->SetText(pConfig->name, false, false); + m_pPateForce->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + y -= cy + 2; + x = m_PateContent.iBaseX - (cx >> 1); + m_pPateForce->RegisterRender(x, y, 0xffff00ff, m_PateContent.z); + } + } + + if (GetCountry() && m_pPateCountry) + { + if (m_pPateCountry->GetItemNum()) + { + // Get text size + m_pPateCountry->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + + // Get icon size + int cx1=0, cy1=0; + if (m_pCountryDecal) + { + m_pCountryDecal->GetExtent(&cx1, &cy1); + cx1 = int(cx1 * fScale); + cy1 = int(cy1 * fScale); + } + + // Reset line height + int ty, lh = a_Max(cy, cy1); // Line height + + // Calculate start position + x = m_PateContent.iBaseX - ((cx + cx1) >> 1); + y -= lh + 2; + + if (m_pCountryDecal) + { + // Draw faction icon + ty = y + ((lh - cy1) >> 1); + m_pCountryDecal->SetZValue(m_PateContent.z); + m_pCountryDecal->SetScreenPos(x, ty); + m_pCountryDecal->SetScaleX(fScale); + m_pCountryDecal->SetScaleY(fScale); + m_pCountryDecal->Render(pViewport); + x += cx1 + 2; + } + + // Draw country name + ty = y + ((lh - cy) >> 1); + m_pPateCountry->RegisterRender(x, ty, IsInBattle() ? dwNameCol : NAMECOL_WHITE, m_PateContent.z); + } + } + + if( IsKing() ) + { + CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); + ACString strKing = pGameUI->GetStringFromTable(10303); + + pImageRes->GetImageItemSize(CECImageRes::IMG_KING, 0, &cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + + int w, h; + A3DFont* pFont = g_pGame->GetFont(RES_FONT_TITLE); + if( pFont ) + { + pFont->GetTextExtent(strKing, strKing.GetLength(), &w, &h); + w += cx; + + y -= cy + 2; + x = m_PateContent.iBaseX - (w >> 1); + pImageRes->RegisterDraw(CECImageRes::IMG_KING, x, y, 0xffffffff, 0, m_PateContent.z); + pFont->TextOut(x + cx + 2, y + cy - h, strKing, 0xffff00ff, 0, m_PateContent.z, true); + } + } + + if (dwFlags & RNF_WORDS) + { + // Draw last said words, line 2 first + if (m_pPateLastWords2 && m_pPateLastWords2->GetItemNum()) + { + m_pPateLastWords1->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + y -= cy + 2; + x = m_PateContent.iBaseX - (cx >> 1); + // m_pPateLastWords2->Render(pViewport, x, y, 0xffffffff, m_PateContent.z); + m_pPateLastWords2->RegisterRender(x, y, 0xffffffff, m_PateContent.z); + } + + // Last said words, line 1 + if (m_pPateLastWords1 && m_pPateLastWords1->GetItemNum()) + { + m_pPateLastWords1->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + y -= cy + 2; + x = m_PateContent.iBaseX - (cx >> 1); + // m_pPateLastWords1->Render(pViewport, x, y, 0xffffffff, m_PateContent.z); + m_pPateLastWords1->RegisterRender(x, y, 0xffffffff, m_PateContent.z); + } + } + + // Draw trade words / team requirement + if (m_iBoothState == 2 && ((dwFlags & RNF_BUY) || (dwFlags & RNF_SELL))) + { + /* + if (m_pPateBooth && m_pPateBooth->GetItemNum()) + y = RenderBoothName(pViewport, y);*/ + + if (m_pPateBooth && m_pPateBooth->GetItemNum()) + { + m_pPateBooth->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + y -= cy + 6; + x = m_PateContent.iBaseX - (cx >> 1); + // m_pPateBooth->Render(pViewport, x, y, A3DCOLORRGB(255, 156, 0), m_PateContent.z); + m_pPateBooth->RegisterRender(x, y, A3DCOLORRGB(255, 156, 0), m_PateContent.z); + } + + } + else if (m_TeamReq.bShowReq && m_pPateTeamReq && m_pPateTeamReq->GetItemNum()) + { + m_pPateTeamReq->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + y -= cy + 2; + x = m_PateContent.iBaseX - (cx >> 1); + // m_pPateTeamReq->Render(pViewport, x, y, A3DCOLORRGB(255, 228, 0), m_PateContent.z); + m_pPateTeamReq->RegisterRender(x, y, A3DCOLORRGB(255, 228, 0), m_PateContent.z); + } + + m_PateContent.iCurY = y; + + return true; +} + +// Render booth name +int CECPlayer::RenderBoothName(CECViewport* pViewport, int y) +{ + AUIManager* pUIMan = g_pGame->GetGameRun()->GetUIManager()->GetCurrentUIManPtr(); + float fScale = CECPateText::GetRenderScale(); + + int cx, cy, cx1, cy1; + m_pPateBooth->GetExtent(&cx, &cy); + cx = int(cx * fScale); + cy = int(cy * fScale); + + // Draw background + cx1 = cx + 10; + cy1 = cy + 10; + + y -= cy1 + 2; + int iNewY = y; + int x = m_PateContent.iBaseX - (cx1 >> 1); + + float z = m_PateContent.z + 0.00001f; + + CECImageRes* pImageRes = g_pGame->GetImageRes(); + DWORD dwCol = 0xffffffff; + + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x, y, dwCol, 0, z, 5, 5); + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x+5, y, dwCol, 1, z, cx, 5); + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x+5+cx, y, dwCol, 2, z, 5, 5); + + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x, y+5, dwCol, 3, z, 5, cy); + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x+5, y+5, dwCol, 4, z, cx, cy); + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x+5+cx, y+5, dwCol, 5, z, 5, cy); + + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x, y+5+cy, dwCol, 6, z, 5, 5); + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x+5, y+5+cy, dwCol, 7, z, cx, 5); + pImageRes->DrawImage(CECImageRes::IMG_BOOTHBAR, x+5+cx, y+5+cy, dwCol, 8, z, 5, 5); + + // Draw text + y += 5; + x = m_PateContent.iBaseX - (cx >> 1); +// m_pPateBooth->Render(pViewport, x, y, A3DCOLORRGB(255, 203, 74), m_PateContent.z); + m_pPateBooth->RegisterRender(x, y, A3DCOLORRGB(255, 203, 74), m_PateContent.z); + + return iNewY; +} + +// Render bar above player's header +bool CECPlayer::RenderBars(CECViewport* pViewport) +{ + if (!FillPateContent(pViewport)) + return true; + + int x, y, cx=100, cy=3; + A3DFlatCollector* pFlat = g_pGame->GetA3DEngine()->GetA3DFlatCollector(); + CECConfigs* pConfigs = g_pGame->GetConfigs(); + DWORD colEmpty = A3DCOLORRGB(234, 159, 90); + + x = m_PateContent.iBaseX - (cx >> 1); + y = m_PateContent.iCurY - 3; + + if (IsHostPlayer()) + { + // Draw exp bar + if (false/*pConfigs->GetVideoSettings().bPlayerEXP*/) + { + if (m_BasicProps.iExp < 0) + pFlat->AddRect_2D(x, y, x+cx-1, y+cy-1, colEmpty); + else + { + int iLen = (int)ceil(cx * ((float)m_BasicProps.iExp / GetLevelUpExp(m_BasicProps.iLevel))); + pFlat->AddRect_2D(x, y, x+cx-1, y+cy-1, colEmpty); + pFlat->AddRect_2D(x, y, x+iLen-1, y+cy-1, A3DCOLORRGB(249, 44, 255)); + } + + y -= cy + 1; + } + + // Draw MP bar + if (pConfigs->GetVideoSettings().bPlayerMP) + { + if (m_ExtProps.bs.max_mp && m_BasicProps.iCurMP) + { + int iLen = (int)ceil(cx * ((float)m_BasicProps.iCurMP / m_ExtProps.bs.max_mp)); + a_Clamp(iLen, 1, cx); + pFlat->AddRect_2D(x, y, x+cx-1, y+cy-1, colEmpty); + pFlat->AddRect_2D(x, y, x+iLen-1, y+cy-1, A3DCOLORRGB(57, 151, 255)); + } + + y -= cy + 1; + } + } + + // Draw HP bar + if (pConfigs->GetVideoSettings().bPlayerHP) + { + if (m_ExtProps.bs.max_hp && m_BasicProps.iCurHP) + { + int iLen = (int)ceil(cx * ((float)m_BasicProps.iCurHP / m_ExtProps.bs.max_hp)); + a_Clamp(iLen, 1, cx); + pFlat->AddRect_2D(x, y, x+cx-1, y+cy-1, colEmpty); + pFlat->AddRect_2D(x, y, x+iLen-1, y+cy-1, A3DCOLORRGB(255, 57, 62)); + } + + y -= cy + 1; + } + + m_PateContent.iCurY = y; + + return true; +} + +// Fill pate content +// Return false if pate content is un-visible +bool CECPlayer::FillPateContent(CECViewport* pViewport) +{ + // If m_PateContent.iVisible != 0, pate content has been filled + // for this frame + if (m_PateContent.iVisible) + return m_PateContent.iVisible == 2 ? true : false; + + A3DVIEWPORTPARAM* pViewPara = pViewport->GetA3DViewport()->GetParam(); + + // Calculate basic center position on screen + A3DVECTOR3 vPos; + + if (m_iBoothState == 2 && GetBoothModel() && GetBoothModel()->HasCHAABB()){ + vPos = m_aabb.Center + g_vAxisY * GetBoothModel()->GetCHAABB().Extents.y * 1.15f; + }else{ + if (IsInChariot()){ + vPos = m_aabb.Center + g_vAxisY * GetDummyModel(PLAYERMODEL_DUMMYTYPE2)->GetModelAABB().Extents.y * 2.f; + }else{ + vPos = m_aabb.Center + g_vAxisY * m_aabb.Extents.y; + } + if (GetGender() == GENDER_MALE){ // 男模型比较高,拉近时名字容易嵌到身体里面 + vPos.y += 0.1f; + } + } + + if((IsRidingOnPet() && m_pPetModel)) + { + const A3DAABB &aabb = GetPlayerPickAABB(); + vPos = aabb.Center + A3DVECTOR3(0, aabb.Extents.y, 0); + } + + // 打坐使用特殊模型时,名字需要抬高位置 by SunXuewei 2009-11-18 + if(IsSitting() && (GetRace() == RACE_GHOST || GetRace() == RACE_OBORO)) + { //头像上的名字 + static float scaleRatio[NUM_RACE][NUM_GENDER] = + {{0.0f, 0.0f}, // 人类 + {0.0f, 0.0f}, // 妖族 + {0.0f, 0.0f}, // 羽人 + {1.0f, 0.5f}, // 汐族 + {0.0f, 0.0f}, // 灵族 + {1.0f, 1.0f}, // 胧族 + + }; + vPos.y += m_aabb.Extents.y * scaleRatio[GetRace()][GetGender()]; + } + + // 胧族飞行时,名字需要抬高位置 + if (IsFlying() && GetRace() == RACE_OBORO) { + vPos.y += m_aabb.Extents.y * 0.5f; + } + + A3DVECTOR3 vScrPos; + pViewport->GetA3DViewport()->Transform(vPos, vScrPos); + if (vScrPos.z < pViewPara->MinZ || vScrPos.z > pViewPara->MaxZ) + { + m_PateContent.iVisible = 1; // In-visible + return false; + } + + m_PateContent.iVisible = 2; + m_PateContent.iBaseX = (int)vScrPos.x; + m_PateContent.iBaseY = (int)vScrPos.y-10; + m_PateContent.iCurY = m_PateContent.iBaseY; + m_PateContent.z = vScrPos.z; + + return true; +} + +float CECPlayer::GetGroundSpeed() +{ +// return m_bWalkRun ? g_pGame->GetConfigs()->GetHostRunSpeed() : m_ExtProps.mv.walk_speed; + return m_bWalkRun ? m_ExtProps.mv.run_speed : m_ExtProps.mv.walk_speed; +} + +// Set resources ready flag +void CECPlayer::SetResReadyFlag(DWORD dwFlag, bool bSet) +{ + if (bSet) + { + m_dwResFlags |= dwFlag; + } + else + m_dwResFlags &= ~dwFlag; +} + +// When all resources are ready, this function will be called +void CECPlayer::OnAllResourceReady() +{ +/* if (!m_pLevelUpGFX) + m_pLevelUpGFX = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_LEVELUP)); + + if (!m_pWaterWaveStill) + m_pWaterWaveStill = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_WATER_WAVE_STILL)); + + if (!m_pWaterWaveMove) + m_pWaterWaveMove = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_WATER_WAVE_MOVE)); + + if (!m_pAirBubble) + m_pAirBubble = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_AIR_BUBBLE)); + + if (!m_pTransformGfx) + m_pTransformGfx = g_pGame->GetGFXCaster()->LoadGFXEx(res_GFXFile(RES_GFX_AIR_TRANSFORM)); +*/ + if (m_iShape) TransformShape(GetShapeMask()); +} + +// Caculate player's AABB base on profession and gender +void CECPlayer::CalcPlayerAABB() +{ + int iIndex = m_iProfession * NUM_GENDER + m_iGender; + + m_aabb.Extents = aExts[iIndex] * m_fScaleBySkill; + m_aabbServer.Extents = aExts[iIndex]; + + static const MOVECONST aMoveConsts[NUM_PROFESSION*NUM_GENDER] = + { + // 武侠 + // fStepHei fMinAirHei fMinWaterHei fShoreDepth fWaterSurf + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 法师 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 巫师 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 妖精 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 妖兽 + { 0.8f, 1.6f, 0.3f, 1.8f, 0.7f }, + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + // 刺客 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 羽芒 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 羽灵 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 剑灵 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 魅灵 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 夜影 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + // 月仙 + { 0.8f, 1.6f, 0.3f, 1.6f, 0.6f }, + { 0.8f, 1.6f, 0.3f, 1.5f, 0.55f }, + }; + + m_MoveConst = aMoveConsts[iIndex]; +} + +// Get player's model aabb +const A3DAABB & CECPlayer::GetModelAABB() +{ + return GetPlayerPickAABB(); +} + +// Get player's skin model +A3DSkinModel * CECPlayer::GetA3DSkinModel() +{ + if( m_pPlayerModel ) + return m_pPlayerModel->GetA3DSkinModel(); + else + return NULL; +} + +// Get player's pick AABB +const A3DAABB& CECPlayer::GetPlayerPickAABB() +{ + if (m_pPlayerModel && m_pPlayerModel->GetA3DSkinModel()) + return m_pPlayerModel->GetA3DSkinModel()->GetModelAABB(); + else + return m_aabb; +} + +// Get property move or stand action +int CECPlayer::GetMoveStandAction(bool bMove, bool bFight/* false */)const +{ + int iMoveEnv = m_iMoveEnv; + if (m_AttachMode != enumAttachNone) + { + bFight = false; + if( m_bHangerOn ) + iMoveEnv = MOVEENV_GROUND; + } + + int iAction = ACT_STAND; + + if (bMove) + { + // Play appropriate actions + if (iMoveEnv == MOVEENV_GROUND) + { + if (m_bWalkRun) + iAction = ACT_RUN; + else + iAction = ACT_WALK; + } + else if (iMoveEnv == MOVEENV_AIR) + { + if (/*UsingWing()*/ m_wingType == WINGTYPE_WING) + iAction = ACT_FLY; + else + iAction = ACT_FLY_SWORD; + } + else if (iMoveEnv == MOVEENV_WATER){ + if (CanCombineWithMoveForSkill()){ + iAction = ACT_SWIM_FOR_MOVESKILL; + }else{ + iAction = ACT_SWIM; + } + } + } + else + { + // Play appropriate actions + if (iMoveEnv == MOVEENV_GROUND) + { + if (bFight) + iAction = ACT_FIGHTSTAND; + else + iAction = ACT_STAND; + } + else if (iMoveEnv == MOVEENV_AIR) + { + if (/*UsingWing()*/ m_wingType == WINGTYPE_WING) + iAction = ACT_HANGINAIR; + else + iAction = ACT_HANGINAIR_SWORD; + } + else if (iMoveEnv == MOVEENV_WATER) + iAction = ACT_HANGINWATER; + } + + return iAction; +} + +bool CECPlayer::IsMoveStandAction(int iAction){ + const static int s_MoveStandAction[] = { + CECPlayer::ACT_RUN, + CECPlayer::ACT_WALK, + CECPlayer::ACT_FLY, + CECPlayer::ACT_FLY_SWORD, + CECPlayer::ACT_SWIM, + CECPlayer::ACT_SWIM_FOR_MOVESKILL, + CECPlayer::ACT_STAND, + CECPlayer::ACT_FIGHTSTAND, + CECPlayer::ACT_HANGINAIR, + CECPlayer::ACT_HANGINAIR_SWORD, + CECPlayer::ACT_HANGINWATER, + }; + const int *begin = &s_MoveStandAction[0]; + const int *end = &s_MoveStandAction[ARRAY_SIZE(s_MoveStandAction)-1]; + return std::find(begin, end, iAction) != end; +} + +// Check water moving environment +bool CECPlayer::CheckWaterMoveEnv(const A3DVECTOR3& vPos, float fWaterHei, float fGndHei) +{ + bool bRet = false; +/* if (vPos.y < fWaterHei - m_MoveConst.fShoreDepth) + bRet = true; + else if (vPos.y < fWaterHei && fWaterHei - fGndHei > m_MoveConst.fShoreDepth) + bRet = true; +*/ +// if (vPos.y < fWaterHei - m_MoveConst.fWaterSurf - m_aabb.Extents.y) +// if (vPos.y < fWaterHei - m_MoveConst.fWaterSurf + 0.01f) + +/* + if (vPos.y < fWaterHei - m_MoveConst.fShoreDepth) + bRet = true; + else if (vPos.y < fWaterHei - m_MoveConst.fWaterSurf + 0.01f && fWaterHei - fGndHei > m_MoveConst.fShoreDepth) + bRet = true; +*/ + + //@note : modify the water test. By Kuiwu[12/10/2005] + if (vPos.y + m_aabbServer.Extents.y < fWaterHei - m_MoveConst.fWaterSurf - 0.001f) + { + bRet = true; + } + //else if (vPos.y + m_aabb.Extents.y < fWaterHei- m_MoveConst.fWaterSurf + 1E-4f && vPos.y - fGndHei >= 0.2f ) + else if (vPos.y + m_aabbServer.Extents.y < fWaterHei- m_MoveConst.fWaterSurf + 1E-4f && vPos.y - fGndHei >= 0.01f ) + { + bRet = true; + } + + return bRet; + +} + +// Set last said words +void CECPlayer::SetLastSaidWords(const ACHAR* szWords, int nEmotionSet, CECIvtrItem *pItem) +{ + if (!m_pPateLastWords1 || !m_pPateLastWords2) + return; + + ACString str = FilterEmotionSet(szWords, nEmotionSet); + szWords = str; + + m_pPateLastWords1->SetText(szWords, true, true, pItem); + m_pPateLastWords2->Clear(); + m_strLastSayCnt.Reset(); +} + + +void CECPlayer::SetFactionPVPMask(unsigned char mask) +{ + m_factionPVPMask = mask; +} + +bool CECPlayer::IsInFactionPVP()const +{ + return (m_factionPVPMask & 0x01) != 0; +} + +bool CECPlayer::CanAttackFactionPVPMineCar()const +{ + return (m_factionPVPMask & 0x02) != 0; +} + +bool CECPlayer::CanAttackFactionPVPMineBase()const +{ + return (m_factionPVPMask & 0x04) != 0; +} + +// Set faction ID +void CECPlayer::SetFactionID(int id) +{ + m_idFaction = id; + if (!m_idFaction) + return; + + // Clear pate faction text, it will be rebuilt in RenderName() + if (m_pPateFaction) + m_pPateFaction->Clear(); + + // Reset faction icon + if (!m_pFactionDecal) + m_pFactionDecal = new CECSpriteDecal; + + AString strIcon; + strIcon.Format("%d_%d.dds", g_pGame->GetGameInit().iServerID, m_idFaction); + + int iIndex; + CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); + A2DSprite* pIcon = pGameUI->GetGuildIcon(strIcon, &iIndex); + m_pFactionDecal->ChangeSpriteIcon(pIcon, iIndex); +} + +void CECPlayer::OnFactionNameChange(){ + if (m_pPateFaction){ + m_pPateFaction->Clear(); + } +} + +// Set faction role +void CECPlayer::SetFRoleID(int role) +{ + m_idFRole = role; + + // Clear pate faction text, it will be rebuilt in RenderName() + if (m_pPateFaction) + m_pPateFaction->Clear(); +} + +// On start binding buddy +void CECPlayer::OnStartBinding(int idMule, int idRider) +{ + m_bCandHangerOn = (idRider == GetCharacterID()) ? true : false; + m_idCandBuddy = m_bCandHangerOn ? idMule : idRider; +} + +CECPlayer * CECPlayer::GetBuddy()const{ + CECPlayer *result = NULL; + if (GetBuddyID() != 0){ + result = m_pPlayerMan->GetPlayer(GetBuddyID()); + } + return result; +} + +bool CECPlayer::CanBindBuddy()const{ + return GetBuddyState() == 0 + && !IsInvisible() + && GetShapeType() != PLAYERMODEL_DUMMYTYPE2 + && (GetRace() != RACE_OBORO || GetShapeType() != PLAYERMODEL_PROFESSION); +} + +// Set booth name +void CECPlayer::SetBoothName(const ACHAR* szName) +{ + m_strBoothName = szName; + if (m_pPateBooth) + m_pPateBooth->SetBoothContent(szName); +} + +// Is specified id a member of our team ? +bool CECPlayer::IsTeamMember(int idPlayer) +{ + if (!m_pTeam) + return false; + + return m_pTeam->GetMemberByID(idPlayer) ? true : false; +} + +// Set new visible extend states +void CECPlayer::SetNewExtendStates(int start, const DWORD* pData, int count) +{ + if(!pData || start + count > OBJECT_EXT_STATE_COUNT) + { + ASSERT(false); + a_LogOutput(1, "CECPlayer::SetNewExtendStates, invalid data(start=%d, count=%d)", start, count); + return; + } + + ShowExtendStates(start, pData, count); + memcpy(m_aExtStates + start, pData, sizeof(DWORD) * count ); + if(GetExtState(111)) + { + // 武侠旋转技能 太虚·诛神诀 + m_PlayerActions = _turning_actions; + } + else + { + m_PlayerActions = _default_actions; + } +} + +void CECPlayer::ClearShowExtendStates() +{ + DWORD aExtStates[OBJECT_EXT_STATE_COUNT] = {0}; + ShowExtendStates(0, aExtStates, OBJECT_EXT_STATE_COUNT, true); +} + +void CECPlayer::ShowExtendStates(int start, const DWORD* pData, int count, bool bIgnoreOptimize/* =false */) +{ + if(!pData || start + count > OBJECT_EXT_STATE_COUNT) + { + ASSERT(false); + return; + } + if (!IsAllResReady() || !GetMajorModel()) + return; + if (!bIgnoreOptimize && + !CECOptimize::Instance().GetGFX().CanShowState(GetCharacterID(), GetClassID())) + return; + + static const char* szBasePath = "策划联入\\状态效果\\"; + + const int bitSize = sizeof(DWORD) * 8; + for(int index = 0; indexGetEffect(); + if (!strGFXFile.GetLength()) + continue; + + strGFXFile = szBasePath + strGFXFile; + + if (TestProcessPetCureGFX(strGFXFile, dwFlag2 != 0, i + idState*bitSize)) + continue; + + if (dwFlag1) + { + // Remove old state + + CECModel *pWeapon = NULL; + bool bLeft (false); + if (IsWeaponHookPos(pvs->GetHH(), &bLeft, &pWeapon)) + { + // 武器上特效 + if (pWeapon) + { + const char * gfxHook = GetWeaponGFXHookPos(pWeapon, bLeft); + pWeapon->RemoveGfx(strGFXFile, gfxHook); + } + } + else + { + // 模型上特效 + RemoveGfx(strGFXFile, pvs->GetHH(), PLAYERMODEL_TYPEALL); + } + } + else + { + // Add new state + + const char* szHH = pvs->GetHH(); + float fScale; + + if (stricmp("HH_Head", szHH) == 0) + fScale = GetMajorModel()->GetOuterData()[0]; + else if (stricmp("HH_Spine", szHH) == 0) + fScale = GetMajorModel()->GetOuterData()[1]; + else + fScale = 1.0f; + + CECModel *pWeapon = NULL; + bool bLeft (false); + if (IsWeaponHookPos(pvs->GetHH(), &bLeft, &pWeapon)) + { + if (pWeapon) + { + const char *gfxHook = GetWeaponGFXHookPos(pWeapon, bLeft); + pWeapon->PlayGfx(strGFXFile, gfxHook, fScale); + } + } + else + { + PlayGfx(strGFXFile, pvs->GetHH(), fScale, PLAYERMODEL_TYPEALL, true); + } + } + } + } + + memcpy(m_aExtStatesShown + start, pData, sizeof(DWORD) * count ); +} + +void CECPlayer::SetExtendStatesToWeapon() +{ + // 根据玩家的当前状态,将相关特效等挂到当前武器上(当前武器可能刚更换,挂在原武器上的状态特效因删除而消失) + // 避免因武器更换等原因导致不能正确指示状态 + // + + static const char* szBasePath = "策划联入\\状态效果\\"; + + const int bitSize = sizeof(DWORD) * 8; + if (IsAllResReady() && GetMajorModel()) + { + AString strGFXFile; + + for(int idState = 0; idStateGetEffect(); + if (!strGFXFile.GetLength()) + continue; + strGFXFile = szBasePath + strGFXFile; + + if (IsPetCureGFX(strGFXFile)) + { + // 是宠物治疗光效,与武器无关 + continue; + } + + CECModel *pWeapon = NULL; + bool bLeft (false); + if (!IsWeaponHookPos(pvs->GetHH(), &bLeft, &pWeapon)) + { + // 不是放到武器上的光效,也忽略 + continue; + } + + if (!pWeapon) + { + // 对应的武器当前没有,也不需要处理 + continue; + } + + // 光效有了,武器有了,现在需要添加到当前的武器上 + + // 计算缩放 + const char* szHH = pvs->GetHH(); + float fScale; + if (stricmp("HH_Head", szHH) == 0) + fScale = GetMajorModel()->GetOuterData()[0]; + else if (stricmp("HH_Spine", szHH) == 0) + fScale = GetMajorModel()->GetOuterData()[1]; + else + fScale = 1.0f; + + // 计算武器悬挂位置并播放特效 + const char *gfxHook = GetWeaponGFXHookPos(pWeapon, bLeft); + pWeapon->PlayGfx(strGFXFile, gfxHook, fScale); + + // 继续检查其它有效状态,可能有左/右手武器还需要继续添加 + + // 注意,有效状态应保证对同一武器只会播放一次光效 + // 如果有多次,此处逻辑以最后一次处理到的为准 + // 与服务器传来的可能不一致 + } + } + } +} + +void CECPlayer::SetTeamRequireText(const TEAMREQ& Req) +{ + // Build pate string + CECStringTab* pFixStr = g_pGame->GetFixedMsgTab(); + m_strTeamReq = _AL(""); + + if (Req.bShowLevel) + { + const ACHAR* szProf = g_pGame->GetGameRun()->GetProfName(Req.iProfession); + m_strTeamReq.Format(pFixStr->GetWideString(FIXMSG_LEVELANDPROF), Req.iLevel, szProf); + m_strTeamReq += _AL(" "); + } + + if (!Req.iType) + m_strTeamReq += pFixStr->GetWideString(FIXMSG_SEARCHTEAM); + else + m_strTeamReq += pFixStr->GetWideString(FIXMSG_SEARCHTEAMMEM); +} + +void CECPlayer::SetTeamRequire(const TEAMREQ& Req, bool bConfirm) +{ + m_TeamReq = Req; + + // Build pate string + SetTeamRequireText(Req); + + if (m_pPateTeamReq) + { + if (m_strTeamReq.GetLength()) + m_pPateTeamReq->SetText(m_strTeamReq, false, true); + else + m_pPateTeamReq->Clear(); + } + + // If this is host player, notify server + if (bConfirm && IsHostPlayer()) + { + // Compress data + int iData1=0, iData2=0; + + if (Req.bShowReq) + iData1 |= (1 << 31); + + if (Req.bShowLevel) + { + iData1 |= (1 << 30); + iData1 |= (Req.iLevel << 8); + iData1 |= Req.iProfession; + } + + iData1 |= (Req.iType << 16); + + g_pGame->GetGameSession()->c2s_CmdSetAdvData(iData1, iData2); + } +} + +// Decompress advertisement data +void CECPlayer::DecompAdvData(int iData1, int iData2) +{ + TEAMREQ Req; + Req.bShowReq = (iData1 & (1 << 31)) ? true : false; + Req.bShowLevel = (iData1 & (1 << 30)) ? true : false; + Req.iType = (iData1 & 0x00ff0000) >> 16; + Req.iLevel = (iData1 & 0x0000ff00) >> 8; + Req.iProfession = iData1 & 0xff; + + SetTeamRequire(Req, false); +} + +// Message handlers +void CECPlayer::OnMsgPlayerExtState(const ECMSG& Msg) +{ + using namespace S2C; + + if (Msg.dwParam2 == UPDATE_EXT_STATE) + { + cmd_update_ext_state* pCmd = (cmd_update_ext_state*)Msg.dwParam1; + ASSERT(pCmd && pCmd->id == m_PlayerInfo.cid); + SetNewExtendStates(0, pCmd->states, OBJECT_EXT_STATE_COUNT); + } + else if (Msg.dwParam2 == ICON_STATE_NOTIFY) + { + cmd_icon_state_notify cmd; + if (!cmd.Initialize((const void *)Msg.dwParam1, Msg.dwParam3)) + { + ASSERT(0); + return; + } + + ASSERT(cmd.id == m_PlayerInfo.cid); + + m_aIconStates = cmd.states; + if (m_aIconStates.size() > 1) + qsort(m_aIconStates.begin(), m_aIconStates.size(), sizeof (m_aIconStates[0]), glb_IconStateCompare); + } +} + +void CECPlayer::OnMsgEnchantResult(const ECMSG& Msg) +{ + using namespace S2C; + cmd_enchant_result* pCmd = (cmd_enchant_result*)Msg.dwParam1; + ASSERT(pCmd && pCmd->caster == m_PlayerInfo.cid); + + DWORD mask = (DWORD)(-1); + // we should filter out these things that will cause bubble texts + if (pCmd->target != m_pPlayerMan->GetHostPlayer()->GetCharacterID() && + !IsHostPlayer()) + mask &= CECAttackEvent::MOD_PHYSIC_ATTACK_RUNE | CECAttackEvent::MOD_MAGIC_ATTACK_RUNE | CECAttackEvent::MOD_CRITICAL_STRIKE | CECAttackEvent::MOD_ENCHANT_FAILED; + +// DWORD dwModifier = (((DWORD)pCmd->modifier2) << 8) | pCmd->modifier; + DWORD dwModifier = (DWORD)pCmd->attack_flag; + + if( GNET::ElementSkill::GetType(pCmd->skill) == GNET::TYPE_ATTACK ) + { + // only attack skill will cause wounded action, when damage is -1 + PlayAttackEffect(pCmd->target, pCmd->skill, pCmd->level, -1, dwModifier & mask, 0,NULL,pCmd->section); + } + else + { + // other skills will not cause wounded action, so we set damage to -2 which will be discarded to play wounded action + PlayAttackEffect(pCmd->target, pCmd->skill, pCmd->level, -2, dwModifier & mask, 0,NULL,pCmd->section); + } +} + +void CECPlayer::OnMsgPlayerAdvData(const ECMSG& Msg) +{ + using namespace S2C; + + if (Msg.dwParam2 == PLAYER_SET_ADV_DATA) + { + cmd_player_set_adv_data* pCmd = (cmd_player_set_adv_data*)Msg.dwParam1; + DecompAdvData(pCmd->data1, pCmd->data2); + } + else + { + ASSERT(Msg.dwParam2 == PLAYER_CLR_ADV_DATA); + cmd_player_clr_adv_data* pCmd = (cmd_player_clr_adv_data*)Msg.dwParam1; + + m_TeamReq.bShowReq = false; + } +} + +void CECPlayer::OnMsgPlayerPVP(const ECMSG& Msg) +{ + using namespace S2C; + + switch (Msg.dwParam2) + { + case PLAYER_ENABLE_PVP: m_pvp.bEnable = true; break; + case PLAYER_DISABLE_PVP: m_pvp.bEnable = false; break; + + case PVP_COMBAT: + { + cmd_pvp_combat* pCmd = (cmd_pvp_combat*)Msg.dwParam1; + m_pvp.bInPVPCombat = pCmd->state ? true : false; + break; + } + } +} + +void CECPlayer::RemoveEquipGfx() +{ + // 移除显示武器、防具、套装特效 + RemoveUpperBodyStones(); + RemoveWristStones(); + RemoveLowerBodyStones(); + RemoveFootStones(); + RemoveFullSuiteGFX(); +} + +void CECPlayer::AddEquipGfx() +{ + // 显示武器、防具、套装特效 + if (GetMajorModel() != NULL && !InFashionMode()){ + AddUpperBodyStones(); + AddWristStones(); + AddLowerBodyStones(); + AddFootStones(); + AddFullSuiteGFX(); + } +} + +void CECPlayer::OnMsgSwitchFashionMode(const ECMSG& Msg) +{ + using namespace S2C; + cmd_player_enable_fashion* pCmd = (cmd_player_enable_fashion*)Msg.dwParam1; + m_bFashionMode = pCmd->is_enabble ? true : false; + + UpdateCurSkins(); + OnSwitchFashionWeapon(); + if (InFashionMode()){ + RemoveEquipGfx(); + }else{ + AddEquipGfx(); + } + + for (int i = 0; i < SIZE_ALL_EQUIPIVTR; i++) + { + if (m_aEquips[i] <= 0) + continue; + + int nEquipFlag = 0; + if (IsShownEquip(i)) + { + nEquipFlag = 1; + } + + if ( GetMajorModel() ) + { + DWORD dwRealID = GetRealElementID(i, m_aEquips[i]); + GetMajorModel()->OnScriptChangeEquip(dwRealID, nEquipFlag, false); + } + } + + if (InFashionMode()) + UpdateHairModel(true, m_aEquips[EQUIPIVTR_FASHION_HEAD]); + else + UpdateHairModel(true, m_aEquips[EQUIPIVTR_HEAD]); +} + +void CECPlayer::OnMsgPlayerEffect(const ECMSG& Msg) +{ + using namespace S2C; + + if (Msg.dwParam2 == PLAYER_ENABLE_EFFECT) + { + cmd_player_enable_effect* pCmd = (cmd_player_enable_effect*)Msg.dwParam1; + ApplyEffect(pCmd->effect, true); + } + else if (Msg.dwParam2 == PLAYER_DISABLE_EFFECT) + { + cmd_player_disable_effect* pCmd = (cmd_player_disable_effect*)Msg.dwParam1; + DiscardEffect(pCmd->effect); + } +} + +void CECPlayer::OnMsgChangeNameColor(const ECMSG& Msg) +{ + using namespace S2C; + + switch (Msg.dwParam2) + { + case INVADER_RISE: + + m_dwStates |= GP_STATE_INVADER; + break; + + case PARIAH_RISE: + { + m_dwStates |= GP_STATE_PARIAH; + cmd_pariah_rise* pCmd = (cmd_pariah_rise*)Msg.dwParam1; + m_byPariahLvl = pCmd->pariah_lvl; + break; + } + case INVADER_FADE: + { + m_dwStates &= ~(GP_STATE_INVADER | GP_STATE_PARIAH); + if (IsHostPlayer() && PKLevelCheck()) + { + CELPrecinct* p = g_pGame->GetGameRun()->GetWorld()->GetCurPrecinct(); + if (p && p->IsPKProtect()) + g_pGame->GetGameSession()->c2s_CmdSendEnterPKPrecinct(); + } + break; + } + } +} + +void CECPlayer::OnMsgPlayerMount(const ECMSG& Msg) +{ + using namespace S2C; + const cmd_player_mounting* pCmd = (const cmd_player_mounting*)Msg.dwParam1; + + if (pCmd->mount_id) + RideOnPet(pCmd->mount_id, pCmd->mount_color); + else + GetOffPet(); +} + +void CECPlayer::PlayTaoistEffect() +{ + CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); + CDlgLevel2UpgradeShow *pDlg = dynamic_cast(pGameUI->GetDialog("Win_Level2UpgradeShow")); + pDlg->SetNewLevel2(GetBasicProps().iLevel2); + pDlg->Show(true); +} + +bool CECPlayer::CanPlayTaoistEffect(int originalLevel2, int newLevel2, bool bFirstTime) +{ + bool result(false); + if(!bFirstTime){ + if (IsHostPlayer()){ + if ( (!IsGodEvilConvert(originalLevel2, newLevel2)) && (originalLevel2 < newLevel2) ){ + result =true; + } + } + } + return result; +} + +void CECPlayer::SetLevel2(int level2, bool bFirstTime) +{ + int lastLevel2 = m_BasicProps.iLevel2; + m_BasicProps.iLevel2 = level2; + if (CanPlayTaoistEffect(lastLevel2, level2, bFirstTime)){ + PlayTaoistEffect(); + } +} + +bool CECPlayer::IsGodEvilConvert(int originalLevel2, int newLevel2) +{ + const CECTaoistRank* originalTaoist = CECTaoistRank::GetTaoistRank(originalLevel2); + const CECTaoistRank* newTaoist = CECTaoistRank::GetTaoistRank(newLevel2); + return (originalTaoist->IsGodRank() && newTaoist->IsEvilRank()) || (originalTaoist->IsEvilRank() && newTaoist->IsGodRank()); +} + +void CECPlayer::OnMsgPlayerLevel2(const ECMSG& Msg) +{ + using namespace S2C; + cmd_task_deliver_level2* pCmd = (cmd_task_deliver_level2*)Msg.dwParam1; + // 修真进阶时播放全屏特效 + SetLevel2(pCmd->level2, false); + UpdateGodEvilSprite(); +} + +void CECPlayer::OnMsgKingChanged(const ECMSG &Msg) +{ + using namespace S2C; + char is_king = 0; + + if( Msg.dwParam2 == PLAYER_KING_CHANGED ) + { + cmd_player_king_changed* pCmd = (cmd_player_king_changed*)Msg.dwParam1; + is_king = pCmd->is_king; + } + else if ( Msg.dwParam2 == SELF_KING_NOTIFY ) + { + cmd_self_king_notify* pCmd = (cmd_self_king_notify*)Msg.dwParam1; + is_king = pCmd->is_king; + } + + if( is_king ) + m_dwStates2 |= GP_STATE2_ISKING; + else + m_dwStates2 &= ~GP_STATE2_ISKING; +} + +static A3DSkinModel::LIGHTINFO MakePlayerModelLightInfoForAUI(const A3DVECTOR3 &vLightDir){ + A3DSkinModel::LIGHTINFO result; + + memset(&result, 0, sizeof(result)); + + const A3DLIGHTPARAM& lp = g_pGame->GetDirLight()->GetLightparam(); + result.colDirDiff = lp.Diffuse; + result.colDirSpec = lp.Specular; + result.vLightDir = vLightDir; + result.colAmbient = A3DCOLORVALUE(0.5f, 0.5f, 0.5f, 1.0f) + g_pGame->GetA3DDevice()->GetAmbientValue(); + result.colAmbient.r = min(result.colAmbient.r, 1.0f); + result.colAmbient.g = min(result.colAmbient.g, 1.0f); + result.colAmbient.b = min(result.colAmbient.b, 1.0f); + result.colAmbient.a = min(result.colAmbient.a, 1.0f); + result.colPtDiff = A3DCOLORVALUE(0.0f, 0.0f, 0.0f, 1.0f); + result.colPtAmb = A3DCOLORVALUE(0.0f, 0.0f, 0.0f, 1.0f); + result.fPtRange = 0.01f; + result.vPtAtten = A3DVECTOR3(0.0f); + result.vPtLightPos = A3DVECTOR3(0.0f); + result.bPtLight = true; + + return result; +} + +// player's render for shadow call back function +bool PlayerRenderForShadow(A3DViewport * pViewport, void * pArg) +{ + CECPlayer * pPlayer = (CECPlayer *) pArg; + g_pGame->GetA3DDevice()->SetZTestEnable(true); + g_pGame->GetA3DDevice()->SetZWriteEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(true); + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(false); + g_pGame->GetA3DDevice()->SetAlphaFunction(A3DCMP_GREATEREQUAL); + g_pGame->GetA3DDevice()->SetAlphaRef(84); + + CECViewport viewport; + viewport.InitFromA3D(pViewport); + +// if (pPlayer->GetBoothState() == 2) +// pPlayer->GetBoothModel()->RenderAtOnce(pViewport, A3DSkinModel::RAO_NOTEXTURE | A3DSkinModel::RAO_NOMATERIAL, false); + if (pPlayer->GetPetModel()) + { + if (pPlayer->IsShapeModelChanged()) + { + pPlayer->GetPetModel()->GetA3DSkinModel()->RenderAtOnce(pViewport, A3DSkinModel::RAO_NOMATERIAL, false); + } + else + { + bool bShowHeadOld = pPlayer->GetPlayerModel()->IsSkinShown(SKIN_HEAD_INDEX); + pPlayer->GetPlayerModel()->ShowSkin(SKIN_HEAD_INDEX, true); + pPlayer->GetPetModel()->GetA3DSkinModel()->RenderAtOnce(pViewport, A3DSkinModel::RAO_NOMATERIAL, false); + pPlayer->GetPlayerModel()->ShowSkin(SKIN_HEAD_INDEX, bShowHeadOld); + } + } + else if (pPlayer->IsShapeModelChanged()) + pPlayer->GetPlayerModel()->GetA3DSkinModel()->RenderAtOnce(pViewport, A3DSkinModel::RAO_NOMATERIAL, false); + else + { + bool bShowHeadOld = pPlayer->GetPlayerModel()->IsSkinShown(SKIN_HEAD_INDEX); + pPlayer->GetPlayerModel()->ShowSkin(SKIN_HEAD_INDEX, true); + pPlayer->GetPlayerModel()->GetA3DSkinModel()->RenderAtOnce(pViewport, A3DSkinModel::RAO_NOMATERIAL, false); + pPlayer->GetPlayerModel()->ShowSkin(SKIN_HEAD_INDEX, bShowHeadOld); + } + + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(false); + g_pGame->GetA3DDevice()->SetZTestEnable(true); + g_pGame->GetA3DDevice()->SetZWriteEnable(true); + return true; +} + +typedef abase::vector SkinShowFlagArray; +static SkinShowFlagArray GetSkinShowFlag(A3DSkinModel *pModel){ + SkinShowFlagArray result; + if (pModel){ + result.reserve(pModel->GetSkinNum()); + for (int i(0); i < pModel->GetSkinNum(); ++ i){ + bool bShown(false); + if (A3DSkinModel::SKIN *pSkinItem = pModel->GetSkinItem(i)){ + bShown = pSkinItem->bRender; + } + result.push_back(bShown); + } + } + return result; +} + +static SkinShowFlagArray GetShowSomeSkinOnlyFlag(A3DSkinModel *pModel, int skinIndex){ + SkinShowFlagArray result; + if (pModel){ + result.reserve(pModel->GetSkinNum()); + for (int i(0); i < pModel->GetSkinNum(); ++ i){ + result.push_back(i == skinIndex ? true : false); + } + } + return result; +} + +static SkinShowFlagArray GetShowHeadOnlyFlag(A3DSkinModel *pModel){ + return GetShowSomeSkinOnlyFlag(pModel, SKIN_HEAD_INDEX); +} + +static SkinShowFlagArray GetHideAllSkinFlag(A3DSkinModel *pModel){ + return GetShowSomeSkinOnlyFlag(pModel, -1); +} + +static void ShowSkin(A3DSkinModel *pModel, const SkinShowFlagArray &showSkinFlagArray){ + if (pModel){ + for (size_t i(0); i < showSkinFlagArray.size(); ++ i){ + pModel->ShowSkin(i, showSkinFlagArray[i]); + } + } +} + +typedef abase::vector ChildModelsSkinShowFlagArray; +static ChildModelsSkinShowFlagArray GetChildModelsSkinShowFlag(A3DSkinModel *pModel){ + ChildModelsSkinShowFlagArray result; + if (pModel){ + result.reserve(pModel->GetChildModelNum()); + for (int i(0); i < pModel->GetChildModelNum(); ++ i){ + result.push_back(GetSkinShowFlag(pModel->GetChildModel(i))); + } + } + return result; +} + +static ChildModelsSkinShowFlagArray GetHideChildModelsSkinShowFlag(A3DSkinModel *pModel){ + ChildModelsSkinShowFlagArray result; + if (pModel){ + result.reserve(pModel->GetChildModelNum()); + for (int i(0); i < pModel->GetChildModelNum(); ++ i){ + result.push_back(GetHideAllSkinFlag(pModel->GetChildModel(i))); + } + } + return result; +} + +static void ShowChildModelsSkin(A3DSkinModel *pModel, const ChildModelsSkinShowFlagArray &childModelsShowSkinFlagArray){ + if (pModel){ + for (size_t i(0); i < childModelsShowSkinFlagArray.size(); ++ i){ + ShowSkin(pModel->GetChildModel(i), childModelsShowSkinFlagArray[i]); + } + } +} + +void PlayerRenderPortraitNoFace(const A3DRECT &rc, DWORD param1, DWORD param2, DWORD param3) +{ + static const float headsize[NUM_PROFESSION*NUM_GENDER] = { + 0.221f, 0.200f, // 武侠 + 0.221f, 0.200f, // 法师 + 0.221f, 0.200f, // 巫师 + 0.221f, 0.200f, // 妖精 + 0.294f, 0.200f, // 妖兽 + 0.221f, 0.200f, // 刺客 + 0.221f, 0.200f, // 羽芒 + 0.221f, 0.200f, // 羽灵 + 0.221f, 0.200f, // 剑灵 + 0.221f, 0.200f, // 魅灵 + 0.221f, 0.200f, // 夜影 + 0.221f, 0.200f, // 月仙 + }; + + static const float pose[NUM_PROFESSION*NUM_GENDER][2] = { + {1.1f, 0.02f}, {0.9f, 0.04f}, // 武侠 + {1.1f, 0.02f}, {0.9f, 0.04f}, // 法师 + {0.9f, 0.02f}, {0.9f, 0.04f}, // 巫师 + {0.9f, 0.04f}, {0.8f, 0.04f}, // 妖精 + {1.3f, 0.05f}, {0.8f, 0.04f}, // 妖兽 + {0.9f, 0.02f}, {0.9f, 0.04f}, // 刺客 + {0.8f, 0.02f}, {0.6f, 0.04f}, // 羽芒 + {0.9f, 0.02f}, {0.7f, 0.04f}, // 羽灵 + {1.0f, 0.02f}, {0.8f, 0.04f}, // 剑灵 + {1.0f, 0.02f}, {0.8f, 0.04f}, // 魅灵 + {1.0f, 0.02f}, {0.8f, 0.04f}, // 夜影 + {1.0f, 0.02f}, {0.8f, 0.04f}, // 月仙 + }; + + // CECFace的 "脖子对齐"骨头的初始相对矩阵,确定相机位置时会用到 + // 由于此时没有CECFace, 故写死在这里 + static const A3DMATRIX4 matNeck( + 0.0f, 0.996917f, 0.0784576f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -0.0784576f, 0.996917f, 0.0f, + 0.0f, -0.0454048f, -0.00635493f, 1.0f + ); + + if( param1 == 0 ) + return; + + CECPlayer * pPlayer = (CECPlayer *) param1; + float vDeg = (int)param2 * 0.01f; + CECModel* pMajorModel = pPlayer->GetMajorModel(); + if( !pMajorModel ) return; + A3DSkinModel* pModel = pMajorModel->GetA3DSkinModel(); + if( !pModel ) return; + A3DSkin* pHeadSkin = pMajorModel->GetA3DSkin(SKIN_HEAD_INDEX); + if( !pHeadSkin ) return; + + A3DViewport * pCurViewport = g_pGame->GetA3DEngine()->GetActiveViewport(); + + A3DViewport * pPortraitViewport = g_pGame->GetPortraitRender()->GetPortraitViewport(); + A3DCameraBase * pCamera = pPortraitViewport->GetCamera(); + + A3DVIEWPORTPARAM viewParam = *pPortraitViewport->GetParam(); + viewParam.X = rc.left; + viewParam.Y = rc.top; + viewParam.Width = rc.right - rc.left; + viewParam.Height = rc.bottom - rc.top; + pPortraitViewport->SetParam(&viewParam); + ((A3DCamera *) pCamera)->SetProjectionParam(-1.0f, -1.0f, -1.0f, ((float)viewParam.Width) / viewParam.Height); + + A3DBone* pHeadBone = pModel->GetSkeleton()->GetBone("Bip01 Head", NULL); + A3DMATRIX4 matHead = a3d_RotateY(vDeg) * (InverseTM(matNeck) * pHeadBone->GetAbsoluteTM()); + A3DVECTOR3 vecPos = matHead.GetRow(3); + A3DVECTOR3 vecDir = Normalize(matHead.GetRow(2)); + A3DVECTOR3 vecUp = Normalize(matHead.GetRow(1)); + + // 计算头部肖像的缩放 + float vHeadScale = 1.0f; + int index = pPlayer->GetProfession() * NUM_GENDER + pPlayer->GetGender(); + A3DVECTOR3 vHeadTop = A3DVECTOR3(0.0f, pHeadSkin->GetInitMeshAABB().Extents.y, 0.0f) * pHeadBone->GetAbsoluteTM(); + A3DVECTOR3 vHeadBottom = A3DVECTOR3(0.0f, 0.0f, 0.0f) * pHeadBone->GetAbsoluteTM(); + float fHeadHeight = (vHeadTop - vHeadBottom).Magnitude(); + vHeadScale = fHeadHeight / headsize[index] * (pPlayer->IsChangingFace() ? 1.0f : pPlayer->GetPortraitCameraScale()); + + static float s_scalePos = 1.5f; + float *p = &s_scalePos; + static float s_scaleUp = 1.5f; + p = &s_scaleUp; + pCamera->SetPos(vecPos + vecDir * (pose[index][0] * vHeadScale * s_scalePos) + vecUp * pose[index][1] * vHeadScale * s_scaleUp); + pCamera->SetDirAndUp(-vecDir, vecUp); + + pPortraitViewport->Active(); + pPortraitViewport->ClearDevice(); + + CECViewport viewport; + viewport.InitFromA3D(pPortraitViewport); + + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(true); + + // 保存原有透明度(显示头像时需屏蔽透明度) + float fTransparent = pModel->GetTransparent(); + + // 重置透明度 + pModel->SetTransparent(-1.0f); + + // 设置专用光照,使不受场景光照影响 + pModel->SetLightInfo(MakePlayerModelLightInfoForAUI(pCamera->GetDir())); + + // 隐藏其它皮肤 + SkinShowFlagArray savedSkinShowFlag = GetSkinShowFlag(pModel); + ChildModelsSkinShowFlagArray savedChildModelShowFlag = GetChildModelsSkinShowFlag(pModel); + ShowSkin(pModel, GetShowHeadOnlyFlag(pModel)); + ShowChildModelsSkin(pModel, GetHideChildModelsSkinShowFlag(pModel)); + + // 打坐使用特殊模型时,需要对头像显示特殊处理 by SunXuewei 2009-11-18 + if(pPlayer->IsSitting() && (pPlayer->GetRace() == RACE_GHOST || pPlayer->GetRace() == RACE_OBORO)) + { + int iMaterialMethod = pModel->GetMaterialMethod(); + pModel->SetMaterialMethod(A3DSkinModel::MTL_ORIGINAL); + pModel->RenderAtOnce(viewport.GetA3DViewport(), 0, false); + pModel->SetMaterialMethod(iMaterialMethod); + } + else + { + pModel->RenderAtOnce(viewport.GetA3DViewport(), 0, false); + } + + // 恢复隐藏的皮肤 + ShowSkin(pModel, savedSkinShowFlag); + ShowChildModelsSkin(pModel, savedChildModelShowFlag); + + // 渲染脸部引用的GFX特效(如灵族的额饰特效) + // 需使用正确的 alpha 模式 + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(false); + g_pGame->GetA3DGFXExMan()->RenderAllGfx(pPortraitViewport, true); + + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(false); + + // 恢复原有透明度 + pModel->SetTransparent(fTransparent); + + pCurViewport->Active(); +} + +void PlayerRenderPortrait(const A3DRECT &rc, DWORD param1, DWORD param2, DWORD param3) +{ + static float headsize[NUM_PROFESSION*NUM_GENDER] = { + 0.221f, 0.200f, // 武侠 + 0.221f, 0.200f, // 法师 + 0.221f, 0.200f, // 巫师 + 0.221f, 0.200f, // 妖精 + 0.294f, 0.200f, // 妖兽 + 0.221f, 0.200f, // 刺客 + 0.221f, 0.200f, // 羽芒 + 0.221f, 0.200f, // 羽灵 + 0.221f, 0.200f, // 剑灵 + 0.221f, 0.200f, // 魅灵 + 0.221f, 0.200f, // 夜影 + 0.221f, 0.200f, // 月仙 + }; + + static float pose[NUM_PROFESSION*NUM_GENDER][2] = { + {0.7f, 0.04f}, {0.6f, 0.04f}, // 武侠 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 法师 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 巫师 + {0.7f, 0.04f}, {0.7f, 0.05f}, // 妖精 + {0.9f, 0.06f}, {0.6f, 0.04f}, // 妖兽 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 刺客 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 羽芒 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 羽灵 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 剑灵 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 魅灵 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 夜影 + {0.7f, 0.04f}, {0.6f, 0.04f}, // 月仙 + }; + + if( param1 == 0 ) + return; + + CECPlayer * pPlayer = (CECPlayer *) param1; + float vDeg = (int)param2 * 0.01f; + CECFace * pFace = pPlayer->GetFaceModel(); + if (!pFace) + { + PlayerRenderPortraitNoFace(rc, param1, param2, param3); + return; + } + + A3DViewport * pCurViewport = g_pGame->GetA3DEngine()->GetActiveViewport(); + + A3DViewport * pPortraitViewport = g_pGame->GetPortraitRender()->GetPortraitViewport(); + A3DCameraBase * pCamera = pPortraitViewport->GetCamera(); + + A3DVIEWPORTPARAM viewParam = *pPortraitViewport->GetParam(); + viewParam.X = rc.left; + viewParam.Y = rc.top; + viewParam.Width = rc.right - rc.left; + viewParam.Height = rc.bottom - rc.top; + pPortraitViewport->SetParam(&viewParam); + ((A3DCamera *) pCamera)->SetProjectionParam(-1.0f, -1.0f, -1.0f, ((float)viewParam.Width) / viewParam.Height); + + A3DMATRIX4 matHead = a3d_RotateY(vDeg) * pFace->GetFaceTM(); + A3DVECTOR3 vecPos = matHead.GetRow(3); + A3DVECTOR3 vecDir = Normalize(matHead.GetRow(2)); + A3DVECTOR3 vecUp = Normalize(matHead.GetRow(1)); + + int index = pPlayer->GetProfession() * NUM_GENDER + pPlayer->GetGender(); + + float vHeadScale = 1.0f; + A3DSkinModel * pFaceModel = pPlayer->GetFaceModel()->GetA3DSkinModel(); + A3DBone * pBoneUp = pFaceModel->GetSkeleton()->GetBone("脸盘010", NULL); + A3DBone * pBoneLow = pFaceModel->GetSkeleton()->GetBone("脸盘120", NULL); + + float vDis = Magnitude(pBoneUp->GetAbsoluteTM().GetRow(3) - pBoneLow->GetAbsoluteTM().GetRow(3)); + vHeadScale = vDis / headsize[index] * pPlayer->GetPortraitCameraScale(); + + pCamera->SetPos(vecPos + vecDir * (pose[index][0] * vHeadScale * 1.3f) + vecUp * pose[index][1] * vHeadScale * 1.05f); + pCamera->SetDirAndUp(-vecDir, vecUp); + pPortraitViewport->Active(); + pPortraitViewport->ClearDevice(); + + CECViewport viewport; + viewport.InitFromA3D(pPortraitViewport); + + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(true); + + // 保存原有透明度(显示头像时需屏蔽透明度) + float fTransparent = pFace->GetTransparent(); + + // 重置透明度 + pFace->SetTransparent(-1.0f); + + A3DSkinModel::LIGHTINFO light = MakePlayerModelLightInfoForAUI(pCamera->GetDir()); + if(pPlayer->IsSitting() && (pPlayer->GetRace() == RACE_GHOST || pPlayer->GetRace() == RACE_OBORO)) + { + // 打坐使用特殊模型时,需要对头像显示特殊处理 by SunXuewei 2009-11-18 + int iMaterialMethod = pFaceModel->GetMaterialMethod(); + + pFaceModel->SetMaterialMethod(A3DSkinModel::MTL_ORIGINAL); + + pFace->Render(&viewport, true, false, &light); + + pFaceModel->SetMaterialMethod(iMaterialMethod); + + } + else + { + pFace->Render(&viewport, true, false, &light); + } + + // 渲染脸部引用的GFX特效(如灵族的额饰特效) + // 需使用正确的 alpha 模式 + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(false); + g_pGame->GetA3DGFXExMan()->RenderAllGfx(pPortraitViewport, true); + + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(false); + + // 恢复原有透明度 + pFace->SetTransparent(fTransparent); + + pCurViewport->Active(); +} + +void PlayerRenderDemonstration(const A3DRECT &rc, DWORD param1, DWORD param2, DWORD param3) +{ + static const float _dist[NUM_PROFESSION][2] = + { + { -9.2f, -8.4f }, // 武侠 + { -9.2f, -8.4f }, // 法师 + { -9.2f, -8.4f }, // 僧侣 + { -9.2f, -8.4f }, // 妖精 + { -9.5f, -8.4f }, // 妖兽 + { -9.2f, -8.4f }, // 魅灵 + { -9.2f, -8.4f }, // 羽芒 + { -9.2f, -8.4f }, // 羽灵 + { -9.2f, -8.4f }, // 剑灵 + { -9.2f, -8.4f }, // 魅灵 + { -9.2f, -8.4f }, // 夜影 + { -9.2f, -8.4f }, // 月仙 + }; + static const float _height[NUM_PROFESSION][2] = + { + { 0.95f, 0.85f }, // 武侠 + { 0.95f, 0.85f }, // 法师 + { 0.95f, 0.85f }, // 僧侣 + { 0.95f, 0.85f }, // 妖精 + { 1.0f, 0.85f }, // 妖兽 + { 0.95f, 0.85f }, // 魅灵 + { 0.95f, 0.85f }, // 羽芒 + { 0.95f, 0.85f }, // 羽灵 + { 0.95f, 0.85f }, // 剑灵 + { 0.95f, 0.85f }, // 魅灵 + { 0.95f, 0.85f }, // 夜影 + { 0.95f, 0.85f }, // 月仙 + }; + static const float _nearest_dist = -1.f; + static const float _x_max = 1.f; + + if( param1 == 0 ) + return; + + CECPlayer * pPlayer = (CECPlayer *) param1; + float vDeg = DEG2RAD(param2); + char nDist = (param3 >> 8) & 0xff; + char nHeight = param3 & 0xff; + char nLeftRight = (param3 >> 16) & 0xff; + + A3DViewport * pCurViewport = g_pGame->GetA3DEngine()->GetActiveViewport(); + + A3DViewport * pPortraitViewport = g_pGame->GetPortraitRender()->GetPortraitViewport(); + A3DCameraBase * pCamera = pPortraitViewport->GetCamera(); + + A3DRECT rcClamped = rc; + if (rcClamped.left < (int)pCurViewport->GetParam()->X){ + rcClamped.left = (int)pCurViewport->GetParam()->X; + } + if (rcClamped.top < (int)pCurViewport->GetParam()->Y){ + rcClamped.top = (int)pCurViewport->GetParam()->Y; + } + if (rcClamped.right > (int)pCurViewport->GetParam()->X + (int)pCurViewport->GetParam()->Width){ + rcClamped.right = (int)pCurViewport->GetParam()->X + (int)pCurViewport->GetParam()->Width; + } + if (rcClamped.bottom > (int)pCurViewport->GetParam()->Y + (int)pCurViewport->GetParam()->Height){ + rcClamped.bottom = (int)pCurViewport->GetParam()->Y + (int)pCurViewport->GetParam()->Height; + } + + A3DVIEWPORTPARAM viewParam = *pPortraitViewport->GetParam(); + viewParam.X = rcClamped.left; + viewParam.Y = rcClamped.top; + viewParam.Width = rcClamped.right - rcClamped.left; + viewParam.Height = rcClamped.bottom - rcClamped.top; + pPortraitViewport->SetParam(&viewParam); + ((A3DCamera *) pCamera)->SetProjectionParam(-1.0f, -1.0f, -1.0f, ((float)viewParam.Width) / viewParam.Height); + + int nGender = pPlayer->GetGender(); + int nProfession = pPlayer->GetProfession(); + float fDist = nDist / 100.f * (_nearest_dist - _dist[nProfession][nGender]) + _dist[nProfession][nGender]; + float fHeight = (nHeight + 100) / 100.f * _height[nProfession][nGender]; + float fXpos = nLeftRight / 50.f * _x_max - _x_max; + A3DMATRIX4 mat = a3d_RotateY(vDeg); + A3DVECTOR3 vecPos = A3DVECTOR3(fXpos, fHeight + (/*pPlayer->UsingWing()*/pPlayer->GetWingType() == WINGTYPE_WING ? 0.3f : 0.0f), fDist) * mat; + A3DVECTOR3 vecDir = A3DVECTOR3(0, 0, 1.0f) * mat; + A3DVECTOR3 vecUp = A3DVECTOR3(0, 1.0f, 0); + + pCamera->SetPos(vecPos); + pCamera->SetDirAndUp(vecDir, vecUp); + pPortraitViewport->Active(); + pPortraitViewport->ClearDevice(); + + A3DSkinModel::LIGHTINFO light = MakePlayerModelLightInfoForAUI(pCamera->GetDir()); + if (pPlayer->GetPlayerModel()->GetA3DSkinModel()) + pPlayer->GetPlayerModel()->GetA3DSkinModel()->SetLightInfo(light); + + // 开始渲染 + + // 设置图元收集对象 + A3DEngine* pA3DEngine = g_pGame->GetA3DEngine(); + A3DSkinRender* psr = g_pGame->GetSkinRender2(); + A3DSkinRender* psrold = pA3DEngine->GetA3DSkinMan()->GetCurSkinRender(); + pA3DEngine->GetA3DSkinMan()->SetCurSkinRender(psr); + + // 渲染 非Alpha 物体 + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(false); + g_pGame->GetA3DDevice()->SetAlphaTestEnable(true); + g_pGame->GetA3DDevice()->SetAlphaFunction(A3DCMP_GREATER); + g_pGame->GetA3DDevice()->SetAlphaRef(84); + + // CECModel::Render 中不更新光照参数,以免试衣模型受环境光影响 + CECModel *pPlayerModel = pPlayer->GetPlayerModel(); + bool bUpdateFlagOld = pPlayerModel->GetUpdateLightInfoFlag(); + pPlayerModel->SetUpdateLightInfoFlag(false); + pPlayerModel->Render(pPortraitViewport); + pPlayerModel->SetUpdateLightInfoFlag(bUpdateFlagOld); + + if (pPlayer->GetFaceModel()) + { + CECViewport v; + v.InitFromA3D(pPortraitViewport); + pPlayer->GetFaceModel()->Render(&v, true, false, &light); + } + + psr->Render(pPortraitViewport, false); + psr->ResetRenderInfo(false); + + // 渲染 Alpha 物体 + g_pGame->GetA3DDevice()->SetAlphaTestEnable(false); + g_pGame->GetA3DDevice()->SetAlphaBlendEnable(true); + + psr->RenderAlpha(pPortraitViewport); + psr->ResetRenderInfo(true); + + g_pGame->GetA3DGFXExMan()->RenderAllGfx(pPortraitViewport, true); + + // 恢复原有设置 + pA3DEngine->GetA3DSkinMan()->SetCurSkinRender(psrold); + pCurViewport->Active(); +} + +bool CECPlayer::HasWingModel()const +{ + return m_pPlayerModel != NULL + && (m_pPlayerModel->GetChildModel(_wing) || m_pPlayerModel->GetChildModel(_wing2)); +} + +//把比例从int 转换到float +float CECPlayer::TransformScaleFromIntToFloat(int nScale, float fScaleFactor, float fMax) +{ + if(fScaleFactor >= 1.0) + fScaleFactor = 0.99f; + + if(fScaleFactor >= fMax) + fScaleFactor = fMax; + + else if ( fScaleFactor < 0.0f) + fScaleFactor = 0.0f; + + float fScale = (nScale - 128.0f)/128.0f; + + fScale = fScale * fScaleFactor + 1.0f; + + return fScale; +} + +void CECPlayer::InitCustomizeFactor(void) +{ + m_CustomizeFactor.fScaleHeadFactor = SCALE_HEAD_FACTOR; + m_CustomizeFactor.fScaleUpFactor = SCALE_UP_FACTOR; + m_CustomizeFactor.fScaleWaistFactor = SCALE_WAIST_FACTOR; + m_CustomizeFactor.fWidthArmFactor = WIDTH_ARM_FACTOR; + m_CustomizeFactor.fWidthLegFactor = WIDTH_LEG_FACTOR; + m_CustomizeFactor.fScaleBreastFactor = SCALE_BREAST_FACTOR; +} + +bool CECPlayer::LoadCustomizeFactorFromIni(void) +{ + AIniFile IniFile; + + if (!IniFile.Open("Configs\\CustomizeFactor.ini")) + { + a_LogOutput(1, "CECPlayer::LoadFaceShapeFactorFromINIFile, Failed to open file Configs\\CustomizeFactor.ini"); + return false; + } + + AString strSection = "Stature"; + m_CustomizeFactor.fScaleHeadFactor = IniFile.GetValueAsFloat(strSection, "HeadScaleFactor",m_CustomizeFactor.fScaleHeadFactor); + m_CustomizeFactor.fScaleUpFactor = IniFile.GetValueAsFloat(strSection, "UpScaleFactor", m_CustomizeFactor.fScaleUpFactor); + m_CustomizeFactor.fScaleWaistFactor = IniFile.GetValueAsFloat(strSection, "WaistScaleFactor", m_CustomizeFactor.fScaleWaistFactor); + m_CustomizeFactor.fWidthArmFactor = IniFile.GetValueAsFloat(strSection, "ArmWidthFactor", m_CustomizeFactor.fWidthArmFactor); + m_CustomizeFactor.fWidthLegFactor = IniFile.GetValueAsFloat(strSection, "LegWidthFactor", m_CustomizeFactor.fWidthLegFactor); + m_CustomizeFactor.fScaleBreastFactor = IniFile.GetValueAsFloat(strSection, "BreastScaleFactor", m_CustomizeFactor.fScaleBreastFactor); + + IniFile.Close(); + + return true; +} + +// Print bubble text +void CECPlayer::BubbleText(int iIndex, DWORD dwNum, int p1/* 0 */) +{ + if (!m_pBubbleTexts) + return; + + bool bHost = IsHostPlayer(); +// if (iIndex == BUBBLE_EXP || iIndex == BUBBLE_SP) +// bHost = false; + + DWORD dwCol = A3DCOLORRGB(255, 205, 75); + + // Print notify text into chat dialog + if(bHost) + { + CECHostPlayer* pHost = g_pGame->GetGameRun()->GetHostPlayer(); + if (iIndex == BUBBLE_EXP) + { + g_pGame->GetGameRun()->AddFixedChannelMsg((int)dwNum > 0 ? + (pHost->GetReincarnationTome().tome_active ? FIXMSG_GOT_BOOKEXP : FIXMSG_GOTEXP) + : FIXMSG_LOSTEXP, GP_CHAT_FIGHT, (int)dwNum); + } + if (iIndex == BUBBLE_SP) + { + g_pGame->GetGameRun()->AddFixedChannelMsg(FIXMSG_GOTSP, GP_CHAT_FIGHT, (int)dwNum); + } + if (iIndex == BUBBLE_REALMEXP) + { + g_pGame->GetGameRun()->AddFixedChannelMsg(FIXMSG_GOT_REALMEXP, GP_CHAT_FIGHT, (int)dwNum); + } + } + + A3DVECTOR3 vPos; + + if (IsInChariot()) + { + vPos = GetPos() + g_vAxisY * (GetDummyModel(PLAYERMODEL_DUMMYTYPE2)->GetModelAABB().Extents.y * 2.3f); + } + else + vPos = GetPos() + g_vAxisY * (m_aabb.Extents.y * 2.5f); + + CECBubbleDecal* pBubbleDecal = m_pBubbleTexts->AddDecal(vPos, CECDecal::DCID_ICONDECAL); + CECIconDecal* pDecal = (CECIconDecal*)pBubbleDecal->GetDecal(); + + switch (iIndex) + { + case BUBBLE_DAMAGE: + + if (!bHost) + dwCol = A3DCOLORRGB(237, 56, 0); + + if (p1 & 0x0001) + pDecal->AddIcon(CECImageRes::IMG_DEADLYSTRIKE, 0, dwCol); + else if (p1 & 0x0002) + pDecal->AddIcon(CECImageRes::IMG_RETORT, 0, dwCol); + + pDecal->AddNumIcons(CECImageRes::IMG_POPUPNUM, dwNum, dwCol); + break; + + case BUBBLE_EXP: + + pDecal->AddIcon(CECImageRes::IMG_GOTEXP, 0, dwCol); + pDecal->AddNumIcons(CECImageRes::IMG_POPUPNUM, dwNum, dwCol); + // pDecal->SetScreenPos(80, 70); + // pDecal->EnableScreenPos(true); + break; + + case BUBBLE_SP: + + pDecal->AddIcon(CECImageRes::IMG_GOTSP, 0, dwCol); + pDecal->AddNumIcons(CECImageRes::IMG_POPUPNUM, dwNum, dwCol); + // pDecal->SetScreenPos(80, 90); + // pDecal->EnableScreenPos(true); + break; + + case BUBBLE_MONEY: + + pDecal->AddIcon(CECImageRes::IMG_GOTMONEY, 0, dwCol); + pDecal->AddNumIcons(CECImageRes::IMG_POPUPNUM, dwNum, dwCol); + break; + + case BUBBLE_LEVELUP: + + pDecal->AddIcon(CECImageRes::IMG_LEVELUP, 0, dwCol); + break; + + case BUBBLE_HITMISSED: + + if (!bHost) + dwCol = A3DCOLORRGB(237, 56, 0); + + pDecal->AddIcon(CECImageRes::IMG_HITMISSED, 0, dwCol); + break; + + case BUBBLE_INVALIDHIT: + + if (!bHost) + dwCol = A3DCOLORRGB(237, 56, 0); + + pDecal->AddIcon(CECImageRes::IMG_INVALIDHIT, 0, dwCol); + break; + + case BUBBLE_IMMUNE: + + if (!bHost) + dwCol = A3DCOLORRGB(237, 56, 0); + + pDecal->AddIcon(CECImageRes::IMG_IMMUNE, 0, dwCol); + break; + + case BUBBLE_HPWARN: + + dwCol = A3DCOLORRGB(255, 255, 255); + pDecal->AddIcon(CECImageRes::IMG_HPWARN, 0, dwCol); + break; + + case BUBBLE_MPWARN: + + dwCol = A3DCOLORRGB(255, 255, 255); + pDecal->AddIcon(CECImageRes::IMG_MPWARN, 0, dwCol); + break; + + case BUBBLE_REBOUND: + + pDecal->AddIcon(CECImageRes::IMG_REBOUND, 0, dwCol); + pDecal->AddNumIcons(CECImageRes::IMG_POPUPNUM, dwNum, dwCol); + break; + + case BUBBLE_BEAT_BACK: + + pDecal->AddIcon(CECImageRes::IMG_BEAT_BACK, 0, dwCol); + pDecal->AddNumIcons(CECImageRes::IMG_POPUPNUM, dwNum, dwCol); + break; + + case BUBBLE_ADD: + + dwCol = A3DCOLORRGB(126, 206, 244); + pDecal->AddIcon(CECImageRes::IMG_ADD, 0, dwCol); + pDecal->AddNumIcons(CECImageRes::IMG_POPUPNUM, dwNum, dwCol); + break; + + case BUBBLE_DODGE_DEBUFF: + + if (!bHost) + dwCol = A3DCOLORRGB(237, 56, 0); + + pDecal->AddIcon(CECImageRes::IMG_DODGE_DEBUFF, 0, dwCol); + break; + + default: + return; + } +} + +bool CECPlayer::IsClothesOn() +{ + if( m_bFashionMode ) + { + if( m_iGender == GENDER_MALE ) + { + if( !m_aCurSkins[SKIN_FASHION_LOWER_INDEX] ) + return false; + } + else + { + if( !m_aCurSkins[SKIN_FASHION_UPPER_BODY_INDEX] || + !m_aCurSkins[SKIN_FASHION_LOWER_INDEX] ) + return false; + } + } + else + { + if( !m_aCurSkins[SKIN_UPPER_BODY_INDEX] || + !m_aCurSkins[SKIN_LOWER_INDEX] ) + return false; + } + + return true; +} + +bool CECPlayer::UpdateCurSkins() +{ + if (GetMajorModel()) + { + int i; + for(i=0; i 20.0f ) + lodLevel = 2; + else if( m_fDistToCamera > 10.0f ) + lodLevel = 1; + + if( m_bFashionMode ) + { + if (i >= SKIN_UPPER_BODY_INDEX && i <= SKIN_FOOT_INDEX) + { + ReplaceCurSkin(i, NULL); + } + else + { + ReplaceCurSkin(i, m_aSkins[i][lodLevel]); + } + } + else + { + if (i >= SKIN_FASHION_UPPER_BODY_INDEX && i <= SKIN_FASHION_FOOT_INDEX) + { + ReplaceCurSkin(i, NULL); + } + else + { + ReplaceCurSkin(i, m_aSkins[i][lodLevel]); + } + } + } + } + + return true; +} + +//吃变形丸 +bool CECPlayer::EatChgPill(const char* pszPillFile, float vCamScale) +{ + if(!m_ChangePill.IsEmpty()) + return false; + + if(!m_ChangePill.Load(pszPillFile)) + return false; + + if( (m_ChangePill.GetCharacter() != m_iProfession) || (m_ChangePill.GetGender() != m_iGender)) + return false; + + if( m_ChangePill.IsEmpty()) + return false; + + //备份原有个性化数据 + BackupCustomizeData(); + + //设置变形丸数据 + for( int i = 0; i < m_ChangePill.GetChgPillDataCount(); i++) + { + SetChgPillData(m_ChangePill.GetChgPillData(i)->nKey, m_ChangePill.GetChgPillData(i)->nValue); + } + + //变形 + ChangeCustomizeData(m_CustomizeData); + + m_vPortraitCamScale = vCamScale; + m_bIsChangingFace = true; + return true; +} + +//设置变形丸数据 +bool CECPlayer::SetChgPillData(int nID, int nData) +{ + switch(nID) + { + case CUS_HEAD_SCALE: + m_CustomizeData.headScale = nData; + return true; + + case CUS_UP_SCALE: + m_CustomizeData.upScale = nData; + return true; + + case CUS_WAIST_SCALE: + m_CustomizeData.waistScale = nData; + return true; + + case CUS_ARM_WIDTH: + m_CustomizeData.armWidth = nData; + return true; + + case CUS_LEG_WIDTH: + m_CustomizeData.legWidth = nData; + return true; + + case CUS_BREAST_SCALE: + m_CustomizeData.breastScale = nData; + return true; + + case CUS_ID_FACE: + m_CustomizeData.faceData.idFaceShape1 = nData; + m_CustomizeData.faceData.idFaceShape2 = nData; + return true; + + case CUS_ID_FALING: + m_CustomizeData.faceData.idFalingSkin = nData; + return true; + + case CUS_SCALE_FACEH: + m_CustomizeData.faceData.scaleFaceH = nData; + return true; + + case CUS_SCALE_FACEV: + m_CustomizeData.faceData.scaleFaceV = nData; + return true; + case CUS_SCALE_UP: + m_CustomizeData.faceData.scaleUp = nData; + return true; + + case CUS_SCALE_MIDDLE: + m_CustomizeData.faceData.scaleMiddle = nData; + return true; + + case CUS_SCALE_DOWN: + m_CustomizeData.faceData.scaleDown = nData; + return true; + + + + case CUS_OFFSET_FOREHEADH: + m_CustomizeData.faceData.offsetForeheadH = nData; + return true; + + case CUS_OFFSET_FOREHEADV: + m_CustomizeData.faceData.offsetForeheadV = nData; + return true; + + case CUS_OFFSET_FOREHEADZ: + m_CustomizeData.faceData.offsetForeheadZ = nData; + return true; + + case CUS_ROTATE_FOREHEAD: + m_CustomizeData.faceData.rotateForehead = nData; + return true; + + case CUS_SCALE_FOREHEAD: + m_CustomizeData.faceData.scaleForehead = nData; + return true; + + case CUS_OFFSET_YOKEBONEH: + m_CustomizeData.faceData.offsetYokeBoneH = nData; + return true; + + case CUS_OFFSET_YOKEBONEV: + m_CustomizeData.faceData.offsetYokeBoneV = nData; + return true; + + case CUS_OFFSET_YOKEBONEZ: + m_CustomizeData.faceData.offsetYokeBoneZ = nData; + return true; + + case CUS_ROTATE_YOKEBONE: + m_CustomizeData.faceData.rotateYokeBone = nData; + return true; + case CUS_SCALE_YOKEBONE: + m_CustomizeData.faceData.scaleYokeBone = nData; + return true; + + case CUS_OFFSET_CHEEKH: + m_CustomizeData.faceData.offsetCheekH = nData; + return true; + + case CUS_OFFSET_CHEEKV: + m_CustomizeData.faceData.offsetCheekV = nData; + return true; + + case CUS_OFFSET_CHEEKZ: + m_CustomizeData.faceData.offsetCheekZ = nData; + return true; + + case CUS_SCALE_CHEEK: + m_CustomizeData.faceData.scaleCheek = nData; + return true; + + + case CUS_OFFSET_CHAINV: + m_CustomizeData.faceData.offsetChainV = nData; + return true; + + case CUS_OFFSET_CHAINZ: + m_CustomizeData.faceData.offsetChainZ = nData; + return true; + + case CUS_ROTATE_CHAIN: + m_CustomizeData.faceData.rotateChain = nData; + return true; + + case CUS_SCALE_CHAINH: + m_CustomizeData.faceData.scaleChainH = nData; + return true; + + + case CUS_OFFSET_JAWH: + m_CustomizeData.faceData.offsetJawH = nData; + return true; + + case CUS_OFFSET_JAWV: + m_CustomizeData.faceData.offsetJawV = nData; + return true; + + case CUS_OFFSET_JAWZ: + m_CustomizeData.faceData.offsetJawZ = nData; + return true; + + case CUS_SCALE_JAWSPECIAL: + m_CustomizeData.faceData.scaleJawSpecial = nData; + return true; + + case CUS_SCALE_JAWH: + m_CustomizeData.faceData.scaleJawH = nData; + return true; + + case CUS_SCALE_JAWV: + m_CustomizeData.faceData.scaleJawV = nData; + return true; + + case CUS_ID_EYE_BASETEX: + m_CustomizeData.faceData.idEyeBaseTex = nData; + return true; + + case CUS_ID_EYE_SHAPE: + m_CustomizeData.faceData.idEyeShape = nData; + return true; + + case CUS_SCALE_EYEH: + m_CustomizeData.faceData.scaleEyeH = nData; + return true; + + case CUS_SCALE_EYEV: + m_CustomizeData.faceData.scaleEyeV = nData; + return true; + + case CUS_ROTATE_EYE: + m_CustomizeData.faceData.rotateEye = nData; + return true; + + case CUS_OFFSET_EYEH: + m_CustomizeData.faceData.offsetEyeH = nData; + return true; + + case CUS_OFFSET_EYEV: + m_CustomizeData.faceData.offsetEyeV = nData; + return true; + + case CUS_OFFSET_EYEZ: + m_CustomizeData.faceData.offsetEyeZ = nData; + return true; + + case CUS_SCALE_EYE_BALL: + m_CustomizeData.faceData.scaleEyeBall = nData; + return true; + + case CUS_SCALE_EYEH2: + m_CustomizeData.faceData.scaleEyeH2 = nData; + return true; + + case CUS_SCALE_EYEV2: + m_CustomizeData.faceData.scaleEyeV2 = nData; + return true; + + case CUS_ROTATE_EYE2: + m_CustomizeData.faceData.rotateEye2 = nData; + return true; + + case CUS_OFFSET_EYEH2: + m_CustomizeData.faceData.offsetEyeH2 = nData; + return true; + + case CUS_OFFSET_EYEV2: + m_CustomizeData.faceData.offsetEyeV2 = nData; + return true; + + case CUS_OFFSET_EYEZ2: + m_CustomizeData.faceData.offsetEyeZ2 = nData; + return true; + + case CUS_SCALE_EYE_BALL2: + m_CustomizeData.faceData.scaleEyeBall2 = nData; + return true; + + case CUS_ID_BROW_TEX: + m_CustomizeData.faceData.idBrowTex = nData; + return true; + + case CUS_ID_BROW_SHAPE: + m_CustomizeData.faceData.idBrowShape = nData; + return true; + + case CUS_SCALE_BROWH: + m_CustomizeData.faceData.scaleBrowH = nData; + return true; + + case CUS_SCALE_BROWV: + m_CustomizeData.faceData.scaleBrowV = nData; + return true; + + case CUS_ROTATE_BROW: + m_CustomizeData.faceData.rotateBrow = nData; + return true; + + case CUS_OFFSET_BROWH: + m_CustomizeData.faceData.offsetBrowH = nData; + return true; + + case CUS_OFFSET_BROWV: + m_CustomizeData.faceData.offsetBrowV = nData; + return true; + + case CUS_OFFSET_BROWZ: + m_CustomizeData.faceData.offsetBrowZ = nData; + return true; + + case CUS_SCALE_BROWH2: + m_CustomizeData.faceData.scaleBrowH2 = nData; + return true; + + case CUS_SCALE_BROWV2: + m_CustomizeData.faceData.scaleBrowV2 = nData; + return true; + + case CUS_ROTATE_BROW2: + m_CustomizeData.faceData.rotateBrow2 = nData; + return true; + + case CUS_OFFSET_BROWH2: + m_CustomizeData.faceData.offsetBrowH2 = nData; + return true; + + case CUS_OFFSET_BROWV2: + m_CustomizeData.faceData.offsetBrowV2 = nData; + return true; + + case CUS_OFFSET_BROWZ2: + m_CustomizeData.faceData.offsetBrowZ2 = nData; + return true; + + case CUS_ID_NOSE_TEX: + m_CustomizeData.faceData.idNoseTex = nData; + return true; + + case CUS_ID_NOSETIP_SHAPE: + m_CustomizeData.faceData.idNoseTipShape = nData; + return true; + + case CUS_ID_NOSEBRIDGE_SHAPE: + m_CustomizeData.faceData.idNoseBridgeShape = nData; + return true; + + case CUS_SCALE_NOSETIPH: + m_CustomizeData.faceData.scaleNoseTipH = nData; + return true; + + case CUS_SCALE_NOSETIPV: + m_CustomizeData.faceData.scaleNoseTipV = nData; + return true; + + case CUS_OFFSET_NOSETIPV: + m_CustomizeData.faceData.offsetNoseTipV = nData; + return true; + + case CUS_SCALE_NOSETIPZ: + m_CustomizeData.faceData.scaleNoseTipZ = nData; + return true; + + case CUS_SCALE_BRIDGETIPH: + m_CustomizeData.faceData.scaleBridgeTipH = nData; + return true; + + case CUS_OFFSET_BRIDGETIPZ: + m_CustomizeData.faceData.offsetBridgeTipZ = nData; + return true; + + case CUS_ID_MOUTH_UPLIPLINE: + m_CustomizeData.faceData.idMouthUpLipLine = nData; + return true; + + case CUS_ID_MOUTH_MIDLIPLINE: + m_CustomizeData.faceData.idMouthMidLipLine = nData; + return true; + + case CUS_ID_MOUTH_DOWNLIPLINE: + m_CustomizeData.faceData.idMouthDownLipLine = nData; + return true; + + case CUS_THICK_UPLIP: + m_CustomizeData.faceData.thickUpLip = nData; + return true; + + case CUS_THICK_DOWNLIP: + m_CustomizeData.faceData.thickDownLip = nData; + return true; + + case CUS_OFFSET_MOUTHV: + m_CustomizeData.faceData.offsetMouthV = nData; + return true; + + case CUS_OFFSET_MOUTHZ: + m_CustomizeData.faceData.offsetMouthZ = nData; + return true; + + case CUS_SCALE_MOUTHH: + m_CustomizeData.faceData.scaleMouthH = nData; + return true; + + case CUS_SCALE_MOUTHH2: + m_CustomizeData.faceData.scaleMouthH2 = nData; + return true; + + case CUS_OFFSET_CORNEROFMOUTHSPECIAL: + m_CustomizeData.faceData.offsetCornerOfMouthSpecial = nData; + return true; + + case CUS_OFFSET_CORNEROFMOUTHSPECIAL2: + m_CustomizeData.faceData.offsetCornerOfMouthSpecial2 = nData; + return true; + + case CUS_ID_EAR_SHAPE: + m_CustomizeData.faceData.idEarShape = nData; + return true; + + case CUS_SCALE_EAR: + m_CustomizeData.faceData.scaleEar = nData; + return true; + + case CUS_OFFSET_EARV: + m_CustomizeData.faceData.offsetEarV = nData; + return true; + + case CUS_ID_HAIR_MODEL: + m_CustomizeData.faceData.idHairModel = nData; + return true; + + case CUS_ID_HAIR_TEX: + m_CustomizeData.faceData.idHairTex = nData; + return true; + + case CUS_ID_MOUSTACHE_TEX: + m_CustomizeData.faceData.idMoustacheTex = nData; + return true; + + case CUS_ID_MOUSTACHE_SKIN: + m_CustomizeData.faceData.idMoustacheSkin = nData; + return true; + + case CUS_ID_GOATEE_TEX: + m_CustomizeData.faceData.idGoateeTex = nData; + return true; + + case CUS_ID_EYE_HIGHTEX: + m_CustomizeData.faceData.idEyeHighTex = nData; + return true; + + case CUS_COLOR_FACE: + m_CustomizeData.faceData.colorFace = nData; + m_CustomizeData.colorBody = nData; + + return true; + + case CUS_COLOR_EYE: + m_CustomizeData.faceData.colorEye = nData; + return true; + + case CUS_COLOR_BROW: + m_CustomizeData.faceData.colorBrow = nData; + return true; + + case CUS_COLOR_MOUTH: + m_CustomizeData.faceData.colorMouth = nData; + return true; + + case CUS_COLOR_HAIR: + m_CustomizeData.faceData.colorHair = nData; + return true; + + case CUS_COLOR_EYEBALL: + m_CustomizeData.faceData.colorEyeBall = nData; + return true; + + case CUS_COLOR_MOUSTACHE: + m_CustomizeData.faceData.colorMoustache = nData; + return true; + + default: + return false; + } +} + +//备份个性化数据(与变形丸相关) +void CECPlayer::BackupCustomizeData(void) +{ + memcpy(&m_ChgPllCustomizeData, &m_CustomizeData, sizeof(PLAYER_CUSTOMIZEDATA)); +} + +//还原备份个性化数据(与变形丸相关) +bool CECPlayer::DiscardChgPill(void) +{ + if( m_ChangePill.IsEmpty()) + return false; + + ChangeCustomizeData(m_ChgPllCustomizeData); + + m_ChangePill.ClearChangePillData(); + + m_vPortraitCamScale = 1.0f; + m_bIsChangingFace = false; + return true; +} + +// Apply effect on player +bool CECPlayer::ApplyEffect(int iEffect, bool bCheckArray) +{ + if (bCheckArray) + { + // Check whether player has had this effect + for (int i=0; i < m_aCurEffects.GetSize(); i++) + { + if (m_aCurEffects[i] == iEffect) + return true; + } + + m_aCurEffects.UniquelyAdd(iEffect); + } + + CECIvtrItem* pItem = CECIvtrItem::CreateItem(iEffect, 0, 1); + if (!pItem) + return false; + + switch (pItem->GetClassID()) + { + case CECIvtrItem::ICID_FACEPILL: + { + const char* szFile = ((CECIvtrFacePill*)pItem)->GetPillFile(m_iProfession, m_iGender); + if (szFile) + EatChgPill(szFile, ((CECIvtrFacePill*)pItem)->GetDBEssence()->camera_scale); + + break; + } + } + + delete pItem; + return true; +} + +// Discard effect from player +void CECPlayer::DiscardEffect(int iEffect) +{ + // From from effect records + for (int i=0; i < m_aCurEffects.GetSize(); i++) + { + if (m_aCurEffects[i] == iEffect) + { + m_aCurEffects.RemoveAtQuickly(i); + break; + } + } + + CECIvtrItem* pItem = CECIvtrItem::CreateItem(iEffect, 0, 1); + if (!pItem) + return; + + switch (pItem->GetClassID()) + { + case CECIvtrItem::ICID_FACEPILL: + + DiscardChgPill(); + break; + } + + delete pItem; +} + +// Check whether player has effect of specified type +bool CECPlayer::HasEffectType(int iEffType) +{ + int i; + + switch (iEffType) + { + case EFF_FACEPILL: + { + elementdataman* pDataMan = g_pGame->GetElementDataMan(); + + // Get item data type + for (i=0; i < m_aCurEffects.GetSize(); i++) + { + DATA_TYPE DataType = pDataMan->get_data_type(m_aCurEffects[i], ID_SPACE_ESSENCE); + if (DataType == DT_FACEPILL_ESSENCE) + return true; + } + + break; + } + } + + return false; +} + +// Play start use item action +bool CECPlayer::PlayStartUseItemAction(int tid) +{ + DATA_TYPE dt; + const void * pDBEssence = g_pGame->GetElementDataMan()->get_data_ptr(tid, ID_SPACE_ESSENCE, dt); + + switch(dt) + { + case DT_MEDICINE_ESSENCE: + break; + + case DT_TOWNSCROLL_ESSENCE: + case DT_UNIONSCROLL_ESSENCE: + PlayAction(ACT_USEITEM); + PlayAction(ACT_USEITMELOOP, true, 0, true); + break; + + case DT_REVIVESCROLL_ESSENCE: + if( IsDead() ) + break; + else + { + // play an action use revive scroll to revive somebody else + PlayAction(ACT_USEITEM); + PlayAction(ACT_USEITMELOOP, true, 0, true); + } + break; + + case DT_WAR_TANKCALLIN_ESSENCE: + if( IsDead() ) + break; + else + { + // play an action use revive scroll to revive somebody else + PlayAction(ACT_USEITEM); + PlayAction(ACT_USEITMELOOP, true, 0, true); + } + break; + + case DT_FIREWORKS_ESSENCE: + if( IsDead() ) + break; + else + { + PlayAction(ACT_EXP_ASSAULT); + } + break; + } + + return true; +} +bool CECPlayer::PlayGatherMonsterSpiritAction() +{ + PlayAction(ACT_USEITEM); + PlayAction(ACT_USEITMELOOP, true, 0, true); + return true; +} +// Play use item effect +bool CECPlayer::PlayUseItemEffect(int tid, const void* pData, size_t sz) +{ + DATA_TYPE dt; + const void * pDBEssence = g_pGame->GetElementDataMan()->get_data_ptr(tid, ID_SPACE_ESSENCE, dt); + + switch(dt) + { + case DT_MEDICINE_ESSENCE: + { + if( !GetPlayerModel() ) + return true; + + const MEDICINE_ESSENCE * pMedicine = (MEDICINE_ESSENCE *) pDBEssence; + switch(pMedicine->id_major_type) + { + case 1794: // 加生命 + PlayGfx(res_GFXFile(RES_GFX_RED_MEDICINE), NULL, 1.0, PLAYERMODEL_TYPEALL); + break; + + case 1802: // 加真气 + PlayGfx(res_GFXFile(RES_GFX_BLUE_MEDICINE), NULL, 1.0, PLAYERMODEL_TYPEALL); + break; + + case 1810: // 加生命和真气 + PlayGfx(res_GFXFile(RES_GFX_PURPLE_MEDICINE), NULL, 1.0, PLAYERMODEL_TYPEALL); + break; + + case 1815: // 解毒 + case 2038: // 瞬间解毒 + PlayGfx(res_GFXFile(RES_GFX_JIEDU_MEDICINE), NULL, 1.0, PLAYERMODEL_TYPEALL); + break; + } + break; + } + + case DT_TOWNSCROLL_ESSENCE: + break; + + case DT_ARMORRUNE_ESSENCE: + { + if( !GetPlayerModel() ) + return true; + + const ARMORRUNE_ESSENCE * pArmorRune = (ARMORRUNE_ESSENCE *) pDBEssence; + switch(pArmorRune->id_sub_type) + { + case 405: // 物防符 + PlayGfx(res_GFXFile(RES_GFX_PHYSIC_ARMORRUNE), NULL, 1.0, PLAYERMODEL_TYPEALL); + break; + + case 406: // 法防符 + PlayGfx(res_GFXFile(RES_GFX_MAGIC_ARMORRUNE), NULL, 1.0, PLAYERMODEL_TYPEALL); + break; + } + break; + } + break; + + case DT_FIREWORKS_ESSENCE: + { + const FIREWORKS_ESSENCE * pFireWorks = (FIREWORKS_ESSENCE *) pDBEssence; + if( pFireWorks->file_fw[0] ) + { + g_pGame->GetGFXCaster()->PlayFW(pFireWorks->file_fw, pFireWorks->time_to_fire * 1000, GetPos() + GetDir() * 0.5f, GetDir()); + } + + DoEmote(ROLEEXP_FIREWORK); + } + break; + + case DT_SKILLMATTER_ESSENCE: + { + const SKILLMATTER_ESSENCE * pSkillMatter = (SKILLMATTER_ESSENCE *) pDBEssence; + if( pSkillMatter->id_skill ) + { + abase::vector targets; + g_pGame->GetGameRun()->GetWorld()->GetAttacksMan()->GetSkillGfxComposerMan()->Play( + pSkillMatter->id_skill, GetPlayerInfo().cid, GetPlayerInfo().cid, targets); + } + } + break; + + } + return true; +} + +float CECPlayer::GetEquipGfxScale() +{ + float vScale = 1.0f; + if( m_iGender == GENDER_FEMALE ) + vScale = 0.6f; + else if( m_iProfession == PROF_ORC ) + vScale = 1.3f; + return vScale; +} + +void CECPlayer::AddUpperBodyStones() +{ + if (InFashionMode()){ + RemoveUpperBodyStones(); + return; + } + if (!CECOptimize::Instance().GetGFX().CanShowArmorStone(GetCharacterID(), GetClassID())){ + return; + } + const WORD &stone = m_stoneUpperBody; + WORD &stoneShown = m_stoneUpperBodyShown; + if (stoneShown == stone){ + return; + } + if (stoneShown != 0){ + RemoveUpperBodyStones(); + } + if (stone == 0){ + return; + } + int nEquipIndex = EQUIPIVTR_BODY; + float vScale = GetEquipGfxScale(); + AString strGFX = GetArmorStoneGfx(stone, nEquipIndex); + if (PlayGfx(strGFX, "HH_Spine", vScale)){ + stoneShown = stone; + } + strGFX = GetArmorStoneGfx(stone, nEquipIndex, 1); + if (PlayGfx(strGFX, "HH_Shangshen02", vScale)){ + stoneShown = stone; + } + if (PlayGfx(strGFX, "HH_Shangshen05", vScale)){ + stoneShown = stone; + } +} + +void CECPlayer::RemoveUpperBodyStones() +{ + WORD &stoneShown = m_stoneUpperBodyShown; + if (stoneShown == 0) return; + int nEquipIndex = EQUIPIVTR_BODY; + AString strGFX = GetArmorStoneGfx(stoneShown, nEquipIndex); + RemoveGfx(strGFX, "HH_Spine"); + strGFX = GetArmorStoneGfx(stoneShown, nEquipIndex, 1); + RemoveGfx(strGFX, "HH_Shangshen02"); + RemoveGfx(strGFX, "HH_Shangshen05"); + stoneShown = 0; +} + +void CECPlayer::AddWristStones() +{ + if (InFashionMode()){ + RemoveWristStones(); + return; + } + if (!CECOptimize::Instance().GetGFX().CanShowArmorStone(GetCharacterID(), GetClassID())){ + return; + } + const WORD &stone = m_stoneWrist; + WORD &stoneShown = m_stoneWristShown; + if (stoneShown == stone){ + return; + } + if (stoneShown != 0){ + RemoveWristStones(); + } + if (stone == 0){ + return; + } + int nEquipIndex = EQUIPIVTR_WRIST; + float vScale = GetEquipGfxScale(); + AString strGFX = GetArmorStoneGfx(stone, nEquipIndex); + if (PlayGfx(strGFX, "HH_shou03", vScale)){ + stoneShown = stone; + } + if (PlayGfx(strGFX, "HH_shou04", vScale)){ + stoneShown = stone; + } +} + +void CECPlayer::RemoveWristStones() +{ + WORD &stoneShown = m_stoneWristShown; + if (stoneShown == 0) return; + int nEquipIndex = EQUIPIVTR_WRIST; + AString strGFX = GetArmorStoneGfx(stoneShown, nEquipIndex); + RemoveGfx(strGFX, "HH_shou03"); + RemoveGfx(strGFX, "HH_shou04"); + stoneShown = 0; +} + +void CECPlayer::AddLowerBodyStones() +{ + if (InFashionMode()){ + RemoveLowerBodyStones(); + return; + } + if (!CECOptimize::Instance().GetGFX().CanShowArmorStone(GetCharacterID(), GetClassID())){ + return; + } + const WORD &stone = m_stoneLowerBody; + WORD &stoneShown = m_stoneLowerBodyShown; + if (stoneShown == stone){ + return; + } + if (stoneShown != 0){ + RemoveLowerBodyStones(); + } + if (stone == 0){ + return; + } + int nEquipIndex = EQUIPIVTR_LEG; + float vScale = GetEquipGfxScale(); + AString strGFX = GetArmorStoneGfx(stone, nEquipIndex); + if (PlayGfx(strGFX, "HH_xiashen02", vScale)){ + stoneShown = stone; + } + if (PlayGfx(strGFX, "HH_xiashen04", vScale)){ + stoneShown = stone; + } +} + +void CECPlayer::RemoveLowerBodyStones() +{ + WORD &stoneShown = m_stoneLowerBodyShown; + if (stoneShown == 0) return; + int nEquipIndex = EQUIPIVTR_LEG; + AString strGFX = GetArmorStoneGfx(stoneShown, nEquipIndex); + RemoveGfx(strGFX, "HH_xiashen02"); + RemoveGfx(strGFX, "HH_xiashen04"); + stoneShown = 0; +} + +void CECPlayer::AddFootStones() +{ + if (InFashionMode()){ + RemoveFootStones(); + return; + } + if (!CECOptimize::Instance().GetGFX().CanShowArmorStone(GetCharacterID(), GetClassID())){ + return; + } + const WORD &stone = m_stoneFoot; + WORD &stoneShown = m_stoneFootShown; + if (stoneShown == stone){ + return; + } + if (stoneShown != 0){ + RemoveFootStones(); + } + if (stone == 0){ + return; + } + int nEquipIndex = EQUIPIVTR_FOOT; + float vScale = GetEquipGfxScale(); + AString strGFX = GetArmorStoneGfx(stone, nEquipIndex); + if (PlayGfx(strGFX, "HH_xie02", vScale)){ + stoneShown = stone; + } + if (PlayGfx(strGFX, "HH_xie04", vScale)){ + stoneShown = stone; + } +} + +void CECPlayer::RemoveFootStones() +{ + WORD &stoneShown = m_stoneFootShown; + if (stoneShown == 0) return; + int nEquipIndex = EQUIPIVTR_FOOT; + AString strGFX = GetArmorStoneGfx(stoneShown, nEquipIndex); + RemoveGfx(strGFX, "HH_xie02"); + RemoveGfx(strGFX, "HH_xie04"); + stoneShown = 0; +} + +AString CECPlayer::GetSharpenerGfx(WORD status) +{ + // 计算被磨刀石磨过的装备应该显示的特效 + // 若装备未被磨过,则返回空 + // + AString strGfxFile; + + BYTE idColor = (status>>8); // 高8位指示磨刀石控制的选项 + if (idColor != 0) + { + static const char * szGFXFile[] = + { + "无", + "物理攻击1级", + "物理攻击2级", + "物理攻击3级", + "法术攻击1级", + "法术攻击2级", + "法术攻击3级", + "生命增强1级", + "生命增强2级", + "生命增强3级", + "属性增强1级", + "属性增强2级", + "属性增强3级", + "特殊属性1级", + "特殊属性2级", + "特殊属性3级", + }; + const int nGFXFile = sizeof(szGFXFile)/sizeof(szGFXFile[0]); + if (idColor >= nGFXFile) + idColor = nGFXFile-1; + strGfxFile.Format("程序联入\\磨刀石光效\\磨刀石光效%s.gfx", szGFXFile[idColor]); + } + + return strGfxFile; +} + +AString CECPlayer::GetArmorStoneGfx(WORD status, int nEquipIndex, int nEquipParam0) +{ + // 根据防具类型、防具的GFX属性,查找对应的GFX路径 + // status: 颜色参数 + // nEquipIndex: 防具装备在装备包裹中的位置 + // nEquipParam0: 附加参数 + + AString strGfxFile = GetSharpenerGfx(status); + + if (strGfxFile.IsEmpty() && status != 0) + { + const char * szColors[] = {"无", "红色", "白色", "金色", "蓝色", "紫色", "绿色", "混合"}; + int idLevel = 1; + int idColor = status & 0x00ff; + if( idColor > 7 ) + { + idColor %= 8; + idLevel = 2; + } + switch(nEquipIndex) + { + case EQUIPIVTR_BODY: + if (nEquipParam0 == 0) + { + strGfxFile.Format("程序联入\\装备宝石镶嵌\\%s%s%d级.gfx", szColors[idColor], "胸甲", idLevel); + } + else if (nEquipParam0 == 1) + { + strGfxFile.Format("程序联入\\装备宝石镶嵌\\%s%s%d级.gfx", szColors[idColor], "肩甲", idLevel); + } + break; + + case EQUIPIVTR_WRIST: + strGfxFile.Format("程序联入\\装备宝石镶嵌\\%s%s%d级.gfx", szColors[idColor], "腕甲", idLevel); + break; + + case EQUIPIVTR_LEG: + strGfxFile.Format("程序联入\\装备宝石镶嵌\\%s%s%d级.gfx", szColors[idColor], "腿甲", idLevel); + break; + + case EQUIPIVTR_FOOT: + strGfxFile.Format("程序联入\\装备宝石镶嵌\\%s%s%d级.gfx", szColors[idColor], "靴甲", idLevel); + break; + } + } + + return strGfxFile; +} + +AString CECPlayer::GetWeaponStoneGfx(WORD status) +{ + // 查找武器对应的GFX路径 + // status: 颜色参数 + + AString strGfxFile = GetSharpenerGfx(status); + + if (strGfxFile.IsEmpty() && status != 0) + { + const char * szColors[] = {"无", "红", "白", "金", "蓝", "紫", "绿", "混"}; + int idColor1 = status & 0x7; + int idColor2 = (status >> 3) & 0x7; + + if( idColor1 > idColor2 ) + { + int temp = idColor1; + idColor1 = idColor2; + idColor2 = temp; + } + strGfxFile.Format("程序联入\\武器宝石镶嵌\\%s%s.gfx", szColors[idColor1], szColors[idColor2]); + } + + return strGfxFile; +} + +void CECPlayer::AddWeaponStones() +{ + // 向当前武器模型(非时装武器)添加宝石特效 + if (!CECOptimize::Instance().GetGFX().CanShowWeaponStone(GetCharacterID(), GetClassID())){ + return; + } + if (!GetPlayerModel() || IsShowFashionWeapon()){ + return; // 没显示武器模型 + } + if (m_stoneWeaponShown == m_stoneWeapon){ + return; // 特效没变化 + } + if (m_stoneWeaponShown != 0){ + RemoveWeaponStones(); + } + if (m_stoneWeapon == 0){ + return; + } + AString strGFX = GetWeaponStoneGfx(m_stoneWeapon); + if (strGFX.IsEmpty()){ + return; + } + float vScale = GetEquipGfxScale(); + if (CECModel* pLeftHandWeapon = GetLeftHandWeapon()){ + const char * leftGFXHookPos = GetLeftWeaponGFXHookPos(pLeftHandWeapon); + pLeftHandWeapon->PlayGfx(strGFX, leftGFXHookPos, vScale); + m_stoneWeaponShown = m_stoneWeapon; + } + if (CECModel* pRightHandWeapon = GetRightHandWeapon()){ + const char * rightGFXHookPos = GetRightWeaponGFXHookPos(pRightHandWeapon); + pRightHandWeapon->PlayGfx(strGFX, rightGFXHookPos, vScale); + m_stoneWeaponShown = m_stoneWeapon; + } +} + +void CECPlayer::RemoveWeaponStones() +{ + // 移除当前武器(非时装武器)上的特效(宝石镶嵌、磨刀石) + if (m_stoneWeaponShown == 0) return; + if (GetPlayerModel() && !IsShowFashionWeapon()){ + AString strGFX = GetWeaponStoneGfx(m_stoneWeaponShown); + if (!strGFX.IsEmpty()){ + if (CECModel* pLeftHandWeapon = GetLeftHandWeapon()){ + const char * leftGFXHookPos = GetLeftWeaponGFXHookPos(pLeftHandWeapon); + pLeftHandWeapon->RemoveGfx(strGFX, leftGFXHookPos); + } + if (CECModel* pRightHandWeapon = GetRightHandWeapon()){ + const char * rightGFXHookPos = GetRightWeaponGFXHookPos(pRightHandWeapon); + pRightHandWeapon->RemoveGfx(strGFX, rightGFXHookPos); + } + } + } + m_stoneWeaponShown = 0; +} + +void CECPlayer::AddFullSuiteGFX() +{ + if (InFashionMode()){ + RemoveFullSuiteGFX(); + return; + } + if (!CECOptimize::Instance().GetGFX().CanShowSuite(GetCharacterID(), GetClassID())){ + return; + } + if (m_idFullSuiteShown == m_idFullSuite){ + return; + } + if (m_idFullSuiteShown != 0){ + RemoveFullSuiteGFX(); + } + if (m_idFullSuite == 0){ + return; + } + DATA_TYPE dt; + SUITE_ESSENCE * pSuite = (SUITE_ESSENCE *) g_pGame->GetElementDataMan()->get_data_ptr(m_idFullSuite, ID_SPACE_ESSENCE, dt); + if (dt == DT_SUITE_ESSENCE && pSuite->file_gfx[0]){ + float vScale = GetEquipGfxScale(); + if (PlayGfx(pSuite->file_gfx + 4, "HH_Spine", vScale)){ + m_idFullSuiteShown = m_idFullSuite; + } + } +} + +void CECPlayer::RemoveFullSuiteGFX() +{ + if (m_idFullSuiteShown == 0) return; + DATA_TYPE dt; + SUITE_ESSENCE * pSuite = (SUITE_ESSENCE *) g_pGame->GetElementDataMan()->get_data_ptr(m_idFullSuiteShown, ID_SPACE_ESSENCE, dt); + if (dt == DT_SUITE_ESSENCE && pSuite->file_gfx[0] ){ + RemoveGfx(pSuite->file_gfx + 4, "HH_Spine"); + } + m_idFullSuiteShown = 0; +} + +// Set player's transparence +void CECPlayer::SetTransparent(float fTrans) +{ + if (m_pPlayerModel) + m_pPlayerModel->SetTransparent(fTrans); + + if (m_pFaceModel) + { + m_pFaceModel->SetTransparent(fTrans); + m_pFaceModel->AutoHairTrans(fTrans >= 0.0f ? false : true); + } +} + +// Get player name color +DWORD CECPlayer::GetNameColor() +{ + DWORD dwNameCol = NAMECOL_MAUVE; + + if (IsInBattle()) // Player is in battle + { + if (m_iBattleCamp == GP_BATTLE_CAMP_INVADER) + dwNameCol = NAMECOL_BC_RED; + else // (m_iBattleCamp == GP_BATTLE_CAMP_DEFENDER) + dwNameCol = NAMECOL_BC_BLUE; + } + else if (IsPariah()) + { + switch (m_byPariahLvl) + { + case 0: dwNameCol = NAMECOL_RED0; break; + case 1: dwNameCol = NAMECOL_RED1; break; + default: dwNameCol = NAMECOL_RED2; break; + } + } + else if (IsInvader()) + dwNameCol = NAMECOL_PINK; + else if (m_pvp.bEnable) + dwNameCol = NAMECOL_WHITE; + + return dwNameCol; +} + +// Check whether player in a same battle camp +bool CECPlayer::InSameBattleCamp(CECPlayer* pPlayer) +{ + if (!pPlayer || m_iBattleCamp == GP_BATTLE_CAMP_NONE || + m_iBattleCamp != pPlayer->GetBattleCamp()) + return false; + + return true; +} + +// Check whether specified npc in a same battle camp +bool CECPlayer::InSameBattleCamp(CECNPC* pNPC) +{ + if (!pNPC || m_iBattleCamp == GP_BATTLE_CAMP_NONE || + (m_iBattleCamp == GP_BATTLE_CAMP_INVADER && !pNPC->IsInBattleInvaderCamp()) || + (m_iBattleCamp == GP_BATTLE_CAMP_DEFENDER && !pNPC->IsInBattleDefenderCamp())) + return false; + + return true; +} + +// Change player's tank leader state +void CECPlayer::ChangeTankLeader(int idTank, bool bBecomeLeader) +{ + if (!IsInBattle()) + return; + + static const char* szHeadHook = "HH_Head"; + + // Get effect file name base on player's battle camp + const char* szGFXFile; + if (m_iBattleCamp == GP_BATTLE_CAMP_INVADER) + szGFXFile = res_GFXFile(RES_GFX_TANKLEADER_RED); + else + szGFXFile = res_GFXFile(RES_GFX_TANKLEADER_BLUE); + + // When player leave battle, idTank == 0 + if (!idTank) + { + // Remove effect if there is one + RemoveGfx(szGFXFile, szHeadHook, PLAYERMODEL_TYPEALL); + + m_aBattleTanks.RemoveAll(false); + return; + } + + if (bBecomeLeader) + { + // No matter how many tanks are controlled by player, ensure only + // one effect is played + if (!m_aBattleTanks.GetSize()) + { + PlayGfx(szGFXFile, szHeadHook, 1.0, PLAYERMODEL_TYPEALL); + } + + int iIndex = m_aBattleTanks.Find(idTank); + if (iIndex < 0) + { + m_aBattleTanks.Add(idTank); + + if (IsHostPlayer()) + g_pGame->GetGameRun()->AddFixedMessage(FIXMSG_OBTAINTANK); + } + } + else + { + if (m_aBattleTanks.GetSize()) + { + // Check whether tank has been controlled by us + int iIndex = m_aBattleTanks.Find(idTank); + if (iIndex >= 0) + { + m_aBattleTanks.RemoveAtQuickly(iIndex); + + if (IsHostPlayer()) + g_pGame->GetGameRun()->AddFixedMessage(FIXMSG_LOSETANK); + } + + // Remove effect only when no tank is controlled by player + if (!m_aBattleTanks.GetSize()) + { + RemoveGfx(szGFXFile, szHeadHook, PLAYERMODEL_TYPEALL); + } + } + } +} + +// Update player's tank leader state +void CECPlayer::UpdateTankLeader() +{ + if (!IsInBattle() || !m_aBattleTanks.GetSize()) + return; + + CECNPCMan* pNPCMan = g_pGame->GetGameRun()->GetWorld()->GetNPCMan(); + + for (int i=0; i < m_aBattleTanks.GetSize(); i++) + { + int id = m_aBattleTanks[i]; + if (!pNPCMan->GetNPC(id)) + { + // If tank has left player's eye shot, remove it + m_aBattleTanks.RemoveAtQuickly(i); + i--; + + if (IsHostPlayer()) + g_pGame->GetGameRun()->AddFixedMessage(FIXMSG_LOSETANK); + } + } + + if (!m_aBattleTanks.GetSize()) + { + // Remove effect if no tank is controlled by player + ChangeTankLeader(0, false); + } +} + +void CECPlayer::SetSpouse(int idSpouse) +{ + m_idSpouse = idSpouse; +} + +void CECPlayer::SetGoblinRenderCnt(int iCnt) +{ + m_GoblinRenderCnt.SetCounter(iCnt); +} + +void CECPlayer::SetRenderGoblin(bool bRender) +{ + m_bRenderGoblin = bRender; + if(m_pGoblin) + { + m_pGoblin->SetTransparent(0.0f); + } +} + +// Does an equipment need to show +bool CECPlayer::IsShownEquip(int nEquipIndex) +{ + if((nEquipIndex >= EQUIPIVTR_FASHION_BODY && nEquipIndex <= EQUIPIVTR_FASHION_WRIST) || + nEquipIndex == EQUIPIVTR_FASHION_HEAD || nEquipIndex == EQUIPIVTR_FASHION_WEAPON) + return m_bFashionMode; + else + return !m_bFashionMode; +} + +// Get real element id +DWORD CECPlayer::GetRealElementID(int nEquipIndex, DWORD dwEquipID) +{ + // Some equipments use first 16 bits to store color info, so we need to clear color. + +// DWORD dwRealID = dwEquipID; +// if (nEquipIndex >= EQUIPIVTR_FASHION_BODY && nEquipIndex <= EQUIPIVTR_FASHION_WRIST) + DWORD dwRealID = dwEquipID & 0x0000ffff; + + return dwRealID; +} + +bool CECPlayer::GetWeaponMajorType(unsigned int &id_major_type){ + bool result(false); + int idWeapon = GetWeaponID(); + if (idWeapon && idWeapon != 0xffff){ + DATA_TYPE dt; + WEAPON_ESSENCE * pWeapon = (WEAPON_ESSENCE *) g_pGame->GetElementDataMan()->get_data_ptr(idWeapon, ID_SPACE_ESSENCE, dt); + ASSERT(dt == DT_WEAPON_ESSENCE); + if (dt == DT_WEAPON_ESSENCE && pWeapon){ + id_major_type = pWeapon->id_major_type; + result = true; + } + } + return result; +} + +// 是否正在使用法宝类武器 +bool CECPlayer::IsUsingMagicWeapon() +{ + if (GetProfession() == PROF_MONK){ + unsigned int id_major_type(0); + if (GetWeaponMajorType(id_major_type) && + id_major_type == 25333){ // 法宝 + return true; + } + } + return false; +} + +// 是否正在使用胧族武器(胧刀类、月镰类) +bool CECPlayer::IsUsingOboroWeapon(){ + return IsUsingLongKnifeWeapon() || IsUsingSickleWeapon(); +} + +bool CECPlayer::IsUsingLongKnifeWeapon(){ + if (GetProfession() == PROF_YEYING || GetProfession() == PROF_YUEXIAN){ + unsigned int id_major_type(0); + if (GetWeaponMajorType(id_major_type)){ + return id_major_type == 44878; // 胧刀 + } + } + return false; +} + +bool CECPlayer::IsUsingSickleWeapon(){ + if (GetProfession() == PROF_YEYING || GetProfession() == PROF_YUEXIAN){ + unsigned int id_major_type(0); + if (GetWeaponMajorType(id_major_type)){ + return id_major_type == 44879; // 月镰 + } + } + return false; +} + +CECModel * CECPlayer::GetBoothModel() +{ + return m_pBoothModel; +} + +CECModel * CECPlayer::LoadBoothModel(const AString path) +{ + CECModel *pBoothModel = NULL; + + if (af_IsFileExist(path)) + { + // 为减少不必要的错误信息输出,仅当文件存在时尝试加载 + + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile( + (const char*)glb_BoothReplaceShader, A3DSkinMan::SHADERREPLACE_USERDEFINE); + + pBoothModel = new CECModel; + if (!pBoothModel || !pBoothModel->Load(path)) + { + a_LogOutput(1, "CECPlayerMan::LoadBoothModel, Failed to load default booth models %s", (const char *)path); + A3DRELEASE(pBoothModel); + pBoothModel = NULL; + } + else + { + A3DSkinModel *pBoothSkinModel = pBoothModel->GetA3DSkinModel(); + if (pBoothSkinModel) + pBoothSkinModel->EnableSpecular(true); + } + + g_pGame->GetA3DEngine()->GetA3DSkinMan()->SetReplaceShaderFile("", 0); + } + + return pBoothModel; +} + +void CECPlayer::SetBoothModel(const char *idBoothModel) +{ + ClearBoothModel(); + + // 使用默认模型 + if (!idBoothModel || !idBoothModel[0]){ + AString defModelFile = res_ModelFile(GetGender()==GENDER_MALE?RES_MOD_DEFAULT_BOOTH_M:RES_MOD_DEFAULT_BOOTH_F); + m_pBoothModel = LoadBoothModel(defModelFile); + return; + } + + // 尝试直接加载(一般用于不区分男女的模型) + if (m_pBoothModel = LoadBoothModel(idBoothModel)){ + return; + } + + // 按上述路径未查找到相应路径,尝试按照性别修改路径名 + AString path = idBoothModel; + if (path.GetLength() <= 4){ + return; + } + path.CutRight(4); + path += (GetGender()==GENDER_MALE ? "男" : "女"); + path += ".ecm"; + m_pBoothModel = LoadBoothModel(path); +} + +void CECPlayer::ClearBoothModel(){ + if (!m_bBoothModelLoaded){ + return; + } + if (m_pBoothModel){ + A3DRELEASE(m_pBoothModel); + } + m_iBoothModelCertificateID = -1; + m_bBoothModelLoaded = false; +} + +void CECPlayer::UpdateBoothModel(){ + if (m_iBoothState != 2){ // 不是摆摊状态不需要模型 + ClearBoothModel(); + return; + } + if (!ShouldUseBoothModel()){ // 不需要显示模型时不显示模型 + ClearBoothModel(); + return; + } + if (GetCertificateID() == m_iBoothModelCertificateID){ + return; + } + const char *szBoothModel = ""; + if (const SELL_CERTIFICATE_ESSENCE *pEssence = CECElementDataHelper::GetCertificate(GetCertificateID())){ + szBoothModel = pEssence->show_model; + } + SetBoothModel(szBoothModel); + m_iBoothModelCertificateID = GetCertificateID(); + m_bBoothModelLoaded = true; +} + +void CECPlayer::SetBoothBar(const char* szFile) +{ + if (!m_pPateBooth){ + return; + } + if (!szFile || !szFile[0]){ + m_pPateBooth->SetBarImage(NULL); + }else{ + m_pPateBooth->SetBarImage(szFile); + } +} + +void CECPlayer::ClearBoothBar(){ + if (!m_iBoothBarCertificateID){ + return; + } + SetBoothBar(""); + m_iBoothBarCertificateID = -1; +} + +void CECPlayer::UpdateBoothBar(){ + if (m_iBoothState != 2){ + ClearBoothBar(); + return; + } + if (GetCertificateID() == m_iBoothBarCertificateID){ + return; + } + const char *szBoothBar = ""; + if (const SELL_CERTIFICATE_ESSENCE *pEssence = CECElementDataHelper::GetCertificate(GetCertificateID())){ + if (*pEssence->name_image){ + szBoothBar = pEssence->name_image + 9; // 跳过 surfaces/ + } + } + SetBoothBar(szBoothBar); + m_iBoothBarCertificateID = GetCertificateID(); +} + +void CECPlayer::ReplaceCurSkin(int nSkinIndex, A3DSkin *pNewSkin) +{ + if (GetMajorModel()) + { + if (nSkinIndex>=0 && nSkinIndexGetA3DSkin(nSkinIndex); + if (pOldSkin != pNewSkin) + { + GetMajorModel()->ReplaceSkin(nSkinIndex, pNewSkin, false); + m_aCurSkins[nSkinIndex] = pNewSkin; + } + } + } +} + +bool CECPlayer::IsFrog() +{ + return GetShapeType() == PLAYERMODEL_DUMMYTYPE2 && + GetShapeID() == RES_MOD_MONEYFROG; +} + +bool CECPlayer::SetWeaponHangerPos(WeaponHangerPosition newWeaponHangerPos) +{ + // 设置新的武器悬挂位置 + // + bool bRet(false); + + while (true) + { + if (m_weaponHangerPos == newWeaponHangerPos) + { + bRet = true; + break; + } + + if (!GetPlayerModel()) + break; + + // 获取已有武器模型 + CECModel *pLeftHandWeapon = GetLeftHandWeapon(); + CECModel *pRightHandWeapon = GetRightHandWeapon(); + + // 获取模型的显示状态 + bool bShowLeft = pLeftHandWeapon && pLeftHandWeapon->IsShown(); + bool bShowRight = pRightHandWeapon && pRightHandWeapon->IsShown(); + + // 删除旧有模型的挂接状态,但保留模型自身 + DetachWeapon(); + + // 挂接原有武器模型到新挂点 + m_weaponHangerPos = newWeaponHangerPos; + AttachWeapon(); + + // 恢复显示状态 + if (pLeftHandWeapon) + pLeftHandWeapon->Show(bShowLeft); + if (pRightHandWeapon) + pRightHandWeapon->Show(bShowRight); + + bRet = true; + break; + } + + return bRet; +} + +// 根据当前武器、查询 Player 模型上的挂点 +// +const char * CECPlayer::GetLeftWeaponHookPos(WeaponHangerPosition p) +{ + const char *pRet = NULL; + switch(p) + { + case WEAPON_HANGER_HAND: + pRet = _hh_left_hand_weapon; + break; + + case WEAPON_HANGER_SHOULDER: + pRet = _hh_left_shoulder_weapon; + break; + } + return pRet; +} + +const char * CECPlayer::GetRightWeaponHookPos(WeaponHangerPosition p) +{ + const char *pRet = NULL; + switch(p) + { + case WEAPON_HANGER_HAND: + if (IsUsingMagicWeapon()){ + pRet = _hh_qiu; + }else if (IsUsingSickleWeapon()){ + pRet = _hh_right_hand_nickle_weapon; + }else{ + pRet = _hh_right_hand_weapon; + } + break; + + case WEAPON_HANGER_SHOULDER: + pRet = _hh_right_shoulder_weapon; + break; + } + return pRet; +} + +// 根据当前武器、查询武器自身上与玩家挂点位置对接的挂点名称 +// +const char * CECPlayer::GetLeftWeaponOwnHookPos(CECModel *pModel) +{ + A3DSkinModel *pSkinModel = pModel->GetA3DSkinModel(); + return pSkinModel && (pSkinModel->GetSkeletonHook(_cc_weapon, true)!=NULL) ? _cc_weapon : NULL; +} + + +const char * CECPlayer::GetRightWeaponOwnHookPos(CECModel *pModel) +{ + const char *pRet = NULL; + + A3DSkinModel *pSkinModel = pModel->GetA3DSkinModel(); + if (pSkinModel) + { + if (pSkinModel->GetSkeletonHook(_cc_weapon, true)) + pRet = _cc_weapon; + else if (pSkinModel->GetSkeletonHook(_cc_qiu, true)) + pRet = _cc_qiu; + } + + return pRet; +} + +const char * CECPlayer::GetWeaponOwnHookPos(CECModel *pModel, bool bLeft) +{ + return bLeft + ? GetLeftWeaponOwnHookPos(pModel) + : GetRightWeaponOwnHookPos(pModel); +} + +// 获取武器上GFX的挂点 +// +const char *CECPlayer::GetLeftWeaponGFXHookPos(CECModel *pModel) +{ + return GetLeftWeaponOwnHookPos(pModel); +} + +const char * CECPlayer::GetRightWeaponGFXHookPos(CECModel *pModel) +{ + const char * rightHookPos = NULL; + if (IsMagicWeapon(pModel)) + { + // 右手持法球时,不能使用武器上的 CC_qiu 挂点位置作为特效的悬挂点(因为处在人脚下) + rightHookPos = _hh_qiu_base; + } + else + { + rightHookPos = GetRightWeaponOwnHookPos(pModel); + } + return rightHookPos; +} + +const char * CECPlayer::GetWeaponGFXHookPos(CECModel *pModel, bool bLeft) +{ + return bLeft + ? GetLeftWeaponGFXHookPos(pModel) + : GetRightWeaponGFXHookPos(pModel); +} + +CECModel * CECPlayer::GetLeftHandWeapon() +{ + return m_pLeftHandWeapon; +} + +CECModel * CECPlayer::GetRightHandWeapon() +{ + return m_pRightHandWeapon; +} + +bool CECPlayer::IsWeaponHookPos(const char *szHH, bool *pbLeft /* = NULL */, CECModel **ppWeaponModel /* = NULL */) +{ + // 判断给定的人身上的挂点是否为武器挂点,并返回对应武器模型和左右位置 + // + bool bWeaponHook(false); + if (!stricmp(szHH, _hh_left_hand_weapon) || + !stricmp(szHH, _hh_left_shoulder_weapon)) + { + if (ppWeaponModel) + *ppWeaponModel = GetLeftHandWeapon(); + if (pbLeft) + *pbLeft = true; + bWeaponHook = true; + } + else if (!stricmp(szHH, _hh_right_hand_weapon) + || !stricmp(szHH, _hh_right_shoulder_weapon) + || !stricmp(szHH, _hh_right_hand_nickle_weapon)) + { + if (ppWeaponModel) + *ppWeaponModel = GetRightHandWeapon(); + if (pbLeft) + *pbLeft = false; + bWeaponHook = true; + } + return bWeaponHook; +} + +void CECPlayer::UpdateWeaponHangerPosBySkillAction(int idSkill) +{ + if (GetProfession() == PROF_JIANLING) + { + WeaponHangerPosition weaponPos = m_weaponHangerPos; + + int type = GNET::ElementSkill::GetType(idSkill); + if (type == TYPE_ATTACK || + type == TYPE_CURSE || + type == TYPE_BLESS) + { + // 攻击类技能,改为手持剑状态 + weaponPos = WEAPON_HANGER_HAND; + } + else + { + // 其它技能改为背剑状态 + weaponPos = WEAPON_HANGER_SHOULDER; + } + + // 实施悬挂位置修改 + SetWeaponHangerPos(weaponPos); + } +} + +void CECPlayer::UpdateWeaponHangerPosByAction(int iAction) +{ + if (GetProfession() == PROF_JIANLING) + { + WeaponHangerPosition weaponPos = m_weaponHangerPos; + + int changeAction = 0; // 修改选项含义:0 维持原样,1 修改为背在背上,2 修改为握在手上 + + // 根据要播放的动作、确定武器悬挂位置如何修改 + static byte s_WeaponChangeAction[ACT_MAX] = + { + 1,// ACT_STAND = 0, // 站立 + 2,// ACT_FIGHTSTAND, // 战斗站立 + 0,// ACT_WALK, // 行走 + 0,// ACT_RUN, // 奔跑 + 0,// ACT_JUMP_START, // 起跳 + + // 5 + 0,// ACT_JUMP_LOOP, // 起跳空中循环 + 0,// ACT_JUMP_LAND, // 跳跃落地 + 1,// ACT_SWIM, // 游动 + 1,// ACT_HANGINWATER, // 水中漂浮 + 0,// ACT_TAKEOFF, // 翅膀起飞 should be ACT_TAKEOFF_WING + + // 10 + 0,// ACT_HANGINAIR, // 翅膀悬停 should be ACT_HANGINAIR_WING + 0,// ACT_FLY, // 翅膀前进 should be ACT_FLY_WING + 0,// ACT_FLYDOWN, // 翅膀高空下降 should be ACT_FLYDOWN_WING_HIGH + 0,// ACT_FLYDOWN_WING_LOW, // 翅膀低空下降 should be ACT_FLYDOWN_WING_LOW + 0,// ACT_LANDON, // 翅膀落地 should be ACT_LAND_WING + + // 15 + 0,// ACT_TAKEOFF_SWORD, // 飞剑起飞 + 0,// ACT_HANGINAIR_SWORD, // 飞剑悬停 + 0,// ACT_FLY_SWORD, // 飞剑前进 + 0,// ACT_FLYDOWN_SWORD_HIGH, // 飞剑高空下降 + 0,// ACT_FLYDOWN_SWORD_LOW, // 飞剑低空下降 + + // 20 + 0,// ACT_LANDON_SWORD, // 飞剑落地 + 1,// ACT_SITDOWN, // 打坐 + 1,// ACT_SITDOWN_LOOP, // 打坐循环 + 1,// ACT_STANDUP, // 打坐站起 + 0,// ACT_WOUNDED, // 受伤 + + // 25 + 0,// ACT_GROUNDDIE, // 陆地死亡 + 0,// ACT_GROUNDDIE_LOOP, // 死亡地面循环 + 0,// ACT_WATERDIE, // 水中死亡 + 0,// ACT_WATERDIE_LOOP, // 死亡水中循环 + 0,// ACT_AIRDIE_ST, // 空中死亡 + + // 30 + 0,// ACT_AIRDIE, // 空中死亡下落循环 + 0,// ACT_AIRDIE_ED, // 空中死亡落地 + 0,// ACT_AIRDIE_LAND_LOOP, // 死亡落地循环 + 0,// ACT_REVIVE, // 复活 + 1,// ACT_CUSTOMIZE, // 长休闲动作 + + // 35 + 0,// ACT_STRIKEBACK, // 被击退 + 0,// ACT_STRIKEDOWN, // 被击倒 + 0,// ACT_STRIKEDOWN_LOOP, // 被击倒倒地循环 + 0,// ACT_STRIKEDOWN_STANDUP, // 被击倒站起 + 1,// ACT_PICKUP, // 采摘 + + // 40 + 1,// ACT_PICKUP_LOOP, // 采摘植物循环 + 1,// ACT_PICKUP_STANDUP, // 采摘站起 + 0,// ACT_PICKUP_MATTER, // 捡东西 + 0,// ACT_GAPE, // 伸懒腰 + 0,// ACT_LOOKAROUND, // 四处张望 + + // 45 + 2,// ACT_PLAYWEAPON, // 转动兵器 + 0,// ACT_EXP_WAVE, // 招手 + 0,// ACT_EXP_NOD, // 点头 + 0,// ACT_EXP_SHAKEHEAD, // 摇头 + 0,// ACT_EXP_SHRUG, // 耸肩膀 + + // 50 + 0,// ACT_EXP_LAUGH, // 大笑 + 0,// ACT_EXP_ANGRY, // 生气 + 0,// ACT_EXP_STUN, // 晕倒 + 0,// ACT_EXP_DEPRESSED, // 沮丧 + 0,// ACT_EXP_KISSHAND, // 飞吻 + + // 55 + 0,// ACT_EXP_SHY, // 害羞 + 0,// ACT_EXP_SALUTE, // 抱拳 + 0,// ACT_EXP_SITDOWN, // 坐下 + 0,// ACT_EXP_SITDOWN_LOOP, // 坐下循环 + 0,// ACT_EXP_SITDOWN_STANDUP, // 坐下站起 + + // 60 + 0,// ACT_EXP_ASSAULT, // 冲锋 + 0,// ACT_EXP_THINK, // 思考 + 0,// ACT_EXP_DEFIANCE, // 挑衅 + 0,// ACT_EXP_VICTORY, // 胜利 + 0,// ACT_EXP_KISS, // 亲吻 + + // 65 + 0,// ACT_EXP_KISS_LOOP, // 亲吻循环 + 0,// ACT_EXP_KISS_END, // 亲吻结束 + 2,// ACT_ATTACK_1, // 普攻1 + 2,// ACT_ATTACK_2, // 普攻2 + 2,// ACT_ATTACK_3, // 普攻3 + + // 70 + 2,// ACT_ATTACK_4, // 普攻4 + 1,// ACT_ATTACK_TOSS, // 放暗器 + 0,// ACT_TRICK_RUN, // 跑动中的花招 + 0,// ACT_TRICK_JUMP, // 跳跃中的花招 + 0,// ACT_FLY_GLIDE, // 翅膀滑翔 + + // 75 + 0,// ACT_FLY_GLIDE_SWORD, // 飞剑滑翔 + 0,// ACT_EXP_FIGHT, // 战斗 + 0,// ACT_EXP_ATTACK1, // 攻击1 + 0,// ACT_EXP_ATTACK2, // 攻击2 + 0,// ACT_EXP_ATTACK3, // 攻击3 + + // 80 + 0,// ACT_EXP_ATTACK4, // 攻击4 + 0,// ACT_EXP_DEFENCE, // 防御 + 0,// ACT_EXP_FALL, // 摔倒 + 0,// ACT_EXP_FALLONGROUND, // 倒地 + 0,// ACT_EXP_LOOKAROUND, // 张望 + + // 85 + 0,// ACT_EXP_DANCE, // 舞蹈 + 2,// ACT_EXP_FASHIONWEAPON // 时装武器 + 1,// ACT_USEITEM, // 通用的使用物品动作 + 1,// ACT_USEITMELOOP, // 通用的使用物品循环动作 + 0,// ACT_TWO_KISS, // 双人亲吻 + 1,// ACT_USING_TARGET_ITEM, // 使用道具 + }; + + if (iAction >= 0 && iAction < ACT_MAX) + changeAction = s_WeaponChangeAction[iAction]; + + switch(changeAction) + { + case 1: + weaponPos = WEAPON_HANGER_SHOULDER; + break; + + case 2: + weaponPos = WEAPON_HANGER_HAND; + break; + } + + // 实施悬挂位置修改 + SetWeaponHangerPos(weaponPos); + } +} + +void CECPlayer::UpdatePetCureGFX(DWORD dwDeltaTime) +{ + if (!m_pPetCureGFX) + { + // 光效必须在“中状态”时加载 + return; + } + + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + + // 判断是否需要此光效 + bool bNeedGFX(false); + CECPet *pPet = NULL; + while (true) + { + // 判断宠物是否存在 + int idPet = GetCurPetID(); + if (!idPet) + break; + + CECNPCMan *pNPCMan = g_pGame->GetGameRun()->GetWorld()->GetNPCMan(); + if (!pNPCMan) + break; + + pPet = pNPCMan->GetPetByID(idPet); + if (!pPet) + break; + + // 宠物存在 + + // 仍然需要光效 + bNeedGFX = true; + break; + } + if (!bNeedGFX) + { + // 不需要光效了,释放已加载 + pGFXExMan->CacheReleasedGfx(m_pPetCureGFX); + m_pPetCureGFX = NULL; + m_pPetCureGFXtate = -1; + return; + } + + // 需要光效、且光效存在 + + // 判断是否适合显示光效 + A3DVECTOR3 pos[2]; + const A3DAABB &playerAABB = GetPlayerAABB(); + pos[0] = playerAABB.Center; + const A3DAABB &petAABB = pPet->GetPickAABB(); + pos[1] = petAABB.Center; + A3DVECTOR3 delta = pos[0]-pos[1]; + float dist2 = delta.SquaredMagnitude(); + if (dist2 > 625.0f) + { + // 两者距离过远(超过 25 米),隐藏光效 + if (m_pPetCureGFX->IsVisible()) + m_pPetCureGFX->SetVisible(false); + return; + } + + // 需要显示光效果 + + if (m_pPetCureGFX->GetState() != ST_PLAY) + m_pPetCureGFX->Start(true); + if (!m_pPetCureGFX->IsVisible()) + m_pPetCureGFX->SetVisible(true); + + // 更新电弧光效的位置等 + int nElement = m_pPetCureGFX->GetElementCount(); + for (int i = 0; i < nElement; ++ i) + { + A3DGFXElement *pElement = m_pPetCureGFX->GetElement(i); + if (pElement) + GFX_UpdateLightingEdgePos(m_pPetCureGFX, pElement->GetName(), pos); + } + m_pPetCureGFX->TickAnimation(dwDeltaTime); +} + +bool CECPlayer::IsPetCureGFX(const AString &strGFXFile) +{ + // 判断指定光效是否为宠物治疗光效 + // + bool bRet(false); + + while (true) + { + AString strTitle; + if (!af_GetFileTitle(strGFXFile, strTitle) || + strTitle.IsEmpty()) + break; + + const char *szTitle = (const char *)strTitle; + if (strstr(szTitle, "电弧") != szTitle) + break; + + // 只要是电弧光效都返回 true + bRet = true; + break; + } + + return bRet; +} + +bool CECPlayer::TestProcessPetCureGFX(const AString &strGFXFile, bool bLoad, int iState) +{ + bool bRet(false); + + while (true) + { + if (!IsPetCureGFX(strGFXFile)) + break; + + // 只要是电弧光效都返回 true + bRet = true; + + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + + if (!bLoad) + { + // 要消除旧有状态,判断是否加载过 + if (m_pPetCureGFXtate == iState) + { + // 状态正确,判断是否已加载 + if (m_pPetCureGFX) + { + // 曾经加载,现释放 + pGFXExMan->CacheReleasedGfx(m_pPetCureGFX); + m_pPetCureGFX = NULL; + m_pPetCureGFXtate = -1; + } + } + + // 处理完成 + break; + } + + // 加载新特效 + + // 先释放已经加载 + if (m_pPetCureGFX) + { + // 释放已加载 + pGFXExMan->CacheReleasedGfx(m_pPetCureGFX); + m_pPetCureGFX = NULL; + m_pPetCureGFXtate = -1; + } + + // 加载新的特效 + m_pPetCureGFX = pGFXExMan->LoadGfx(g_pGame->GetA3DDevice(), strGFXFile); + if (!m_pPetCureGFX) + break; + + // 设置默认参数 + m_pPetCureGFX->SetScale(1.0f); + + // 初始为不可见 + m_pPetCureGFX->SetVisible(false); + + // 记录对应状态下标 + m_pPetCureGFXtate = iState; + + // 更新位置 + UpdatePetCureGFX(0); + + break; + } + + return bRet; +} + +void CECPlayer::UpdatePosRelatedGFX(DWORD dwDeltaTime) +{ + // 位置相关 GFX 的更新,需要在玩家位置计算完成后执行,以减少延迟 + // + UpdatePetCureGFX(dwDeltaTime); + UpdateMultiObjectEffect(dwDeltaTime); +} + +void CECPlayer::RenderPetCureGFX() +{ + if (m_pPetCureGFX && m_pPetCureGFX->IsVisible()) + { + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + pGFXExMan->RegisterGfx(m_pPetCureGFX); + } +} + +bool CECPlayer::IsMagicWeapon(CECModel *pModel) +{ + // 根据武器的挂点、判断是否为法宝类武器 + // + bool bRet(false); + if (pModel) + { + A3DSkinModel *pSkinModel = pModel->GetA3DSkinModel(); + if (pSkinModel) + { + // 同 GetRightWeaponOwnHookPos 保持一致 + // 武器模板上无 _cc_weapon 挂点、但有 _cc_qiu 挂点时 + // 认定为法宝武器 + // + if (pSkinModel->GetSkeletonHook(_cc_weapon, true)) + bRet = false; + else if (pSkinModel->GetSkeletonHook(_cc_qiu, true)) + bRet = true; + } + } + return bRet; +} + +AString CECPlayer::GetFashionActionName() +{ + // 查找时装装备上携带的动作名称 + // + AString strAction; + + static int local_check_order[] = + { + EQUIPIVTR_FASHION_HEAD, + EQUIPIVTR_FASHION_BODY, + EQUIPIVTR_FASHION_WRIST, + EQUIPIVTR_FASHION_LEG, + EQUIPIVTR_FASHION_FOOT, + }; + + int idEquipment(0); + for (int i = 0; i < sizeof(local_check_order)/sizeof(local_check_order[0]); ++ i) + { + // 按顺序查找指定服装 + idEquipment = m_aEquips[local_check_order[i]] & 0x0000ffff; + GetFashionActionNameByID(idEquipment, strAction); + + if (!strAction.IsEmpty()) + { + // 发现非空时装动作,即返回 + break; + } + } + + return strAction; +} + + +void CECPlayer::SetShape(int iShape) +{ + // The shape id from the server is a 8-bit number + // The meaning of each bit: + // | 7 6 | 5 4 3 2 1 0 | + // |-TYPE-|-----Model ID-----| + int iNewShape = (iShape & 0xff); // only accept 8bit + // 对旧的Shape数据数据进行修正 + FixOldShapeInfo(iNewShape); + + // 职业变身,Model ID要经转化 + if( PLAYERMODEL_GETTYPE(iNewShape) == PLAYERMODEL_PROFESSION ) + { + int iRealID = _GetProfessionTransformModelID( + m_iProfession, m_iGender, PLAYERMODEL_GETID(iNewShape)); + iNewShape = 0x40 | iRealID; + } + // 非职业变身,Model ID为EC_Resource.h里的值,故不做处理 + else + { + + } + + // store the original data into 8~15 bit + m_iShape = iNewShape | ((iShape & 0xff) << 8); +} + +void CECPlayer::FixOldShapeInfo(int& iShape) +{ + // 那个时候shape为0代表没有变身,非0代表职业变身 + if(iShape != 0 && PLAYERMODEL_GETTYPE(iShape) == 0) + iShape |= 0x40; +} +unsigned char CECPlayer::GetShapeMask() const +{ + // restore the original data from 8~15 bit + return static_cast((m_iShape & 0xff00) >> 8); +} + +// Get player's current model type +int CECPlayer::GetShapeType() const +{ + return PLAYERMODEL_GETTYPE(m_iShape); +} + +// Get player's current model id +int CECPlayer::GetShapeID() const +{ + return PLAYERMODEL_GETID(m_iShape); +} + +// Play Gfx on Models +bool CECPlayer::PlayGfx(const char* szPath, const char* szHook, float fScale /*1.0f*/, unsigned int iShapeTypeMask /*(1<PlayGfx(szPath, szHook, fScale); + bPlayed = true; + } + if(bSkipRecord) continue; + A3DGFXEx* pGfx = m_pModels[i]->GetGfx(szPath, szHook); + if (pGfx && pGfx->IsInfinite()){ + GFXRECORD rec; + rec.strPath = szPath; + rec.strHook = szHook; + rec.fScale = fScale; + AString key = rec.strPath + rec.strHook; + m_GfxRecords[key] = rec; + bSkipRecord = true; + } + } + } + return bPlayed; +} + +void CECPlayer::RemoveGfx(const char* szPath, const char* szHook, unsigned int iShapeTypeMask /*(1<RemoveGfx(szPath, szHook); + + // remove the gfx info when it is unnecessary + if(!bSkipRecord) + { + bSkipRecord = true; + AString key = AString(szPath) + szHook; + m_GfxRecords.erase(key); + } + } + } +} + +bool CECPlayer::IsShapeModelChanged() const +{ + return m_pModels[PLAYERMODEL_MAJOR] != m_pPlayerModel; +} + +bool CECPlayer::IsShapeModelReady() const +{ + if(GetShapeID() == 0) + { + // logic transform only, no model changed + return m_pPlayerModel == m_pModels[PLAYERMODEL_MAJOR]; + } + else + { + // whether the target model is set to current using model + return m_pPlayerModel == m_pModels[min(PLAYERMODEL_MAX-1, GetShapeType())]; + } +} + +bool CECPlayer::ShouldUseGroundNormalForCurrentShapeModel()const{ + return IsShapeModelChanged() && !IsTransofrmModelLikeHuman(GetProfession(), GetGender(), GetShapeID()); +} + +bool CECPlayer::GetExtState(int n) +{ + const int bitSize = sizeof(DWORD) * 8; + const int totalBitSize = bitSize * OBJECT_EXT_STATE_COUNT; + if(n < 0 || n >= totalBitSize) + return false; + + int index = n / bitSize; + int bitOffset = n % bitSize; + return (m_aExtStates[index] & (1 << bitOffset)) ? true : false; +} + +bool CECPlayer::StartAdjustTransparency(float fCur, float fDest, DWORD dwTime) +{ + // use current value for starting + if(fCur < 0.f) fCur = m_fCurTrans; + if(fDest < 0.f) fDest = 0.f; + + // ignore the invalid params + if(dwTime == 0 || fabs(fDest - m_fDstTrans) < 0.0001f || fabs(fDest - fCur) < 0.0001f) + { + return false; + } + + // start the counter to change the transparency + m_fCurTrans = fCur; + m_fDstTrans = fDest; + m_fTransDelta = (fDest - m_fCurTrans) / dwTime; + + m_TransCnt.SetPeriod(dwTime); + m_TransCnt.Reset(); + + return true; +} + +float CECPlayer::UpdateTransparency(DWORD dwDeltaTime) +{ + // Use this valid to change model's transparency + if (!m_TransCnt.IsFull() && m_fDstTrans != m_fCurTrans) + { + m_fCurTrans += m_fTransDelta * dwDeltaTime; + if (m_fTransDelta > 0.0f) + a_ClampRoof(m_fCurTrans, m_fDstTrans); + else + a_ClampFloor(m_fCurTrans, m_fDstTrans); + } + + return (m_fCurTrans <= 0.f) ? -1.0f : m_fCurTrans; +} + +float CECPlayer::GetTransparentLimit() +{ + if(GetExtState(109))//无敌状态的光效ID + { + return 0.7f;//无敌 + } + else if (m_dwStates & GP_STATE_INVISIBLE) + { + return 0.7f;//隐身 + } + else if (!IsSelectable()) + { + return 0.5f;//无法选中 + } + else if(GetExtState(110))//雷神变状态光效ID + { + return 0.3f;//雷神变 + } + + return -1.f; +} + +void CECPlayer::AddMultiObjectEffect(int idTarget,char cType) +{ + if (cType < 0 || cType > 2) + return; + + static const char* szBasePath = "策划联入\\状态效果\\"; + + MULTIOBJECT_EFFECT mo; + mo.cType = cType; + mo.iTarget = idTarget; + + MOEffectMAP::iterator it = m_mapMOEffect.find(mo); + if (it == m_mapMOEffect.end()) + { + AString strGfxFile(_multiobject_effect[cType]); + strGfxFile = szBasePath + strGfxFile; + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + A3DGFXEx* pGfx = pGFXExMan->LoadGfx(g_pGame->GetA3DDevice(), strGfxFile); + if (!pGfx) + return; + pGfx->SetScale(1.0f); + pGfx->SetVisible(false); + + if (IsAllResReady() && m_pPlayerModel) + UpdateOneMultiObjectEffect(idTarget,pGfx,0); + + m_mapMOEffect[mo] = pGfx; + } +} + +void CECPlayer::RemoveMultiObjectEffect(int idTarget,char cType) +{ + MULTIOBJECT_EFFECT mo; + mo.cType = cType; + mo.iTarget = idTarget; + + MOEffectMAP::iterator it = m_mapMOEffect.find(mo); + if (it != m_mapMOEffect.end()) + { + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + pGFXExMan->CacheReleasedGfx(it->second); + + m_mapMOEffect.erase(mo); + } +} + +void CECPlayer::UpdateOneMultiObjectEffect(int idTarget,A3DGFXEx* pGfx,DWORD dwDeltaTime) +{ + if (idTarget == 0 || pGfx == NULL) + return; + + CECObject* pObject = NULL; + + if (ISNPCID(idTarget)) + { + if (!(pObject = g_pGame->GetGameRun()->GetWorld()->GetNPCMan()->GetNPC(idTarget))) + return; + } + else if (ISPLAYERID(idTarget)) + { + if (!(pObject = g_pGame->GetGameRun()->GetWorld()->GetPlayerMan()->GetPlayer(idTarget))) + return; + } + else + { + if (!(pObject = g_pGame->GetGameRun()->GetWorld()->GetMatterMan()->GetMatter(idTarget))) { + StopMonsterSpiritConnectGfx(); + return; + } + } + + + + A3DVECTOR3 pos[2]; + const A3DAABB &playerAABB = GetPlayerAABB(); + pos[0] = playerAABB.Center; + if (pObject->IsNPC()) { + const A3DAABB &targetAABB = ((CECNPC*)pObject)->GetPickAABB(); + pos[1] = targetAABB.Center; + } else if (pObject->IsPlayer()) { + const A3DAABB &targetAABB = ((CECPlayer*)pObject)->GetPlayerAABB(); + pos[1] = targetAABB.Center; + } else { + pos[1] = pos[0]; + pos[1].y += .5f; + pos[0] = pObject->GetPos(); + } + A3DVECTOR3 delta = pos[0]-pos[1]; + float dist2 = delta.SquaredMagnitude(); + if (dist2 > 2025.0f) + { + if (pGfx->IsVisible()) + pGfx->SetVisible(false); + return; + } + + if (pGfx->GetState() != ST_PLAY) + pGfx->Start(true); + if (!pGfx->IsVisible()) + pGfx->SetVisible(true); + + int nElement = pGfx->GetElementCount(); + for (int i = 0; i < nElement; ++ i) + { + A3DGFXElement *pElement = pGfx->GetElement(i); + if (pElement) + GFX_UpdateLightingEdgePos(pGfx, pElement->GetName(), pos); + } + pGfx->TickAnimation(dwDeltaTime); +} + +void CECPlayer::UpdateMultiObjectEffect(DWORD dwDeltaTime) +{ + for (MOEffectMAP::iterator it = m_mapMOEffect.begin();it != m_mapMOEffect.end();++it) + { + UpdateOneMultiObjectEffect(it->first.iTarget,it->second,dwDeltaTime); + } +} + +void CECPlayer::RenderMultiObjectGFX() +{ + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + + for (MOEffectMAP::iterator it = m_mapMOEffect.begin();it != m_mapMOEffect.end();++it) + { + if (it->second && it->second->IsVisible()) + { + pGFXExMan->RegisterGfx(it->second); + } + } +} + +void CECPlayer::ClearBubbleText() +{ + if (m_pBubbleTexts) + m_pBubbleTexts->ClearDecalList(); +} + +A3DAABB CECPlayer::GetShadowAABB() +{ + A3DAABB shadowAABB; + + shadowAABB.Clear(); + + shadowAABB.Merge(m_aabb); + + if (IsRidingOnPet() && m_pPetModel && m_pPetModel->GetA3DSkinModel()) + shadowAABB.Merge(m_pPetModel->GetModelAABB()); + else if (GetPlayerModel() && GetPlayerModel()->GetA3DSkinModel()) + shadowAABB.Merge(GetPlayerModel()->GetModelAABB()); + + return shadowAABB; +} + +void CECPlayer::SetCountry(int id) +{ + if (m_idCountry == id) return; + + m_idCountry = id; + if (!m_idCountry) + { + delete m_pPateCountry; + m_pPateCountry = NULL; + + delete m_pCountryDecal; + m_pCountryDecal = NULL; + return; + } + + const ACString *pName = CECCountryConfig::Instance().GetName(id); + if (!pName || pName->IsEmpty()) + { + delete m_pPateCountry; + m_pPateCountry = NULL; + } + else + { + if (!m_pPateCountry) + { + m_pPateCountry = new CECPateText; + m_pPateCountry->EnableBorder(true); + } + m_pPateCountry->SetText(*pName, false, false); + } + + A2DSprite *pSprite = CECCountryConfig::Instance().GetIcon(id); + if (!pSprite) + { + delete m_pCountryDecal; + m_pCountryDecal = NULL; + } + else + { + if (!m_pCountryDecal) + m_pCountryDecal = new CECSpriteDecal; + m_pCountryDecal->ChangeSpriteIcon(pSprite, 0); + } +} + +bool CECPlayer::GetShowName()const +{ + // 正常显示人物名称时返回 true + + bool bShow(true); + while (true) + { + if (CECUIConfig::Instance().GetGameUI().bShowNameInCountryWar) + break; + + CECGameRun *pGameRun = g_pGame->GetGameRun(); + if (!pGameRun->GetWorld()->IsCountryWarMap()) + break; + + if (IsHostPlayer()) + break; + + CECHostPlayer *pHost = pGameRun->GetHostPlayer(); + if (pHost->GetCountry() == GetCountry()) + break; + + bShow = false; + break; + } + return bShow; +} + +bool CECPlayer::GetShowNameInCountryWar() +{ + // 需要以国战特有的方式显示名称时返回 true + return !GetShowName() && GetCountry(); +} + +ACString CECPlayer::GetNameInCountryWar() +{ + // 以国家和职业代替角色名 + ACString strName; + CECGameRun *pGameRun = g_pGame->GetGameRun(); + strName.Format( + pGameRun->GetUIManager()->GetBaseUIMan()->GetStringFromTable(9924), + *CECCountryConfig::Instance().GetName(GetCountry()), + pGameRun->GetProfName(GetProfession())); + return strName; +} + +const FASHION_WEAPON_CONFIG* CECPlayer::GetFashionConfig() +{ + static FASHION_WEAPON_CONFIG* pFashionConfig = NULL; + if (NULL == pFashionConfig) + { + elementdataman *pDataMan = g_pGame->GetElementDataMan(); + DATA_TYPE DataType; + unsigned int tid = pDataMan->get_first_data_id(ID_SPACE_CONFIG,DataType); + + while(tid) + { + if(DataType == DT_FASHION_WEAPON_CONFIG) + { + pFashionConfig = (FASHION_WEAPON_CONFIG *)pDataMan->get_data_ptr(tid,ID_SPACE_CONFIG, DataType); + break; + } + tid = pDataMan->get_next_data_id(ID_SPACE_CONFIG, DataType); + } + } + return pFashionConfig; +} +bool CECPlayer::IsFashionWeaponTypeFit(int weapon_type, int fashion_weapon_type) +{ + if (fashion_weapon_type < 0 || fashion_weapon_type >= NUM_WEAPON_TYPE) return false; + const FASHION_WEAPON_CONFIG* pConfig = GetFashionConfig(); + if (NULL == pConfig) + { + ASSERT(!"Fashion weapon config load failed!"); + a_LogOutput(1, "CECPlayer::GetFashionConfig, Failed to load fashion weapon config"); + return false; + } + int fashion_weapon_mask = pConfig->action_mask[fashion_weapon_type]; + return (fashion_weapon_mask & (1 << GetWeaponType(weapon_type))) != 0; +} +bool CECPlayer::CanShowFashionWeapon(int weapon_type, int fashion_weapon_type) +{ + return IsFashionWeaponTypeFit(weapon_type, fashion_weapon_type) && InFashionMode(); +} + +#define CLEAR_WEAPON(index) \ +{ \ + Mask &= ~(1 << (__int64)index); \ + pEquipmentID[index] = -1; \ +} + +#define SET_WEAPON(index, id) \ +{ \ + Mask |= (1 << (__int64)index); \ + pEquipmentID[index] = id; \ +} + +#define RECORD_NORMAL_WEAPON(file_model_left, file_model_right, action_type) \ +{ \ + m_strLeftWeapon = file_model_left; \ + m_strRightWeapon = file_model_right; \ + m_uAttackType = action_type; \ +} + +#define RECORD_FASHION_WEAPON(file_model_left, file_model_right, index) \ +{ \ + m_strLeftFashionWeapon = file_model_left; \ + m_strRightFashionWeapon = file_model_right; \ + m_iFashionWeaponType = index; \ +} + +void CECPlayer::DecideWeaponLoad(int* pEquipmentID, __int64& Mask) +{ + bool bNormalWeaponChanged = ((1 << EQUIPIVTR_WEAPON) & Mask) != 0; + bool bFashionWeaponChanged = ((1 << EQUIPIVTR_FASHION_WEAPON) & Mask) != 0; + const WEAPON_ESSENCE* pWeapon = NULL; + const FASHION_ESSENCE* pFashion = NULL; + const WEAPON_SUB_TYPE* pSubType = NULL; + int Index[2] = {EQUIPIVTR_WEAPON, EQUIPIVTR_FASHION_WEAPON}; + for (int i = 0; i < 2; ++i) + { + int idEquipment = pEquipmentID[Index[i]]; + idEquipment &= 0x0000ffff; + DATA_TYPE dt; + if (idEquipment) + { + const void* pEquip = g_pGame->GetElementDataMan()->get_data_ptr( + idEquipment, + ID_SPACE_ESSENCE, + dt); + if (dt == DT_WEAPON_ESSENCE) + { + pWeapon = (const WEAPON_ESSENCE*)pEquip; + pSubType =(const WEAPON_SUB_TYPE*) g_pGame->GetElementDataMan()->get_data_ptr( + pWeapon->id_sub_type, + ID_SPACE_ESSENCE, + dt); + if (dt != DT_WEAPON_SUB_TYPE) pSubType = NULL; + } + else if (dt == DT_FASHION_ESSENCE) pFashion = (const FASHION_ESSENCE*)pEquip; + } + } + + bool bCanShowFashionWeaponBefore = CanShowFashionWeapon(m_uAttackType, m_iFashionWeaponType); + bool bCanShowFashionWeapon(false); + CECGameUIMan* pGameUI = g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan(); + // 普通武器改变 + if (bNormalWeaponChanged) + { + // 时装武器改变 + if (bFashionWeaponChanged) + { + // 装备时装武器和普通武器 + if (pSubType && pFashion) + { + if (CanShowFashionWeapon(pSubType->action_type, pFashion->action_type)) + { + CLEAR_WEAPON(EQUIPIVTR_WEAPON); + } + else + { + CLEAR_WEAPON(EQUIPIVTR_FASHION_WEAPON); + } + } + // 装备普通武器 + else if(pSubType && !pFashion) + { + + } + // 装备时装武器 + else if (!pSubType && pFashion) + { + if (CanShowFashionWeapon(m_uAttackType, pFashion->action_type)) + { + CLEAR_WEAPON(EQUIPIVTR_WEAPON); + } + else + { + CLEAR_WEAPON(EQUIPIVTR_FASHION_WEAPON); + } + } + // 没有装备任务武器 + else + { + + } + if (pSubType) RECORD_NORMAL_WEAPON( + pWeapon->file_model_left, pWeapon->file_model_right, pSubType->action_type); + if (pFashion) RECORD_FASHION_WEAPON( + pFashion->file_model_left, pFashion->file_model_right, pFashion->action_type); + } + else + { + // 装备普通武器 + if (pSubType) + { + bCanShowFashionWeapon = CanShowFashionWeapon(pSubType->action_type, m_iFashionWeaponType); + if (bCanShowFashionWeapon) + { + CLEAR_WEAPON(EQUIPIVTR_WEAPON); + if (!bCanShowFashionWeaponBefore) + SET_WEAPON(EQUIPIVTR_FASHION_WEAPON, m_aEquips[EQUIPIVTR_FASHION_WEAPON]); + } + RECORD_NORMAL_WEAPON( + pWeapon->file_model_left, pWeapon->file_model_right, pSubType->action_type); + } + // 脱下普通武器 + else + { + bCanShowFashionWeapon = CanShowFashionWeapon(DEFAULT_ACTION_TYPE, m_iFashionWeaponType); + if (bCanShowFashionWeapon) + { + CLEAR_WEAPON(EQUIPIVTR_WEAPON); + if (!bCanShowFashionWeaponBefore) + SET_WEAPON(EQUIPIVTR_FASHION_WEAPON, m_aEquips[EQUIPIVTR_FASHION_WEAPON]); + } + RECORD_NORMAL_WEAPON((char*)NULL, (char*)NULL, DEFAULT_ACTION_TYPE); + } + } + } + else if (bFashionWeaponChanged) + { + // 装备时装武器 + if (pFashion) + { + bCanShowFashionWeapon = CanShowFashionWeapon(m_uAttackType, pFashion->action_type); + if (!bCanShowFashionWeapon) + { + if (pGameUI && this == g_pGame->GetGameRun()->GetHostPlayer() && InFashionMode()) + pGameUI->AddChatMessage(pGameUI->GetStringFromTable(906), GP_CHAT_SYSTEM); + CLEAR_WEAPON(EQUIPIVTR_FASHION_WEAPON); + if (bCanShowFashionWeaponBefore) + SET_WEAPON(EQUIPIVTR_WEAPON, m_aEquips[EQUIPIVTR_WEAPON]); + } + // 能显示时装武器,此处要把m_stoneWeapon清零 + else CLEAR_WEAPON(EQUIPIVTR_WEAPON); + RECORD_FASHION_WEAPON( + pFashion->file_model_left, pFashion->file_model_right, pFashion->action_type); + } + // 脱下时装武器 + else + { + if (0 != m_aEquips[EQUIPIVTR_WEAPON]) + { + CLEAR_WEAPON(EQUIPIVTR_FASHION_WEAPON); + if (bCanShowFashionWeaponBefore) + SET_WEAPON(EQUIPIVTR_WEAPON, m_aEquips[EQUIPIVTR_WEAPON]); + } + RECORD_FASHION_WEAPON((char*)NULL, (char*)NULL, DEFAULT_ACTION_TYPE); + } + } + if (bCanShowFashionWeaponBefore && !bCanShowFashionWeapon + && pGameUI && !bFashionWeaponChanged && bNormalWeaponChanged && this == g_pGame->GetGameRun()->GetHostPlayer()) + pGameUI->AddChatMessage(pGameUI->GetStringFromTable(906), GP_CHAT_SYSTEM); +} + +void CECPlayer::SetWeaponResult(EquipsLoadResult& Result) +{ + ReleaseWeapon(); + m_weaponHangerPos = WEAPON_HANGER_HAND; + if (GetProfession() == PROF_JIANLING && !IsFighting()){ + // 剑灵非战斗状态下,武器挂在肩膀上 + m_weaponHangerPos = WEAPON_HANGER_SHOULDER; + } + m_pLeftHandWeapon = Result.pLeftHandWeapon; + m_pRightHandWeapon = Result.pRightHandWeapon; + AttachWeapon(); + if (Result.bFashionWeaponChanged){ + if (m_pLeftHandWeapon){ + m_pLeftHandWeapon->GetA3DSkinModel()->SetAlphaSortID(m_PlayerInfo.cid); + m_pLeftHandWeapon->GetA3DSkinModel()->SetAlphaSortWeight(SKIN_SORT_WEAPON); + } + if (m_pRightHandWeapon){ + m_pRightHandWeapon->GetA3DSkinModel()->SetAlphaSortID(m_PlayerInfo.cid); + m_pRightHandWeapon->GetA3DSkinModel()->SetAlphaSortWeight(SKIN_SORT_WEAPON); + } + } + if (m_pLeftHandWeapon || m_pRightHandWeapon){ + // 有武器存在,将当前还存在的状态特效,补充挂到新的武器上 + // 避免因武器更换等原因导致不能正确指示状态 + SetExtendStatesToWeapon(); + } + + // now show weapon according to old weapon states. + ShowWeapon(m_bShowWeapon); +} + +void CECPlayer::OnSwitchFashionWeapon() +{ + if (!GetMajorModel() || !IsFashionWeaponTypeFit(m_uAttackType, m_iFashionWeaponType)) + return; + + EquipsLoadResult Result; + memset(&Result, 0, sizeof(Result)); + if (InFashionMode()) + { + ChangeWeapon(&Result, m_strLeftFashionWeapon, m_strRightFashionWeapon); + } + else + { + ChangeWeapon(&Result, m_strLeftWeapon, m_strRightWeapon); + } + SetWeaponResult(Result); + m_stoneWeaponShown = 0; + if (InFashionMode()){ + m_stoneWeapon = 0; + }else{ + m_stoneWeapon = m_aEquips[EQUIPIVTR_WEAPON] >> 16; + AddWeaponStones(); + } +} + +int CECPlayer::GetShowingWeaponType() +{ + int weapon_type = 0; + if (CanShowFashionWeapon(m_uAttackType, m_iFashionWeaponType) && m_aEquips[EQUIPIVTR_FASHION_WEAPON] != 0) + { + weapon_type = (m_iFashionWeaponType == DEFAULT_ACTION_TYPE || !IsWeaponAttached()) ? + 10 : m_iFashionWeaponType; + } + else + { + weapon_type = (m_uAttackType == DEFAULT_ACTION_TYPE || !IsWeaponAttached()) ? + 10 : m_uAttackType; + } + return weapon_type; +} + +// 更新经脉属性 +void CECPlayer::SetMeridiansProp(const MeridiansProp& meridianProp) +{ + m_meridiansProp = meridianProp; +} + +bool CECPlayer::IsShowFashionWeapon() +{ + // 当前状态是否应该显示时装武器模型,返回 true 不保证模型已经加载完成并显示 + // + return m_aEquips[EQUIPIVTR_FASHION_WEAPON] != 0 + && CanShowFashionWeapon(m_uAttackType, m_iFashionWeaponType); +} + +void CECPlayer::OptimizeShowExtendStates() +{ + if (CECOptimize::Instance().GetGFX().CanShowState(GetCharacterID(), GetClassID())){ + ShowExtendStates(0, m_aExtStates, OBJECT_EXT_STATE_COUNT); + }else{ + ClearShowExtendStates(); + } +} + +void CECPlayer::OptimizeWeaponStoneGfx() +{ + if (CECOptimize::Instance().GetGFX().CanShowWeaponStone(GetCharacterID(), GetClassID())){ + AddWeaponStones(); + }else{ + RemoveWeaponStones(); + } +} + +void CECPlayer::OptimizeArmorStoneGfx() +{ + if (CECOptimize::Instance().GetGFX().CanShowArmorStone(GetCharacterID(), GetClassID())){ + AddUpperBodyStones(); + AddWristStones(); + AddLowerBodyStones(); + AddFootStones(); + }else{ + RemoveUpperBodyStones(); + RemoveWristStones(); + RemoveLowerBodyStones(); + RemoveFootStones(); + } +} + +void CECPlayer::OptimizeSuiteGfx() +{ + if (CECOptimize::Instance().GetGFX().CanShowSuite(GetCharacterID(), GetClassID())){ + AddFullSuiteGFX(); + }else{ + RemoveFullSuiteGFX(); + } +} +const TITLE_CONFIG* CECPlayer::GetTitleConfig(unsigned short id) +{ + DATA_TYPE DataType = DT_INVALID; + const void* pDataPtr = g_pGame->GetElementDataMan()->get_data_ptr(id, ID_SPACE_CONFIG, DataType); + if (DataType != DT_TITLE_CONFIG && DataType != DT_COMPLEX_TITLE_CONFIG) + return NULL; + + const TITLE_CONFIG* pData = (const TITLE_CONFIG*)pDataPtr; + return pData; + +} +A3DBone* CECPlayer::ScaleRootBone(CECModel* pModel, float scale) +{ + int num_bone, j, root_index; + A3DBone* pBone = NULL; + A3DSkeleton* pSkeleton = NULL; + if (pModel) { + pSkeleton = pModel->GetA3DSkinModel()->GetSkeleton(); + if (pSkeleton) { + num_bone = pSkeleton->GetRootBoneNum(); + for (j = 0; j < num_bone; ++j) { + root_index = pSkeleton->GetRootBone(j); + pBone = pSkeleton->GetBone(root_index); + if (pBone) { + pBone->SetScaleFactor(A3DVECTOR3(scale, scale, scale)); + pBone->SetScaleType(A3DBone::SCALE_WHOLE); + break; + } + } + } + } + return pBone; +} +void CECPlayer::ScaleChildModel() +{ + CECModel* pModel; + int num_child = m_pPlayerModel->GetChildCount(); + int i; + AString strCCFeijian("CC_feijian"); + for (i = 0; i < num_child; ++i) { + pModel = m_pPlayerModel->GetChildModel(i); + // 飞剑的CC_feijian挂点没有挂在骨骼上,跳过飞剑 + if (pModel->GetCCName() == strCCFeijian) + continue; + ScaleRootBone(pModel, m_fScaleBySkill); + pModel->SetGfxScale(m_fScaleBySkill); + } +} +void CECPlayer::ScaleBody(float fScale) +{ + m_fScaleBySkill = fScale; + if (!m_pPlayerModel) return; + A3DSkeleton * pSkeleton = m_pPlayerModel->GetA3DSkinModel()->GetSkeleton(); + + // 缩放root bone + A3DBone* pRootBone = ScaleRootBone(m_pPlayerModel, fScale); + + // 脚底位移修正 + if (pRootBone) { + float offset = pRootBone->GetOriginalMatrix().GetRow(3).y * (1.f - fScale); + pSkeleton->SetFootOffset(offset); + } + // 缩放武器、飞行器 + ScaleChildModel(); + + // 缩放gfx + m_pPlayerModel->SetGfxScale(fScale); + // 缩放包围盒 + CalcPlayerAABB(); + A3DVECTOR3 vPos = GetPos(); + m_aabb.Center = vPos + A3DVECTOR3(0.0f, m_aabb.Extents.y, 0.0f); + m_aabb.CompleteMinsMaxs(); + m_aabbServer.Center = vPos + A3DVECTOR3(0.0f, m_aabbServer.Extents.y, 0.0f); + m_aabbServer.CompleteMinsMaxs(); +} +void CECPlayer::LoadMonsterSpiritGFX(int index, int res_index) +{ + int count = sizeof(m_pMonsterSpiritGFX) / sizeof(m_pMonsterSpiritGFX[0]); + if (index >= 0 && index < count) { + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + if (m_pMonsterSpiritGFX[index] == NULL) { + m_pMonsterSpiritGFX[index] = pGFXExMan->LoadGfx(g_pGame->GetA3DDevice(), res_GFXFile(res_index)); + if (!m_pMonsterSpiritGFX[index]) return; + m_pMonsterSpiritGFX[index]->SetScale(1.0f); + m_pMonsterSpiritGFX[index]->Start(true); + } + } +} +void CECPlayer::StartMonsterSpiritConnectGfx(int mine_id, A3DVECTOR3 pos) +{ + LoadMonsterSpiritGFX(0, RES_GFX_MONSTER_SPIRIT_LINE); + m_iMonsterSpiritMineID = mine_id; + m_posMonsterSpirit = pos; +} +void CECPlayer::StopMonsterSpiritConnectGfx() +{ + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + if (pGFXExMan) pGFXExMan->CacheReleasedGfx(m_pMonsterSpiritGFX[0]); + m_pMonsterSpiritGFX[0] = NULL; + m_iMonsterSpiritMineID = 0; +} +void CECPlayer::StartMonsterSpiritBallGfx() +{ + LoadMonsterSpiritGFX(1, RES_GFX_MONSTER_SPIRIT_BALL); + if (m_pMonsterSpiritGFX[1]) { + m_pMonsterSpiritGFX[1]->SetPos(m_posMonsterSpirit); + m_stateMonsterSpirit = BALL_STATE_RISING; + } +} +void CECPlayer::UpdateMonsterSpiritGfx(DWORD dwDeltaTime) +{ + UpdateOneMultiObjectEffect(m_iMonsterSpiritMineID, m_pMonsterSpiritGFX[0], dwDeltaTime); + if (m_stateMonsterSpirit == BALL_STATE_NONE) {} + else if (m_stateMonsterSpirit == BALL_STATE_DISAPPER) { + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + if (pGFXExMan) pGFXExMan->CacheReleasedGfx(m_pMonsterSpiritGFX[1]); + m_pMonsterSpiritGFX[1] = NULL; + m_stateMonsterSpirit = BALL_STATE_NONE; + } else { + A3DVECTOR3 targetPos = GetPos(); + if (m_pPlayerModel){ + int index; + if (A3DBone *pBone = m_pPlayerModel->GetA3DSkinModel()->GetSkeleton()->GetBone("Bip01 Head", &index)){ + targetPos = pBone->GetAbsoluteTM().GetRow(3); + } + } + A3DVECTOR3 pos; + if (m_stateMonsterSpirit == BALL_STATE_RISING) { + float deltaY = 1.f * dwDeltaTime / 1000; + pos = m_pMonsterSpiritGFX[1]->GetPos(); + pos.y += deltaY; + m_pMonsterSpiritGFX[1]->SetPos(pos); + if (pos.y > targetPos.y) m_stateMonsterSpirit = BALL_STATE_FOLLOW; + } else if (m_stateMonsterSpirit == BALL_STATE_FOLLOW) { + pos = m_pMonsterSpiritGFX[1]->GetPos(); + A3DVECTOR3 direction = targetPos - pos; + if (direction.Magnitude() < 1.f) m_stateMonsterSpirit = BALL_STATE_DISAPPER; + direction.Normalize(); + pos += (direction * (10.f * dwDeltaTime / 1000)); + m_pMonsterSpiritGFX[1]->SetPos(pos); + } + m_pMonsterSpiritGFX[1]->TickAnimation(dwDeltaTime); + } +} + +void CECPlayer::RenderMonsterSpiritGfx() +{ + A3DGFXExMan *pGFXExMan = g_pGame->GetA3DGFXExMan(); + unsigned int count = sizeof(m_pMonsterSpiritGFX) / sizeof(m_pMonsterSpiritGFX[0]); + for (unsigned int i = 0; i < count; ++i) + { + if (m_pMonsterSpiritGFX[i] && m_pMonsterSpiritGFX[i]->IsVisible()) + pGFXExMan->RegisterGfx(m_pMonsterSpiritGFX[i]); + } +} +int CECPlayer::HasSkillStateForAction() +{ + for (int i=0;iGetGameRun(); + if(!pRun) return false; + + CECHostPlayer* pHost = pRun->GetHostPlayer(); + if (pHost && pHost->GetBattleInfo().IsChariotWar() && GetShapeType() == PLAYERMODEL_DUMMYTYPE2 && GetDummyModel(PLAYERMODEL_DUMMYTYPE2)) + return true; + + return false; +} + +bool CECPlayer::ChangeFashionColor(int equipSlot, unsigned short newColor){ + if (EQUIPIVTR_FASHION_BODY != equipSlot && + EQUIPIVTR_FASHION_LEG != equipSlot && + EQUIPIVTR_FASHION_FOOT != equipSlot && + EQUIPIVTR_FASHION_WRIST != equipSlot){ + return false; // 不是时装位置 + } + if (!GetRealElementID(equipSlot, m_aEquips[equipSlot])){ + return false; // 没有装备 + } + int equipID = m_aEquips[equipSlot]; + unsigned short currentColor = ((equipID >> 16) & 0x0000ffff); + if (currentColor == newColor){ + return true; // 与已有颜色相同,下面就不用麻烦检查了 + } + int skinIndex = -1; + switch (equipSlot){ + case EQUIPIVTR_FASHION_BODY: + skinIndex = SKIN_FASHION_UPPER_BODY_INDEX; + break; + case EQUIPIVTR_FASHION_LEG: + skinIndex = SKIN_FASHION_LOWER_INDEX; + break; + case EQUIPIVTR_FASHION_FOOT: + skinIndex = SKIN_FASHION_FOOT_INDEX; + break; + case EQUIPIVTR_FASHION_WRIST: + skinIndex = SKIN_FASHION_WRIST_INDEX; + break; + } + A3DCOLOR a3dColor = FASHION_WORDCOLOR_TO_A3DCOLOR(newColor); + for (int i(0); i < sizeof(m_aSkins[0])/sizeof(m_aSkins[0][0]); ++ i){ + if (A3DSkin *pSkin = m_aSkins[skinIndex][i]){ + for(int idTex=0; idTexGetTextureNum(); idTex ++){ + A3DTexture * pTex = pSkin->GetTexture(idTex); + if (pTex && pTex->IsShaderTexture()){ + A3DShader * pShader = (A3DShader *) pTex; + pShader->GetGeneralProps().dwTFactor = a3dColor; // Loop 每件 Skin,直接设置到每个 Shader 中 + } + } + } + } + int newEquipID = (m_aEquips[equipSlot] & 0x0000ffff) | (newColor << 16); + m_aEquips[equipSlot] = newEquipID; + return true; +} + +void CECPlayer::RecreateActionController(){ + if (!m_pPlayerModel){ + delete m_pActionController; + m_pActionController = NULL; + return; + } + if (!m_pActionController){ + m_pActionController = new CECPlayerActionController; + } + m_pActionController->Bind(this, m_pPlayerModel); +} + +void CECPlayer::RecreateBodyController(){ + if (!m_pPlayerModel){ + delete m_pBodyController; + m_pBodyController = NULL; + return; + } + if (!SupportCastSkillWhenMove()){ + return; + } + if (!m_pBodyController){ + m_pBodyController = new CECPlayerBodyController; + } + if (!m_pBodyController->Bind(this, m_pPlayerModel)){ + delete m_pBodyController; + m_pBodyController = NULL; + } +} + +void CECPlayer::TurnFaceTo(int idTarget, DWORD dwTime){ + if (idTarget){ + if (idTarget == GetCharacterID()){ + return; + } + if (IsWorkMoveRunning() && !IsPlayingCastingSkillAndMoveActions()){ // 移动时一般应面向移动方向、只在移动施法中才转向 + return; + } + } + if (!m_pBodyController){ + CECObject::TurnFaceTo(idTarget, dwTime); + return; + } + m_pBodyController->TurnFaceTo(idTarget); +} + +void CECPlayer::SetName(const ACHAR *szName){ + m_strName = szName; + if (m_pPateName){ + m_pPateName->SetText(m_strName, false); + } +} + +void CECPlayer::CloneSimplePropertyTo(CECPlayer *player)const{ + // CECObject + player->SetPos(GetPos()); + player->SetDirAndUp(GetDir(), GetUp()); + player->SetGroundNormal(GetGroundNormal()); + player->SetUseGroundNormal(GetUseGroundNormal()); + + // CECPlayer + player->m_PlayerInfo = GetPlayerInfo(); + player->SetBornStamp(GetBornStamp()); + player->SetName(GetName()); + player->SetProps(&GetBasicProps(), &GetExtendProps()); + player->m_iGender = GetGender(); + player->m_iProfession = GetProfession(); + player->m_pvp = GetPVPInfo(); + player->m_aIconStates = GetIconStates(); + player->SetMoneyAmount(GetMoneyAmount()); + player->m_iMaxMoney = GetMaxMoneyAmount(); + player->m_i64EquipDisabled = m_i64EquipDisabled; + player->SetBoothState(GetBoothState()); + player->m_factionPVPMask= m_factionPVPMask; + player->m_RidingPet = GetRidingPetInfo(); + player->m_iBattleCamp = GetBattleCamp(); + player->m_dwGMFlags = m_dwGMFlags; + player->SetSpouse(GetSpouse()); + player->m_idForce = GetForce(); + player->SetCountry(GetCountry()); + player->SetMoveMode(GetMoveMode()); + player->SetMoveEnv(GetMoveEnv()); + player->SetWalkRunFlag(GetWalkRunFlag()); + player->m_MoveConst = m_MoveConst; + player->m_aabbServer = m_aabbServer; + player->m_aabb = m_aabb; + player->m_dwStates = m_dwStates; + player->m_dwStates2 = m_dwStates2; + player->m_fTouchRad = GetTouchRadius(); + player->m_byPariahLvl = GetPariahLevel(); + player->SetBoothName(GetBoothName()); + + player->m_CustomizeFactor = m_CustomizeFactor; + player->ChangeCustomizeData(m_CustomizeData); + for (int i=0; i < GetEffectCount(); i++) + player->ApplyEffect(GetEffect(i), true); + + player->SetFactionID(GetFactionID()); + player->SetCurPetID(GetCurPetID()); + player->SetCurrentTitle(GetCurrentTitle()); + player->SetReputation(GetReputation()); + player->SetFashionMode(InFashionMode()); + player->SetFRoleID(GetFRoleID()); + player->SetMeridiansProp(GetMeridiansProp()); + player->SetReincarnationCount(GetReincarnationCount()); + player->SetRealmLevel(GetRealmLevel()); + player->ScaleBody(GetScaleBySkill()); + player->SetTeamRequire(GetTeamRequire(), false); + player->SetNewExtendStates(0, m_aExtStates, OBJECT_EXT_STATE_COUNT); + player->m_GoblinRenderCnt = m_GoblinRenderCnt; + player->m_bRenderGoblin = m_bRenderGoblin; +} + +int CECPlayer::SearchFullSuite() +{ + DATA_TYPE DataType = DT_INVALID; + elementdataman* pDataMan = g_pGame->GetElementDataMan(); + CECGame::SuiteEquipTable& SuiteMapTab = g_pGame->GetSuiteEquipTable(); + + int i; + typedef abase::hashtab SuiteTable; + SuiteTable SuiteTab(32); + + // Check how many suits of equipment host have dressed + for (i=0; i < SIZE_ALL_EQUIPIVTR; i++) + { + int idEquip = GetEquippedItem(i); + if (!idEquip) + continue; + + CECGame::SuiteEquipTable::pair_type pair = SuiteMapTab.get(idEquip); + if (!pair.second) + continue; + + int idSuite = *pair.first; + + const void* pData = pDataMan->get_data_ptr(idSuite, ID_SPACE_ESSENCE, DataType); + if (DataType != DT_SUITE_ESSENCE) + continue; + + const SUITE_ESSENCE* pSuiteEss = (const SUITE_ESSENCE*)pData; + if (pSuiteEss->file_gfx[0]) + SuiteTab.put(idSuite, pSuiteEss->max_equips); + } + + if (!SuiteTab.size()) + return 0; // No suite was found + + // Check whether suite is full + SuiteTable::iterator it = SuiteTab.begin(); + for (; it != SuiteTab.end(); ++it) + { + int idSuite = *it.key(); + int iMaxNum = *it.value(); + + if (iMaxNum == GetEquippedSuiteItem(idSuite)) + return idSuite; + } + + return 0; +} + +int CECPlayer::GetEquippedSuiteItem(int idSuite, int* aItems/* NULL */) +{ + CECGame::SuiteEquipTable& SuiteTab = g_pGame->GetSuiteEquipTable(); + + int i, iItemCnt = 0; + + for (i=0; i < SIZE_ALL_EQUIPIVTR; i++) + { + int idEquip = GetEquippedItem(i); + if (!idEquip) + continue; + + CECGame::SuiteEquipTable::pair_type pair = SuiteTab.get(idEquip); + if (!pair.second || idSuite != *pair.first) + continue; + + if (aItems) + aItems[iItemCnt] = idEquip; + + iItemCnt++; + } + + return iItemCnt; +} + +int CECPlayer::GetEquippedItem(int index)const{ + if (index < 0 || index >= SIZE_ALL_EQUIPIVTR){ + ASSERT(false); + return 0; + } + return m_aEquips[index] & 0xffff; +} + +void CECPlayer::DetachWeapon(){ + if (IsWeaponAttached()){ + ASSERT(GetPlayerModel() != NULL); + if (GetLeftHandWeapon()){ + GetPlayerModel()->RemoveChildModel(_left_hand_weapon, false); + } + if (GetRightHandWeapon()){ + GetPlayerModel()->RemoveChildModel(_right_hand_weapon, false); + } + m_bWeaponAttached = false; + } +} + +bool CECPlayer::AttachWeapon(){ + bool result(false); + ASSERT(!IsWeaponAttached()); + while (GetPlayerModel() && (GetLeftHandWeapon() || GetRightHandWeapon())){ + A3DSkinModel *pSkinModel = GetPlayerModel()->GetA3DSkinModel(); + if (!pSkinModel || + !pSkinModel->GetSkeleton()){ + break; + } + if (!pSkinModel->GetSkeletonHook(GetLeftWeaponHookPos(m_weaponHangerPos),true) || + !pSkinModel->GetSkeletonHook(GetRightWeaponHookPos(m_weaponHangerPos),true)){ + break; + } + if (GetLeftHandWeapon()){ + GetPlayerModel()->AddChildModel( + _left_hand_weapon, + false, + GetLeftWeaponHookPos(m_weaponHangerPos), + GetLeftHandWeapon(), + GetLeftWeaponOwnHookPos(GetLeftHandWeapon())); + } + if (GetRightHandWeapon()){ + GetPlayerModel()->AddChildModel( + _right_hand_weapon, + false, + GetRightWeaponHookPos(m_weaponHangerPos), + GetRightHandWeapon(), + GetRightWeaponOwnHookPos(GetRightHandWeapon())); + } + m_bWeaponAttached = true; + result = true; + break; + } + return result; +} + +void CECPlayer::ReleaseWeapon(){ + DetachWeapon(); + if (m_pLeftHandWeapon){ + A3DRELEASE(m_pLeftHandWeapon); + } + if (m_pRightHandWeapon){ + A3DRELEASE(m_pRightHandWeapon); + } +} + +bool CECPlayer::IsWeaponAttached()const{ + return m_bWeaponAttached; +} + +int CECPlayer::GetRace()const{ + return CECProfConfig::Instance().GetRaceByProfession(GetProfession()); +} + +bool CECPlayer::ShouldUseFaceModel() const { + if (!IsElsePlayer()){ + return true; + } + return m_iMemUsage < CECMemSimplify::MEMUSAGE_NOFACE; +} + +bool CECPlayer::ShouldUseClothedModel() const { + if (!IsElsePlayer()){ + return true; + } + return m_iMemUsage < CECMemSimplify::MEMUSAGE_SIMPLEMODEL; +} + +bool CECPlayer::ShouldUseModel() const { + if (!IsElsePlayer()){ + return true; + } + bool result(true); + switch (m_iMemUsage){ + case CECMemSimplify::MEMUSAGE_NOMODEL: + result = g_pGame->GetGameRun()->GetMemSimplify()->IsMostImportant(this); + break; + case CECMemSimplify::MEMUSAGE_NOMODEL_UNIMPORTANT: + result = g_pGame->GetGameRun()->GetMemSimplify()->IsImportant(this); + break; + } + return result; +} + +bool CECPlayer::ShouldUseBoothModel()const{ + if (m_iBoothState != 2){ // 不是摆摊状态不需要模型 + return false; + } + if (IsHostPlayer()){ // 主玩家始终显示 + return true; + } + if (!IsElsePlayer()){ // 主玩家、周围玩家以外不显示 + return false; + } + bool result(true); + switch (GetMemUsage()){ + case CECMemSimplify::MEMUSAGE_NOMODEL: + result = g_pGame->GetGameRun()->GetMemSimplify()->IsMostImportant(this); + break; + } + return result; +} \ No newline at end of file diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset index 6fbd599cad..492e5842a9 100644 --- a/ProjectSettings/QualitySettings.asset +++ b/ProjectSettings/QualitySettings.asset @@ -7,7 +7,7 @@ QualitySettings: m_CurrentQuality: 0 m_QualitySettings: - serializedVersion: 4 - name: Mobile + name: High pixelLightCount: 2 shadows: 2 shadowResolution: 1 @@ -17,7 +17,7 @@ QualitySettings: shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} - shadowmaskMode: 0 + shadowmaskMode: 1 skinWeights: 2 globalTextureMipmapLimit: 0 textureMipmapLimitSettings: [] @@ -25,12 +25,12 @@ QualitySettings: antiAliasing: 0 softParticles: 0 softVegetation: 1 - realtimeReflectionProbes: 0 + realtimeReflectionProbes: 1 billboardsFaceCameraPosition: 1 - useLegacyDetailDistribution: 1 + useLegacyDetailDistribution: 0 adaptiveVsync: 0 - vSyncCount: 0 - realtimeGICPUUsage: 100 + vSyncCount: 1 + realtimeGICPUUsage: 50 adaptiveVsyncExtraA: 0 adaptiveVsyncExtraB: 0 lodBias: 1 @@ -47,7 +47,7 @@ QualitySettings: asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 - customRenderPipeline: {fileID: 11400000, guid: 5e6cbd92db86f4b18aec3ed561671858, type: 2} + customRenderPipeline: {fileID: 0} terrainQualityOverrides: 0 terrainPixelError: 1 terrainDetailDensityScale: 1 @@ -57,7 +57,25 @@ QualitySettings: terrainBillboardStart: 50 terrainFadeLength: 5 terrainMaxTrees: 50 - excludedTargetPlatforms: - - Standalone + excludedTargetPlatforms: [] m_TextureMipmapLimitGroupNames: [] - m_PerPlatformDefaultQuality: {} + m_PerPlatformDefaultQuality: + Android: 2 + EmbeddedLinux: 5 + GameCoreScarlett: 5 + GameCoreXboxOne: 5 + Kepler: 5 + LinuxHeadlessSimulation: 5 + Nintendo Switch: 5 + Nintendo Switch 2: 5 + PS4: 5 + PS5: 5 + QNX: 5 + Server: 5 + Standalone: 5 + VisionOS: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/build_log.txt b/build_log.txt new file mode 100644 index 0000000000000000000000000000000000000000..926bc3555e462722f789eb1de2af1b2af0f35302 GIT binary patch literal 356942 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 IP0oA%e{GUO+5i9m literal 0 HcmV?d00001 diff --git a/build_log2.txt b/build_log2.txt new file mode 100644 index 0000000000000000000000000000000000000000..570b6fe002e7940ccff3a31bcb39e9ee4a50ac49 GIT binary patch literal 355900 zcmeI5YjfL1+NSw=s^&jvWrA7l4p}_$*0&g@nJ0|d3VQCF4?jbjjh8g$#FD4 zes}MyAGR6*L7)MGv=lz2l0<@F17zRN`Skz(@5*Rpbbs`2bgJLu(f(*}bP#@bb^J(2 z4oCmiD{uAsiH@A=`HGId(=#6PXf^!&dGsf}vNC$Aqo<=^&)2Yb?C9v~_|*fwXGPE7 z>n~^WZ1PGcI>vSPbl#pG?dYtxde?#8vmI9FUE4bLL4S93#`OD(_qG{6+rG{}(EIqj ztD}dbtgYv4%&GQ_^A`Qd`rPBIQJ;5JceNb_#WNQDy%euqM^&pO6z5>NxMKCYsJp^eK+aJ> z$>OQF{f?l3Np4fV>&edvZ!GiP2czG=@F#7*r+R9y-g))Y=21xz7mG%D_dCp`{o+eB$_DQd!o1iWHtpCSaZEyT4`onS17|?01j@CyvwD$GU*Q1-G zuXK+qlIfh0^q?ow5$@`Y_0etpaK=Neg|tsv$$NbY?#uL~H{mmU)?e;nCCKUbL2HP2 zwKDpxbYIZ9=|q3k)y{Nh$nbrAny32DXMCi;r_w0?sx#4>w)NL^qEEUucXFT=IL6hZ zmWHh1N;_I%W%N(o;ZPdRrta}1+~XTv<4n57J>5C?b*#JmCg^Q^@7(itICoW7N3S!j z@?SxZ`6Hp?Wzc5XVZ#u6x~*T+TN6TThfamFh>x~C|5~e*ar~W7yE$eJ4D;3%Ve*_A#9CT+i7pkoph3dNz6~%zV(J1D*dy>s;5q-PG@G{jTX} zN6)V5{nzyi^3a{ZTHiaiyItHJJL>1n3$gn6>5GWPV;KGpZN3$~9_o?tXt?HEz49p> zSrLuEOXIn4Ez@6)MKza?4$>zvDdBXnD7ttdT;VS;$&cRw?0pvY;KIM^@s5t+S1`_s z4s)5P>7SB2ZJJ%tqw*-opt9ZcYII9dh9A@QZc5X8FUeLt>%Qm{{q5CQE7}Ur_}2ji!wFm(G+8ibusC( zOeuSl+RwkpGKg=vENgQjaEp#IcfUj8?cL*wDf_0~y7nkDnRjj0!e4dH#g8(3MA>o< zJfi$ZjVS-o5gv8BHhy$*G9Y3^*rUh8X?e{ue_3dtpP%sZP>x56Q++^R-n&E9G(_!D5e@P2BK4NbhCSClhOK>zn5+IzxVVVX z*=5m4s=G3rk8ar_v2}nTyrt>Vllp<=;0UQk_$H{;&u zwd(SJQ@rk2|E+Gcdiz@(mU>rcn~W{0D|YpG zZ}cBMBggq~T9r(&U1dj8>qtimJCnW!RGxlm+5gAcv+LTCTfS=@JF)boG^@L|{w~y^ zLlf0wMteq+PlR>LXrvoWq%)}Ob_11N6P;ZXH9}=pKfEF@B@&wsBK7q2KJbT?(G^vt zLu;SK70@rOX8c&ksZTr#^iGbZQ7Ux$P83NUJXfG*+v*uC(!Zx;)R0@(66&<+uH*C@ zYt5RG@Wc3f+tN1%3SzebD&FZau>J*buv*H@J#kzFx|!_NG*1rEh70;mESf)5aj`m~ zRX7KU8d$ljS6`$|YHgd+tnP>J-MV)Del9WARr|UjRIEuV)b}Gn5?u?rz_lPZt|({D zR@;;ec_AI}zogxVH zU2tUlAS9hCBD*bQ{3_k*FyQB^V?vr{(f-3rdm8jRX!)bQF*^G2nC^w9UECI@fM0Ah z@(a4T+!&*xdDUm)7xx3DgTO=SiR=sfgq{Ul8(soep?f13#pc8sq>tUcsGOtdr>2)4 zieH&NOW!*<&%x;1QPVwqug^B^E~veyYYkW14!vtqAIZ1kHYwx1dOwrgDrLCR+rv7p z@fobkBo-|9O77XVJ|CZucc2qbu0T-mx!L6N^}rNwrtxGXX@2T6Fpz81&4*1%OAN&Tz#qArcRnCu^SF_BdD|J3<} zHsvjS95kBLkub_>bo#G_t0s%jF|}NnsdLN^?wD}n5(M&As z%6XUcyRw6RKaWe_l}&>sygD)t5aZH)Jq$&kV*Q#}KtGMG?`XZ(TisY=)W({Z!#ozL zZzj*Ns^~U|x5F)~x@?4Y%W5s?zgX$l-AWgd+)B3`nAtegO`!>UeO+G8=}{AxddnsC zCiUq&{VfYiCMECue4&|5{0#h6E3atrKZs~cn!@4h8uf@lTAwN}4 zG2UsSP)ETdU6zH>vv@N|I&u)FOFD8CQ|!2XW$Vg*ENiK+RcAi*`?AN#9UdW=XlbYCDnxYTcQe!50lkf%xK8FS~}}_@ORMNlzfJJdPaT$8CYbB@;Qz~CFH*l zEwX6@cXc#Qiz4@cPl{in`1#B`5$7RTc1ATP!7{$y=v%UhgK)`ePQ;bcw;ki##ValD z6F||{9X91zkWW^=e|Fz8qvE7X&L$s(eCQl3q_YNC7z##Vk^nrDrrZcVexS3||HUV>>d&N?Z0eV6OtNe6!} z-mtCOJK`#EBjX>%qrZfRi*-avb+cq5;%{1S)WEGnOPMz{t-Tp4H)8(}&vLErpm3S0 zzPiY}jo>5bj;Nxvvv|R7i2Lmd+0eR6z8zKYd{P{R`dFq;UYuBVzU1qIq}w4%-Imc> z&dr$AobC%d)SOS|PjuIsCRd+a)w43_+jUv9GV+mZSwKqe+T{btk)^Y=Dq|JKn%-I`>lp({We^XvID?K645AMb=#MtSlo zmN{@$CRnZWY%-xJX`)=Xs*Fo=39-J0;`poQQm4AQbMDK`x%0K=ei$e3fI5%iP>yzS zBr7`R57%is!=XO+YEA`C1yTPk@?^{9O5WP$zNp1ol}W9;xGvfF2eJZ?DYMy?MH#g! ztD(BCBioVPgzTGRuW?&|hq7>qha_8fNQ^gK!O^}PXvdCxB^~0UJf55K z5ix6EC&b^X$D+-8BgEU_(9^R?sut_yT6QfuruId2U43D7RY&o1ptzb{SJ%_Ad`?-5 zkJ!E*czAP%B+urYJ=^3mMUKn3;j>T9kSU)t4O>K0$i*SR~=i9tv=Wy@7S^B6@bj-8d@e=<&Dq7@yeTdV9R?q7kfkzd+=UD^vC7ozx^PR4bYrL9bPOfh)m%gS}C;4jnMAW@PdgxYO z*EOHIuC({{Xrdd4c_^AZt7T4@){HgKbSGx6Jo#-*V&XY{!uK^(`d#P**gv0hI<`!1 z=U?0Nhn6FMs2rV2bt{jhP5vf4qEjMo&O#Ud$XD--yW*@jL`KVWU_ExGt?F8V&t9K@ zeeN{$#SHzmfl*zZ`7n>V2&TwR7s}oSNz@v)+c{)#T1U&~Kc!Fk}{U{o0OjM|cy$ zV;`)c5dM>*Pdn#!Uq36a&Py@5t|NOnke#f+ddrgRd$TOtd~B)DWLguA;FEg8L<`fW z5?|&nTh+{oy2#P=p0{}W>7z`Zj%TfU)@pv%Y7?gJgeW9j$}#1b8Uj=6ss|!V)iZKD zBd1$N&U8Knrs&pv-DRIVx6Y@GPuWaHVl5~7iCzmjunu+2F3*0cu4u6syvm*E_Wuc zOMYx#A6AcYE=QEJKNVP?m8OVQH!7%hFdYi-)pZ@&j_lYOwaABKMj@34-704$`D|Jj z`Z|?{X*R-Yn5pEbkIt3oR5wFHu(=VH4@~(!u7pEYnph!VcFJh*6PQ7ANKjG=q z3i`@|qHUD!c-q7T$F<`+p~U3DM!|sv-XN!=g{!7 zd+=t#H}#-hYb)9)$MOO8L}f8kVd(mb`=Zh6>dwuVi<>j^;DIuPh%+T_9*;yloS8M~ z;obRnTFEOry|Qz-%Fg2CdecMjvlnO9Z>i@JsE%HeyCGhYdYRnm>5(Z;MS7tt5c?=z zmw7elKg82kHCg0Dk={?b3;L*#cZS48ONySFwl!w)(-iM(OV_8{0F~U_i}f4$7(7XP zx*xM8hQ4!`C(9vEW~WDDVC@}ct5yP3-$p?>RM>^8FD^xP{S;1}PMya4+oUD4uKM|6 zhoH_MC8Z7|u`FAvDAkJ6_TtiI{{1^eIz{GEWULf!njBFlFZ0ltBQ+mN(>U-UNpyU++BRz3=!LPb zO!DGnGmabIrk-tE@1Q>h@1S$RVVG7?>}Bvl$36G%BIVwZFP%4uu4-q_gF@Ri#fwRq z?f2~(?s90j&9w_j`y>o&K>zOH=Trg_!sRKf1HZ(Ut|bydgpa^U)jcpM(R zk0Z;~fpg-|M&6@6(9xD&xN%;$;e_hRsJk<_km(?8_)h(H^H!&01y*qE`d&w^XRuYptmr&Ca>kkD)OqsF zdv^yA*H^o?&bP&0Cg|1H>!EG-cS2-YyDhG{CFxH;75MsD=nL8hhvoH?_FT8RqwKxD z?M3y3uHTue4Oc{jc`WWIZyD?Dx}^K`Nn1_UTX9X(ET0Cw!8-L0iL$!YUA`}id`Et6 z2hH?2$oBq--x$Y)+W2aU36-xkiTK3-c0xygyd?9&f77*`8k`!Me0W9VM*e>%Oz&K8 z<^MTa23<&A2>C~%)V*L!z6m-z+3E`#Ploj)A5TmDQL5k)*3CbosT z;W_W*+Rka6({|&u^fp@$Gkw~5ZI0$aee9`X?1y8^&Zhqyi31PKZQHKowBWSRl@^GU zTV_^%#AG_}H_Py@Xn{FHzmD!I(s4GPa@jwAQodf&K60++G~qPSl_uyyZ*xh8^TkBe z{wQg|?5&T3O9!KdY1eT&a60Hp2OIKxZv?ww2s*Gy#Arw6l}S-`;u)v9&aAUarpbw} zS9V%)TIohB%%8tK&OGUxeO0ufjh7QIrStxAX?Ez$^H^#pAAuwo`9WzJJfQdN0448a(t|JeP#M~=79xn?XH^%aNQFxq(+|EH|x~^k$ ze%O2&q~Ql;pvFhWoi-&E(dy15D{WFtoDBK3G{@#HGq;4$JL(a#qTYTl)k$V+^bgg8 zJsAC$bix<@trhU;RsX)zNV}`bOFJd`rXCgIYyUs*JGbacW>XZZ-YSb#=UhscOPJ z^=(otWi+&}?DfuG@7#uOvS6bhI#v2S5;n03i@lvzOV47K46+d4Tg353@S=m#<}CQ+ zO|j~q`YT$-%yaQKu7V`SyGNZ&ygm{z#h;3@u%Bne?xy^?XT1y8*ShL!T~HaNazxd@761xL;zk#lqGU2Cgn$m3n6N!KB#uTP^6J#ANU z1kDpcW&=|3dP^~cp`;!h0dFjFjcP+YvZ;q2QRPoMavH2ft0*;Vfb;3Tfjmb}(q(U7 z|EbyKDE4O^hVrlHXqrEon1GTuy*ZB$&29zMVWzsOBV+!^AoD#|84W3;=-C68*$zG= zIK^Z6--nk{P1kY!ECN4d1R|-rM7xdp>+Q3j(?Y^9|ms4@4Z0iNxFD7R3q0^)o%7*MH8znju@0I>T>-3HS-RSDcS^(JZNc zSkB9pEpl}85L9WSm=V-hN2v1)Os?y^*?IG>cr_YAc_mZatpO?yAeQ z8xq}ZS8<(qeme2AsYVaQh3J=LlT$k8NMsySD4LmD#uu<@k64B7w4uLJ#1@13d! zR^%D8s^9~8GC%5Pdz|sIGS+{I5OD`|D8lb)(~YPdKMD3A??NgPg+xA4WzLzOg4E;{ zsAEO%w+Uj*x57_Ag#yR7^?POXH$5(2pE|0#JLbx)Ma1e|sQd$&%Pw)dA&8iwBghd{ ztnlrIpzGrfvQ754^C0ZEPk~qMs*A_ZvggPTOVNmSf8FKN`lE%JIvWr>!?R)?O{0Ev zV=TpH>1ux$}#ZIReve&75I62qNhSd9~t?me4 zCfoM0Fvk1>bRz75v*3Fq5{}0_P9KQ8o2i!FW?EV-yu6lKoR7!ae4H!X7u)d)1RBSoBm(-w=*L3K{_ zvb+A>%*S)C=@hUW6!28~%IUaYADuaWlJ^g*te~p@q6VnOuG}Oi{+q;X`Z}fVMa+oN@q(RLMR#HW1CXUbRX>x*t; zv$S3YD~}qhm?2}8TFpAG<NJKnkz+BNp1@chPJjg~dTXI*Hn03*N9rMxr zID-{FD&kB2i@k?4@vGBiGiAgqd$vl|awp1AJ@(){9>KKnR7XnoKYrOJ50+;hELY}1 z6+7MVL|0YElV{V})Lm+ZXTuYD?DPzuUYX?l$@xmk7gu{TdZY*$bF2A(s*KI|s^#`rTYE)l*RGcRciXxKH~F!z z$%jNYg0?8?$>=}Y;fI=R?>jvF4p%kSZaT-U5KncT!*!0NbM)gG*h5zR+MIc@rrsnQ z89akL`q(cw@eO3gTV+{T?@{`T#4L;h&$WAoK9j;7AW zH^e1ke&XG*1KZ<#7VCv=*)B|_C096QztC-l%$d*nMb48q^R zp0=iwkgc<=b2#fv$H@3gcXp-ldBEl2xT^zQb*z^T|I_*O&pFXu(g9)eQ`+6R?TqJ% zo!Qsl1L63-zGbvgn?pkPA+oQ?uDASp(3J3dW6XJOHTYsca1Q5rSI|pFT7Bs+jTn z76tyM?}usIAZTA)vwZx%j!Cx+izKC)YT|1EI(!-F!HgHe^(QpfyR8>G8^II*ZJ(qDK#{ zW}5xNUeJBowDV}mnKpz>!0tQKyXo->Ps3J2pF$cP2=(YV$dF>?M6?5~(k z@3ggxw*EU+X?zM22%8Jb>x$O4ZlY$NnO!-#-v10MF1uzkG^avuw+fu5Tnm#yhBUe> zfFWWJbT+q{%cg~x9?V~)AvHx-d$a7jHoQK1N1B603!A!n=KB2i3e@VAQ#;bT(TmN( zOFaU*Y86(MX??=2{{2`M8orb2v8Xq!iJMb{PjA8|zMmh-ne@r}{(iarWm2r|Z*!zB z&-X>7ZgPL&zV36+?X~q{Uj|EIX@8z9XBQ{qrb`M_E!g zvDx!&yJ`?Q+J5Z%P~Sfd3lg7Y+XR7JP4PhfEj->a63A7Hww`Mj&DupZ;%h#DxQ8!- z-bo+a*b|Sa8dg-)5aVR#CeKptUcS*@{vNd4P5A|m+y;=||5@0!ncBnH07FnP(}}E) zLUT4>uGWZeC&kHJUo=lH@sKz*sCn`<9M5nCWxYXH1-pNV9 zo^uaOvj-+WGGOt$ALS9)4LJ?3M(MHnao))@a}c_1C#!BFD{}+u&*SNv$}OS$dUwC* zbJ^W!SNPA&caJCXc-+4;?uPZcaWB4m@xP=OpIr45)#)aP15pUM=u`?Pub)eap`=9FhQXIuCGmbguuN zABU`&RK-sx7Ly~*nH+gB{bJp#Yl6dQg0|Ojx*EQPr~_P=4q%b{W-Y#0>&$A@Qc^MT zQ7gR=U#4F7vGmB(!11pJ&GO&DQahD~VU@k7(lU}3SYOjzx7IUhsl~O@b-vQd`?@x@ z%XDF|8qn=f)k9r#s^~z+r3w@oWmVxT`V45N+&g|7CVg;s%U<8nmoG#sBO(h6!YV?C z=*mL9SHh0-g>HPINcp@oR_jsu`A!~qC925T+^pJGV4ftko-32V0h!a{{HX5 zH=Qcp?uqO0P3)zaL~B&9rBG*q<6B7o^Tw&$@TI32f{sc3!l8j1G7 z&A{`9i8H|IsN!Mb-9-~WrU|KxNJv;Xuuvl*$5fJN#$iN>)* z(z7j5<0nbd6YcM`~hTviL%+~X^cHh+|b@DF*zU#su#Ps&`_l#l4O{y$S* z;{#<#zm`|vnO=D+KZ0A3!~4o|EyxF=ad>D%flX;-kEiD?d7n4B-}dkLl|zm6__`fp z|7803URa;N;pwE0H;w44X)#ST1GzDu_o2f%@t&d{;}ULo35OSytc;;-;seR9y$~B% za~U?5+_`qGCGF|DJlsuuGEZ+wd%Es>n!Tshenl3SdnB!szVLP0Ci2au&$am}y#{Xn zx&{)BjG}dU44Y^Oxo7o=W&z?IJ{AvTf(B7Mi~12u#40C_hg?gu2Z+-0TAZ9=Qw_|v zJQ*>iAbMxsh800i`5i zokm*%Pxg*<%iYocl14~I$_LHTLK}^{gFkyH4Q5Z?Y|}_%EX1YW@KUeYXC1A{xW5)3 zXA(k_#geC??kFFTY*r=)nn%a?_hRfX+UhX-3-A8j_xHSe?U&zQ>)tm6{T_*GV;+K2 z?(jQX?W&jhE#1j;GG<{6WkI)pu@+_w0$I8v-kAAY{&SL7~cTV{%5uNDe zXo!x@=uh=2=~ybm)HQX%X3C<9*u)<{+&qpw<=@bah#a7YdPFv#%gN#8BswOshG-?? zg|OV-C@16o61TMMZ0A1CeUd&k6!*!`4Xo;RbsduvctLMU$mwQ_o|KC2_iU zfgf2s@JeBtO<9wS9v`xgt+T4d0AJ|+^g}eRR_tDMFkaavms5vN9pSP^{$j0x#pdg@ zp*U5tIgf(g#W&7tW!Xn2z>_D;w|qs?6ONbGr(%V?V0?7ja(s{qKBDw=5!?%rbDqCY+?@z}>N9#fWK6BMPM9urbYI02k!${TQUp zX+XrsAbXC34&qXNp1j;9L{eEYFS@skP4vf2NoQiIPUX2&-iMD{)^2kQt&LWjWV7*c zlgd~M#he)5+1#DY_r}~4UnC2Ud}e$>=~>jq=@i9Ia8A_4iD8fjI4cg?u7;&=S=Prw z0=?%CoWAFf%iHd&SsQ1bHCI;WQGSlE=8gEdbs_&rw&9L&36FOUFgyo{-Z1Dp#Sb2c z;_+k_UyalsGu>v>=9@Z~niTjc+`*&315OJbK1aduD448Ewf({8K@< z1nalZRteX}6`HRE79NTIPsZMOYHoJq^JU6|dFtM(s{d1vQI3q^kx}FTx%|PV3C){7 z5S^h*qv;4qsyGUUM}g(aJ_+CTo_O)Lq%!)VO${p_kKDof0?w`_)?Xu93Uo>R9PWtz zp1$|Fdyn7!tvVWA*Dk&q_f#cnhBbh9KE1jpFZ`CW-Ko6X30{-9!&KGIZU?ko3;L#K z$evI~?C3v10{Bkn?sUF2*jEcM6wDUK9YPB@6iU!oK#SvJcznd@S^msOx?jv1%bh-% zlAcaK>i30o#Qz=oJn`%o-}^cE2-4oiNAPWn+$MQ&TUlmS?>n1sXtO3hY*`#=`tT_t zvrgl~)A)25@SC{mvH0t0*!P%e*wlZKoKlkwL)Ze7SK5?5LeKYub4$=LLR0{cA$c5c z9VK(%2L0>-GB_4v55S{hgl|)i@M8Pq()?IozRuY0JEj7ix`##ij-Q2{Cw{@SiDRe# zxzYdqu}yX;s}SE%^arH+0-G4{qt1gTM?C?J@avF~K3iPaG{fmv!$-gr6!Ny9e(8^f>Ac5eLJ>5$o0`8)z#`(D^LyF$i#ra30*h zj~-yNVP>@oBd@9Y9-;a7qyHm%KlE%6p?-J^A=-cWUI5D)nJuF;&kOKEUV!^9@#nDX zIeMOTr&*Mq_dxWGUV-!`M(G+~_gvn5J>NKeRpT?Nd>-tp9YtZuvyIX{Z)$8?ba>ho z=$=9~I+7iq?|bq47z1>_(C}QJ@3z>}pYI$`P6iu=F>B_kF|JHexmt#jnN;Y zpP+0>{_&*7it42|^?Awb+?RiZxxe2?s`B~q)g0;dZ${ti?_1SdW8KX@dpX7kUnzeC zFU+eDm0^D0Ztx5>-A$T!Qh&Fr;^Al?e;)lQ{NB~7Opc;1oJnhM^<({?$rxNcKkr`n zbf&Xf&+vV{qu4_OZ{ex#mic(EwAN0jSiPe)uIcx>{%+_vczLVujjr>D(m(NUVuS4K z$GTEkkH4s;{;c)LvE3WLdK$iU@)_wcepNaWJq_@TPV1HYM0<5tXHZddJJiHK)LL8=wGyCP+NLLq6b=Q^=jzI`T3bBwDqeVRBNIsy74JF&>_2;b{$6zOUO0cR z)z^d@@(H(fd^;cyEd#9ey<@xE#oe)^zG$(RF|;adqFNCWk`O%G#3pUjh1fm_h zlZ*s~a8`7f%S288l-xmYZPM(DI_;w%gUDk-E3*2QSL%azE&Q0SN3W^(l5EwpVrQXO zW36Z_a4^2N81+Qfk{yS3^eV_k_{%jtKGAm#1rsqh9TOeP=s3Q+S@VjT@4vL7>r%-N zJWr_-rOUEm%<;8Ye1S9LVE+~;#*VX&XH;ODE`asUqXWJAQ9KN;UFGCSe}I#tg_k)$ ze0|xF{8#6eLd)XO45FLmne&*%>`Ffk5O4U3be8SlzcHVISt@v*@F&ra5k6z{`1;yk zi?+x?ooi6@_fba8DgHOPQ`E%!cEBbc_(te7Tlkf@4F1?XY2Bw4Pd5lf-%eLu)frR| zARF;QZwG%Ak5@|;LDaRKM=gd&MU?P=k$O-DndK*_|O4*y#e!j~fX8g>b-aVSy9&;yOhZuxMPKkzl)E*KJGtyZa~yw`daB8C0$H@LyY%+i74r^&_F+b*yW)dkBH8m!PuNJxh#|trN*+w zj23CPFB_J?)FM56cCGr1-{jGGrFwC46U#RG<;n|Lv~O@3kqde+PE>PQcA%fe<`K-F z#m~s>>rx>+t?J^B?E#9KPWL!;VQtsNvur*#q+!Y@uTwKH` z;V%PJP(ceP!ZCgjF0kVotJChFSR9Jp#pT!?%&*)b{ z?<(K5KK4UbO3RrgX}u(jCZ7oFmbpg_b)++>ta;wxnyPSa1}d{U*%kSFkl0KbtEVS= z3y}kRB}_qhr>HNzWe)T%YNM^gC=)TDcXAOe8nCD;rx%Tb)lvqk^_Ov6&=cSrQ9z2t^p(X` z4D`VT{T046E>>;&SHnkGjL=CNZ-KYXJ8S%twHj>)txs1Gd^{nYf*L+K%=&r&rC=Q$XC+t{b? zdvU{QcR~K$ORcdIEKYi~nEuNDx8gP_vzs%D_cO_@QokT7;E%+oIG<`VD&VnTxmP;G zFxk&)%J|H1FKpW)=i^S!q>IB#t*g*8tzh2}UEk@aF}V&E_n&oq^4-oEtDXC7Qn%%& zW1D=?cWRO~T3Z{EgUC+$J5HCK8^N=&Ub4BkmgTmkeW%jdB;$c_MQ^WIl~rHEOuu^M zD-x2f3fNiJfeI9vy=6V9td|OSf-bCEmB~+#0&46y9+fNdu~e^b`q|_ib7kwQ1Vu8F z^vh?fUPS*(4w9?~}|zRfU`7>y=%-ZL-2?V!_tEgscm@i}+uU-9@y zu>f9(d(&$p$~82%b`7s=5ymyXmA3S~TZ5I}qV~<=6HS#v7g17e%AW6wg=7Q9H7*Vo z-2OweL^~|^2_VKdtCYDBqD0LT&RohYmz0?mQ}p;nb7`KYLM&@z>|=;E$alqIe?Ono zc~>?RmhkGxI6#a`_w_IoeTwyKVgdaWu9^G<^;S357@daA%V8di)E|=PSXFe@!`tDO zRb4hhyJf|cwcEi;zwTDLkmR=25VqA#p^8}Ax@;`B*Bw)asmZuo9)H>h(r#UPlw--U zGz@>jljy!5)1e$wLttt{UZM6Ghi>UDm!)S@jch)q%@R5a9_g|yjGo1tLDFHjO_y}n zgt8PnZeQ8Dj~~lg>TA`R54||ukz6*L$`Dt@{_;5RQ_Vk_XD!{le4=CqU`Kxp{!O!~ zkM;bx#vg|FGM@2JkBQI{=O@z2B&UyBi)hRA^CH^8Ii7T8CN<&xmZ-ww!({aqGaB-) zmd<(}{2g>$BcI`(o{?WbkI*gs=W`s1N@BFgy6!VMDo#Zr_kd4|U!wT=%sUbL{#$lN zH7CI`zTW6tV%iZ`BLktF6LF=KBO2q|#ValD6F||{1vPnU$R{h`Kf7PF&XQoc(s*+pzfsiUL=ZM)F=6pl)cE$_{XI1 z=R$j`)GuZyR;$xyi!$IBHtFm>JogLJH9a>P`XFRu!eh~$(6%h&=8kkdDjlcmdTt#r zmvx-Op{Ai}O*Vn!DQPku0nN+Rn2sm%_HQ@ZqRyY3KUI0wbUcy!?s)Rtcbhqrt36H6 zSY`q^9S6w%aUIH|R@0*D)(7E%`k0t?_~Bdwr;61O)a-FBW{z2mzPQ?B`HJyi)@OLd zj4i9Y!86)cm*!bxsaw~LzV>FQ+_1_~=U3BuZCvge{ps>Xg`{wN8oC0sF~6Qa)2{FRe!LS}8RfY$!D^jnlL?UO29D9x1x@)8=tV!GzDF$u+p)6eDA<5RA z&396?U9FmB+7%q_%Yk;QG`uhFxvRPzCPdTu6rW)AShQJh1P+cxIz5}D>cN>@%dSPo z)V_$Wt1qms>L^|g6j!tB>bm%s&nav15!=@T4{z>}q0ren*oRmM^^JAE3q%&KbAJsn$)t$x^|!hwk<&bK^!IKQ5+jJ0~c6{Eo} z6TMcnORZ=gw%}XMy#-t*x=bu%DNo;8)BH|kV$&2)$ChKO54OlVc5HctGIQgGmWft* zpm+}s!mEZ*2)tdx~2! zmzLb~;@rpdT8!+FXG`aoW}UrU%`kmUpL$`Biv?7^s)Ua9w z>s8g&*bE&yVA`X8Ii}NjYtHr1t-P*lK6PDb@9WV-HxTnsv~>eXnQm##SWQoNV&=+| z-^L^+o-<>9Uo)lOg-(F|^Eszu%j9n^T%m~LDg}gZnUHBtk zy)*8Lv)&LHEo%f;AvtYT*9v_0`t%+NL)GU_Lto6$UmFK&(QJA!Y)}9O_;h9qL6SY&xH0&==@CR>6ltqekxh2o{{4jIo&dHnlVMU?(3eN zDxJsql<_H>$w;gPT`+nr&<%I=Q$1exhev-mT{|p?qDcalJ>Gfiaj#OR{7gx)q3{Ya%Ha#jj>yj_sIMv&g>X-j8$=0dQ9!>eMNWfLbW=TJ_r#k_ZXb<{WD_q?K9o?rEtN#abq=4iLsFVz(-7K2yWbA;xT zBUIK8!OW&ma~SsS^n5l45jQY1-CJ@^c)BH@@87joS4wMqt3I3G2W)_g_hDtj2VDo9R5~c{Y!ZDjeNtWdVNRd**XV$#F6U6^%YHdFLoGz5b~7BO*&Dp zqTjwD8x>SLm=1;a>bj0>M|SLt zTI9nqqmasjZk02Wd^W8MeVwW@$83bvFj>K#*LA>7bqYLsBVLWSre8M8C+1O%7UV8lD3nKkI)-T8M~$tyd(vU9k~ z&garGww2jW6wLGx{OrY<^;_z>1gfK#gS6cf;xYclsb^a zvTUiMR4dA?i%XaJ_wN+x6q!qru~N8cazvfH%tK?2)O;vS_iU>6dvWQp{l1+JoeuNq z(Dc@>bl8l-9Qy03?{)W)TRgQWafi1fyM79%O{dLV+GO@kpA3T8X){mCUR)WOfB#O8 zPLKKY*w4q4dfXIcQPKKyd0FSjg}*p`I(_ESXKtVDYR26#^qDVnE~X}%fB#OCPLugG zxzRPO8vCExjJVYkJ zO+_pFczxzUt@%2k>$Jz?e5w_3$zMZe61_0?m6KhZY?d;XsAt<&TzE(SdB;2bpX_Du zLB~D!?jq&hkuRM$iLPpA&VxeRHN}fbneF%O8t!svxXrZ-N&6%d^^K-Wk@uf^)ZFNu z-93F}&-QkSGn~ZfhVN%%U4Z;+tGA5STle~fD#D9vJH{R3!(n_{&y>fSzDB3G=s94{ z@4eA$J-6deLN5QFkWO}Fs&80RMcR#E1>I=uK1OEejd8!Jww3P%9s8}OYnmQL-yh4% zj8=Q!8nyD3<7cI_5L$iv6nJxw6VI?9oNf&>v|2xYT!485+-r;O}ta>7*k^R&vlzy zV_#RkZqvMKb*f-@+qbT+zPhU8dO2|YL_CfP$&cgeLO3UN#>jiL2j(%+3pdW|Hk?pB z8FhE&7BU^A4c}=VR^IA#tiTG6o!t8L;N1#3*KY7m(uwt8^ljE>qr?8>^`QyVtCBYp zjqbCKmwlLcMc?bF^$fPEm=&Ezg=?HiPMs&;{En{WeL4GT*Vg&A*vkaH+Il^-&Hhe^ zENi#LHMb=F={^EqKa<|_u2(PU^7=`8u3Ozv_FmueqIyEt?@ZN(D{-*hdf2B(H5A6^l;k^kQb(>vE&`G1a}?Bm!*kxrwVl&Cr|rgR>20P?Z~;B(}dGRSDK&;z0D;V&KDC=`=g}AZ~FZ>xO6aTn06hf z1E+(obg&`6_eQV_hM)tRM2vQ1UYQhCC!TSt>&!Zjn}OIP(e|NGVBPviCduJ2gA41WF_ z!s_+0pFcmhs*IGPj`C7yq*jx9SMsWe1Dy)H$HL{%p3nAcIR-Ba25*dg??YhFEIVe7 zpaBzcdKobJ?p#M68i~1AbUa=bJZ_BT_oDDP54oL#%5+`F=KQevGDyP@%6X2Dj5}>g zDx%e$NmkmVm^c~oYiW+nU1n|xp?Ag=TQAi~W^42h)q_14{g-sY7y7@YpU0zTqo*O4 zwAv4W4B_Gg(^b8LxiH)fG0H<(J^$7{p=;Ak;j7?>qe5jxxx|Nhg%u9;$X>^LO?`q* z!l|xp-hxNrTlh6(e3O$57xtXwh=^gaK|RRd_3d5};@YsmUcA@NVgItLhrt@A)`V>K zV;%WUKOE=rD)%VH2CM*xPBf^ekq{AWQJQMI3JgFFGh~&Vomvb8Ga~*}l!g5~D|R>K&pqp1xW3j^U+aR(D3v1uuXpz6 zH6d_cn%bW7U}8p0s(0&Ckx_@PrYksd7KxmjWA9p9JwqPvGEKS;IemQ^b?9lkiX&*A z2r?Uxiq~6;Aq*w;;0SnQk!w^N;*m`~^oS~d(vj0%m4|g0%D(A;WR`WF+H;cgyv6A65W%l%ikP;^iKa*Zeo(=gm!})*OuHx8P40g!X8WuZ`g_qxi zpA-GgR$)Dj<4n7PV`ed!xf#5HL&_=31SRX#^jUI)>Dn|j$7#Nf<7YAWxfLpChQyC` zfycMAJ2+-0*LIvN0w+i&n_x1eU2ZWh?*Al6DEie6$z{xUJpC$;o%v&jx+9C3uIted zF;m-B{KsSnS$^-Co*fH2aGkz6=XDxR-nAS@i^7q0fte4E;w*|(|2%IrHC@p$)fH1# ze-hvIi+n7{;txB@bgV|(n|d1;Gb5hK*BAT9pPXlaGl3KmZ#E=UIo9;v9cpA6SfK8 zPj&7+y~Ao_%wH6!YCll!ka*6!mdpIe-M&%p@F>5( z3E3c#mu?1I5l+;NC&mhcQ}IR6&+2i~MeP4xJ#KlCk#l0lSjG27N8DA{ja)8v*zSw8 zVm4^~nvST8j3_!=+O!O$ax*VW+N4Qi=E2y!0(3bth9d$vO6Gm6G`t#k8{PS5w`Zmy zIp>-#yO%?Dn|1Xhplxr&oP(pF1Dh38J|1~%_n9%VI;6T@)#h4rc z4;xSF%isk?ejVtCdhb*r+QncgI|rwTM`~ z3zdH$bJ-R>Y_=H2<~0<1xXfqo3uYAm=ef*xG&2e_Tw{GcvEc>7*4qonD?v z*P-s=d~-oNGD-EC&TVm9}s?&Um3SXUZM?L7Fm|o@An= z8i{CmQoEB^ZJMZ{eQndDx!$Str$cE_eKnaUdN+RBt>7Wu4O$Ue*}>@B*3bC8@)@Sx ziPOb$(8V-QUa~MFB@iL7DS=&c0*h;zj^kNb)m*#F=jVugGv?fr^YgxC@=d#f=d5@i)3!dQU8`H>K>^n?;7O@;!gz@`%(1S&v+dV6jnKA8Br!9-6yFFS*8EsdAMtsWG ze5QQGzP{)tHcRVeu=1#}iWxFisnx90S{_X?Kjt@CjYPy#2Fw*rR3Hxp-G*!wvn6+B ziCHya?3j<{#~G~fQ4wGAU+g`cX_FO-88OSAtx~nzi854=Jvfg?Fl{{5k&^w7U$)7E z<(UV|m3dIbPB%Q!Rn_t2*>pB_m-@Kb@I)RvJ;SG0COLm{{#3=-ba{ub+VSN4X(;}* z7IHUlH~Qq9Lpg`a=TO#7(JK<1M-9cJZiksGw;Ow(IgfH4mCK{xP1cFAX`Z_y$`O^1 zDDz{ZO}VAV1U|gp?%6EoBz5sbF+YK6pN=TkqlVF=)}=*RrknF9uN9^%C*IA;{4mS8 z-PG&UhQC$L&&l|#7*iwa68jN1=knym)!vLADMH5FYW|;&eo!6y?;dMYm%*Vsxb@#{ z>l)nT$G#>X65R;eqNpdM|7eFFYO=jYQ-|N-s>a$)=eQN(sjhRl&XIJEemnzv$f{qP zGcVTEn`9${XOKt#RPu;S{=_$s8E=(kVZBG`FA}pb4m{UF(!JWXJ9afT8E>@(Wa?70 z9i{tvusfPM8{ZI@i1~?k!wzhRtPJZ3LoXNW%R>Ik$3pfC-Db#~`K({$Jb5!rWJeog zADQd67it^mAVarB>$^sem&wlByhaz9)B%PbGUVHEb^6X&H=B4@{AW^b@t)`%v#*+U zYEA8auv@7sqNlCtB=iQS{~l+Z=@=P*>CUbcJ`cD&99O{8RmXbi@IReT|C|%uB^?kZ zKc(HB+s=5N*qMF(JrIuX>sv+}wK*hoA0qqel{&UtFI&)*@Oxv-d2TiMVnA>X=W%Ys zx6HR4`vt=_;JY8g{As#TSy!mz8h-Oauf;PS>M^?rC9{98Mt8LadCL2xmCEPEUB%|e z>83yzOp_;9O588z@fGhVay^qObg~9@+|#l8c_34Eb*HDYN~}99Tq4OHItQPG^Nn}d zoC5l58UN!`#?KzP23@k@TQMUr_S=elh*z01NA)|Hrh^tVz;|5is&!R7lxYSBI$Hch zE7Gj_97ly~F!wNZi`WrOz+YlU>hyPSirOCQOzz!e1U)ZT!#A+(p1nNo1bWaz*b$`5 z>#(QHoOm6cBY7geZ^?Qh_v}VvB%WT!x0U|^*6xPYVn@<%0&45zPwKq}FGZHot!+=( zK9GfJo{)%C>)mEOTnzL7LuZ4|vOGkNf{BQ0lX154zFtGd^0~=#wRfgG+1NFXZgYD& zhjrXnJgH?9-r_uY&G;Lq|t#;kB);3DOOHI zJJ33f^3G4;?9=d0Tf1oMzf+aQryzl_xv;#hXl?5zYWA7gm6PlJ&#>aMYc@l3D)e@% zz-h|0Fd1Y>qq_naBKGjB{@ToC(?U!S<}cEanj)*cS@vBUULU<9&B3CDOfeuLq2W8J9*cUznz%VN`1B@h;`{lLoJpUo@9&q} zUna%c{x(PI@_b)J>L&LW?(07Hd@3EDH=8faCS*8wjy@DL&x<%g(qb)fHltM~-;qtu z{&|$eqb#YL*zEbX^YBZ=t`GJ7)36}%S+-3O$kh}NlMGY}dW^VE<RzVaer4p$g09>0A`n|@vNRoCj;<==%Fn0qF7snt7rvZ#c|DccHD%=V z=UwQy*rEpC@E34kntrJBZ~HF(Q*ozT9L}@m{i^pA;&M*3M-_bMAMZ^e0ny$N9({BD=Hh`6J)+VEbLB=v@CjKMq+l zsfypC`d<6>R@E!TX^^wWuZ|p)_Nu_wYXNg&R1G_U)QE~ znJx@g1G*imdZ=qo6&>ifRDmL+tSWp(p8@TZd&h6Xqz~?H+3P!|VW_P-G+G%ESy&KO z5jsRy7V5nccHBbjW+4_SpLfP;Jt{xn$pa6iV5=4RLhNkOlyi}5-TxMP1ssNqz#U!h zP}lX{?CUWu?pL?nY_|0Bck`)s5G?@y@B7$yAM-h_dEp`l{w2t)Bke)*+n|Htjr02Z zi>HIx?rlo*AOpIt%Ozc|+G_0CVX|jqg>U=BWhqBG8sT+E3JgyI>al`Qu#Dj^@tzSM6GIjN2~KW zo-3xo)uNo@yI{=^dNuVkc{BcAcf+^AnshSZbD~dL-x}8- zVJTuO6m19J|BJH!kmN|52>Pl?8{L(;P?QR62` z(i83PwBwNvP+>e24vJCah=|k;EcR=q7XlhjvG>blcbC-5Y}q|RF+<6`26!V=2h7+5 zpvimaJzTr%!BKBUr<~RUDQdrH;meNENqsXf2jW5K;Y0`O$d3%IIUTF=qLy^egyR(Z zc{*T#+#2MGF!uP9M&^V zasP^b&Pbg%<>9hgz~&xbd9?YXEQEi^8~$2-cYjjm!lv}5*ZTiVd5sU0A^loNdZt&N z%8%d{^-gaD>5GXNOF$8@O9cI^3A5twfQN%25$bk1`>^o zqIG!;n`j8RXZ46?0pcA#77t{C2JuXb`VmXSDkqMITuZYDh|=;}oSb2vK(j4RMocM) z-kG;yMbJ}z2N6Vkj5gILozIo<&f0{HYIkV6BkT!`VwPiMer>U<&ZY20f2yyHE%j_k*8l9(|Yv=jb4#Yvpb1CkqTOq?+ zA;o=buHEUjZ$*98_pR%`p*?H|{ek>@d^^|WsZ8E&n`Ug@g6JzlgUyMwy9PUp25X+o z*!?ov68O$`q+9Ne{+BdDGEzQhmKNG*+#UScLuoL3@@AVx8e<_Y^@f*v%|7dBO~(DT z_&Adgnk<$)4RuHPi1&meCI*^E$M^SQ>@V8tF#8Md{@wTYynF4J-(TzAHw672iE3k& zlvD2TJ6rA9nEK>IxlCUr%M-*C6nQj#Ow5na6vOf?rsT&e=GcSeYCS>pu|+@Ga%VAh z@jIt{mWWRDax_H8X7s1}lyoc=G{M<(eY;>Y*_jcW_~VD0$FV1SmTp939X!+{EM1qA z!^=r@OkxeuO2m_3*^*IC#{DH0ZQ0q*eVqFweQGG~lb;({)$QsgRYdgGYkAh|ia30e zrDjvlV~r(ox_5yeSv>GcVVX@@lZ+l8>Mg9Zs>J|b=>7CVG_F?cUUV>C*(R4$hff{h zvPb@6t%1en>$IUbRkAscg5Je9&TD1aM<&42?}KmoiliqTFRf3-3VFf!=z!*&)?~9++mLo&adr$&`!&G@ z8L346*T+6NP{MQ9SccbFUWy`5M9urbYI z02k!${TQUpX+XrsAbXC34&qXNp1j;9L{eEYFS@skP4vf2NoQiIPUX2&-iMD{)^2kQ zt&LWjWV7*clgd~M#he)5+1#DY_r}~4UnC2Ud}e$>=~>jq=@i9Ia8A_4iD8fjI4cg? zu7;&=S=Prw0=?%CoWAFf%iHd&SsQ1bHCI;WQGSlE=8gEdbs_&rw&9L&36FOUFgyo{ z-Z1Dp#Sb2c;_+k_UyalsGu>v><}FT9e;tzoKZQGZ6nMaC!Ncb$7#;fCwgyzj3h|bWZ(R73)RU8Gwqrh@ypM>vvPrP_rQW<^GriPV|NA6&K0cY0|>#q?l z1-hhu4tGRi30o#Qz=oJn`%o-}^cE2-4oiNAPWn+$MQ&TUlmS?>n1sXtO3h zY*`#=`tT_tvrgl~)A)25@SC{mvH0t0*!P%e7-t#O^fy^Znr5 z5;Tku6~JRi9>-fp$sD*rKYM@-j>Xsm@TeH!+teex*gm;5Kh~G8Gq(GVsX(XhVNt&0 zXJO}wUodUr*y(?6^nZVBlO4(`#5WZE0ja*gCIey7kEh+LCvJ zU5^n2*A|D@7W?P{HXCMEt1$AKs_zk+e?R&^+WAAz1`$OMzvt2Z%l86U*2ruboq1k> z7xDt!cZolTUC+_;tUJx3^t=b6Z}bYJHa~mOR@i z-Sei#wnc}hU4iZ?RHIWdfY0~6_+{_fyNL6h;|Xf7=bIb3 z{$%%KZNxivGz^c1UxbEZebeaoSa-$z96BfQN-<+_JydnIO|5O?1ry> zG4_l8b9b@N5)}b&b$^^yoLuZy;Ba$Xs=e)+BYRf$b8DPY`lVxt4}w&2uhD#Z#`dum zZ^}JJO;f=@Qg!spHHBa|MRT_UD{b_vGioCWf ovZl;;eq4B+b0R>(~C?mNrOFCf@V^0sF;>Q2+n{ literal 0 HcmV?d00001 diff --git a/build_log3.txt b/build_log3.txt new file mode 100644 index 0000000000000000000000000000000000000000..a00dbc545c3cd1ff4edab2dbcf85d7495d9c11b3 GIT binary patch literal 355900 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 P|0lO+Ro#AXg?0Zw=VFLc literal 0 HcmV?d00001 diff --git a/build_log5.txt b/build_log5.txt new file mode 100644 index 0000000000000000000000000000000000000000..cb2c67e8a7c5e68efd4d02a4ddafc44e286d4ee6 GIT binary patch literal 355900 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 ME(?k7L0I?y1BEGwR{#J2 literal 0 HcmV?d00001 diff --git a/build_log6.txt b/build_log6.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a3581575902fbca3f0742d32cdec5b405322e77 GIT binary patch literal 358350 zcmeI5YjYdN*{1P1RrwEmlMhm=VwOZv5|vHz7Im=Aieza;%Fb?>a#5s6(dKDM$~N=k zclW;fL9=HDn3=|40FlB|DvBU5=pLZ&=Y0Bq|95GyGhC2z`$2#4m`BUu-|q*%(<@7Z@AT~D;N$fg_KrP0yF7gLMDJPB z-~0NXEBR}5rwcvDzK6Q*P>=R>)vn%kqWA2C)p^&Bp8HwSJBa zXMR8Uq`#kqRWJ1ak)B%~yw$Vpau}{#8hkbQd)WIggYCh)@aqK%pl4mb zFZKH{U={K%IJW#-rIQ8L>;heZE_LOF{{C6NpGYR()6WC_zox%m=^eXa6=e3lu3Zi? z{8&;IdCXs*^g6l;a{T)s%P#aTdlmiRTz9}O7lXTlmBFgkelYm+;Njp;y2m9+Zmvjr z&?`xkr@CTg@L2!2;%lviOix)}L#tt@ zJ*}`b_?PamFFj^m_jnQR@xAuAl2m%8JLkU6b(dcSy^YVuJ@15Tm$f^3ooSW-4w}c8 zgo>@8&2qwqA<@=(8ec^mxtKS!H9_ra0 zJ--u>x1%e;TGu0ZsKQ&E0cj#~1L#=2t{KfYMUGC|5 zWG&tmuG5&m*dk1xVEeyrcy`i4#WW3A%5n>DXx?SRMR-&)aiso84SQL4n>i-s}B z*L?8>&dfHA{_Z7lAhS(P7r+|h5#G@w@i4e{m6Ip^0Ua5RU*`Pq^+iMSADmkXE%Qe+ zh;Ei=&SU0tD%~_d{QpbRS$2Z|#(V~5saP~A#lT!Wguk1}db@wdsH zq9)!og85QX@V(G!w(z#N4F1?dY2BBK8x*~QL~hZy)2_?9Vo(2&ja<1C{82nEd*Gw4 z?L2BeJSw7u-}BUi5;D3+)aQvu*JkF=xgs_bR&u$BbC3CR4^C^hwMuF*T#=-P6kAb2z&InKP|6W<}V5jbn_G59?J1ZajF97>U(oa zi4ud~?b?3w8Eh+qlI*v#6?nTc59aC5SQM^X#M3-IpSIWdOz1BQwe$9wE+TS4@y&_$ zF3JgX)7U(+J4wByYdl2>Wg1UWK6KS2=NUC~y`{g}qaqsO<9X^Ww+(x)eZ(5G<&Z5s zZS7mcT=jRt#Z8RPE{aA{J(d1^bc+^=9fz3yzRjrBP%lbVY_WI552|6X&JpF@Dz1DL zDi*Bc1=W>!GoB58RNshSMBV55ZFQs7=eJa~M2(4ks=KP{TUABilbVWGDmm|~CWE?I z>Rq92GPbC$IMCz6!GH7@InIC7s$_y4gxV;pbhRt#ph4y7=a&6n&Y3;Xi9GUCo3P(X zS4y+GYwO}d9Xd2oea>jlX!4b?ZW)c#mPa~+${sgR**($OJy9c6X7$5M@=_wP=`1sz zp7sNOSQ^|>RXVixtGELCrPYj|>v`%E&jP)ZqiK{1oqiBSQg6-<)NETlgGKre^&B<&k7yOnL81m$?%mZDDWh83x-_fj;d{6KPQP7Cj1|?st_c%1s&O!|3~ko`vhF@wD20c{Yh)iuOrEA z(dXk?az=cw-H>trh9lz}A?Z>P*&QL{qjam&fSM{U2&U~@m{^3 zQErtoTi_VpFaU_RzTY5db`drVVUGu)` zI&kJ+_5A3&zf8^uA`HoI^~q1dW9*W1;l5Z$hG(OA#n-w9j>!3fKe}fgX)U@v)L$2^ zFl;O=F?xtK%hbox)Um~|=88zp+bv!7Mt6%HU^srJzrYoe7Yl*UaVDyQ@6or+x==jT zv$0DUnB$X@t6ltjMn|y^hEZh|izeycYj^8u3UyX#;ZEt>j#YTYo#yumplGWKO!S2w zQNDjp-z1{xBYY;Se8mpV!9u!fzMsu0xrnQ2Dfxlq*yo{SI(^~;MH~Jg>MioCaTEk~ zBS}8!eV0Ls+!TtQ*C+X$ls(VS_+?UfepKR}Y)<@SZ-SSUu4-qB8Egdm7LOtx(WI|F zl*a>owOJMy_0{&3trOI_=&Y-*x*qQMximHGRpK1@NvvCFT-9~Y$@`s0p9$L3XrIuV zX_Z&~{NsH8hL@P-B74y2*;@W_{3y$TG%GBzfpR z7G+J;5TPw}Y4g_8Wu8bMS|GMH-hS&3%RQRc-#Wxvr$ek7v`Xt!if&GKI&3=Y;*ZUO zE%!%VQ{Laagrj%N&b!(-$v)_2(Rt)_m;y^?!Q5}KU>sB3yGSe|pIQy^ND@wygRRdi5k^I;fYA;iZrWw|DAIx=T!NeiVQgV z=DU_2Jyfox=jHm;OFt%wkfDbRbUuaG(=jzIrq;x#@OZnH?tIF6Y&PYAzmN~s@&|st z_DA83GOpC-aZNh;vyfL_Tx~KNE_d&=Ap$k z{;C{}zlWR=Jc^!!MjrOr@S7zQo4Kks0qZ8#C#;8@gKk(znP)c-3!Zfr^IAuWWMP6rcS;uf)276wSLYofkf_(i+dJN|&a!*8?YtS@bS(ZmPs7=0KpA7;5Tc zzeIH{ySdFjn6ee?*L*D>+jAk7%9q(?D%S1qS&AOj8`Yz%CakDAM}2BdJhBU)EUsla zMRX1NpbQ0~#O@)lujskm8hJgE9!uupyKMG{Q(IW&qD3shImt&J&2x#7jr>e+y1goduT z(uU-Hx#HDc=^7cd)ytJj&3kyHFdwA7mIoz`imIlS2FNT%b(MGhbN@m+aHsl_DD(HEy(zUKDT)tzH52FL8G zZxC&1^D(w*97U8P)z4I|8M}GV52UJP^oRP~-JA-X3ZmXwM5}2GDch&HJKbpHv8e49 z*QGbkiS!Zl`>xjHDBUcJma6MIvK`q?$bLA?&zLkbsSep>Cw(o;l6<;kWA=&hrX3vZ zi-GoO?7#Z~@ooWnWl5?mnGR3%J$dZ0Q)x5qQ816fba*1G!|~+&sUQAiefXRGRG!F*8Q+vN3~zzvadnZ!4QZ2L z5uFlwa}_f8B42$n?9Q>$kcUyG1B=Pmt?F8V&*7-AB%73vcf&OCKkGTu7pkkhxckv{ ztE)Pvc1~T-sfm}E_0|`viQv7^PweU4H99!XjvK$WBis?*gz(tSr7wiPQ;yT#wcXdv z${UC7(RCf!i-GK91&+5YABaombw@}YiPD;A1fMjkB3hWm#9O-@hp5Uc8r)_cF?f)V~qUoCYt( zu6|q0tomG{8!G9U*XH#lo(+b9AmS=7%_i*SrH$VMi(#h$-+ zRb@%FB^gNdoT}{QZ(DcN0`A8rI=Ze~){9|TPlIpbxs&=v+YoN{5wp{0zxLF zNSZo?4beR7tU7Zjt>IILh;Lmfn-_iKqpYUeHFYSRWc2Umq9IKkN^9I4H2B=1`nf}8 zY0_PriTwawQ+7?cZ%rBB78ZU}rGjfSi>1x<$G0pl&{S3B*m7)D@mBu3tXoIzDdxdB zwp=FmEfcMmg3rnD-W-$l=E&n)c)gpao4HJMnOMeB)iYiv6V2=GS&1$aqf9I#1<&MY z*XzwNvyVP8QM0r^(8w zX0}!PT$ib!+PSwcyjRzCWIM8BN8lm?W3TI#Va{fgu8@yx=<8CQu0JXd9)H1vv#p3W|CW{W`JUrjZBrQ#!iJg5rGNbjNj*MjY3U z>x2@M2WtV>&9z2t(qMT&PIKQ~;m2dUX(z|`V&J>#AsLs4YTXsEJ&*P8RQQaXoID&l zFyC+F=@@$ zi^Zp6g(TCpUueb$y>a*ktVefzCh1QW$DF+8H?8E|JiVJ|f89LaD1No03~{`ER=bWT zeqCNks@2i`H&vkqs-vIssjk1!^%2ugI{HM5^Z4Fshdr%X+!vdentAg_ZfMU<_2Jsp zKkMB@1~HZO(O25G#x$PN;(cvue>zOly_b8j9@Bh_@f(}Pox6R$820(}^hgxCJ%e>M+`e1j9hV((5FD0c;B(bdHP*JKCW!BB5%l!Ly zigb$1rO22S-&83!PG07rF-K~C9jY{MA3e6;x6`51VLlz2-a08AHlr|y{wCG;y5<}d zDZGC>a?(%XwCS{&OPkcXcFEzHo;LHOtm~}!$q`3w~SyZ(CTwadzNVs_xky0vRM

#NXVM7BX*p2O-0$_;SGi&t z#iLN0M0gYAX}hL)Gvg-h_w5?)V#v%_(hkT&Jqi_>_{>vItkuI&cb~LSn--5I8na=U z2u|HeTZVUOwki>{!udTG-|=?}yKI8T6pPD%Y2M-{#$!1viYCx}qjs}8lm%@XcCp8zRb$q#?AncMH~nch-$^5XCavE1 zAzFVzFPS^tG6Ui|roDV8DRZoq;oe6<1L?}O%>uN0j@nDz>dv7TgF`nX*rEc>(@)Sz zsy>b~g14j(p)FlV&u1=`Wof|ekM(morCwgfbKYL=(TKi@c0c>17T;tt|FzCPcEz+_ zYcXy-z0@zR?Q-Acet&Y`F#amoaEGCr>zTBG=(Q_9AGO&>!wBo-xUR0}SavLbZY$ir6by+uN>);pO(hs(KcMYI2#bv~}O#p7w4RSDEjum&!k*mXYd3DFMW4aF0cx-KQ(^`whn}cd1 z6T~LAf=3t5jgNO6V#GzuYPs~+8=;Ts zcI0JTN`Hyk_gaW1xBD!LHXigden)z`a2LcgE<-W> zRY%6`kzrk`tQMtD8AU!Z8um`mo6+buG;gDSDb=)(<7XcDSsOeKlBzH5xETL^5vDj$ zb7gtKWTN$np}f5uN3+Kfbwl@rq@3J@JQ>9tgmu~OXf~H&3JG}z(?%!CnsW0Vj+^=5 zhDn$G;>P;;qbnbT*{_xr+b8D%IZeAbcIJZ}>o?YyZ;?~o5NduAr@7D%ooM>!ICVQX zX6A#Lham#fCuWEX?gY=b>9g=1stx<*IL-Swe&&OpN5lEdZ8@0vdX9BRF?rQDW=7X` zoXi6!NG9ur*{5A@F)r@^B1kCmcKSq&`Ht&%aqP?Stm*Ns`zus z(>@;1=gsC(#z<*@khTXDbMZ)=2pQIAoV|*srX3wqlVS?H zc#>{O#Yzu4Z&;Re(WmlST5ya?U0lcCb7NsC=c#?=YP4bUoW60@E7euO6ccdiY`zmZ zpr$Dv-us+9<@$Q4+terW^)O3_$>P;4PwERTpRe49$Z~*UjtMdw|L2N+PbRag`^63c zTawXaQ+3tvikVO2*L03KJB~R$qR17s+Et=_&6rA@Gh#J%9E5L?9TyRhlfOARFO9pF zDP)p8ozvdtoY8zuBIf38ORid$-iyI+;&balDb~rA>{_&v$XAJN6Cr5A|08+yo~RS~ zk}~kNw9@9_AIg_`G5Du)Lf`85rv7ax67XHf(5p`0y3qR~wUUvztamVdp1UDlS>&VS zlm1E9zYkHVCDGNX?v@o!^oUtt)zzzOn@0O>`0O7gS*#l>Jbr1g=>60qBKpM!JTw;@ zdpgbiV8z&}#(ZXStmb>;=3#g%uI-%GIqhVe7XPTlnA&-5 z4&KeP|58@IS$4(B;%<@hGHoSp+qRR_g44pJv_KzM)57zk24#E}dB0hPsb=-5JfByJ z{Nf+$KG~zZU!sSc-JB+zCMKnc$D#@AyOQ5csEpB3nuw@>e|+c zKUL1u>tOhetR9LfhI9ZU5+5yf9@B&DuH?^^9)0MUg1YPu&3MnRNqT4T?k1&ze&gXr~0#n7d&doFzTZQHi*y z;aW%T8;Q9)Iv#He9@mEQdtP{)rs}OuZs(vf?d#Z_KQ_M(($abl$IMdVk94zXjzg}A zUlOq-D|stS;aieEc%pygkf*+mOUgyIOyd(t;UoQvSszP*GxC|?kS9Thq%zI&PsvBW z(7NMXi4w4JoKf(uX={Aw)XsvUb{2+qpm6MPloHb zJ7#CrLZAJhyneFjJ+HmaIvI~3s=ggTj-coZB-4E|1U(q~_?zOt&V#7y{1kZAfhvQ( z*SBod9Ql&uHdVOVbhbDRFwJSFx*4A|RoiFRRadJ+Db}VzCC_*J%EfMtU3K_zHQS@> zB6GZI+c90nG+w=vtu3Oy^z8LYB@1#XvN+-t(_*oL_ozRAU zel_?;c*83~24U(273~mul;w5ROjvJU+tA2<#C}e77hSRbO7F(n*$j5Zf#eULljr{0 z`WgQ&DOGnTP8W+o7j^n@(rY3mU}xvmB&BFh>}K$@(23O@`xwOf&aS90R+5ij6a0FN za5gbQ(|{x2j9rW9_Y%*!PDhSir;zfOssYBgk*3Rnu$%}?;a@`KqengXZ<`}0j zS66kexfon?b`)?g!|O>+QWqMq{z$)uT#jjS3CB^w__duLoE}W_cb5oQn(dYUMV-TX z-X&>NClf4t?^|ihNTVpnhy--iOU6+~+b*uD@8L@7ALxja;!2C&%cyF(GsnI9+>7wN5$T|+D)q~xei!c1 z)Jf3&_lxDfuVQC1Jdwxlc=DKZk}=caiP@ISMy>D4=lsd}Qx#+N@(y3MeI*~e$avn83kAgSZ#Hps*e%H%g zFRx0%I=!68Ieu)kDd$nnqx#@cmYeK|ay_aaJ&Jm5%XD)dxDqlh}H6k|h4( zJj!`g;!&d+hn6L{uIL>#d%MaayBOB##3(Isyg13T$rDAtM(PIJBDL=a-zw(Hj1qod zDhGI9HFh4ooyV+^+|#yh3{8=d+|#~Lv zY`q;zf2x?D_%xir&M=?FW`0_>i*g z*!y~g`fBoL#!V3AHEOX_9W|BMN%FBG ztY{cd!)ny|(*xiu?L2;#S<2<&Zb1uO1rBw0CwiCJ8rJ>5GQ|=`m^b-9bhUK?C?XE( zfie@`K#{F{tlywGW|A3JQzlyMZa`NAI>WGzBdUKyJrP#SqHd}m&(&fMU3-~<_R{~U zW2-#Y$z%8Wxkvtb7C7@ygMSZxKlq*a*SWp7Wo^~|iqiavXb&26J+-JcxSl#~rxN2cRlCW0kHw0} zx6Hja_AbmBMV?dpWssb zY(;fR#mZ-FWAy(^$)R-WTydf6qRon>a%b#XuXW{5LM2tgtd;g*hBj72vCkP3gQ;1e zo-0+ous?PLwmJPxv8MT--N7H0*mAhLMYmYTNh^9zoDXY$B62K0&gGWNtynGL=wF4)$AFIUc;NY3H!i9SGR=d!@}T-x;|?fN-;vw`ucnWq>|hj$Ji z8Bt{HWTcS> zTU~_&Gyif?cC!<`IPn>?4}b0IUqrXfi8X&T?>4`h`Sj=@lswM4d)ey5+PUk4uw&UP zN95u_l9lz5^vNe|9ZHey&EFm0RP0`AJxlpU(2Dq+cl8-fYF~wCk^A^F;lAmP@Lv?yc^#t-xnGLSC>1i{+fDY)w>D?#ff8vwPtRi-|uA z_>MG0H*lgBOaezkw$n1ovN~k&mb}GxblRu7axZXQi(A~$DO)cIsK<3YxIpSMHZEJH zS!z;=ZZ+xX`}HxlT>lcj{qus9bRQFzACXPIiK8G5;Dk^OKcg|xw|YKa@wFZ!In2^w zrKeg0{2!NADqoj0qP)31MVqkbX^2)=@s7A>*0&yZ#JTWn+GH%cA0R!ze zZ;=_rPqZYz37_Mvum>_H^@ZFMT`cQe?}D6ZdiS~_f*avZk=4j->}j`0jRru(PQV9V zhi&QC&lDHk5SNE%Y=rOYc;xx#`W88Bi>X$1{zt*<+FT)@%e#t5b-u+3<;OR2bPJ1? zBgZ+>sp-Z?Cz?-9Upzi%nJ7xxQXb z(!302r?_voFW+w6nZJ;3X`S?b9!iG0*ZzAv`SlX|p1l0@2PxKEaDH`Jtmscjo#<6H z|7kquqmV6orFY}m#10{27mL+Aka%M)&#~A)FJ=QLpXeu9jQC5Ft$U((#_oC_)T{54 zxCEKL(T^1;Gx3i4_5M4o=$s?hb~Nc7ZOR{X23Yo-d6eSR>%ckOX8hbaado^Qo?e|t zU|m#-hbPqtaHgi)H`$Bs2bg?PQeT-XZD;z8&J!I9E*dEnON|WqHhGX&gLfrfK$iNc zIDX>NF6sM{^wx3NdLdh%`g7@2Sd-P~;`!beA2uECVWYgyJ722qMk})XeET}z91mal znlgV+r*2gW&(dG3cd^IA@&fIysgl>x<>)G-X}i+nyB=L; zJH#v_YAw&Dv&3j6xOH?b7P=#Z32AXXXm&&zQ#%n(AQ|{lVT;>_$c?7i$R%JP`NC z%%c<1WYM-)9&J+$DCL#k&TNKt=_1c%lf+4x)_Ke{m*|n9!wxoco1c}u29JKP3l-pE zUY_w6A$CYVyLevK@5uOcbEGuSb4p$;nX|?GNB5idB9DUi z6w{*9z`5oPQf1@kdsEIAPfxv_mYbi%`69D_^YaareY2c1o=?B#<>`Zd&qTE`+re$2 z{%xVE_a#-jH&@4z?@M3U=8RwOKbiVT-VvAb{*(P9817GuDe7;jekvk3>J`KktP6Tm zjLI?Vm_3u8cs@gUT8@t-#J1&vMM;|w3Ug0!N2e{Kv zJwT)ozkRW9Or$A1M3(E+OjUV4gqN~F{-M2oRGrN`S)1$1i1|^!U(0{|V(?OrpXu>C zJ-b!lpYA8@%TE~lwON#g%IhNcv^-hY=)6l?vR$XovYI@G(ax2~>V6z@6q>DOucn-? zM`*4lwkf~xd&v*$nv`UR*@%63C#ivry}35zeLm1`L^L*JucBWU^=+3IeaVYy&p#*P zo2NO@gU33!QSayoIFOF?y((X3Z(rwd<5g*i{l$t;t|JilT??E+43SP@P*IcKkB86B zbfvN1d~S3po#~37>1R7rd|XTrpc`M4kK~c4*?c5s)5CqwC2#4$O>Z-+FUMC*M#q6( zv$}JO|I?L>T538iS`EBS3nC`~9?tZiG@XhSN8dp=Pc%oHN0qK;Cwh2NF+W<-Zg+&| z&8yjSXq--)7JKSLN3$M+C-VLvmAu=l z?0FHpS6nEY_C%}F@eqGOoIppvVcUzY7R?bkI$uDybmwKZn9ZZ34+h*M?(k831a1KB zU+9`Ed3MK5rYm;tjeMl-4kFJ=_i6o*nN~(kIQ`4SC9B-(+iAG(&?Gc@kX94c1xX#V^>wy=9{47^Lzczy}(t|m3&U?1!VL3 zoWJGpH@a!rM9!v4hFl%yOrFpb)rs~dY4$X_xG^r=IVt8LCZ2D6%Q`l*BGFnYk!u!%+#MSY+ zUr2)0Jsbe?Rlyvg&!7US)%0rl%=e<^+cAlM+=iiO9=)-4}<`b74_hHQ&nojTnq z*Rp)B$W_mGS=V>grJ|~s69ENU#b>wl4dX*8&L+K= z#Vhk<;E8-WO!>e|fGSsrWh;I!*5F5^Zg!A|mhW62{+ zjtbgE`?Pn@?@w{5qE{H6a5{|4Gdaj6$(b)W`m39|Nk^|U-tRBdH#iA4lg$(P0x?Xh zYwaT^^-xlXp0N03zt$uCBF@!wxjH$dslLOytsjY(jLsaZ*2j(OzW9y4eY~=p`mt;u z;v(_fA4(ehIpihON3oc(@JRZ1jA9WdHlMr2qwf?Zn3wk`N+7eo(7G?f94~YULrAfM zzkBn?opaSIOokp=0>u0dL*$;n--;{p?Is;9R&O-r9}}&{6Inc6rlYbtk(4+$w0+NG z<(fn5WFEvjcB;>%`YnCq%p`drEpB@l`6N3N+TB)6G`)JLeEv;!ogjN;FPwg)X0*HZ z-UMyu-L@AIUE**aNhA+pBumi9`%6-P#2$cR-ZX7MFdBu;Q&TpcT~Ikwb> zh3fkF$Zc}xt~>-h+L0~+=YKDq&*uHSlb#-t;ru_B|4*Xzy$Ld+dN%3oi4f28>|aY3 z9q69oL|H%e{!cx6;`04-mX$i4?pN;xq^j_xe%^}q-^z#o+%3ErG>R%XJV$^B2#Ldl=ydb}aZgD0d@&*^a0e&RMGVNY}%*GJ3S z4B5)Rs@fbousAu-wfnxb`_X*#*aP?^_*D7c@qk~IvKOoeL|bi9+qpOIgr@&(pmy`; zQ8ltB=}$**`2L0LzVBoQ;F)r6Fe`5GD$w3x;Jarb?q_;h`T58X9?H8ny;msq!>sN> z7Ix$b<=6cDXLbJ1b^iF?$Y&z%Yp4H49D$hQC(&Th2aFxi)`O{yE7a{k?^5D!a3nm$ z`?`bZjqvmD|NNt0J;wUmz4DgX_BiBftOy59ah^65n8%@gPoxBWq=*Ep$S(j&Oggx} zkl*2Ul8COI@)O1B`r!2F0OPYr%+qJph{*V2k9w!~{^>p1cKKc(vdyQZ^E|K5{|f!3 z@B#hcx?Z;LXL723Jj{LVgLjXo>N8RDj;smuA$g_ozP^dsR2o0i_e?~WY)WK!`YU?# z(Ba^$#kd|Fnfl=CZMzZow@v`aYbGwAIAza}vTdS0*%#AmW#cK^2a7uT_0aOk-E8hV zl#7Y_-9QH&M(8zyPJjlB_sJzkzAc~69qN7mzv%4GucI-sZZ39ZA2)A|oNmhqoKC}t z62LTnp5uS=3dYpihuH{8BsuNr5$b6!J@=*6FCo zMaAUaMEVmV1)4htwVka+g2wwCI8)0DUzcV;1%sbw?s?|xiCwHn>o+uQbRgO%W$nYV z!S)CLTm1dh&s;Rn|CzUE{ae=O{IdRE=%leq=_*ULr{{pD%FX`IzgSP1*K32G!&CzF zVgJ35l)lgz7w;n0evl2eBbjdTgO~y0IbgHk0>$q?7X^5VW{-k*0Ut3l)KM@?UXUXT zjP|Fq_^MS#n?vo6*ipFxY(IE`_OJQFm)x`Z_k-W50+$K!RwezlJmnj*XsCl(9`GtZ z&s0^tt?z?aigk29Rz-Dj${XIZOKHjY+P6hLd-9oVt1INPwDjeWwYR3f(X@8MDs+qA z*R{(*(jH6tS~b7rSj9}d@3r1}kjeHc`gpXhtWK0k1n`8q9=@++l|9uJ*a=qo|Fw3a zw=^~3yJ6RTeRl54I`h5{pW#>iPd4CEkOli;N@ct&y7w0Ei+;Y=Z~hP3?Mn9>^UTeH zT~-bf{kP8sf6$e5`M|2M-N`WKPUvjSbL_sP&qxL`@pE>XcUR}H^l1;n_s%_AEY*DV zXL|N@@YCSmUmPm7Lf0m~ce;@CnXTh?6(M#)m%1k6qwUP;4qnFb-=xJXONuQEmCy%Q zfcqmMfDFSAgTDnzq6*cfCve3Zy+${3kopf@b0ts1si+K;{!Opk6Uz5R2m8YLzE;04 z+&t8?J9>U6ATM@V?|R2}xAVK>`S!S3MAR<{SN!XWi1}kU(k2}y=yzy58m>t!&-$h_ zSp;4huZ3&jy+L+d>G|762kDcTlyJHzX6u)E>#|OMdL1Fs#arQu96EUNi4cZV<}^MA zF8ooC>03dbq3IK7DYuE5{*|HG73V#E8)Og>TP$R&-P%^JHl3Bw^Z20?)4n8I^{S|2 zZ>z2fEz$IEzBkT`Zb8UeDuU6Dwu5YhzueQ~i|~yf>-V<4Vcu=N+2XsKHLqpuKsMsP zwcif)uJ+fwEpkxj8r1B4ltszLW0N~YEs1-;+L(0KFS)nx#nH?bE_Y&GmrTT> zJ|6m+kdf5uS(ij6?pdBBvXLuy!gV}eE?ERo*LEH?A0Aai%RKdk!>J?76nIIZ0f>r3a=owm1vETON<(_nki-{XU1#wSUwvUg}*HX5-GWRrD% zuF8~2uXhx?1TT zC7n-xLyY%+jwtD(&_FkT*zKVlkBCNZPj|h4vGt)SE#mJ*iy6(+ZeJ9x8?nUid3yM4 zukjhbMGwL4&kLEiZ*UQj3wm!(RC7^Ipqs|#5zO!9qa?F$lG+!GoGqFMs3@Iy?9s|a z_X$=J!Qb=LTZ+1gM<$Py^}bj>>2dhuxrB?G7=>IEjih=y9&v21u+P^l`ujGcABSvv zsy~amP;sJXwRcN>U#I5IZ(HJ1Xk11IcU4KiypN`;-}|aPp#GDYztA?BNmLOWgq$Ut z=ejibqgEy3=^)eqSw*K^$?R9l(EBoHsn7q+nAuA2em}J?_QRxvi%EiqP9iCZ?a4*P>JIa!>)tJEf zRy{`kDeaCkq!!EOH6WYlq=8I8U$gqsC3QFWTX-IMGWzO&g;yTP`P7ANG+n;p(qRW<9A>;lHN5(fo%jIxN9bUg!g)>xe{K9^f7fTL zy9;W}>CVD#JE2F5>925~U2&U~*c z(Y5Fnb0uAzv$L*3ueFMOLv+Tc`e$?lnBiK7bvZgN1a}d_S90auHY4Qt|`IvCl)vOt8iW zYE?9MOV;%`3WBzhQ;^ZF#8ld|Xe8NW;l&-ZX!pKMSZn>W$So!E^5Pu=%TBxhdVCLS;Bv29k1f5u6s^i6(7B8w<*e&Fi2Y_dKhxK-)3vgMK^a?>~{`q0d&7HBm!EdyYNS z(1|S4hZab0FTDNM2bFs?ufKIdwT^yRHE5OAGZlTDE^OFzSbIFbqn|s9^i}saFX4%v z#~YOHn4Nb^vJbjhbRIb!rofU}FirDT98>eGtoZ!d0MlU#L=jJJ_8d8;98*=CHOW4@ zfg}6Y8lx9ze``Z;WbAnPR5IhY>)C}*B~kF1Ef36B95YzD>UYKH!}v9kQt!f^$q#X& zI~euPAu;jHm_O8fGO3d+e`EF0L6g|KOTXBcG-XbV%ciJD)kEQlNGX$R>SJQgshm^g zb1E|6=nwB&dh}2^pPH6WF);&;s(Esr^C{<3`Fv_kdSj4gV2 zJ=4t87s@5K{DGgZ{ZV8R8CPoaxF((4COQ>Y+mI$pG;zG{9ChLR+yK|dQ^m~m{BY9j z=285thd47GzM~(Le33T3b5F=B=AjYkV~xKmN8|6o504GxIcW9$%09mrsb4Il%(I(^ z1Kb`{oyOv@50V+@2)VtXTr{BUAvKGw5wVP&L96*MAe!Q zg;u%JSTpB-_v!siw_Bp-Ac?LQe-5!hk2L=#iu0)8IB$D9lJJW>4qWMhb0tX&$C63H zQ9VBoOKTyoYDM05=Sz+y!;;lqJXc)qKoxZK=(lOl&*T*=KNmHn)nQDhDUYgZH;%4l zdr`C6Rp%a8ZBbp-?Zw5YwixAz=0p3o5X+_>m*{iqtYp#2W{tm!WY#UN`bHV2_>Q-f z&xu_Tt2wM5m)Rn3RXuSMDs`*xwY%IAn~$+g<0zsWseY!6i`dPBejrsXqd(N=?&ehBR1o#fB3eyjNZCHk z-RVXnk40^_xGueMPNa{Z-*>epN9l(7tkrcL*^cZcWIr6{XS7u=R%7aQ$R<1KYgv}$ z(czP%e`Eg?=ut(t+D+)gyp2+HOJUM^r zhd)^#{$@Xw^QXn)Pn(MS9Ln~jM>*Ns#U8-vwdgUYdcm@|o2Iaovlr{v^v)*ZCo$Zn zEVC{787_pO?a)7Pdl-3-&u)d9DZCpILFB(qmbq>5_i-y$Bj%ImZF?L(_3K{r4t0s! z!3n{%@x_E(t2xN$d}&4ean!6>#mZepA+1XiU2*MEH&1sxZ_U{cABlH-noS;eI{Wcx zqAP*nsjVwP^0%cmV@)VE#>^PO%SNv){$}3DvE~af?P?mYay_<8Zs+gqzPQEk#nnX? zH{=Nhi)3IRZ?1x_5c%qpVRw#|hCGaNT+w3kb*s8o;Bz?YYa*ww$7x1ij1w|H6g{^u zR9Aa(_oM4pS9MP9oVuP<6E89AtuIs)!F!>f*wedfba0#pHR*1KG(69B)}Z5SPyDj*u$M)0$`mpHvGTEllq_%`Av3d4z7` zBoCV_hHt=TbH5M%eLf5G{Ho8F?YC&35ic^Ie?CvlTOSEM`~F2a_u{>byq8f6zE&^(9_kVt$9dF=pKwkob-<$JlHzM$8{yRx>9n$NWcm zBAu_$Et!>$p5A3fUAbp+bw%?XU6AqTEPAIL`T^>uK;!Jad|+XkQGp z=jfv4YsUKr&_3HHo_SX3Vr7+cd98{kx|6PYIv!T7~lXnn1lSYZYS;oyY*s{L; zP4$k>x14WP@mBu3j&D&3wj#^f{W9*CF}_ts3Y=_RzT?y24@pypupydfomFQJr8Ru& z5b>=mb!A51_$aIC{MrL?7CiSq4gNj&{or@<-v344|4o}fl}<9o5OUFwrVgbwZVnoJ z?oj>Qp|UjTuFb@LfUYUKrrfutjBg7Izo}BeHD%Y7tGulIcUk|567K%M;sQ-oRgNvk zRvAlGFTi+g#XLC2mdnJxWuo;`Saq4G+(NhT`}It&NY`qsk$0KsGO?RXG_SX3B|6`V zGO?^Z;h7xmdc7%68r9@S*BL!bsLSh!kSf?mJCzpK&F#@Ud7jVczI0^B>p$%I5Q~uy z(VYq`*3)EVl#l$`c&vY? z!e`{<P}F#7^*9IH&5^8*Yw#)B7>O9`sgcdTVoneY4N@`wLcxE>E6q|SdVGG#rTcQ;?CVZUkv+v zdU_-Z-JU^Ol`>F$8wD*7+Np37s=m1tIq9cx>U8SNla|cE?G_E`gF0VIN}WhzS;wKG zR4dA?n@gAZ_wN+x6q!qrF)O~QQf!>O%tK?2)cjhS?p3Y!dvocr{l1+JoeuNq(Dc?x z>984vIrKNFzSlM9peS+sw<9P06i%B?o4K?}t!tMYp6O{bPs-k08JU0oPLEEH`SjS$ z$CKt76lGD-`g3_X&W{U!ar$)n%%x8<54y<9e&{n_=G;t8Hvj&eCY>hpY4ZMOq{(JY z_~z=b?f308=rov1gK@@lQx4TQU#m?pNYZg5WCk49`b=GmbuG64o`%P&ySpv@6#K{| zc&KP)7cF)c)S9mox=wpO%%@rsm!#|Gr7A`bRm*WE^nTE9n>?N}mZ&J*QCxUWzj?<8 z{XPv7%FmT$Ww}{PgP--h=ic3<+&lUZ+9bl8AWz#h#hXc)?f2~(?qX=Tuf+ApLp=%= znefV#6KnNw)ZHg7)TYIgqZ+eenFvnZNn3_@nlx**_-(5*OX@#Bqgt?7Ph)-KHe^dN=`(t*Vn4a~{3~5gR_<<)&_!+H zaj1=6(f{@J_GRRkcN|^$S+MVRWzCuNM)q4yaP$~;rHJy*@w3ub4vVsVN2ij-ppsZK zIeuztk^!dOycA8K`9|$#btntkH0)xJMXRB#U)i-A*KYdLZoZR7{_MJE=$l||MfBbm zdb?!?#C1%2`A$*>KOWrsC}<#Exwct=cF$3JsaxGS^kQ)6W&~SQpn3WUI!V>XQAY5V z^dYpR3+egHr81v1-2PZUms9HHWjyEY7GUf8dJr}W1YCzJWFb^fs{ruABj zapUQwesOJ=`!4tU#`vpX!ySfhu4jc_Am!(yKJaK5VVxY;)%6_9j^)pdW%?swcjfvj zqDQ-m<#*Bv_M{aMS?Q|RS|_LHqQ;$|As@(s99`En+QralP5&t4EcIzBKOcGXlhEt( zfe_t{=c1l#o`&jrao*=?@MIJ-bNWPg+b*u(&Rf4VG_1?IF_$V$T#%%&{n{JSU@p7!VYmQtUw#%zKrXACDn8ssk zlbhCBG~OIk6PX}3u@yYJaBh6O`^p-l`_!d&=#sQ^T6GSnmR zc<`g}^;F0@6{bE)KPP7x6n5vfWpp*|;K-TO!!QkU9u9NE?zKqkJl zE~BSy7e~-65oERtH41s2`aI;o5#EZ!AlG(->>~#?^LgM4B%?46)DqtS0@-bVjYs%am` z&phz6Hh3H)RbSe1G5-4^OmV=Hw7g(4(fY(t-d>KQ+2e?5VE2TioZN&w8O0og^)Q2( znjfZ+kY_M$bfT;&H}B!NnGbH5blER%tdBps@V`ui* zc_2H=Vx}v4)SpiWe=d32#{>Gj**wY^DeW&ZY6xoE%W*U>9Ib{vHM7A{tRDXmG8d1; ziI8D^#@VZAYTD5;H7TaBi_euWWZ4lv$j5Rn{;;Ra(`w9gd~Km+Nf&)8uS3>|b*jC% zj=$%|!cxvt`^wd5!{j-AfT5<8nVtUXO}DJ`}jV5r{j7% zdY{PDZ|F?l+o|$${yq5p;CG5K{6(01C*DC-vSmJh((B!`s4k@)Z7U-v7vHakSwc(} zuV#5t!(#b-9l zEWHL1fUo((|jtzKdE;jabn)|_uu~Uut%;Z|39WM?3rhl9= zk4Inqukfm)V;22QUWsPl5eI=kpx>dD{V2+A?);as5F$^xFHOyKwS3=Pd9UJ{*h=sU z>P}5#>ym|6UO(0Qja%LIw%O@z{h^E+O}sCYRB4t`P(+!@jo3A;t4&Z|KcQ^g>aLM3 zhDO#W;@4#VVgtAF)nu=f5jl$Z#Q&_qhYqgYZQ}B7Rs1*Y<<#KR(9~bJh}>Ae95Zv< zDwtEGxQx%@nwD|&L6*{v^iwi(r^`+$@02K^c^}t2ZiD85=Lilv*<7hIB1W-i9VGkK zJPdEewVl&Cr=5(`;vcmbQ+pPBj^<&W{g<-u&9W<27I%x3muV|;+qRvY7MvC)r3L!9 zniifPH7Mh=$otJQOf{=d<@vl)E3O2c{4?0*^{Hy4dNm9Y`!05liG(UP2^-CF( z?(DSUv@#j3P|^5UKiJT1ng01QgLtpa5}|7hU6wePyVv?u?7KwGtYy)?lAd+YGxeBU zndV-co}8X0rKeTtU>0?4>%^by54CzIvI@z;Bx@vE>O3Zs@UG;~l^%WQnS$_IO}aZY z<2}D7>G_qPzu8|53vSQMZ}@(jj+BGb@5RS2hS@}igZ~JLRAimXe@_=Xa7sLjtoiA( z=j$x$Zu>h{Z-bwIRakv6^z-+@Q@4UfV)a4vdZdcyQrJBgKKr(9+xKz|-WCk54m-&7 zfkCtEEGLvWCs|d@EBkP*BlnHO+#MZ{w*`-DL-{=~JWf;fRwuV}P?`31Y|bB>Uk7Pv zy@z9FDLNzFY?^h)HStR#mSiPwg(-YX(g#oUj~w#U*KtX?$d+k*A}M^Ne=+N0DR4$U zv(=xWLsFS$`KRQgUua#rLQ>C1rt*m%UFi|^X-j&ZEPGx7Lrc;@Ef0N3Qwa~X&d1^N zJ9_QT;E%edygwPPxruV$|I_qRSf~fj-1UZ7DFOW?4$q@8l=;LpS z|2hw%u5-F7)7w;AV-?(S8ep2!PIWUrX?h8tT~}SL4y9O|29-SD?JF0%HFnkE$JK0) zu8Yj^s_pLv--~nL8%;I-?RBnSE~fG7<@ui{LG^3tI&`MIxYo`3#CF~4j_}2ZzkDa^ zy&O&o|5?uBr@DKK zfKok2h2_cMudSc)?~+n=cj9!h7<5sm4=24QQUZ2%UQJSp=EQCWKMS2$-La2Btnci~ z;fs~zcy^`s^#X&a;VVjT@wf23bSF2^*vgySe-{Mt?rP7fye zyGsNt&GyRwqRwGG?~*jCqX*00`&QaA(kRL?to*Ke$vDbr+r>4N*+raUXC!nJ%Ng1V zRvyt5t1YI&9-X7P&fcO?#VFG+vKsL#r#R9b^*SP&gKk4i$85<1Sz;C+ioI-z3ULK1 z9LaA=6v^JhmDE4b5hul!7QL5I)pBQ!d-b^&;d>*}K~+`imr4CD+@qE=AjD{jt0lxlk<(lVJI^(e$&oJTp2N<3;b zr1t&bTg6xxa4Zp^|s@mvd4K`^JGSRMJDvY?*ndE>}dTR?6UIVq7BGnettqPluJ zmi|;RKk;ceft_JKi_QGBY!~YYeN@PPvAJJ6s?wk{KfCT}rzQRVEv)iM&si@w>ah3q z3iZ|G&y1TO%4^hOr#fmXB*?cR`-bXms|mMy@3;;*Iq9CLz&=*xmsM48KV)ms@9jjZ zm;@nPXGhm?)s>zj<1gLW9laJm54aqibVapBx}GZZ3*9AkgrlF*?v8s{^sFGx;8_1S ziEY)5QR}}m>{><*KiOB?VZs%Z&Xfo6^y3g$$9lxyoNdJQBS{moJ60QGT}+aEtOzSI z=V@4tI)8cqe5HFCKg%rTa&fnyg{}gJy1Ns-%WMtneqfnm2_wv#{2#j7Isp_B2lYUi z32>RzB8mP#iPK467*t7G)&7do z{E27}8gxCis5Q8rI&G&C^M6$BChI*GDILsL zz4bTgahKv%SRu*!^%{gdX$+hN-_Din0psRW$WO*;M!i^e;`V&8awwkg1K9|ci`lFL zFjLw58+U@w6K(us(BGFs*U%kV02}%Qm*QtDs!J+XK4TlB|6fWDrBmmM3tbm&RxFh} zW7m4ED}R!eK$S3SrG1#8jTKSsbH>DAYF4P{N>wlHj~#(+PJdIZY5r$-@P{R~9PVz> zE!J_;ik=ha!i zTse0lIfuU|`T(Jw%L3nXY1fyuoBr&nr?kpSq*lGBIM+(TLv>y_YqylFm!b6dR4gDBcnG%=*@e z32!a-xy^T>hW9*paxWFFrB~`+&@S3E9{Ryrq=D}wes=Vg1L-JC|0O~YYj0zJ*xm3g z*sF)qkI;5Zdqg9OyrN^6fjVIF7mFGp+4dnZl2*bNrs#C^*|=Ym}3!9 z#KZA6*6TT5D=@tUbDR39j{1u%qr!Xu_G&~Bo@)A@wMC2a;t9nlp=-(L1NpPB`j$3C z0!@?uqQ9%F^^s-{89B1`ks7M zRJj*(Njbl|TrBh_V%kN|SF|O`z9Hx9O7F(^jvYeY85S#E%@a{Cv`n^6u?tkpX|vfQ zyz^7Iy6ELP(K}<0s1NEm^+~IcuVz_fceJ+67h$jZ_5M5Di*t@#+tH+Vv?+hk8DQDt z1&C6dJY+bB+l+sa%>A66>pI>LPp{4+ur4aa!;>;yIaAZ^o9spR157>}VbW+s*E9V_ z=ZOvl&yAFdrAD4-n>@&?Ew~adAlLF#96xy^UDEd@>8)?5b@a4u%jeRmuqLa|#q+%{ zK5RPN!$x_ZcfQn)jMf$S`Sx|bIW|}Mnldj)r*65nM9=HBJom#amLH~!96tMK@6hr! zb?AC5d4YDBE z`;^N(V?2)_V;HN2->%_5kB=SJ}sdunSvRLCw zgk@dcovUH(Z!r(V{W0_C=P_Bd?UhH{6a%929KYn1>Qk^uiS?dee=~;gp9cRP{C@B| z#fom8M}1wo$aCFyocCt^b4+uI9vS*XU^BP*S;=eg==Zu%0WRj{8GjLChje9%=Ve_x zj88X5O7lFYlo?=?m%bsfn{GlY4pYKgM zUpzhac3N(J66cG|{>{%<6|9z1*T2R*DgBz4rw{r)6V=9S2e*a#KiR7HC1t%dS4NWW zOLxQOj9>4fmb#+d5ts5VYW*V^?oVz^)ZbG5R77ypD~KtO%j}r-&VRQvzb{I{glOy1 z^+338(xvk1F31N?GS;JHlSRpzb);nbypVN^Pa&S6P5!}}X*)spFm?AZ#VYfnl(39w ztB89qA3!>z(QZdjka+_8$fNZNr|$K%uXIvW4-hHDZ$GYc5gsDTb!w)nJRibKSs?$= zUO%eN=AEp~b!AijsNb*UKYlTIsmIUs_?@2JD)3MD6ZYjNj9rH;N<%GNk$YO6Y*W^e zTOn?RoC!7OGxU(;a3!+3ABP-;W(<DW~gnm#c|w$}jw0@`J81N7wq;!=a7Xhj)@1 zSb9AnuQud;KG1H5dTvAZD*AO%-*$P?m%Nzv{Bt6{d71+~c&zuXBY+s&fpnzrrMcY< zZoDcjvAeU+(TaAvBRp?j&E9U?lYvR6gZ8YfJQ;001W)AsK`ME-3pCc* zL}|)B?#z3$uHB%!aTD__T0;(AJ=Ig+PKA5yd0~``)^#O{+Z9cb^O5E#(f8T*qBliz zM2^lE&@JbK>HWE=1P{txcN%F3Z)+3QKsI&C`CATuqnnn^m20YG$kk!aWiv3zlCPEQ@mhES0J%s6@-=@3dMd;w5@U!BHdYo>u zI9-k%Q9QS1KhR|7RqvGDlVy5(a}pU8gLkZrN6Q>Q!ST9(fhx$5~Y>-x^RR8$pnBA`HvoWGF8$ZEw` z`2kOO36FUC5fO`U~h`#^#5+#gCB{8`#{Q-&%t z79L6ej!`V)#O8Cic=VmZ9NzLCMG0iq7h3manB#>mVF)R9@ON)My?3i}rs@^m&?J!rLby8Bv{)rq9s8EO^ndmby-99k#yAl|W4eJ<5+ z=^JMz$pd}g+r!8w*_lLCwiOdiuO2F&e^Xs2$R61Xryr>q?XJByLECw^?L|bFc-*1R z8vOl7_g17~*V>!4c4`Dtwzo~wfYwIozWCb9@SP{E5-p#1#mSENuWb5Sn_b;@{>?l6 zD3n^>0qC=->p$o>h=^S*kTsUKv{&-2I5PS|Myxt9Eia)Waf18e>R201`6*;73qocQ~3|hu94yVKbQaKbA|S^$2T+2vwtmqb|ASGC(8P% z_kZfq6PNFwv#iwVbiaBpAXSAg_48J=|5iTy=WgN6ATu$YdVGX-b}SBi6zC1T-#Sd7 z2W;v*q68qnw0&Aq8~@MW^~Z207xLde4|jO#bwmX%Z(^PC z34bQ{nTAG}6Heawe*foOjK&n_^Q6ZcqC9v)I`y0mSM4WGKOklSrS;MB=%vamrf;PK z!hQVA`#*E&shmv~djOvVpDN!wG0&?~_JZ|*Xsa!1JNM?D(Dc6z)NcMfd{29l{&e() z?_bC+_)c~Jo+(F0|Hv?I@G8*WVc@%G@*BinmF4FnKX@oBZ+fp#-5z9NN1jmL-_L(m z=l@*ikME6qCgQ%PrN0qJAm;c+5+34x-9hvg`1$vL z{(QGY)ncr_-79aIZI45)#!5?^rws+>ZED{WDM248A^|JnPN2l3gX;_V^=s8AP*X(L zPWeg5Flg&doOjCOvq;R-XVr+v_+pQGr}zHpJ=%8pULSI@rls>duh0Jq{iW~${ouM@ zw(njZ$j#>zi&%AO%*+eCXJaP>9hxiPvXdA%V`46W@S!}BFRdoDeaC|*EaE#8IYsK31x9L4NFN6;G(7988HudMnZ@c+$h~`YM*_&op_S3}pee~j> za}v*`GsXH43BNiLW`=~SHz#`9QkJgu0wga7i#q!C(DKRMZ0mtC z3D98iKDp$`x8?J>L%rnqC1{5+8WZd0HYCT<8pq8WBd6Ok0;kh3qC~0gv@S$|iIbss zXLpzj1WKdFXV*E$%s5AxkGIDG$O@W|3<1S-;c=f(|30C54$&MPG*0ljbxW!>EmlCr zsNzvO1I+&9y*nnnhjZ3Hk)N!!N}nkQ7*>Mq{t!T%P2KlL*g4fKEJ zIho({&H8_#lg28gdn9pb&jC-BJ^i15v7R!o*9JX@sRZc5{(B)QeW5ch-bM5skqx#Z znQrlem;vHBV6)%?#qU2C1$c^PkAimrA2Bo3Q7}thkRuC>_NTM>Ow&texjEGCMk~Pf zgBNK3nm>5Y>e-$@{`p_Uck#2}``J|HZTY!63oLd!BeS4fB}81%B0qFj-W1QE_WypU z4v&@K<06e{_;>=z0JbjBbgH)+P^T`cvD7zji|$)3gvePg{oqa+1kRL^rWr%H6i_D7u0FvTZM0{Vd$A^|Gd? zG@s#IYx6lRUh*kK_KD$->!V+9G4YA`rpTJ6;{JWOf4-5})B0}MflqZ6o~3Vi)Vk6g z6?=FiiOI`M6MkZ^ZN5A1*m}1eYd5-rFNLegd$4Ry;==!}sI+%duF_N&I>Y8R?^}%3 zROyT!8)WX&V}q=a6aB+a#UeX=#Zzc2;SyJv8)PtV@B;OS-|1|jh;CI8TxNb?YjmVYK>s%A-&A9?@ bai({}FP8Le-qUv(|6fyF;*lz$dC&g`*1tq} literal 0 HcmV?d00001 diff --git a/build_log7.txt b/build_log7.txt new file mode 100644 index 0000000000000000000000000000000000000000..886a5b25bbb6e4cc8060e036664301486c46d759 GIT binary patch literal 355900 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 literal 0 HcmV?d00001 diff --git a/extracted_func_utf8.txt b/extracted_func_utf8.txt new file mode 100644 index 0000000000..2d6c02c298 --- /dev/null +++ b/extracted_func_utf8.txt @@ -0,0 +1,13 @@ +3289: void CECGameUIMan::AddChatMessage(const ACHAR *pszMsg, +3290: char cChannel, int idPlayer, const ACHAR *pszPlayer, char byFlag, char cEmotion, CECIvtrItem *pItem,const ACHAR *pszMsgOrigion) +3291: { +3292: auto_delete tmp(pItem); +3293: +3294: bool bIsKing = false; +3295: if( cChannel == GP_CHAT_COUNTRY && (cEmotion & 0x80) ) +3296: { +3297: cEmotion &= ~0x80; +3298: bIsKing = true; +3299: } +3300: +Error: 'charmap' codec can't encode characters in position 10-13: character maps to diff --git a/extracted_func_v2.txt b/extracted_func_v2.txt new file mode 100644 index 0000000000..887c736677 --- /dev/null +++ b/extracted_func_v2.txt @@ -0,0 +1,212 @@ +3289: void CECGameUIMan::AddChatMessage(const ACHAR *pszMsg, +3290: char cChannel, int idPlayer, const ACHAR *pszPlayer, char byFlag, char cEmotion, CECIvtrItem *pItem,const ACHAR *pszMsgOrigion) +3291: { +3292: auto_delete tmp(pItem); +3293: +3294: bool bIsKing = false; +3295: if( cChannel == GP_CHAT_COUNTRY && (cEmotion & 0x80) ) +3296: { +3297: cEmotion &= ~0x80; +3298: bIsKing = true; +3299: } +3300: +3301: // 修正表情 +3302: ACString strModified = FilterEmotionSet(pszMsg, cEmotion); +3303: +3304: // 修正给GM的额外信息 +3305: ACString GM_MsgShow; +3306: ACHAR GM_MsgType(0); +3307: bool GM_MsgValid(false); +3308: if (g_pGame->GetGameRun()->GetHostPlayer()->IsGM() && +3309: GP_CHAT_WHISPER == cChannel) +3310: { +3311: int nSymbolPos = strModified.Find(GM_HELP_MSG_TYPE_BASE); +3312: if (nSymbolPos >= 0 && nSymbolPos+1 < strModified.GetLength()) +3313: { +3314: ACHAR type = strModified[nSymbolPos + 1]; +3315: if (TheGMHelpMsgArray::Instance()->FindByType(type, NULL)) +3316: { +3317: GM_MsgValid = true; +3318: GM_MsgType = type; +3319: GM_MsgShow = strModified; +3320: GM_MsgShow.CutLeft(nSymbolPos + 2); +3321: +3322: strModified = strModified.Left(nSymbolPos) + strModified.Right(strModified.GetLength() - (nSymbolPos+2)); +3323: } +3324: } +3325: } +3326: +3327: // 考虑本地化对某些内容不显示的要求,当ingame.stf找不到相关字符串、并导致此处字符中为空时,隐藏显示 +3328: if (strModified.IsEmpty()) +3329: return; +3330: +3331: // 标明来自GT频道的消息 +3332: if (byFlag == CHANNEL_GAMETALK) +3333: strModified += GetStringFromTable(9312); +3334: +3335: pszMsg = strModified; +3336: +3337: if( PlayerIsBlack(idPlayer) ) +3338: return; +3339: if( cChannel == GP_CHAT_SYSTEM && a_stricmp(pszMsg, GetStringFromTable(809)) == 0 ) +3340: return; +3341: +3342: if( byFlag == CHANNEL_FRIEND || byFlag == CHANNEL_FRIEND_RE || byFlag == CHANNEL_GAMETALK) +3343: { +3344: AddFriendMessage(pszMsg, idPlayer, pszPlayer, byFlag, cEmotion, pItem ? pItem->Clone() : NULL,pszMsgOrigion); +3345: return; +3346: } +3347: else if( byFlag == CHANNEL_USERINFO ) +3348: { +3349: if( pszMsg[0] == 'R' ) +3350: { +3351: CECFriendMan *pMan = g_pGame->GetGameRun()->GetHostPlayer()->GetFriendMan(); +3352: if (!pMan) +3353: return; +3354: if( pMan->GetFriendByID(idPlayer) ) +3355: FriendAction(idPlayer, -1, CDlgFriendList::FRIEND_ACTION_INFO_REFRESH, 0); +3356: } +3357: else if( pszMsg[0] == 'L' ) +3358: FriendAction(idPlayer, -1, CDlgFriendList::FRIEND_ACTION_INFO_LEVEL, a_atoi(pszMsg + 1)); +3359: else if( pszMsg[0] == 'A' ) +3360: FriendAction(idPlayer, -1, CDlgFriendList::FRIEND_ACTION_INFO_AREA, a_atoi(pszMsg + 1)); +3361: return; +3362: } +3363: +3364: ACHAR *pszText; +3365: CDlgChat::CHAT_MSG msg; +3366: ACHAR szText[512], szMsg[512]; +3367: CDlgChat *pChat1 = m_pDlgChat; +3368: CDlgChat *pChat2 = m_pDlgChat2; +3369: CDlgChat *pChat3 = m_pDlgChat3; +3370: EC_GAME_SETTING gs = g_pGame->GetConfigs()->GetGameSettings(); +3371: PAUITEXTAREA pShow1 = dynamic_cast(pChat1->GetDlgItem("Txt_Content")); +3372: PAUITEXTAREA pShow2 = dynamic_cast(pChat2->GetDlgItem("Txt_Content")); +3373: PAUITEXTAREA pShow3 = dynamic_cast(pChat3->GetDlgItem("Txt_Content")); +3374: abase::vector &vecChatMsg = m_pDlgChat->GetAllChatMsgs(); +3375: abase::vector &superFarCryMsg = m_pDlgChat->GetSuperFarCryMsgs(); +3376: abase::vector &whisperMsg = m_pDlgChat->GetWhisperChatMsgs(); +3377: +3378: msg.idPlayer = idPlayer; +3379: msg.strMsg = pszMsg; +3380: msg.cChannel = cChannel; +3381: msg.cEmotion = cEmotion; +3382: msg.strMsgOrigion = pszMsgOrigion; +3383: if( cChannel == GP_CHAT_LOCAL || +3384: cChannel == GP_CHAT_FARCRY || +3385: cChannel == GP_CHAT_TEAM || +3386: cChannel == GP_CHAT_FACTION || +3387: cChannel == GP_CHAT_WHISPER || +3388: cChannel == GP_CHAT_TRADE || +3389: cChannel == GP_CHAT_SUPERFARCRY || +3390: cChannel == GP_CHAT_BATTLE || +3391: cChannel == GP_CHAT_COUNTRY) +3392: { +3393: if (ISPLAYERID(idPlayer)){ +3394: g_pGame->GetGameRun()->GetUIManager()->FilterBadWords(msg.strMsg); +3395: } +3396: } +3397: +3398: int nMsgLen = a_strlen(pszMsg); +3399: // Booth Message +3400: if( cChannel == GP_CHAT_WHISPER && +3401: pszMsg[nMsgLen - 2] == '!' && +3402: pszMsg[nMsgLen - 1] == '#' ) +3403: { +3404: if( m_pDlgBooth1->IsShow() ) +3405: m_pDlgBooth1->AddBoothMessage(pszMsg); +3406: return; +3407: } +3408: +3409: ACString strName; +3410: A3DCOLOR clrName; +3411: TransformNameColor(pItem, strName, clrName); +3412: AWString msgWithColor = _AL(""); +3413: if( ISNPCID(idPlayer) ) +3414: msgWithColor += _AL("^C8FF64"); +3415: else +3416: { +3417: if( cChannel == GP_CHAT_COUNTRY && bIsKing ) +3418: msgWithColor += CDlgChat::m_pszKingColor; +3419: else +3420: msgWithColor += CDlgChat::GetChatColor(cChannel, idPlayer); +3421: } +3422: +3423: msgWithColor += GetChatChannelImage(cChannel); +3424: if( cChannel == GP_CHAT_COUNTRY && bIsKing ) +3425: msgWithColor += GetStringFromTable(10310); +3426: msgWithColor += msg.strMsg; +3427: +3428: if( cChannel == GP_CHAT_WHISPER && +3429: (int)m_pDlgChatWhisper->GetData() == idPlayer ) +3430: { +3431: pShow1 = dynamic_cast(m_pDlgChatWhisper->GetDlgItem("Txt_Chat")); +3432: pszText = (ACHAR *)pShow1->GetText(); +3433: +3434: if( a_strlen(pShow1->GetText()) > 0 ) +3435: pShow1->AppendText(_AL("\r")); +3436: +3437: pShow1->AppendText(msgWithColor, pItem ? CDlgChat::m_nMsgIndex : 0, strName, clrName); +3438: +3439: if( !m_pDlgChat->IsLocked() ) +3440: { +3441: pShow1->ScrollToTop(); +3442: pShow1->ScrollToBottom(); +3443: } +3444: if( !m_pDlgChatWhisper->IsShow() ) +3445: m_pMiniBarMgr->FlashDialog(m_pDlgChatWhisper); +3446: +3447: // 将可点击的消息,保存起来供点击查询 +3448: if (pItem) +3449: { +3450: CDlgChat::LINKED_MSG linkedMsg; +3451: linkedMsg.pItem = pItem; +3452: tmp._ptr = NULL; +3453: linkedMsg.nMsgIndex = CDlgChat::m_nMsgIndex++; +3454: +3455: whisperMsg.push_back(linkedMsg); +3456: if( (int)whisperMsg.size() >= CECGAMEUIMAN_MAX_MSGS ) +3457: { +3458: CDlgChat::LINKED_MSG &msgDelete = *(whisperMsg.begin()); +3459: delete msgDelete.pItem; +3460: msgDelete.pItem = NULL; +3461: whisperMsg.erase(whisperMsg.begin()); +3462: } +3463: } +3464: } +3465: else +3466: { +3467: msg.pItem = pItem; +3468: msg.nMsgIndex = CDlgChat::m_nMsgIndex++; +3469: tmp._ptr = NULL; +3470: msg.strMsg = msgWithColor; +3471: +3472: int nActiveChannelSet = m_pDlgChat->GetActiveChannelSet(); +3473: if( gs.bChannel[nActiveChannelSet][cChannel] ) +3474: { +3475: pszText = (ACHAR *)pShow1->GetText(); +3476: +3477: /* +3478: if( m_pDlgChat->GetMsgCount(1) >= CECGAMEUIMAN_MAX_MSGS ) +3479: { +3480: pch = a_strstr(pszText, _AL("\r")); +3481: if( pch ) +3482: { +3483: ACString strText = pszText; +3484: strText.CutLeft(pch - pszText + 1); +3485: pShow1->SetText(strText); +3486: } +3487: } +3488: */ +3489: +3490: if( glb_IsTextNotEmpty(pShow1)) +3491: pShow1->AppendText(_AL("\r")); +3492: +3493: pShow1->AppendText(msgWithColor, msg.nMsgIndex, strName, clrName); +3494: +3495: if( !pChat1->IsLocked() ) +3496: { +3497: pShow1->ScrollToTop(); +3498: pShow1->ScrollToBottom(); +3499: } +3500: diff --git a/gemini.md b/gemini.md new file mode 100644 index 0000000000..2dd1a6dfed --- /dev/null +++ b/gemini.md @@ -0,0 +1,32 @@ +# Project Context & Tracking (Antigravity/Gemini) + +## 🎯 Project Overview +- **Main Objective:** Porting features (like Task/Chat) from the Perfect World C++ Client to Unity (C#). +- **C++ Source Repository:** `d:\perfect-world-source\perfect-world-source\CElement\CElementClient` +- **Unity Target Repository:** `c:\Unity\CuongNV\perfect-world-unity` + +## 📊 Current Status +- **Active Focus:** Chuyển đổi hệ thống Task/Chat (đang phân tích `DlgTaskTrace.cpp` và `ChatInputHandler.cs`). + +## 🛠️ Trạng thái Công việc (TODOs) +- [x] Phân tích logic của `DlgTaskTrace.cpp` +- [x] Triển khai `HeadChatBubble.cs` cơ bản. +- [ ] Hoàn thiện luồng Chat cơ bản trong `GameSession.cs` (Bỏ qua Task/Policy/Filter). +- [ ] Kiểm thử hiển thị chat trên đầu nhân vật. + +## 📌 Quy ước Code (Guidelines) +*(Ghi chú các quy tắc code, kiến trúc, hoặc lưu ý đặc biệt đối với dự án Unity tại đây)* +- Sử dụng chuẩn C# cho Unity. +- Tối ưu hóa UI/Sự kiện (Events). +- Thêm log debug ở cả 2 dự án theo quy tắc: `[Cuong] + Tên hàm + Nội dung (nếu có)`. + - Ví dụ C++: `a_LogOutput(1, "[Cuong] Method Tick %s", szTxt);` + - Ví dụ C#: `Debug.Log("[Cuong] Method Tick " + szTxt);` + +## ⚙️ Quy trình Build & Deploy +- **C++ Client (`CElementClient`)**: Sau khi build, cần copy ghi đè các file thay đổi từ thư mục build sang thư mục client. + - **Nguồn**: `D:\perfect-world-source\perfect-world-source\CElement\CBin` + - **Đích**: `D:\perfect-world-source\perfect-world-source\PW_client\client\element` + - *(Đã tạo sẵn script tự động: `D:\perfect-world-source\perfect-world-source\CElement\CopyBuild.bat`)* + +--- +*File này được tạo để theo dõi tiến độ và lưu giữ ngữ cảnh của dự án. File sẽ được cập nhật liên tục khi chúng ta xử lý các task mới.* diff --git a/login_info.txt b/login_info.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..d8681476ef --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "perfect-world-unity", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/read_code.py b/read_code.py new file mode 100644 index 0000000000..e17ec711fb --- /dev/null +++ b/read_code.py @@ -0,0 +1,25 @@ +import os +import sys + +def read_file(path, lines=250): + encodings = ['utf-8', 'gbk', 'utf-16', 'latin1', 'utf-8-sig'] + content = "" + for enc in encodings: + try: + with open(path, 'r', encoding=enc) as f: + content = f.read() + print(f"--- SUCCESS reading {os.path.basename(path)} with encoding {enc} ---") + break + except Exception: + pass + + if content: + lines_list = content.splitlines() + print(f"Total lines: {len(lines_list)}") + print("\n".join(lines_list[:lines])) + print("...\n" if len(lines_list) > lines else "\n") + else: + print(f"FAILED to read {path}\n") + +read_file(r"d:\perfect-world-source\perfect-world-source\CElement\CElementClient\DlgTaskTrace.cpp") +read_file(r"c:\Unity\CuongNV\perfect-world-unity\Assets\Scripts\ChatInputHandler.cs")