done create new skill combo
This commit is contained in:
@@ -787,6 +787,11 @@ namespace BrewMonster
|
||||
|
||||
public void DefaultUserSettings(ref EC_SYSTEM_SETTING pss, ref EC_VIDEO_SETTING pvs, ref EC_GAME_SETTING pgs, ref EC_BLACKLIST_SETTING pbs, ref EC_COMPUTER_AIDED_SETTING pcas)
|
||||
{
|
||||
pss = new EC_SYSTEM_SETTING();
|
||||
pvs = new EC_VIDEO_SETTING();
|
||||
pgs = new EC_GAME_SETTING();
|
||||
pbs = new EC_BLACKLIST_SETTING();
|
||||
pcas = new EC_COMPUTER_AIDED_SETTING();
|
||||
pss.Reset();
|
||||
pvs.Reset();
|
||||
pgs.Reset();
|
||||
|
||||
@@ -0,0 +1,625 @@
|
||||
# Flow: Create Skill Combo by Drag-Drop - C++ Implementation
|
||||
|
||||
## Overview
|
||||
This document describes the complete flow for creating a skill combo by drag-dropping individual skills into the combo skill list in the skill edit dialog. This is the process of building a combo skill sequence that can later be placed in the quick bar.
|
||||
|
||||
---
|
||||
|
||||
## Entry Points
|
||||
|
||||
### 1. **Open Skill Edit Dialog**
|
||||
|
||||
**Location:** `DlgSkillSubOther.cpp:49-57`
|
||||
|
||||
The user can open the skill edit dialog in two ways:
|
||||
|
||||
#### **A. Create New Combo Skill** (Button: "Btn_ConNew")
|
||||
```cpp
|
||||
// DlgSkillSubOther.cpp:54-57
|
||||
void CDlgSkillSubOther::OnCommandNew(const char * szCommand) {
|
||||
GetGameUIMan()->m_pDlgSkillEdit->SetData(0); // 0 = new combo
|
||||
GetGameUIMan()->m_pDlgSkillEdit->Show(true);
|
||||
}
|
||||
```
|
||||
|
||||
#### **B. Edit Existing Combo Skill** (Button: "Btn_ConEdi")
|
||||
```cpp
|
||||
// DlgSkillSubOther.cpp:49-52
|
||||
void CDlgSkillSubOther::OnCommandEdit(const char * szCommand) {
|
||||
GetGameUIMan()->m_pDlgSkillEdit->SetData(m_nComboSelect); // 1-based combo index
|
||||
GetGameUIMan()->m_pDlgSkillEdit->Show(true);
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. User clicks "New" or "Edit" button
|
||||
2. Set combo skill index (0 for new, 1-based for existing)
|
||||
3. Show skill edit dialog (`Win_SkillEdit`)
|
||||
|
||||
---
|
||||
|
||||
## Detailed Flow: Drag-Drop Skill to Combo List
|
||||
|
||||
### **Step 1: Skill Edit Dialog Initialization**
|
||||
|
||||
**Location:** `DlgSkillEdit.cpp:143-231` - `OnShowDialog()`
|
||||
|
||||
When the skill edit dialog is shown, it initializes the combo skill list:
|
||||
|
||||
```cpp
|
||||
// DlgSkillEdit.cpp:143-207
|
||||
void CDlgSkillEdit::OnShowDialog() {
|
||||
EC_VIDEO_SETTING setting = GetGame()->GetConfigs()->GetVideoSettings();
|
||||
|
||||
// If creating new combo (GetData() == 0), find first empty slot
|
||||
if( GetData() == 0 ) {
|
||||
for (i = 0; i < EC_COMBOSKILL_NUM; i++)
|
||||
if( setting.comboSkill[i].nIcon == 0 ) {
|
||||
SetData(i + 1); // Set to 1-based index
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Load existing combo skill data if editing
|
||||
if( setting.comboSkill[GetData() - 1].nIcon != 0 ) {
|
||||
// Load existing skills from config
|
||||
for (i = 0; i < EC_COMBOSKILL_LEN; i++) {
|
||||
int idSkill = setting.comboSkill[GetData() - 1].idSkill[i];
|
||||
if( idSkill > 0 ) {
|
||||
CECSkill *pSkill = GetHostPlayer()->GetNormalSkill(idSkill);
|
||||
if (!pSkill) pSkill = GetHostPlayer()->GetEquipSkillByID(idSkill);
|
||||
GetGameUIMan()->m_pDlgSkillSubOther->SetImage(pImage, pSkill);
|
||||
}
|
||||
else {
|
||||
// Special icons (Attack, Loop Start)
|
||||
int iType = -idSkill;
|
||||
SetSpecialIcon(pImage, iType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- Dialog displays empty slots (`Item_1`, `Item_2`, ..., `Item_N`) for combo skill list
|
||||
- Each slot can hold a skill or special icon (Attack, Loop Start)
|
||||
- Existing combo skills are loaded from `EC_VIDEO_SETTING.comboSkill[]`
|
||||
|
||||
---
|
||||
|
||||
### **Step 2: User Drags Skill from Skill List**
|
||||
|
||||
**Location:** `DlgSkillSubOther.cpp:235-253` - `OnEventLButtonDownFixed()` / `OnEventLButtonDownItem()`
|
||||
|
||||
Skills can be dragged from multiple sources:
|
||||
|
||||
#### **A. Fixed Skills** (`Img_InSkill*`)
|
||||
```cpp
|
||||
// DlgSkillSubOther.cpp:235-243
|
||||
void CDlgSkillSubOther::OnEventLButtonDownFixed(WPARAM wParam, LPARAM lParam, AUIObject * pObj) {
|
||||
if (pObj->GetDataPtr("ptr_CECSkill") == 0) {
|
||||
return; // No skill data
|
||||
}
|
||||
|
||||
A3DVIEWPORTPARAM *p = m_pA3DEngine->GetActiveViewport()->GetParam();
|
||||
GetGameUIMan()->m_ptLButtonDown.x = GET_X_LPARAM(lParam) - p->X;
|
||||
GetGameUIMan()->m_ptLButtonDown.y = GET_Y_LPARAM(lParam) - p->Y;
|
||||
GetGameUIMan()->InvokeDragDrop(this, pObj, GetGameUIMan()->m_ptLButtonDown);
|
||||
}
|
||||
```
|
||||
|
||||
#### **B. Item Skills** (`Img_ItemSkill*`)
|
||||
```cpp
|
||||
// DlgSkillSubOther.cpp:245-253
|
||||
void CDlgSkillSubOther::OnEventLButtonDownItem(WPARAM wParam, LPARAM lParam, AUIObject * pObj) {
|
||||
if (pObj->GetDataPtr("ptr_CECSkill") == 0) {
|
||||
return; // No skill data
|
||||
}
|
||||
|
||||
A3DVIEWPORTPARAM *p = m_pA3DEngine->GetActiveViewport()->GetParam();
|
||||
GetGameUIMan()->m_ptLButtonDown.x = GET_X_LPARAM(lParam) - p->X;
|
||||
GetGameUIMan()->m_ptLButtonDown.y = GET_Y_LPARAM(lParam) - p->Y;
|
||||
GetGameUIMan()->InvokeDragDrop(this, pObj, GetGameUIMan()->m_ptLButtonDown);
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- Skill data is stored via `SetDataPtr(pSkill, "ptr_CECSkill")` when skill icon is created
|
||||
- Mouse position is captured for drag operation
|
||||
- `InvokeDragDrop()` initiates the drag-drop system
|
||||
|
||||
---
|
||||
|
||||
### **Step 3: Drag-Drop System Routes to Handler**
|
||||
|
||||
**Location:** `DlgDragDrop.cpp:79-124` - `OnEventLButtonUp()`
|
||||
|
||||
When user releases mouse button, the drag-drop system processes the drop:
|
||||
|
||||
```cpp
|
||||
// DlgDragDrop.cpp:79-124
|
||||
void CDlgDragDrop::OnEventLButtonUp(WPARAM wParam, LPARAM lParam, AUIObject *pObj) {
|
||||
PAUIOBJECT pObjSrc = (PAUIOBJECT)pItem->GetDataPtr("ptr_AUIObject");
|
||||
if( !pObjSrc ) return;
|
||||
|
||||
// Get destination dialog and object via hit test
|
||||
PAUIDIALOG pDlgOver;
|
||||
PAUIOBJECT pObjOver;
|
||||
GetGameUIMan()->HitTest(x, y, &pDlgOver, &pObjOver, this);
|
||||
|
||||
PAUIDIALOG pDlgSrc = pObjSrc->GetParent();
|
||||
|
||||
// Route to appropriate handler
|
||||
OnGeneralScene(pDlgSrc, pObjSrc, pDlgOver, pObjOver, pIvtrSrc);
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. Get source object from drag item
|
||||
2. Hit test to find destination dialog and object
|
||||
3. Route to `OnGeneralScene()` for processing
|
||||
|
||||
---
|
||||
|
||||
### **Step 4: Skill Drag-Drop Handler**
|
||||
|
||||
**Location:** `DlgDragDrop.cpp:589-627` - `OnSkillDragDrop()`
|
||||
|
||||
The handler checks if drop is on skill edit dialog:
|
||||
|
||||
```cpp
|
||||
// DlgDragDrop.cpp:589-627
|
||||
void CDlgDragDrop::OnSkillDragDrop(PAUIDIALOG pDlgSrc, PAUIOBJECT pObjSrc,
|
||||
PAUIDIALOG pDlgOver, PAUIOBJECT pObjOver,
|
||||
CECIvtrItem* pIvtrSrc) {
|
||||
// Check if dropped on skill edit dialog
|
||||
if( pDlgOver && stricmp(pDlgOver->GetName(), "Win_SkillEdit") == 0
|
||||
&& pObjOver && strstr(pObjOver->GetName(), "Item_")
|
||||
&& !strstr(pObjSrc->GetName(), "Img_ConSkill")) // Don't allow combo icons
|
||||
{
|
||||
GetGameUIMan()->m_pDlgSkillEdit->DragDropItem(pDlgSrc, pObjSrc, pObjOver);
|
||||
}
|
||||
// ... other handlers (quick bar, auto policy, etc.)
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- Only processes drops on `Win_SkillEdit` dialog
|
||||
- Destination must be an `Item_*` slot (combo skill list slot)
|
||||
- Prevents dragging combo skill icons into combo list (prevents recursion)
|
||||
|
||||
---
|
||||
|
||||
### **Step 5: Add Skill to Combo List**
|
||||
|
||||
**Location:** `DlgSkillEdit.cpp:249-293` - `DragDropItem()`
|
||||
|
||||
This is the core function that adds the skill to the combo list:
|
||||
|
||||
```cpp
|
||||
// DlgSkillEdit.cpp:249-293
|
||||
void CDlgSkillEdit::DragDropItem(PAUIDIALOG pDlgSrc, PAUIOBJECT pObjSrc, PAUIOBJECT pObjOver) {
|
||||
if( pObjSrc == pObjOver )
|
||||
return; // Same object, do nothing
|
||||
|
||||
if( !pObjOver ) {
|
||||
// Dropped outside - remove skill from source
|
||||
if( !(pDlgSrc == this && strstr(pObjSrc->GetName(), "Special")) )
|
||||
GetGameUIMan()->m_pDlgSkillSubOther->SetImage((PAUIIMAGEPICTURE)pObjSrc, NULL);
|
||||
}
|
||||
else {
|
||||
// Extract target slot number (1-based)
|
||||
int iSlot = atoi(pObjOver->GetName() + strlen("Item_"));
|
||||
|
||||
// Check if source is special icon (Attack, Loop Start)
|
||||
if( strstr(pObjSrc->GetName(), "Special_") ) {
|
||||
int iType = atoi(pObjSrc->GetName() + strlen("Special_"));
|
||||
SetSpecialIcon((PAUIIMAGEPICTURE)pObjOver, iType);
|
||||
}
|
||||
else {
|
||||
// Regular skill drag-drop
|
||||
CECSkill *pSkill = (CECSkill *)pObjSrc->GetDataPtr("ptr_CECSkill");
|
||||
int iType = pObjSrc->GetData();
|
||||
|
||||
if( pDlgSrc != this ) {
|
||||
// Dragging from external dialog (skill list)
|
||||
if( iType != 0)
|
||||
SetSpecialIcon((PAUIIMAGEPICTURE)pObjOver, iType);
|
||||
else
|
||||
GetGameUIMan()->m_pDlgSkillSubOther->SetImage((PAUIIMAGEPICTURE)pObjOver, pSkill);
|
||||
}
|
||||
else {
|
||||
// Dragging within skill edit dialog (reordering)
|
||||
CECSkill *pSkill1 = (CECSkill *)pObjOver->GetDataPtr("ptr_CECSkill");
|
||||
int iType1 = pObjOver->GetData();
|
||||
|
||||
// Swap skills
|
||||
if( iType != 0)
|
||||
SetSpecialIcon((PAUIIMAGEPICTURE)pObjOver, iType);
|
||||
else
|
||||
GetGameUIMan()->m_pDlgSkillSubOther->SetImage((PAUIIMAGEPICTURE)pObjOver, pSkill);
|
||||
|
||||
if( iType1 != 0)
|
||||
SetSpecialIcon((PAUIIMAGEPICTURE)pObjSrc, iType1);
|
||||
else
|
||||
GetGameUIMan()->m_pDlgSkillSubOther->SetImage((PAUIIMAGEPICTURE)pObjSrc, pSkill1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- **External Drag (from skill list):** Adds skill to target slot, clears source if needed
|
||||
- **Internal Drag (within dialog):** Swaps skills between slots (reordering)
|
||||
- **Special Icons:** Can add Attack (`Special_1`) or Loop Start (`Special_2`) icons
|
||||
- Uses `SetImage()` to display skill icon in target slot
|
||||
- Skill data is stored via `SetDataPtr(pSkill, "ptr_CECSkill")`
|
||||
|
||||
---
|
||||
|
||||
### **Step 6: Save Combo Skill Configuration (Local)**
|
||||
|
||||
**Location:** `DlgSkillEdit.cpp:56-85` - `OnCommandConfirm()`
|
||||
|
||||
When user clicks "Confirm" button, the combo skill is saved locally:
|
||||
|
||||
```cpp
|
||||
// DlgSkillEdit.cpp:56-85
|
||||
void CDlgSkillEdit::OnCommandConfirm(const char *szCommand) {
|
||||
EC_VIDEO_SETTING setting = GetGame()->GetConfigs()->GetVideoSettings();
|
||||
|
||||
// Save icon index
|
||||
setting.comboSkill[GetData() - 1].nIcon = m_nIcon;
|
||||
|
||||
// Save skill sequence
|
||||
int i;
|
||||
int j = 0;
|
||||
for (i = 0; ; i++) {
|
||||
AString strName;
|
||||
strName.Format("Item_%d", i + 1);
|
||||
PAUIIMAGEPICTURE pImage = static_cast<PAUIIMAGEPICTURE>(GetDlgItem(strName));
|
||||
if (!pImage) break;
|
||||
|
||||
CECSkill *pSkill = (CECSkill *)pImage->GetDataPtr("ptr_CECSkill");
|
||||
int iType = pImage->GetData();
|
||||
|
||||
if( iType == 0 && pSkill ) {
|
||||
// Regular skill
|
||||
setting.comboSkill[GetData() - 1].idSkill[j] = pSkill->GetSkillID();
|
||||
j++;
|
||||
}
|
||||
else {
|
||||
// Special icon (Attack = -1, Loop Start = -2)
|
||||
setting.comboSkill[GetData() - 1].idSkill[j] = -iType;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
// Save to local config (does NOT send to server immediately)
|
||||
GetGame()->GetConfigs()->SetVideoSettings(setting);
|
||||
Show(false);
|
||||
|
||||
// Update combo skill display
|
||||
GetGameUIMan()->m_pDlgSkillSubOther->UpdateComboSkill();
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- Saves icon index (`nIcon`) for combo skill
|
||||
- Saves skill sequence to `idSkill[]` array:
|
||||
- **Positive values:** Skill IDs
|
||||
- **Negative values:** Special icons (-1 = Attack, -2 = Loop Start)
|
||||
- Updates combo skill icons in main skill dialog
|
||||
- **Note:** This only saves to local memory (`m_vs`), does NOT send to server yet
|
||||
|
||||
---
|
||||
|
||||
### **Step 7: Send Combo Skill to Server**
|
||||
|
||||
**Location:** `EC_GameRun.cpp:1942-2061` - `SaveConfigsToServer()`
|
||||
|
||||
Combo skill data is sent to server as part of the user configuration when:
|
||||
- Player logs out
|
||||
- Periodically during gameplay
|
||||
- When explicitly triggered (e.g., via UI command)
|
||||
|
||||
```cpp
|
||||
// EC_GameRun.cpp:1942-2061
|
||||
DWORD CECGameRun::SaveConfigsToServer() {
|
||||
// ... validation checks ...
|
||||
|
||||
// Calculate total size needed
|
||||
int iTotalSize = 0;
|
||||
iTotalSize += sizeof(DWORD); // Version
|
||||
|
||||
// Host player config (shortcuts, etc.)
|
||||
int iHostSize = 0;
|
||||
pHost->SaveConfigData(NULL, &iHostSize);
|
||||
iTotalSize += sizeof(int) + iHostSize;
|
||||
|
||||
// UI layout config
|
||||
DWORD dwUISize = 0;
|
||||
pGameUI->GetUserLayout(NULL, dwUISize);
|
||||
iTotalSize += sizeof(int) + (int)dwUISize;
|
||||
|
||||
// User settings config (INCLUDES COMBO SKILLS!)
|
||||
int iSettingSize = 0;
|
||||
g_pGame->GetConfigs()->SaveUserConfigData(NULL, &iSettingSize);
|
||||
iTotalSize += sizeof(int) + iSettingSize;
|
||||
|
||||
// Allocate buffer and pack data
|
||||
void* pDataBuf = a_malloctemp(iTotalSize);
|
||||
// ... pack all data ...
|
||||
|
||||
// Compress and send to server
|
||||
g_pGame->GetGameSession()->SaveConfigData(pCompBuf, dwCompLen+iVerLen);
|
||||
}
|
||||
```
|
||||
|
||||
**Key Function:** `EC_Configs::SaveUserConfigData()`
|
||||
|
||||
**Location:** `EC_Configs.cpp:569-615`
|
||||
|
||||
```cpp
|
||||
// EC_Configs.cpp:569-615
|
||||
bool CECConfigs::SaveUserConfigData(void* pDataBuf, int* piSize) {
|
||||
int iTotalSize = 0;
|
||||
BYTE* pData = (BYTE*)pDataBuf;
|
||||
|
||||
// Version
|
||||
iTotalSize += sizeof(DWORD);
|
||||
if (pDataBuf) {
|
||||
*((DWORD*)pData) = EC_CONFIG_VERSION;
|
||||
pData += sizeof(DWORD);
|
||||
}
|
||||
|
||||
// ⭐ SAVE VIDEO SETTINGS (INCLUDES COMBO SKILLS!)
|
||||
iTotalSize += sizeof(EC_VIDEO_SETTING);
|
||||
if (pDataBuf) {
|
||||
*((EC_VIDEO_SETTING*)pData) = m_vs; // Contains comboSkill[] array!
|
||||
pData += sizeof(EC_VIDEO_SETTING);
|
||||
}
|
||||
|
||||
// ... other settings (game, blacklist, computer aided) ...
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- **Entire `EC_VIDEO_SETTING` structure is sent**, which includes:
|
||||
- `comboSkill[EC_COMBOSKILL_NUM]` array
|
||||
- Each combo skill contains `nIcon` and `idSkill[]` array
|
||||
- **Data is compressed** before sending to reduce network traffic
|
||||
- **Sent asynchronously** - not immediately when combo is saved
|
||||
- **Server stores** this data and sends it back when player logs in
|
||||
|
||||
---
|
||||
|
||||
### **Step 8: Server Receives and Stores Combo Skill Data**
|
||||
|
||||
**Location:** Server-side (not in client codebase)
|
||||
|
||||
When server receives the config data:
|
||||
1. Server decompresses the data
|
||||
2. Server parses `EC_VIDEO_SETTING` structure
|
||||
3. Server extracts `comboSkill[]` array
|
||||
4. Server stores combo skill data in player's account/profile
|
||||
5. Server sends combo skill data back to client on next login
|
||||
|
||||
**Note:** The server does NOT validate or execute combo skills - they are purely client-side UI configurations. The server only stores them for persistence across sessions.
|
||||
|
||||
---
|
||||
|
||||
## Complete Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 1: Open Skill Edit Dialog │
|
||||
│ Location: DlgSkillSubOther.cpp:49-57 │
|
||||
│ - User clicks "New" or "Edit" button │
|
||||
│ - SetData(0) for new, SetData(n) for existing │
|
||||
│ - Show Win_SkillEdit dialog │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 2: Dialog Initialization │
|
||||
│ Location: DlgSkillEdit.cpp:143-231 │
|
||||
│ - Load existing combo skill if editing │
|
||||
│ - Display empty slots (Item_1, Item_2, ...) │
|
||||
│ - Show special icons (Attack, Loop Start) │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
│ (User drags skill from skill list)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 3: Drag Initiation │
|
||||
│ Location: DlgSkillSubOther.cpp:235-253 │
|
||||
│ - OnEventLButtonDownFixed() or OnEventLButtonDownItem() │
|
||||
│ - Capture mouse position │
|
||||
│ - InvokeDragDrop(this, pObj, pt) │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
│ (User drags to combo list slot)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 4: Drop Processing │
|
||||
│ Location: DlgDragDrop.cpp:79-124 │
|
||||
│ - OnEventLButtonUp() detects drop │
|
||||
│ - Hit test to find destination │
|
||||
│ - Route to OnSkillDragDrop() │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 5: Skill Drag Handler │
|
||||
│ Location: DlgDragDrop.cpp:589-627 │
|
||||
│ - Check: pDlgOver == "Win_SkillEdit" │
|
||||
│ - Check: pObjOver name contains "Item_" │
|
||||
│ - Call DragDropItem() │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 6: Add Skill to Combo List │
|
||||
│ Location: DlgSkillEdit.cpp:249-293 │
|
||||
│ - Extract target slot: atoi("Item_N") │
|
||||
│ - Get skill: pObjSrc->GetDataPtr("ptr_CECSkill") │
|
||||
│ - SetImage(pObjOver, pSkill) │
|
||||
│ - Store skill data in slot │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
│ (User can drag more skills)
|
||||
│ (User can reorder by dragging within dialog)
|
||||
│
|
||||
│ (User clicks "Confirm" button)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 6: Save Configuration (Local) │
|
||||
│ Location: DlgSkillEdit.cpp:56-85 │
|
||||
│ - Loop through all Item_* slots │
|
||||
│ - Extract skill IDs or special icon types │
|
||||
│ - Save to setting.comboSkill[n].idSkill[] │
|
||||
│ - Save icon: setting.comboSkill[n].nIcon │
|
||||
│ - SetVideoSettings(setting) → saves to m_vs (local only) │
|
||||
│ - UpdateComboSkill() to refresh display │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
│ (Later: on logout, periodically, or explicit save)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 7: Send to Server │
|
||||
│ Location: EC_GameRun.cpp:1942-2061 │
|
||||
│ - SaveConfigsToServer() called │
|
||||
│ - SaveUserConfigData() includes entire EC_VIDEO_SETTING │
|
||||
│ - Combo skills included in m_vs.comboSkill[] │
|
||||
│ - Data compressed and sent via SaveConfigData() │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 8: Server Storage │
|
||||
│ Location: Server-side (not in client code) │
|
||||
│ - Server receives compressed config data │
|
||||
│ - Server decompresses and parses EC_VIDEO_SETTING │
|
||||
│ - Server stores comboSkill[] in player account │
|
||||
│ - Server sends back on next login │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Structures
|
||||
|
||||
### **EC_VIDEO_SETTING.comboSkill[]**
|
||||
```cpp
|
||||
struct COMBO_SKILL {
|
||||
short nIcon; // Icon index (1-based, 0 = empty)
|
||||
short idSkill[EC_COMBOSKILL_LEN]; // Skill sequence array
|
||||
// idSkill values:
|
||||
// > 0: Skill ID
|
||||
// -1: Attack (SID_ATTACK)
|
||||
// -2: Loop Start (SID_LOOPSTART)
|
||||
// 0: Empty slot
|
||||
};
|
||||
```
|
||||
|
||||
### **Skill Storage in UI Objects**
|
||||
- **Skill Pointer:** `pImage->SetDataPtr(pSkill, "ptr_CECSkill")`
|
||||
- **Special Icon Type:** `pImage->SetData(iType)` where `iType = 1` (Attack) or `2` (Loop Start)
|
||||
- **Regular Skill:** `pImage->SetData(0)`
|
||||
|
||||
---
|
||||
|
||||
## Key Constants
|
||||
|
||||
- **EC_COMBOSKILL_NUM:** Maximum number of combo skill groups (typically 8-12)
|
||||
- **EC_COMBOSKILL_LEN:** Maximum number of skills per combo (typically 8-16)
|
||||
- **SID_ATTACK:** Special skill ID for normal attack (typically -1)
|
||||
- **SID_LOOPSTART:** Special skill ID for loop start marker (typically -2)
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Index Conversion:**
|
||||
- Combo group index is **1-based** in UI (`GetData()` returns 1, 2, 3, ...)
|
||||
- Array index is **0-based** in config (`comboSkill[GetData() - 1]`)
|
||||
|
||||
2. **Skill Sources:**
|
||||
- Skills can be dragged from:
|
||||
- `Win_SkillSubOther` (Fixed skills, Item skills)
|
||||
- `Win_SkillSubPool` (Skill pool)
|
||||
- `Win_SkillSubListSkillItem` (Skill list items)
|
||||
|
||||
3. **Special Icons:**
|
||||
- Attack icon (`Special_1`) = Normal attack in combo
|
||||
- Loop Start icon (`Special_2`) = Marks where combo should loop back
|
||||
|
||||
4. **Reordering:**
|
||||
- Users can drag skills within the skill edit dialog to reorder
|
||||
- `DragDropItem()` handles swapping when `pDlgSrc == this`
|
||||
|
||||
5. **Validation:**
|
||||
- Combo skill icons cannot be dragged into combo list (prevents recursion)
|
||||
- Empty slots are allowed (stored as 0 in `idSkill[]`)
|
||||
|
||||
---
|
||||
|
||||
## Related Files
|
||||
|
||||
- **DlgSkillEdit.cpp/h:** Skill edit dialog implementation
|
||||
- **DlgSkillSubOther.cpp/h:** Skill sub-other panel (source of skills)
|
||||
- **DlgDragDrop.cpp/h:** Drag-drop system handler
|
||||
- **EC_Configs.cpp/h:** Configuration management (stores combo skills locally)
|
||||
- **EC_GameRun.cpp/h:** Game run logic (saves configs to server)
|
||||
- **EC_GameSession.cpp/h:** Network session (sends config data to server)
|
||||
- **EC_ComboSkill.cpp/h:** Combo skill execution logic
|
||||
|
||||
---
|
||||
|
||||
## Network Protocol Details
|
||||
|
||||
### **Protocol Command**
|
||||
- **Command:** `SaveConfigData` (via `EC_GameSession::SaveConfigData()`)
|
||||
- **Data Format:** Compressed binary blob containing:
|
||||
1. Version (DWORD)
|
||||
2. Host player config (shortcuts, etc.)
|
||||
3. UI layout config
|
||||
4. **User settings config (includes combo skills)**
|
||||
|
||||
### **Data Structure Sent**
|
||||
```cpp
|
||||
struct USER_CONFIG_DATA {
|
||||
DWORD version; // USERCFG_VERSION
|
||||
int hostConfigSize;
|
||||
BYTE hostConfig[hostConfigSize]; // Host player shortcuts, etc.
|
||||
int uiLayoutSize;
|
||||
BYTE uiLayout[uiLayoutSize]; // UI window positions, etc.
|
||||
int settingsSize;
|
||||
BYTE settings[settingsSize]; // EC_VIDEO_SETTING + other settings
|
||||
};
|
||||
|
||||
// Inside settings:
|
||||
struct EC_VIDEO_SETTING {
|
||||
// ... other video settings ...
|
||||
EC_COMBOSKILL comboSkill[EC_COMBOSKILL_NUM]; // ⭐ COMBO SKILLS HERE!
|
||||
};
|
||||
|
||||
struct EC_COMBOSKILL {
|
||||
short nIcon; // Icon index (0 = empty)
|
||||
short idSkill[EC_COMBOSKILL_LEN]; // Skill sequence
|
||||
};
|
||||
```
|
||||
|
||||
### **When Data is Sent**
|
||||
1. **On Logout:** `DlgExit.cpp` triggers `SaveConfigsToServer()`
|
||||
2. **Periodically:** Game may auto-save configs during gameplay
|
||||
3. **Explicit Save:** UI commands can trigger save (e.g., `EC_GameUICommand.cpp:143`)
|
||||
4. **Pending Actions:** Various pending actions append save requests
|
||||
|
||||
### **Important Notes**
|
||||
- **Combo skills are NOT sent immediately** when saved - they're queued for next config save
|
||||
- **Server does NOT validate combo skills** - they're client-side UI configurations
|
||||
- **Combo skills are NOT executed on server** - execution is purely client-side
|
||||
- **Server only stores for persistence** - so player's combo skills persist across sessions
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4abca7ea223861b47b89d130f6902111
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class CDlgDragDrop : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.S))
|
||||
OnSkillDragDrop();
|
||||
}
|
||||
#endif
|
||||
public void OnSkillDragDrop()
|
||||
{
|
||||
var iSlot = 2;
|
||||
var nCombo = 2;
|
||||
CECShortcutSet pSCS = CECUIManager.Instance.GetInGameUIMan().GetSCSByDlg(1);
|
||||
if (pSCS.GetShortcut(iSlot - 1) == null || !EC_Game.GetConfigs().GetGameSettings().bLockQuickBar)
|
||||
pSCS.CreateSkillGroupShortcut(iSlot - 1, nCombo - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*AString s = WC2AS(*pstr); // convert wide -> ansi/utf8 tùy macro bạn định nghĩa
|
||||
a_LogOutput(0, "HoangDev: 1 n=%d, pstr=%s", n, static_cast <const char*>(s));*/
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad855801e0cddfe46a48b448016ee952
|
||||
@@ -158,7 +158,7 @@ namespace BrewMonster.UI
|
||||
// Update combo skill icons - called from DlgSkill
|
||||
public void UpdateComboSkill()
|
||||
{
|
||||
CECConfigs configs = EC_Game.GetConfigs();
|
||||
/*CECConfigs configs = EC_Game.GetConfigs();
|
||||
if (configs == null)
|
||||
return;
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace BrewMonster.UI
|
||||
// pImage->SetHint("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// Update fixed skill display
|
||||
|
||||
@@ -32,9 +32,7 @@ namespace BrewMonster
|
||||
public bool UpdateShortcuts()
|
||||
{
|
||||
CECShortcut pSC;
|
||||
Image skillImage;
|
||||
CECSCSkill pSCSkill;
|
||||
int iIconFile, nMax;
|
||||
AUIImagePicture pCell;
|
||||
CECSkill pSkill = new CECSkill(-1, -1);
|
||||
AUIClockIcon pClock;
|
||||
@@ -68,7 +66,6 @@ namespace BrewMonster
|
||||
{
|
||||
if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_SKILL)
|
||||
{
|
||||
iIconFile = (int)EC_GAMEUI_ICONS.ICONS_SKILL;
|
||||
pSCSkill = (CECSCSkill)pSC;
|
||||
pSkill = pSCSkill.GetSkill();
|
||||
if (false/*m_bDelGoblinSkillSC && GNET::ElementSkill::IsGoblinSkill(pSkill->GetSkillID())*/)
|
||||
@@ -504,11 +501,11 @@ namespace BrewMonster
|
||||
pszPanel.Add(dlgName);
|
||||
}
|
||||
}
|
||||
private int GetCurPanel1()
|
||||
public int GetCurPanel1()
|
||||
{
|
||||
return m_nCurPanel1;
|
||||
}
|
||||
private int GetCurPanel2()
|
||||
public int GetCurPanel2()
|
||||
{
|
||||
return m_nCurPanel2;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using static UnityEngine.Rendering.DebugUI;
|
||||
|
||||
namespace BrewMonster.UI
|
||||
{
|
||||
@@ -60,7 +62,24 @@ namespace BrewMonster.UI
|
||||
}
|
||||
m_pDlgNPC.PopupNPCDialog(pTalk);
|
||||
}
|
||||
|
||||
public CECShortcutSet GetSCSByDlg(int indexPanel)
|
||||
{
|
||||
CECHostPlayer pHost = CECGameRun.Instance.GetHostPlayer();
|
||||
CDlgQuickBar cDlgQuickBar = CECUIManager.Instance.GetCDlgQuickBar();
|
||||
CECShortcutSet pSCS = null;
|
||||
int index = (0);
|
||||
if (indexPanel == 1)
|
||||
{
|
||||
int panel = (index < 0 ? cDlgQuickBar.GetCurPanel1() : index) - 1;
|
||||
pSCS = pHost.GetShortcutSet1(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int panel = (index < 0 ? cDlgQuickBar.GetCurPanel2() : index) - 1;
|
||||
pSCS = pHost.GetShortcutSet2(panel);
|
||||
}
|
||||
return pSCS;
|
||||
}
|
||||
// 弹出任务完成对话框(到达/离开地点等触发) // Popup task-finish dialog (reach/leave site, etc.)
|
||||
// C++: pTask->PopupTaskFinishDialog(taskId, &awardTalk); then OnUIDialogEnd() notifies server.
|
||||
public bool PopupTaskFinishDialog(uint taskId, talk_proc pTalk)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
using Animancer;
|
||||
using BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.UI;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class CDlgSkillEdit : AUIDialog
|
||||
{
|
||||
int m_nIcon;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
public override void Start()
|
||||
{
|
||||
m_nIcon = 1;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
public override void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.T))
|
||||
{
|
||||
OnShowDialog();
|
||||
OnCommandConfirm();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public void OnShowDialog()
|
||||
{
|
||||
EC_VIDEO_SETTING setting = EC_Game.GetConfigs().GetVideoSettings();
|
||||
if (GetData() == 0)
|
||||
{
|
||||
for (uint i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_NUM; i++)
|
||||
{
|
||||
if (setting.comboSkill[i].nIcon == 0)
|
||||
{
|
||||
SetData(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (GetData() == 0)
|
||||
{
|
||||
//Show(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void OnCommandConfirm()
|
||||
{
|
||||
EC_VIDEO_SETTING setting = EC_Game.GetConfigs().GetVideoSettings();
|
||||
setting.comboSkill[GetData() - 1].nIcon = (byte)m_nIcon;
|
||||
int i;
|
||||
int j = 0;
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
/* AString strName;
|
||||
strName.Format("Item_%d", i + 1);
|
||||
PAUIIMAGEPICTURE pImage = static_cast<PAUIIMAGEPICTURE>(GetDlgItem(strName));
|
||||
if (!pImage) break;*/
|
||||
|
||||
//CECSkill pSkill = (CECSkill)pImage.GetDataPtr("ptr_CECSkill");
|
||||
//int iType = pImage->GetData();
|
||||
if (true/*iType == 0 && pSkill != null*/)
|
||||
{
|
||||
setting.comboSkill[GetData() - 1].idSkill[j] = (short)(i +1) /*pSkill.GetSkillID()*/;
|
||||
j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//setting.comboSkill[GetData() - 1].idSkill[j] = -iType;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
EC_Game.GetConfigs().SetVideoSettings(setting);
|
||||
//Show(false);
|
||||
CECUIManager.Instance.m_pDlgSkillSubOther.UpdateComboSkill();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b46a84d2ff078524f974fb6d60019d8c
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:def3c6a4f58ef9b5c9558210328e5b6f3ed11bfee5b577271ee43fa110616b82
|
||||
size 200521689
|
||||
oid sha256:80d98663fdebe84a0752357c66954e1834c6d78f1e61ca61df0122a35b5116c7
|
||||
size 200524021
|
||||
|
||||
@@ -528,7 +528,10 @@ public partial class CECGameRun
|
||||
return szRet;
|
||||
}
|
||||
public int GetGameState() { return m_iGameState; }
|
||||
|
||||
public void SaveConfigsToServer()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
public enum GameState
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public class CECUIManager : MonoSingleton<CECUIManager>
|
||||
|
||||
[SerializeField] private UnityEngine.UI.Button btnSecondClick;
|
||||
[SerializeField] CDlgQuickBar m_pDlgQuickBar1;
|
||||
public CDlgSkillSubOther m_pDlgSkillSubOther;
|
||||
CDlgMessageBox m_pDlgMessageBox;
|
||||
public CDlgSkillAction m_pDlgSkillAction;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user