fix: selection

This commit is contained in:
2025-10-12 15:35:17 +02:00
parent 83e28d3828
commit bf15db0066
2 changed files with 475 additions and 464 deletions

View File

@@ -3,555 +3,569 @@
USkillTreeComponent::USkillTreeComponent()
{
PrimaryComponentTick.bCanEverTick = false;
PrimaryComponentTick.bCanEverTick = false;
AvailableSkillPoints = 5; // Start with 5 skill points for testing
TotalSkillPointsEarned = 5;
CurrentSelectionCost = 0;
AvailableSkillPoints = 5;
TotalSkillPointsEarned = 5;
CurrentSelectionCost = 0;
}
void USkillTreeComponent::BeginPlay()
{
Super::BeginPlay();
InitializeSkillTree();
Super::BeginPlay();
InitializeSkillTree();
}
void USkillTreeComponent::InitializeSkillTree()
{
AllSkills.Empty();
AllSkills.Empty();
if (!SkillDataTable)
{
UE_LOG(LogTemp, Warning, TEXT("SkillTreeComponent: No skill data table assigned!"));
return;
}
if (!SkillDataTable)
{
UE_LOG(LogTemp, Warning,
TEXT("SkillTreeComponent: No skill data table assigned!"));
return;
}
// Load all skills from the data table using RowNames as IDs
TArray<FName> RowNames = SkillDataTable->GetRowNames();
// Load all skills from the data table using RowNames as IDs
TArray<FName> RowNames = SkillDataTable->GetRowNames();
for (const FName& RowName : RowNames)
{
FSkillNodeData* Row = SkillDataTable->FindRow<FSkillNodeData>(
RowName, TEXT("SkillTreeComponent"));
if (Row)
{
FSkillNodeRuntime RuntimeNode;
RuntimeNode.NodeData = *Row;
RuntimeNode.CurrentState = ESkillNodeState::Locked;
for (const FName& RowName : RowNames)
{
FSkillNodeData* Row = SkillDataTable->FindRow<FSkillNodeData>(RowName, TEXT("SkillTreeComponent"));
if (Row)
{
FSkillNodeRuntime RuntimeNode;
RuntimeNode.NodeData = *Row;
RuntimeNode.CurrentState = ESkillNodeState::Locked;
RuntimeNode.bIsSelected = false;
// Use RowName as the skill ID
AllSkills.Add(RowName, RuntimeNode);
}
}
// Use RowName as the skill ID
AllSkills.Add(RowName, RuntimeNode);
}
}
UpdateSkillStates();
UpdateSkillStates();
UE_LOG(LogTemp, Log, TEXT("SkillTreeComponent: Initialized with %d skills"), AllSkills.Num());
UE_LOG(LogTemp, Log, TEXT("SkillTreeComponent: Initialized with %d skills"),
AllSkills.Num());
}
void USkillTreeComponent::AddSkillPoints(int32 Amount)
{
if (Amount > 0)
{
AvailableSkillPoints += Amount;
TotalSkillPointsEarned += Amount;
OnSkillPointsChanged.Broadcast(AvailableSkillPoints);
UpdateSkillStates();
}
if (Amount > 0)
{
AvailableSkillPoints += Amount;
TotalSkillPointsEarned += Amount;
OnSkillPointsChanged.Broadcast(AvailableSkillPoints);
UpdateSkillStates();
}
}
void USkillTreeComponent::RemoveSkillPoints(int32 Amount)
{
if (Amount > 0)
{
AvailableSkillPoints = FMath::Max(0, AvailableSkillPoints - Amount);
OnSkillPointsChanged.Broadcast(AvailableSkillPoints);
UpdateSkillStates();
}
if (Amount > 0)
{
AvailableSkillPoints = FMath::Max(0, AvailableSkillPoints - Amount);
OnSkillPointsChanged.Broadcast(AvailableSkillPoints);
UpdateSkillStates();
}
}
bool USkillTreeComponent::SelectSkill(FName SkillID)
{
FText ErrorMessage;
FText ErrorMessage;
// Check if skill exists
if (!AllSkills.Contains(SkillID))
{
ErrorMessage = FText::FromString(TEXT("Skill does not exist"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
// Check if skill exists
if (!AllSkills.Contains(SkillID))
{
ErrorMessage = FText::FromString(TEXT("Skill does not exist"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
// Check if already purchased
if (SkillNode->CurrentState == ESkillNodeState::Purchased)
{
ErrorMessage = FText::FromString(TEXT("Skill already purchased"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
// Validate skill state
switch (SkillNode->CurrentState)
{
case ESkillNodeState::Purchased:
// Already purchased, cannot select
ErrorMessage = FText::FromString(TEXT("Skill already purchased"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
// Check if already selected
if (SkillNode->bIsSelected)
{
ErrorMessage = FText::FromString(TEXT("Skill already selected"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
case ESkillNodeState::Selected:
// Already selected, cannot select again
ErrorMessage = FText::FromString(TEXT("Skill already selected"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
// For locked skills, we allow selection but will validate on purchase
// This allows players to "plan" their build
if (SkillNode->CurrentState == ESkillNodeState::Locked)
{
// Check if prerequisites would be met with current selection
bool bWouldBeAvailable = true;
for (const FName& PrereqID : SkillNode->NodeData.Prerequisites)
{
if (!IsSkillPurchased(PrereqID) && !IsSkillSelected(PrereqID))
{
bWouldBeAvailable = false;
break;
}
}
case ESkillNodeState::Locked:
// For locked skills, we allow selection but validate prerequisites
// This allows players to "plan" their build
bool bWouldBeAvailable = true;
for (const FName& PrereqID : SkillNode->NodeData.Prerequisites)
{
if (!IsSkillPurchased(PrereqID) && !IsSkillSelected(PrereqID))
{
bWouldBeAvailable = false;
break;
}
}
if (!bWouldBeAvailable)
{
ErrorMessage = FText::FromString(TEXT(
"Prerequisites not selected. Select prerequisite skills first"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
break;
if (!bWouldBeAvailable)
{
ErrorMessage = FText::FromString(TEXT("Prerequisites not selected. Select prerequisite skills first."));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
}
case ESkillNodeState::Available:
// Available skills can be selected without additional checks
break;
}
// Add to selection
SkillNode->bIsSelected = true;
SelectedSkills.Add(SkillID);
UpdateSelectionCost();
// Add to selection
SkillNode->CurrentState = ESkillNodeState::Selected;
SelectedSkills.Add(SkillID);
UpdateSelectionCost();
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
OnSkillStateChanged.Broadcast(SkillID);
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
OnSkillStateChanged.Broadcast(SkillID);
return true;
return true;
}
bool USkillTreeComponent::DeselectSkill(FName SkillID)
{
if (!AllSkills.Contains(SkillID))
{
return false;
}
if (!AllSkills.Contains(SkillID))
return false;
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (!SkillNode->bIsSelected)
{
return false;
}
if (SkillNode->CurrentState != ESkillNodeState::Selected)
return false;
// Check if any selected skills depend on this one
for (const FName& SelectedID : SelectedSkills)
{
if (SelectedID != SkillID)
{
FSkillNodeRuntime* SelectedNode = AllSkills.Find(SelectedID);
if (SelectedNode && SelectedNode->NodeData.Prerequisites.Contains(SkillID))
{
// Can't deselect if other selected skills depend on it
FText ErrorMessage = FText::FromString(TEXT("Other selected skills depend on this one"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
}
}
// Check if any selected skills depend on this one
for (const FName& SelectedID : SelectedSkills)
{
if (SelectedID != SkillID)
{
FSkillNodeRuntime* SelectedNode = AllSkills.Find(SelectedID);
if (SelectedNode
&& SelectedNode->NodeData.Prerequisites.Contains(SkillID))
{
// Can't deselect if other selected skills depend on it
FText ErrorMessage = FText::FromString(
TEXT("Other selected skills depend on this one"));
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
}
}
// Remove from selection
SkillNode->bIsSelected = false;
SelectedSkills.Remove(SkillID);
UpdateSelectionCost();
// Remove from selection and update state
SelectedSkills.Remove(SkillID);
UpdateSelectionCost();
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
OnSkillStateChanged.Broadcast(SkillID);
// Update state to either Available or Locked based on prerequisites
if (ArePrerequisitesMet(SkillNode->NodeData))
SkillNode->CurrentState = ESkillNodeState::Available;
else
SkillNode->CurrentState = ESkillNodeState::Locked;
return true;
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
OnSkillStateChanged.Broadcast(SkillID);
return true;
}
void USkillTreeComponent::ClearSelection()
{
for (const FName& SkillID : SelectedSkills)
{
if (FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
SkillNode->bIsSelected = false;
OnSkillStateChanged.Broadcast(SkillID);
}
}
for (const FName& SkillID : SelectedSkills)
{
if (FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
// Update state based on prerequisites
if (ArePrerequisitesMet(SkillNode->NodeData))
SkillNode->CurrentState = ESkillNodeState::Available;
else
SkillNode->CurrentState = ESkillNodeState::Locked;
OnSkillStateChanged.Broadcast(SkillID);
}
}
SelectedSkills.Empty();
UpdateSelectionCost();
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
SelectedSkills.Empty();
UpdateSelectionCost();
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
}
bool USkillTreeComponent::ConfirmPurchase()
{
// Validate we can afford it
if (!CanAffordSelection())
{
FText ErrorMessage = FText::FromString(TEXT("Not enough skill points"));
OnSelectionError.Broadcast(NAME_None, ErrorMessage);
return false;
}
// Validate we can afford it
if (!CanAffordSelection())
{
FText ErrorMessage = FText::FromString(TEXT("Not enough skill points"));
OnSelectionError.Broadcast(NAME_None, ErrorMessage);
return false;
}
// Sort selected skills by purchase order to ensure prerequisites are purchased first
TArray<FName> SortedSelection = SelectedSkills;
SortedSelection.Sort([this](const FName& A, const FName& B) {
FSkillNodeRuntime* NodeA = AllSkills.Find(A);
FSkillNodeRuntime* NodeB = AllSkills.Find(B);
if (NodeA && NodeB)
{
return NodeA->NodeData.PurchaseOrder < NodeB->NodeData.PurchaseOrder;
}
return false;
});
// Sort selected skills by purchase order to ensure prerequisites are
// purchased first
TArray<FName> SortedSelection = SelectedSkills;
SortedSelection.Sort(
[this](const FName& A, const FName& B)
{
FSkillNodeRuntime* NodeA = AllSkills.Find(A);
FSkillNodeRuntime* NodeB = AllSkills.Find(B);
if (NodeA && NodeB)
{
return NodeA->NodeData.PurchaseOrder
< NodeB->NodeData.PurchaseOrder;
}
return false;
});
// Validate all skills can be purchased in order
for (const FName& SkillID : SortedSelection)
{
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (!SkillNode)
{
continue;
}
// Validate all skills can be purchased in order
for (const FName& SkillID : SortedSelection)
{
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (!SkillNode)
continue;
// Check prerequisites (considering already processed skills in this batch)
bool bPrereqsMet = true;
for (const FName& PrereqID : SkillNode->NodeData.Prerequisites)
{
if (!IsSkillPurchased(PrereqID))
{
// Check if it's in our current batch and would be purchased before this
int32 PrereqIndex = SortedSelection.IndexOfByKey(PrereqID);
int32 CurrentIndex = SortedSelection.IndexOfByKey(SkillID);
// Check prerequisites (considering already processed skills in this
// batch)
bool bPrereqsMet = true;
for (const FName& PrereqID : SkillNode->NodeData.Prerequisites)
{
if (!IsSkillPurchased(PrereqID))
{
// Check if it's in our current batch and would be purchased
// before this
int32 PrereqIndex = SortedSelection.IndexOfByKey(PrereqID);
int32 CurrentIndex = SortedSelection.IndexOfByKey(SkillID);
if (PrereqIndex == INDEX_NONE || PrereqIndex >= CurrentIndex)
{
bPrereqsMet = false;
break;
}
}
}
if (PrereqIndex == INDEX_NONE || PrereqIndex >= CurrentIndex)
{
bPrereqsMet = false;
break;
}
}
}
if (!bPrereqsMet)
{
FText ErrorMessage = FText::Format(
FText::FromString(TEXT("Cannot purchase {0}: prerequisites not met")),
SkillNode->NodeData.DisplayName
);
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
}
if (!bPrereqsMet)
{
FText ErrorMessage = FText::Format(
FText::FromString(
TEXT("Cannot purchase {0}: prerequisites not met")),
SkillNode->NodeData.DisplayName);
OnSelectionError.Broadcast(SkillID, ErrorMessage);
return false;
}
}
// Purchase all selected skills
for (const FName& SkillID : SortedSelection)
{
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (SkillNode)
{
// Mark as purchased
SkillNode->CurrentState = ESkillNodeState::Purchased;
SkillNode->bIsSelected = false;
PurchasedSkills.Add(SkillID);
// Purchase all selected skills
for (const FName& SkillID : SortedSelection)
{
FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (SkillNode)
{
// Mark as purchased
SkillNode->CurrentState = ESkillNodeState::Purchased;
PurchasedSkills.Add(SkillID);
// Apply effects
ApplySkillEffects(SkillNode->NodeData);
// Apply effects
ApplySkillEffects(SkillNode->NodeData);
// Broadcast purchase event
OnSkillPurchased.Broadcast(SkillID);
OnSkillStateChanged.Broadcast(SkillID);
}
}
// Broadcast purchase event
OnSkillPurchased.Broadcast(SkillID);
OnSkillStateChanged.Broadcast(SkillID);
}
}
// Deduct skill points
RemoveSkillPoints(CurrentSelectionCost);
// Deduct skill points
RemoveSkillPoints(CurrentSelectionCost);
// Clear selection
SelectedSkills.Empty();
UpdateSelectionCost();
UpdateSkillStates();
// Clear selection
SelectedSkills.Empty();
UpdateSelectionCost();
UpdateSkillStates();
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
return true;
return true;
}
bool USkillTreeComponent::IsSkillPurchased(FName SkillID) const
{
return PurchasedSkills.Contains(SkillID);
return PurchasedSkills.Contains(SkillID);
}
bool USkillTreeComponent::IsSkillSelected(FName SkillID) const
{
return SelectedSkills.Contains(SkillID);
return SelectedSkills.Contains(SkillID);
}
bool USkillTreeComponent::IsSkillAvailable(FName SkillID) const
{
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (!SkillNode)
{
return false;
}
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (!SkillNode)
return false;
return SkillNode->CurrentState == ESkillNodeState::Available;
return SkillNode->CurrentState == ESkillNodeState::Available;
}
bool USkillTreeComponent::CanSelectSkill(FName SkillID, FText& OutErrorMessage) const
bool USkillTreeComponent::CanSelectSkill(FName SkillID,
FText& OutErrorMessage) const
{
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (!SkillNode)
{
OutErrorMessage = FText::FromString(TEXT("Skill does not exist"));
return false;
}
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (!SkillNode)
{
OutErrorMessage = FText::FromString(TEXT("Skill does not exist"));
return false;
}
if (SkillNode->CurrentState == ESkillNodeState::Purchased)
{
OutErrorMessage = FText::FromString(TEXT("Skill already purchased"));
return false;
}
switch (SkillNode->CurrentState)
{
case ESkillNodeState::Purchased:
OutErrorMessage = FText::FromString(TEXT("Skill already purchased"));
return false;
if (SkillNode->bIsSelected)
{
OutErrorMessage = FText::FromString(TEXT("Skill already selected"));
return false;
}
case ESkillNodeState::Selected:
OutErrorMessage = FText::FromString(TEXT("Skill already selected"));
return false;
OutErrorMessage = FText::GetEmpty();
return true;
case ESkillNodeState::Locked:
case ESkillNodeState::Available:
OutErrorMessage = FText::GetEmpty();
return true;
default:
OutErrorMessage = FText::FromString(TEXT("Unknown skill state"));
return false;
}
}
ESkillNodeState USkillTreeComponent::GetSkillState(FName SkillID) const
{
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (SkillNode)
{
return SkillNode->CurrentState;
}
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (SkillNode)
return SkillNode->CurrentState;
return ESkillNodeState::Locked;
return ESkillNodeState::Locked;
}
bool USkillTreeComponent::GetSkillNodeData(FName SkillID, FSkillNodeRuntime& OutNodeData) const
bool USkillTreeComponent::GetSkillNodeData(FName SkillID,
FSkillNodeRuntime& OutNodeData) const
{
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (SkillNode)
{
OutNodeData = *SkillNode;
return true;
}
const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID);
if (SkillNode)
{
OutNodeData = *SkillNode;
return true;
}
return false;
return false;
}
TArray<FSkillNodeRuntime> USkillTreeComponent::GetAllSkillNodes() const
{
TArray<FSkillNodeRuntime> Result;
for (const auto& Pair : AllSkills)
{
Result.Add(Pair.Value);
}
return Result;
TArray<FSkillNodeRuntime> Result;
for (const auto& Pair : AllSkills)
Result.Add(Pair.Value);
return Result;
}
float USkillTreeComponent::GetTotalHealthBonus() const
{
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
Total += SkillNode->NodeData.HealthBonus;
}
}
return Total;
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
Total += SkillNode->NodeData.HealthBonus;
}
return Total;
}
float USkillTreeComponent::GetTotalDamageBonus() const
{
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
Total += SkillNode->NodeData.DamageBonus;
}
}
return Total;
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
Total += SkillNode->NodeData.DamageBonus;
}
return Total;
}
float USkillTreeComponent::GetTotalSpeedBonus() const
{
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
Total += SkillNode->NodeData.SpeedBonus;
}
}
return Total;
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
Total += SkillNode->NodeData.SpeedBonus;
}
return Total;
}
float USkillTreeComponent::GetTotalHealthBonusPercent() const
{
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
Total += SkillNode->NodeData.HealthBonusPercent;
}
}
return Total;
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
Total += SkillNode->NodeData.HealthBonusPercent;
}
return Total;
}
float USkillTreeComponent::GetTotalDamageBonusPercent() const
{
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
Total += SkillNode->NodeData.DamageBonusPercent;
}
}
return Total;
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
Total += SkillNode->NodeData.DamageBonusPercent;
}
return Total;
}
float USkillTreeComponent::GetTotalSpeedBonusPercent() const
{
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
Total += SkillNode->NodeData.SpeedBonusPercent;
}
}
return Total;
float Total = 0.0f;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
Total += SkillNode->NodeData.SpeedBonusPercent;
}
return Total;
}
void USkillTreeComponent::ResetAllSkills()
{
// Refund all skill points
int32 RefundAmount = 0;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
RefundAmount += SkillNode->NodeData.Cost;
RemoveSkillEffects(SkillNode->NodeData);
}
}
// Refund all skill points
int32 RefundAmount = 0;
for (const FName& SkillID : PurchasedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
RefundAmount += SkillNode->NodeData.Cost;
RemoveSkillEffects(SkillNode->NodeData);
}
}
// Clear purchases
PurchasedSkills.Empty();
// Clear purchases
PurchasedSkills.Empty();
// Reset all skill states
for (auto& Pair : AllSkills)
{
Pair.Value.CurrentState = ESkillNodeState::Locked;
Pair.Value.bIsSelected = false;
}
// Reset all skill states
for (auto& Pair : AllSkills)
Pair.Value.CurrentState = ESkillNodeState::Locked;
// Clear selection
SelectedSkills.Empty();
CurrentSelectionCost = 0;
// Clear selection
SelectedSkills.Empty();
CurrentSelectionCost = 0;
// Add refunded points
AddSkillPoints(RefundAmount);
// Add refunded points
AddSkillPoints(RefundAmount);
// Update states
UpdateSkillStates();
// Update states
UpdateSkillStates();
// Broadcast events
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
// Broadcast events
OnSkillSelectionChanged.Broadcast(CurrentSelectionCost);
}
void USkillTreeComponent::UpdateSkillStates()
{
for (auto& Pair : AllSkills)
{
FSkillNodeRuntime& SkillNode = Pair.Value;
for (auto& Pair : AllSkills)
{
FSkillNodeRuntime& SkillNode = Pair.Value;
// Skip if already purchased
if (SkillNode.CurrentState == ESkillNodeState::Purchased)
{
continue;
}
// Only update Locked and Available states based on prerequisites
// Purchased and Selected states should not be changed
switch (SkillNode.CurrentState)
{
case ESkillNodeState::Purchased:
case ESkillNodeState::Selected:
// Don't change these states
continue;
// Check if prerequisites are met
if (ArePrerequisitesMet(SkillNode.NodeData))
{
SkillNode.CurrentState = SkillNode.bIsSelected ?
ESkillNodeState::Selected : ESkillNodeState::Available;
}
else
{
SkillNode.CurrentState = SkillNode.bIsSelected ?
ESkillNodeState::Selected : ESkillNodeState::Locked;
}
}
case ESkillNodeState::Locked:
case ESkillNodeState::Available:
// Update state based on prerequisites
if (ArePrerequisitesMet(SkillNode.NodeData))
{
SkillNode.CurrentState = ESkillNodeState::Available;
}
else
{
SkillNode.CurrentState = ESkillNodeState::Locked;
}
break;
}
}
}
void USkillTreeComponent::UpdateSelectionCost()
{
CurrentSelectionCost = 0;
for (const FName& SkillID : SelectedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
CurrentSelectionCost += SkillNode->NodeData.Cost;
}
}
CurrentSelectionCost = 0;
for (const FName& SkillID : SelectedSkills)
{
if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID))
{
CurrentSelectionCost += SkillNode->NodeData.Cost;
}
}
}
bool USkillTreeComponent::ArePrerequisitesMet(const FSkillNodeData& SkillData) const
bool USkillTreeComponent::ArePrerequisitesMet(
const FSkillNodeData& SkillData) const
{
for (const FName& PrereqID : SkillData.Prerequisites)
{
if (!IsSkillPurchased(PrereqID))
{
return false;
}
}
return true;
for (const FName& PrereqID : SkillData.Prerequisites)
{
if (!IsSkillPurchased(PrereqID))
{
return false;
}
}
return true;
}
bool USkillTreeComponent::HasChildrenPurchased(FName SkillID) const
{
for (const auto& Pair : AllSkills)
{
// Pair.Key is the RowName (SkillID)
if (Pair.Value.NodeData.Prerequisites.Contains(SkillID) &&
IsSkillPurchased(Pair.Key))
{
return true;
}
}
return false;
for (const auto& Pair : AllSkills)
{
// Pair.Key is the RowName (SkillID)
if (Pair.Value.NodeData.Prerequisites.Contains(SkillID)
&& IsSkillPurchased(Pair.Key))
{
return true;
}
}
return false;
}
void USkillTreeComponent::ApplySkillEffects(const FSkillNodeData& SkillData)
{
// This is where you would apply the actual skill effects to the character
// For now, we just log it
UE_LOG(LogTemp, Log, TEXT("Applied skill effects for: %s"), *SkillData.DisplayName.ToString());
// This is where you would apply the actual skill effects to the character
// For now, we just log it
UE_LOG(LogTemp, Log, TEXT("Applied skill effects for: %s"),
*SkillData.DisplayName.ToString());
}
void USkillTreeComponent::RemoveSkillEffects(const FSkillNodeData& SkillData)
{
// This is where you would remove skill effects from the character
// For now, we just log it
UE_LOG(LogTemp, Log, TEXT("Removed skill effects for: %s"), *SkillData.DisplayName.ToString());
}
// This is where you would remove skill effects from the character
// For now, we just log it
UE_LOG(LogTemp, Log, TEXT("Removed skill effects for: %s"),
*SkillData.DisplayName.ToString());
}