Move git root from Client/ to src/ to track all source code: - Client: Game client source (moved to Client/Client/) - Server: Game server source - GameTools: Development tools - CryptoSource: Encryption utilities - database: Database scripts - Script: Game scripts - rylCoder_16.02.2008_src: Legacy coder tools - GMFont, Game: Additional resources 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
432 lines
12 KiB
C++
432 lines
12 KiB
C++
// CharSkillPage.cpp : 구현 파일입니다.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "AdminToolClient.h"
|
|
#include "CharSkillPage.h"
|
|
#include "CharSkillEditDlg.h"
|
|
#include "PacketManager.h"
|
|
#include "GlobalFunctions.h"
|
|
#include "ListCtrlSortClass.h"
|
|
|
|
#include <Log/ServerLog.h>
|
|
#include <Skill/SkillMgr.h>
|
|
#include <Character/ModifyCharacter.h>
|
|
#include <Utility/Math/Math.h>
|
|
|
|
|
|
// CCharSkillPage 대화 상자입니다.
|
|
|
|
IMPLEMENT_DYNAMIC(CCharSkillPage, CPropertyPage)
|
|
CCharSkillPage::CCharSkillPage()
|
|
: CPropertyPage(CCharSkillPage::IDD)
|
|
, m_dwSkillMaxNum(0)
|
|
, m_dwSkillUseNum(0)
|
|
, m_dwDocKey(0)
|
|
, m_ContextType(CONTEXT_MENU_TYPE::NONE)
|
|
, m_EditMode(SKILL_EDIT_MODE::MODE_EMPTY)
|
|
, m_bSortListAsc(false)
|
|
{
|
|
}
|
|
|
|
CCharSkillPage::~CCharSkillPage()
|
|
{
|
|
}
|
|
|
|
void CCharSkillPage::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CPropertyPage::DoDataExchange(pDX);
|
|
|
|
DDX_Control(pDX, IDC_CHARSKILLLIST, m_ctrlCharSkillList);
|
|
DDX_Control(pDX, IDC_SKILL_LIST, m_ctrlWantSkillList);
|
|
DDX_Text(pDX, IDC_SKILL_MAXNUM, m_dwSkillMaxNum);
|
|
DDX_Text(pDX, IDC_SKILL_USENUM, m_dwSkillUseNum);
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CCharSkillPage, CPropertyPage)
|
|
ON_WM_CONTEXTMENU()
|
|
ON_COMMAND(ID_SKILL_INSERT, OnSkillAddCM)
|
|
ON_COMMAND(ID_CHARSKILLEDITCM, OnSkillEditCM)
|
|
ON_COMMAND(ID_CHARSKILLDELCM, OnSkillDelCM)
|
|
ON_NOTIFY(NM_DBLCLK, IDC_CHARSKILLLIST, OnNMDblclkCharskilllist)
|
|
ON_LBN_DBLCLK(IDC_SKILL_LIST, OnLbnDblclkSkillList)
|
|
ON_BN_CLICKED(IDC_SKILLPAGE_CLIPSKILL_BTN, OnBnClickedSkillpageClipskillBtn)
|
|
ON_NOTIFY(LVN_COLUMNCLICK, IDC_CHARSKILLLIST, OnLvnColumnclickCharskilllist)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
// CCharSkillPage 메시지 처리기입니다.
|
|
|
|
BOOL CCharSkillPage::OnInitDialog()
|
|
{
|
|
CPropertyPage::OnInitDialog();
|
|
|
|
m_ctrlCharSkillList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_FLATSB);
|
|
|
|
m_ctrlCharSkillList.InsertColumn(0, GetLocalString("SLOT_NUM"), LVCFMT_LEFT, 40);
|
|
m_ctrlCharSkillList.InsertColumn(1, GetLocalString("SKILL_ID"), LVCFMT_LEFT, 75);
|
|
m_ctrlCharSkillList.InsertColumn(2, GetLocalString("SKILL_NAME"), LVCFMT_LEFT, 140);
|
|
m_ctrlCharSkillList.InsertColumn(3, GetLocalString("SKILL_TYPE"), LVCFMT_LEFT, 80);
|
|
m_ctrlCharSkillList.InsertColumn(4, _T("LV"), LVCFMT_LEFT, 65);
|
|
|
|
SetUIString(this->m_hWnd, IDC_SKILLPAGE_01, "IDC_SKILLPAGE_01");
|
|
SetUIString(this->m_hWnd, IDC_SKILLPAGE_02, "IDC_SKILLPAGE_02");
|
|
SetUIString(this->m_hWnd, IDC_SKILLPAGE_03, "IDC_SKILLPAGE_03");
|
|
SetUIString(this->m_hWnd, IDC_SKILLPAGE_04, "IDC_SKILLPAGE_04");
|
|
SetUIString(this->m_hWnd, IDC_SKILLPAGE_CLIPSKILL_BTN, "IDC_SKILLPAGE_CLIPSKILL_BTN");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
bool CCharSkillPage::SetSkillPageData(unsigned int dwDocKey, CModifyCharacter* lpModifyCharacter)
|
|
{
|
|
if((0 != dwDocKey) && (NULL != lpModifyCharacter))
|
|
{
|
|
m_lpSkillPageInfo = lpModifyCharacter;
|
|
m_dwDocKey = dwDocKey;
|
|
|
|
lpModifyCharacter->SetClientKey(dwDocKey);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CCharSkillPage::SetSkillPage(CModifyCharacter* lpModifyCharacter)
|
|
{
|
|
m_ctrlCharSkillList.DeleteAllItems();
|
|
m_ctrlWantSkillList.ResetContent();
|
|
|
|
if(NULL == lpModifyCharacter)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetSkill(lpModifyCharacter->GetSkill());
|
|
SetAcquirableSkillList();
|
|
m_dwSkillMaxNum = lpModifyCharacter->GetMaxSkillPoint();
|
|
m_dwSkillUseNum = lpModifyCharacter->GetSkillPoint();
|
|
|
|
UpdateData(false);
|
|
}
|
|
|
|
void CCharSkillPage::SetSkill(SKILL& Skill)
|
|
{
|
|
for(int nSlotIndex = 0; nSlotIndex < Skill.wSlotNum; ++nSlotIndex)
|
|
{
|
|
SKILLSLOT& Slot = Skill.SSlot[nSlotIndex];
|
|
|
|
const Skill::ProtoType* lpPrototype =
|
|
CSkillMgr::GetInstance().GetSkillProtoType(Slot.SKILLINFO.wSkill);
|
|
|
|
if(NULL != lpPrototype) // 버그로 인해 빈 슬롯이 있을 수 있음;
|
|
{
|
|
unsigned char cSkillLockCount = Slot.SKILLINFO.cLockCount;
|
|
const Skill::ProtoType ProtoType = lpPrototype[cSkillLockCount];
|
|
|
|
CString strForFormat;
|
|
int nRow = m_ctrlCharSkillList.GetItemCount();
|
|
|
|
strForFormat.Format(_T("%d"), nSlotIndex); // 슬롯 번호
|
|
m_ctrlCharSkillList.InsertItem(nRow, strForFormat);
|
|
|
|
strForFormat.Format(_T("0x%x %d"), ProtoType.m_usSkill_ID, cSkillLockCount); // 스킬 ID, 락 카운트
|
|
m_ctrlCharSkillList.SetItemText(nRow, 1, strForFormat);
|
|
|
|
m_ctrlCharSkillList.SetItemText(nRow, 2, CONV_CLISTR(ProtoType.m_SpriteInfo.m_szName)); // 스킬 명
|
|
|
|
m_ctrlCharSkillList.SetItemText(nRow, 3, GetSkillTypeString(ProtoType.m_eSkillType)); // 스킬 타입
|
|
|
|
strForFormat.Format(_T("%d"), ((int) Slot.SKILLINFO.cSkillLevel)); // 스킬 레벨
|
|
m_ctrlCharSkillList.SetItemText(nRow, 4, strForFormat);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
ERRLOG3(g_Log, "스킬의 프로토타입이 존재하지 않습니다. CID: %u, 스킬ID: 0x%x, 슬롯: %d",
|
|
m_lpSkillPageInfo->GetCID(), Slot.SKILLINFO.wSkill, nSlotIndex);
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
void CCharSkillPage::OnContextMenu(CWnd* pWnd, CPoint point)
|
|
{
|
|
m_ContextType = CONTEXT_MENU_TYPE::NONE;
|
|
|
|
if((pWnd->GetSafeHwnd() == m_ctrlCharSkillList.GetSafeHwnd())
|
|
&& (1 == m_ctrlCharSkillList.GetSelectedCount()))
|
|
{
|
|
m_ContextType = IN_SKILL_LIST;
|
|
}
|
|
else if((pWnd->GetSafeHwnd() == m_ctrlWantSkillList.GetSafeHwnd())
|
|
&& (LB_ERR != m_ctrlWantSkillList.GetCurSel()))
|
|
{
|
|
m_ContextType = IN_WANTSKILL_LIST;
|
|
}
|
|
|
|
if(NONE != m_ContextType)
|
|
{
|
|
CMenu muTemp, *pContextMenu;
|
|
muTemp.LoadMenu(IDR_CONTEXTMENU);
|
|
pContextMenu = muTemp.GetSubMenu(3);
|
|
pContextMenu->ModifyMenu(0, MF_STRING | MF_BYPOSITION, ID_CHARSKILLEDITCM, GetLocalString("MENU_020"));
|
|
pContextMenu->ModifyMenu(1, MF_STRING | MF_BYPOSITION, ID_CHARSKILLDELCM, GetLocalString("MENU_021"));
|
|
pContextMenu->ModifyMenu(2, MF_STRING | MF_BYPOSITION, ID_SKILL_INSERT, GetLocalString("MENU_022"));
|
|
|
|
if(IN_SKILL_LIST == m_ContextType)
|
|
{
|
|
pContextMenu->DeleteMenu(ID_SKILL_INSERT, MF_POPUP | MF_BYCOMMAND);
|
|
}
|
|
else if(IN_WANTSKILL_LIST == m_ContextType)
|
|
{
|
|
pContextMenu->DeleteMenu(ID_CHARSKILLDELCM, MF_POPUP | MF_BYCOMMAND);
|
|
pContextMenu->DeleteMenu(ID_CHARSKILLEDITCM, MF_POPUP | MF_BYCOMMAND);
|
|
}
|
|
|
|
pContextMenu->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this);
|
|
}
|
|
}
|
|
|
|
bool CCharSkillPage::SetManageSkillInfo()
|
|
{
|
|
ZeroMemory(&m_ManageSkill, sizeof(SKILLSLOT));
|
|
|
|
TCHAR szSkillID[32];
|
|
TCHAR cSkillLockCount = 0;
|
|
|
|
int nSelIndex = -1;
|
|
TCHAR szGetString[256];
|
|
|
|
if(IN_WANTSKILL_LIST == m_ContextType) // 스킬 추가
|
|
{
|
|
nSelIndex = m_ctrlWantSkillList.GetCurSel();
|
|
|
|
if(-1 != nSelIndex)
|
|
m_ctrlWantSkillList.GetText(nSelIndex, szGetString);
|
|
}
|
|
else if(IN_SKILL_LIST == m_ContextType) // 스킬 수정
|
|
{
|
|
nSelIndex = m_ctrlCharSkillList.GetNextItem(-1, LVIS_SELECTED);
|
|
|
|
if(-1 != nSelIndex)
|
|
m_ctrlCharSkillList.GetItemText(nSelIndex, 1, szGetString, sizeof(szGetString));
|
|
}
|
|
|
|
if((NULL != szGetString) && (-1 != nSelIndex))
|
|
{
|
|
_stscanf(szGetString, _T("%s %c"), szSkillID, &cSkillLockCount);
|
|
|
|
m_ManageSkill.SKILLINFO.wSkill = Math::Convert::Atos(szSkillID);
|
|
m_ManageSkill.SKILLINFO.cLockCount = cSkillLockCount - 48; // ASCII 코드 48 = 0 -_ -?
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// 스킬 추가
|
|
void CCharSkillPage::OnSkillAddCM()
|
|
{
|
|
if(LB_ERR != m_ctrlWantSkillList.GetCurSel())
|
|
{
|
|
if(SetManageSkillInfo())
|
|
{
|
|
m_EditMode = SKILL_EDIT_MODE::MODE_CREATE;
|
|
|
|
CCharSkillEditDlg Dlg(CCharSkillEditDlg::SKILL_ADD, this);
|
|
Dlg.DoModal();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CCharSkillPage::OnLbnDblclkSkillList()
|
|
{
|
|
m_ContextType = IN_WANTSKILL_LIST; // SetManageSkillInfo에서 참조함;
|
|
|
|
OnSkillAddCM();
|
|
}
|
|
|
|
// 스킬 수정
|
|
void CCharSkillPage::OnSkillEditCM()
|
|
{
|
|
if(1 == m_ctrlCharSkillList.GetSelectedCount())
|
|
{
|
|
if(SetManageSkillInfo())
|
|
{
|
|
m_EditMode = SKILL_EDIT_MODE::MODE_EDIT;
|
|
|
|
CCharSkillEditDlg Dlg(CCharSkillEditDlg::SKILL_EDIT, this);
|
|
Dlg.DoModal();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CCharSkillPage::OnNMDblclkCharskilllist(NMHDR *pNMHDR, LRESULT *pResult)
|
|
{
|
|
m_ContextType = IN_SKILL_LIST; // SetManageSkillInfo에서 참조함;
|
|
|
|
OnSkillEditCM();
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
// 스킬 삭제
|
|
void CCharSkillPage::OnSkillDelCM()
|
|
{
|
|
if(1 == m_ctrlCharSkillList.GetSelectedCount())
|
|
{
|
|
if(SetManageSkillInfo())
|
|
{
|
|
m_EditMode = SKILL_EDIT_MODE::MODE_DELETE;
|
|
|
|
CPacketMgr::GetInstance()->SendPktSkillEdit(m_lpSkillPageInfo->GetUID(), m_lpSkillPageInfo->GetCID(),
|
|
m_lpSkillPageInfo->GetServerGroup(), m_lpSkillPageInfo->GetClientKey(),
|
|
m_ManageSkill.SKILLINFO.wSkill, 0, 0, m_EditMode);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 배울 수 있는 스킬 목록 표시
|
|
void CCharSkillPage::SetAcquirableSkillList()
|
|
{
|
|
m_ctrlWantSkillList.ResetContent();
|
|
|
|
const unsigned short MAX_SKILL = 255; // 배울수 있는 스킬수 (임의 선언;)
|
|
|
|
CSkillID::isListSkillPos itrPos = CSkillID::GetInstance().begin();
|
|
CSkillID::isListSkillPos itrEnd = CSkillID::GetInstance().end();
|
|
|
|
while(itrEnd != itrPos)
|
|
{
|
|
const Skill::ProtoType* lpPrototype =
|
|
CSkillMgr::GetInstance().GetSkillProtoType(*itrPos);
|
|
|
|
if(NULL != lpPrototype)
|
|
{
|
|
SKILLSLOT Slot;
|
|
|
|
Slot.dwSkillSlot = 0;
|
|
Slot.SKILLINFO.cSkillLevel = 0;
|
|
Slot.SKILLINFO.wSkill = lpPrototype->m_usSkill_ID;
|
|
|
|
unsigned char cSkillClass = static_cast<unsigned char>
|
|
((((lpPrototype->m_usSkill_ID - Skill::SKILL_MASK) & 0xFF00) >> 8) & 0x00FF);
|
|
|
|
// 동일 종족의 스킬인지 판별
|
|
bool bIsLearnSkill =
|
|
(CClass::GetRace(cSkillClass) == m_lpSkillPageInfo->GetRace())
|
|
&& (!lpPrototype->m_bIsClassSkill);
|
|
|
|
if(bIsLearnSkill)
|
|
{
|
|
for(int nLockCount = 0; nLockCount < CSkillMgr::MAX_SKILL_LOCKCOUNT; ++nLockCount)
|
|
{
|
|
const Skill::ProtoType& Prototype = lpPrototype[nLockCount];
|
|
|
|
Slot.SKILLINFO.cLockCount = nLockCount;
|
|
|
|
if(Prototype.m_usSkill_ID != lpPrototype->m_usSkill_ID)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(PktBase::NO_SERVER_ERR ==
|
|
m_lpSkillPageInfo->ReadSkill(Slot, Slot.SKILLINFO.wSkill, nLockCount))
|
|
{
|
|
CString strFormat;
|
|
strFormat.Format( _T("0x%x %d [%s]"),
|
|
Prototype.m_usSkill_ID, nLockCount, Prototype.m_SpriteInfo.m_szName);
|
|
|
|
// 얼터너티브 스킬인 경우의 처리
|
|
if(0 != Prototype.m_usChildSkill)
|
|
{
|
|
const Skill::ProtoType* lpChildSkill =
|
|
CSkillMgr::GetInstance().GetSkillProtoType(Prototype.m_usChildSkill);
|
|
|
|
if(NULL != lpChildSkill)
|
|
{
|
|
strFormat.AppendFormat(_T("><[%s]"),
|
|
lpChildSkill->m_SpriteInfo.m_szName);
|
|
}
|
|
}
|
|
else if(0 != Prototype.m_usParentSkill)
|
|
{
|
|
const Skill::ProtoType* lpParentSkill =
|
|
CSkillMgr::GetInstance().GetSkillProtoType(Prototype.m_usParentSkill);
|
|
|
|
if(NULL != lpParentSkill)
|
|
{
|
|
strFormat.AppendFormat(_T("><[%s]"),
|
|
lpParentSkill->m_SpriteInfo.m_szName);
|
|
}
|
|
}
|
|
|
|
m_ctrlWantSkillList.AddString(strFormat);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERRLOG1(g_Log,
|
|
"배울 수 있는 스킬 목록을 구성하는 중 알수없는 스킬 ID 발생. 스킬ID: %s", *itrPos);
|
|
}
|
|
|
|
++itrPos;
|
|
}
|
|
}
|
|
|
|
SKILLSLOT& CCharSkillPage::GetLastSkill()
|
|
{
|
|
return m_ManageSkill;
|
|
}
|
|
|
|
void CCharSkillPage::OnBnClickedSkillpageClipskillBtn()
|
|
{
|
|
ClippingListCtrl(m_ctrlCharSkillList, 5, true);
|
|
}
|
|
|
|
void CCharSkillPage::OnLvnColumnclickCharskilllist(NMHDR *pNMHDR, LRESULT *pResult)
|
|
{
|
|
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
|
|
|
|
if(pNMLV->iSubItem == m_iSortListItem)
|
|
{
|
|
m_bSortListAsc = !m_bSortListAsc;
|
|
}
|
|
else
|
|
{
|
|
m_iSortListItem = pNMLV->iSubItem;
|
|
m_bSortListAsc = true;
|
|
}
|
|
|
|
SortListWndItem(&m_ctrlCharSkillList, m_iSortListItem);
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CCharSkillPage::SortListWndItem(CListCtrl* lpListCtrl, int iCol)
|
|
{
|
|
CListCtrlSortClass SortClass(lpListCtrl,iCol);
|
|
|
|
switch(m_iSortListItem)
|
|
{
|
|
case SORT_TYPE::SLOT_NUM:
|
|
SortClass.Sort(m_bSortListAsc, CListCtrlSortClass::SortDataType::dtINT);
|
|
break;
|
|
case SORT_TYPE::SKILL_ID:
|
|
SortClass.Sort(m_bSortListAsc, CListCtrlSortClass::SortDataType::dtSTRINGNOCASE);
|
|
break;
|
|
case SORT_TYPE::SKILL_NAME:
|
|
SortClass.Sort(m_bSortListAsc, CListCtrlSortClass::SortDataType::dtSTRING);
|
|
break;
|
|
case SORT_TYPE::SKILL_TYPE:
|
|
SortClass.Sort(m_bSortListAsc, CListCtrlSortClass::SortDataType::dtSTRINGNOCASE);
|
|
break;
|
|
case SORT_TYPE::SKILL_LEVEL:
|
|
SortClass.Sort(m_bSortListAsc, CListCtrlSortClass::SortDataType::dtINT);
|
|
break;
|
|
}
|
|
} |