Files
Client/Server/AdminTool/AdminToolClient/CharSkillPage.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
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>
2025-11-29 20:17:20 +09:00

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;
}
}