2025-08-31 15:14:48 +08:00
|
|
|
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "Widgets/BagConfigEditorWidget.h"
|
|
|
|
|
|
|
|
|
|
#include "PropertyCustomizationHelpers.h"
|
|
|
|
|
#include "Framework/Notifications/NotificationManager.h"
|
|
|
|
|
#include "ProjectFish/DataAsset/BagShapeAsset.h"
|
2025-08-31 15:36:48 +08:00
|
|
|
#include "Widgets/BagConfigGridWidget.h"
|
|
|
|
|
#include "Widgets/BagShapeGridWidget.h"
|
2025-08-31 15:59:08 +08:00
|
|
|
#include "Widgets/SkillListWidget.h"
|
2025-08-31 15:14:48 +08:00
|
|
|
#include "Widgets/Notifications/SNotificationList.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SBagConfigEditorWidget::Construct(const FArguments& InArgs)
|
|
|
|
|
{
|
|
|
|
|
BagConfig = InArgs._BagConfigAsset;
|
|
|
|
|
SelectedSkillAsset = nullptr;
|
|
|
|
|
bIsPlacingSkill = false;
|
|
|
|
|
SelectedSkillIndex = -1;
|
2025-08-31 15:36:48 +08:00
|
|
|
|
2025-08-31 15:14:48 +08:00
|
|
|
ChildSlot
|
|
|
|
|
[
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
2025-08-31 15:36:48 +08:00
|
|
|
// 背包选择器
|
2025-08-31 15:14:48 +08:00
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
.Padding(5.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
.Padding(0, 0, 10, 0)
|
|
|
|
|
[
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
.Text(FText::FromString(TEXT("背包形状:")))
|
|
|
|
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 10))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
.FillWidth(1.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SObjectPropertyEntryBox)
|
|
|
|
|
.ObjectPath_Lambda([this]()
|
|
|
|
|
{
|
|
|
|
|
return BagConfig.IsValid() && BagConfig->BagShapeAsset ?
|
|
|
|
|
BagConfig->BagShapeAsset->GetPathName() : FString();
|
|
|
|
|
})
|
|
|
|
|
.AllowedClass(UBagShapeAsset::StaticClass())
|
|
|
|
|
.OnObjectChanged(this, &SBagConfigEditorWidget::OnBagShapeAssetChanged)
|
|
|
|
|
.DisplayUseSelected(true)
|
|
|
|
|
.DisplayBrowse(true)
|
|
|
|
|
.DisplayThumbnail(false)
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 技能资源 选择器
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
.Padding(5.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
.Padding(0, 0, 10, 0)
|
|
|
|
|
[
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
.Text(FText::FromString(TEXT("选择要添加的技能:")))
|
|
|
|
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 10))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
.FillWidth(1.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SObjectPropertyEntryBox)
|
|
|
|
|
.ObjectPath_Lambda([this]()
|
|
|
|
|
{
|
|
|
|
|
return SelectedSkillAsset.IsValid() ?
|
|
|
|
|
SelectedSkillAsset->GetPathName() : FString();
|
|
|
|
|
})
|
|
|
|
|
.AllowedClass(USkillAsset::StaticClass())
|
|
|
|
|
.OnObjectChanged(this, &SBagConfigEditorWidget::OnSkillAssetChanged)
|
|
|
|
|
.DisplayUseSelected(true)
|
|
|
|
|
.DisplayBrowse(true)
|
|
|
|
|
.DisplayThumbnail(false)
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
2025-08-31 15:36:48 +08:00
|
|
|
// 技能按钮
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
.Padding(5.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SHorizontalBox)
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
.Padding(2.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
.Text(FText::FromString(TEXT("点击后,单击背包添加当前选中的技能")))
|
|
|
|
|
.OnClicked(this, &SBagConfigEditorWidget::OnAddSkillClicked)
|
|
|
|
|
.IsEnabled_Lambda([this]() { return BagConfig.IsValid() && BagConfig->BagShapeAsset != nullptr && SelectedSkillAsset.IsValid(); })
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
.Padding(2.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
.Text(FText::FromString(TEXT("点击后,单击背包移除当前选中的技能")))
|
|
|
|
|
.OnClicked(this, &SBagConfigEditorWidget::OnRemoveSkillClicked)
|
|
|
|
|
.IsEnabled_Lambda([this]() { return SelectedSkillAsset.IsValid(); })
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
+ SHorizontalBox::Slot()
|
|
|
|
|
.AutoWidth()
|
|
|
|
|
.Padding(2.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SButton)
|
|
|
|
|
.Text(FText::FromString(TEXT("移除所有技能")))
|
|
|
|
|
.OnClicked(this, &SBagConfigEditorWidget::OnClearAllSkillsClicked)
|
|
|
|
|
.IsEnabled_Lambda([this]() { return BagConfig.IsValid() && BagConfig->GetSkillCount() > 0; })
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 主编辑区域
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.FillHeight(1.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SSplitter)
|
|
|
|
|
.Orientation(Orient_Horizontal)
|
|
|
|
|
|
|
|
|
|
// 左侧:背包格子视图
|
|
|
|
|
+ SSplitter::Slot()
|
|
|
|
|
.Value(0.7f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SBox)
|
|
|
|
|
.HAlign(HAlign_Center)
|
|
|
|
|
.VAlign(VAlign_Center)
|
|
|
|
|
.Padding(10.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
.HAlign(HAlign_Center)
|
|
|
|
|
.Padding(0, 0, 0, 10)
|
|
|
|
|
[
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
.Text_Lambda([this]()
|
|
|
|
|
{
|
|
|
|
|
if (!BagConfig.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return FText::FromString(TEXT("No Bag Class"));
|
|
|
|
|
}
|
|
|
|
|
else if (!BagConfig->BagShapeAsset)
|
|
|
|
|
{
|
|
|
|
|
return FText::FromString(TEXT("Please select a Bag Shape Asset above"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return FText::FromString(FString::Printf(TEXT("Bag: %s (%dx%d)"),
|
|
|
|
|
*BagConfig->BagShapeAsset->GetName(),
|
|
|
|
|
BagConfig->BagShapeAsset->BagWidth,
|
|
|
|
|
BagConfig->BagShapeAsset->BagHeight));
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
|
|
|
|
|
.ColorAndOpacity_Lambda([this]()
|
|
|
|
|
{
|
|
|
|
|
return (!BagConfig.IsValid() || !BagConfig->BagShapeAsset) ?
|
|
|
|
|
FSlateColor(FLinearColor::Red) : FSlateColor(FLinearColor::White);
|
|
|
|
|
})
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
.HAlign(HAlign_Center)
|
|
|
|
|
[
|
|
|
|
|
SAssignNew(BagGridWidget, SBagConfigGridWidget)
|
|
|
|
|
.BagConfig(BagConfig.Get())
|
|
|
|
|
.OnGridCellClicked(this, &SBagConfigEditorWidget::OnGridCellClicked)
|
|
|
|
|
.Visibility_Lambda([this]()
|
|
|
|
|
{
|
|
|
|
|
return (BagConfig.IsValid() && BagConfig->BagShapeAsset) ?
|
|
|
|
|
EVisibility::Visible : EVisibility::Hidden;
|
|
|
|
|
})
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
2025-08-31 15:59:08 +08:00
|
|
|
// 右侧:技能列表
|
|
|
|
|
+ SSplitter::Slot()
|
|
|
|
|
.Value(0.3f)
|
|
|
|
|
[
|
|
|
|
|
SNew(SVerticalBox)
|
|
|
|
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.AutoHeight()
|
|
|
|
|
.Padding(5.0f)
|
|
|
|
|
[
|
|
|
|
|
SNew(STextBlock)
|
|
|
|
|
.Text(FText::FromString(TEXT("已添加技能:")))
|
|
|
|
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
+ SVerticalBox::Slot()
|
|
|
|
|
.FillHeight(1.0f)
|
|
|
|
|
.Padding(5.0f)
|
|
|
|
|
[
|
|
|
|
|
SAssignNew(SkillListWidget, SSkillListWidget)
|
|
|
|
|
.BagConfig(BagConfig.Get())
|
|
|
|
|
.OnSkillSelected(this, &SBagConfigEditorWidget::OnSkillSelected)
|
|
|
|
|
]
|
|
|
|
|
]
|
2025-08-31 15:36:48 +08:00
|
|
|
]
|
2025-08-31 15:14:48 +08:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
RefreshUI();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 15:36:48 +08:00
|
|
|
FReply SBagConfigEditorWidget::OnAddSkillClicked()
|
|
|
|
|
{
|
|
|
|
|
if (!BagConfig.IsValid() || !BagConfig->BagShapeAsset || !SelectedSkillAsset.IsValid())
|
|
|
|
|
{
|
|
|
|
|
ShowWarningMessage(TEXT("Please select a bag shape asset and a skill object first!"));
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 进入放置模式
|
|
|
|
|
bIsPlacingSkill = true;
|
|
|
|
|
ShowWarningMessage(TEXT("Click on the grid to place the skill"));
|
|
|
|
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FReply SBagConfigEditorWidget::OnRemoveSkillClicked()
|
|
|
|
|
{
|
|
|
|
|
if (!BagConfig.IsValid() || SelectedSkillIndex < 0)
|
|
|
|
|
{
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BagConfig->RemoveSkill(SelectedSkillIndex))
|
|
|
|
|
{
|
|
|
|
|
SelectedSkillIndex = -1;
|
|
|
|
|
RefreshUI();
|
|
|
|
|
|
|
|
|
|
// 标记资源为已修改
|
|
|
|
|
BagConfig->MarkPackageDirty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FReply SBagConfigEditorWidget::OnClearAllSkillsClicked()
|
|
|
|
|
{
|
|
|
|
|
if (!BagConfig.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BagConfig->ClearAllSkills();
|
|
|
|
|
SelectedSkillIndex = -1;
|
|
|
|
|
RefreshUI();
|
|
|
|
|
|
|
|
|
|
// 标记资源为已修改
|
|
|
|
|
BagConfig->MarkPackageDirty();
|
|
|
|
|
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 15:14:48 +08:00
|
|
|
void SBagConfigEditorWidget::OnBagShapeAssetChanged(const FAssetData& AssetData)
|
|
|
|
|
{
|
|
|
|
|
if (!BagConfig.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取新选择的BagShapeAsset
|
|
|
|
|
UBagShapeAsset* NewBagShapeAsset = Cast<UBagShapeAsset>(AssetData.GetAsset());
|
|
|
|
|
|
|
|
|
|
// 如果选择了不同的资源,清空现有技能并更新引用
|
|
|
|
|
if (BagConfig->BagShapeAsset != NewBagShapeAsset)
|
|
|
|
|
{
|
|
|
|
|
// 清空现有技能(因为新的背包形状可能不兼容)
|
|
|
|
|
if (NewBagShapeAsset && BagConfig->GetSkillCount() > 0)
|
|
|
|
|
{
|
|
|
|
|
BagConfig->ClearAllSkills();
|
|
|
|
|
ShowWarningMessage(TEXT("Existing skills cleared due to bag shape change"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新BagShapeAsset引用
|
|
|
|
|
BagConfig->BagShapeAsset = NewBagShapeAsset;
|
|
|
|
|
|
|
|
|
|
// 标记资源为已修改
|
|
|
|
|
BagConfig->MarkPackageDirty();
|
|
|
|
|
|
|
|
|
|
// 刷新UI
|
|
|
|
|
RefreshUI();
|
|
|
|
|
|
|
|
|
|
if (NewBagShapeAsset)
|
|
|
|
|
{
|
|
|
|
|
ShowWarningMessage(FString::Printf(TEXT("Bag shape set to: %s"), *NewBagShapeAsset->GetName()));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ShowWarningMessage(TEXT("Bag shape asset cleared"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SBagConfigEditorWidget::OnSkillAssetChanged(const FAssetData& AssetData)
|
|
|
|
|
{
|
|
|
|
|
SelectedSkillAsset = Cast<USkillAsset>(AssetData.GetAsset());
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 15:59:08 +08:00
|
|
|
void SBagConfigEditorWidget::OnSkillSelected(int32 SkillIndex)
|
|
|
|
|
{
|
|
|
|
|
SelectedSkillIndex = SkillIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 15:36:48 +08:00
|
|
|
void SBagConfigEditorWidget::OnGridCellClicked(int32 X, int32 Y)
|
|
|
|
|
{
|
|
|
|
|
if (!BagConfig.IsValid())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bIsPlacingSkill && SelectedSkillAsset.IsValid())
|
|
|
|
|
{
|
|
|
|
|
// 尝试在指定位置放置技能
|
|
|
|
|
if (BagConfig->CanPlaceSkill(SelectedSkillAsset.Get(), X, Y))
|
|
|
|
|
{
|
|
|
|
|
if (BagConfig->AddSkill(SelectedSkillAsset.Get(), X, Y))
|
|
|
|
|
{
|
|
|
|
|
BagConfig->MarkPackageDirty();
|
|
|
|
|
RefreshUI();
|
|
|
|
|
ShowWarningMessage(FString::Printf(TEXT("Successfully placed skill '%s' at (%d,%d)"),
|
|
|
|
|
*SelectedSkillAsset->SkillName.ToString(), X, Y));
|
|
|
|
|
|
|
|
|
|
// 退出放置模式
|
|
|
|
|
bIsPlacingSkill = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ShowWarningMessage(TEXT("Failed to place skill!"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ShowWarningMessage(TEXT("Cannot place skill at this position!"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// // 选择现有技能
|
|
|
|
|
// int32 SkillIndex = BagConfig->GetSkillAtPosition(X, Y);
|
|
|
|
|
// SelectedSkillIndex = SkillIndex;
|
|
|
|
|
//
|
|
|
|
|
// if (SkillListWidget.IsValid())
|
|
|
|
|
// {
|
|
|
|
|
// SkillListWidget->SetSelectedSkill(SkillIndex);
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 15:14:48 +08:00
|
|
|
void SBagConfigEditorWidget::RefreshUI()
|
|
|
|
|
{
|
2025-08-31 15:36:48 +08:00
|
|
|
if (BagGridWidget.IsValid())
|
|
|
|
|
{
|
|
|
|
|
BagGridWidget->RefreshGrid();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 15:59:08 +08:00
|
|
|
if (SkillListWidget.IsValid())
|
|
|
|
|
{
|
|
|
|
|
SkillListWidget->RefreshSkillList();
|
|
|
|
|
}
|
2025-08-31 15:14:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SBagConfigEditorWidget::ShowWarningMessage(const FString& Message)
|
|
|
|
|
{
|
|
|
|
|
FNotificationInfo Info(FText::FromString(Message));
|
|
|
|
|
Info.bFireAndForget = true;
|
|
|
|
|
Info.FadeOutDuration = 3.0f;
|
|
|
|
|
Info.ExpireDuration = 3.0f;
|
|
|
|
|
|
|
|
|
|
FSlateNotificationManager::Get().AddNotification(Info);
|
|
|
|
|
}
|