fix: selection
This commit is contained in:
		| @@ -5,7 +5,7 @@ USkillTreeComponent::USkillTreeComponent() | ||||
| { | ||||
| 	PrimaryComponentTick.bCanEverTick = false; | ||||
|  | ||||
|     AvailableSkillPoints = 5; // Start with 5 skill points for testing | ||||
| 	AvailableSkillPoints   = 5; | ||||
| 	TotalSkillPointsEarned = 5; | ||||
| 	CurrentSelectionCost   = 0; | ||||
| } | ||||
| @@ -22,22 +22,22 @@ void USkillTreeComponent::InitializeSkillTree() | ||||
|  | ||||
| 	if (!SkillDataTable) | ||||
| 	{ | ||||
|         UE_LOG(LogTemp, Warning, TEXT("SkillTreeComponent: No skill data table assigned!")); | ||||
| 		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(); | ||||
|  | ||||
| 	for (const FName& RowName : RowNames) | ||||
| 	{ | ||||
|         FSkillNodeData* Row = SkillDataTable->FindRow<FSkillNodeData>(RowName, TEXT("SkillTreeComponent")); | ||||
| 		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); | ||||
| @@ -46,7 +46,8 @@ void USkillTreeComponent::InitializeSkillTree() | ||||
|  | ||||
| 	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) | ||||
| @@ -84,27 +85,24 @@ bool USkillTreeComponent::SelectSkill(FName SkillID) | ||||
|  | ||||
| 	FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID); | ||||
|  | ||||
|     // Check if already purchased | ||||
|     if (SkillNode->CurrentState == ESkillNodeState::Purchased) | ||||
| 	// 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) | ||||
|     { | ||||
| 	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 | ||||
| 	case ESkillNodeState::Locked: | ||||
| 		// For locked skills, we allow selection but validate prerequisites | ||||
| 		// 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) | ||||
| 		{ | ||||
| @@ -114,17 +112,22 @@ bool USkillTreeComponent::SelectSkill(FName SkillID) | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (!bWouldBeAvailable) | ||||
| 		{ | ||||
|             ErrorMessage = FText::FromString(TEXT("Prerequisites not selected. Select prerequisite skills first.")); | ||||
| 			ErrorMessage = FText::FromString(TEXT( | ||||
| 				"Prerequisites not selected. Select prerequisite skills first")); | ||||
| 			OnSelectionError.Broadcast(SkillID, ErrorMessage); | ||||
| 			return false; | ||||
| 		} | ||||
| 		break; | ||||
|  | ||||
| 	case ESkillNodeState::Available: | ||||
| 		// Available skills can be selected without additional checks | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	// Add to selection | ||||
|     SkillNode->bIsSelected = true; | ||||
| 	SkillNode->CurrentState = ESkillNodeState::Selected; | ||||
| 	SelectedSkills.Add(SkillID); | ||||
| 	UpdateSelectionCost(); | ||||
|  | ||||
| @@ -137,16 +140,12 @@ bool USkillTreeComponent::SelectSkill(FName SkillID) | ||||
| bool USkillTreeComponent::DeselectSkill(FName SkillID) | ||||
| { | ||||
| 	if (!AllSkills.Contains(SkillID)) | ||||
|     { | ||||
| 		return false; | ||||
|     } | ||||
|  | ||||
| 	FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID); | ||||
|  | ||||
|     if (!SkillNode->bIsSelected) | ||||
|     { | ||||
| 	if (SkillNode->CurrentState != ESkillNodeState::Selected) | ||||
| 		return false; | ||||
|     } | ||||
|  | ||||
| 	// Check if any selected skills depend on this one | ||||
| 	for (const FName& SelectedID : SelectedSkills) | ||||
| @@ -154,21 +153,28 @@ bool USkillTreeComponent::DeselectSkill(FName SkillID) | ||||
| 		if (SelectedID != SkillID) | ||||
| 		{ | ||||
| 			FSkillNodeRuntime* SelectedNode = AllSkills.Find(SelectedID); | ||||
|             if (SelectedNode && SelectedNode->NodeData.Prerequisites.Contains(SkillID)) | ||||
| 			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")); | ||||
| 				FText ErrorMessage = FText::FromString( | ||||
| 					TEXT("Other selected skills depend on this one")); | ||||
| 				OnSelectionError.Broadcast(SkillID, ErrorMessage); | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|     // Remove from selection | ||||
|     SkillNode->bIsSelected = false; | ||||
| 	// Remove from selection and update state | ||||
| 	SelectedSkills.Remove(SkillID); | ||||
| 	UpdateSelectionCost(); | ||||
|  | ||||
| 	// Update state to either Available or Locked based on prerequisites | ||||
| 	if (ArePrerequisitesMet(SkillNode->NodeData)) | ||||
| 		SkillNode->CurrentState = ESkillNodeState::Available; | ||||
| 	else | ||||
| 		SkillNode->CurrentState = ESkillNodeState::Locked; | ||||
|  | ||||
| 	OnSkillSelectionChanged.Broadcast(CurrentSelectionCost); | ||||
| 	OnSkillStateChanged.Broadcast(SkillID); | ||||
|  | ||||
| @@ -181,7 +187,11 @@ void USkillTreeComponent::ClearSelection() | ||||
| 	{ | ||||
| 		if (FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID)) | ||||
| 		{ | ||||
|             SkillNode->bIsSelected = false; | ||||
| 			// Update state based on prerequisites | ||||
| 			if (ArePrerequisitesMet(SkillNode->NodeData)) | ||||
| 				SkillNode->CurrentState = ESkillNodeState::Available; | ||||
| 			else | ||||
| 				SkillNode->CurrentState = ESkillNodeState::Locked; | ||||
| 			OnSkillStateChanged.Broadcast(SkillID); | ||||
| 		} | ||||
| 	} | ||||
| @@ -201,14 +211,18 @@ bool USkillTreeComponent::ConfirmPurchase() | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
|     // Sort selected skills by purchase order to ensure prerequisites are purchased first | ||||
| 	// 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) { | ||||
| 	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 NodeA->NodeData.PurchaseOrder | ||||
| 					< NodeB->NodeData.PurchaseOrder; | ||||
| 			} | ||||
| 			return false; | ||||
| 		}); | ||||
| @@ -218,17 +232,17 @@ bool USkillTreeComponent::ConfirmPurchase() | ||||
| 	{ | ||||
| 		FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID); | ||||
| 		if (!SkillNode) | ||||
|         { | ||||
| 			continue; | ||||
|         } | ||||
|  | ||||
|         // Check prerequisites (considering already processed skills in this batch) | ||||
| 		// 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 | ||||
| 				// Check if it's in our current batch and would be purchased | ||||
| 				// before this | ||||
| 				int32 PrereqIndex  = SortedSelection.IndexOfByKey(PrereqID); | ||||
| 				int32 CurrentIndex = SortedSelection.IndexOfByKey(SkillID); | ||||
|  | ||||
| @@ -243,9 +257,9 @@ bool USkillTreeComponent::ConfirmPurchase() | ||||
| 		if (!bPrereqsMet) | ||||
| 		{ | ||||
| 			FText ErrorMessage = FText::Format( | ||||
|                 FText::FromString(TEXT("Cannot purchase {0}: prerequisites not met")), | ||||
|                 SkillNode->NodeData.DisplayName | ||||
|             ); | ||||
| 				FText::FromString( | ||||
| 					TEXT("Cannot purchase {0}: prerequisites not met")), | ||||
| 				SkillNode->NodeData.DisplayName); | ||||
| 			OnSelectionError.Broadcast(SkillID, ErrorMessage); | ||||
| 			return false; | ||||
| 		} | ||||
| @@ -259,7 +273,6 @@ bool USkillTreeComponent::ConfirmPurchase() | ||||
| 		{ | ||||
| 			// Mark as purchased | ||||
| 			SkillNode->CurrentState = ESkillNodeState::Purchased; | ||||
|             SkillNode->bIsSelected = false; | ||||
| 			PurchasedSkills.Add(SkillID); | ||||
|  | ||||
| 			// Apply effects | ||||
| @@ -298,14 +311,13 @@ bool USkillTreeComponent::IsSkillAvailable(FName SkillID) const | ||||
| { | ||||
| 	const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID); | ||||
| 	if (!SkillNode) | ||||
|     { | ||||
| 		return false; | ||||
|     } | ||||
|  | ||||
| 	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) | ||||
| @@ -314,34 +326,38 @@ bool USkillTreeComponent::CanSelectSkill(FName SkillID, FText& OutErrorMessage) | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
|     if (SkillNode->CurrentState == ESkillNodeState::Purchased) | ||||
| 	switch (SkillNode->CurrentState) | ||||
| 	{ | ||||
| 	case ESkillNodeState::Purchased: | ||||
| 		OutErrorMessage = FText::FromString(TEXT("Skill already purchased")); | ||||
| 		return false; | ||||
|     } | ||||
|  | ||||
|     if (SkillNode->bIsSelected) | ||||
|     { | ||||
| 	case ESkillNodeState::Selected: | ||||
| 		OutErrorMessage = FText::FromString(TEXT("Skill already selected")); | ||||
| 		return false; | ||||
|     } | ||||
|  | ||||
| 	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; | ||||
|     } | ||||
|  | ||||
| 	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) | ||||
| @@ -357,9 +373,8 @@ TArray<FSkillNodeRuntime> USkillTreeComponent::GetAllSkillNodes() const | ||||
| { | ||||
| 	TArray<FSkillNodeRuntime> Result; | ||||
| 	for (const auto& Pair : AllSkills) | ||||
|     { | ||||
| 		Result.Add(Pair.Value); | ||||
|     } | ||||
|  | ||||
| 	return Result; | ||||
| } | ||||
|  | ||||
| @@ -369,10 +384,9 @@ float USkillTreeComponent::GetTotalHealthBonus() const | ||||
| 	for (const FName& SkillID : PurchasedSkills) | ||||
| 	{ | ||||
| 		if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID)) | ||||
|         { | ||||
| 			Total += SkillNode->NodeData.HealthBonus; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	return Total; | ||||
| } | ||||
|  | ||||
| @@ -382,10 +396,9 @@ float USkillTreeComponent::GetTotalDamageBonus() const | ||||
| 	for (const FName& SkillID : PurchasedSkills) | ||||
| 	{ | ||||
| 		if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID)) | ||||
|         { | ||||
| 			Total += SkillNode->NodeData.DamageBonus; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	return Total; | ||||
| } | ||||
|  | ||||
| @@ -395,10 +408,9 @@ float USkillTreeComponent::GetTotalSpeedBonus() const | ||||
| 	for (const FName& SkillID : PurchasedSkills) | ||||
| 	{ | ||||
| 		if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID)) | ||||
|         { | ||||
| 			Total += SkillNode->NodeData.SpeedBonus; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	return Total; | ||||
| } | ||||
|  | ||||
| @@ -408,10 +420,9 @@ float USkillTreeComponent::GetTotalHealthBonusPercent() const | ||||
| 	for (const FName& SkillID : PurchasedSkills) | ||||
| 	{ | ||||
| 		if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID)) | ||||
|         { | ||||
| 			Total += SkillNode->NodeData.HealthBonusPercent; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	return Total; | ||||
| } | ||||
|  | ||||
| @@ -421,10 +432,9 @@ float USkillTreeComponent::GetTotalDamageBonusPercent() const | ||||
| 	for (const FName& SkillID : PurchasedSkills) | ||||
| 	{ | ||||
| 		if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID)) | ||||
|         { | ||||
| 			Total += SkillNode->NodeData.DamageBonusPercent; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	return Total; | ||||
| } | ||||
|  | ||||
| @@ -434,10 +444,9 @@ float USkillTreeComponent::GetTotalSpeedBonusPercent() const | ||||
| 	for (const FName& SkillID : PurchasedSkills) | ||||
| 	{ | ||||
| 		if (const FSkillNodeRuntime* SkillNode = AllSkills.Find(SkillID)) | ||||
|         { | ||||
| 			Total += SkillNode->NodeData.SpeedBonusPercent; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
| 	return Total; | ||||
| } | ||||
|  | ||||
| @@ -459,10 +468,7 @@ void USkillTreeComponent::ResetAllSkills() | ||||
|  | ||||
| 	// Reset all skill states | ||||
| 	for (auto& Pair : AllSkills) | ||||
|     { | ||||
| 		Pair.Value.CurrentState = ESkillNodeState::Locked; | ||||
|         Pair.Value.bIsSelected = false; | ||||
|     } | ||||
|  | ||||
| 	// Clear selection | ||||
| 	SelectedSkills.Empty(); | ||||
| @@ -484,22 +490,27 @@ void USkillTreeComponent::UpdateSkillStates() | ||||
| 	{ | ||||
| 		FSkillNodeRuntime& SkillNode = Pair.Value; | ||||
|  | ||||
|         // Skip if already purchased | ||||
|         if (SkillNode.CurrentState == ESkillNodeState::Purchased) | ||||
| 		// 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 | ||||
| 		case ESkillNodeState::Locked: | ||||
| 		case ESkillNodeState::Available: | ||||
| 			// Update state based on prerequisites | ||||
| 			if (ArePrerequisitesMet(SkillNode.NodeData)) | ||||
| 			{ | ||||
|             SkillNode.CurrentState = SkillNode.bIsSelected ? | ||||
|                 ESkillNodeState::Selected : ESkillNodeState::Available; | ||||
| 				SkillNode.CurrentState = ESkillNodeState::Available; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
|             SkillNode.CurrentState = SkillNode.bIsSelected ? | ||||
|                 ESkillNodeState::Selected : ESkillNodeState::Locked; | ||||
| 				SkillNode.CurrentState = ESkillNodeState::Locked; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -516,7 +527,8 @@ void USkillTreeComponent::UpdateSelectionCost() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool USkillTreeComponent::ArePrerequisitesMet(const FSkillNodeData& SkillData) const | ||||
| bool USkillTreeComponent::ArePrerequisitesMet( | ||||
| 	const FSkillNodeData& SkillData) const | ||||
| { | ||||
| 	for (const FName& PrereqID : SkillData.Prerequisites) | ||||
| 	{ | ||||
| @@ -533,8 +545,8 @@ 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)) | ||||
| 		if (Pair.Value.NodeData.Prerequisites.Contains(SkillID) | ||||
| 			&& IsSkillPurchased(Pair.Key)) | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| @@ -546,12 +558,14 @@ 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()); | ||||
| 	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()); | ||||
| 	UE_LOG(LogTemp, Log, TEXT("Removed skill effects for: %s"), | ||||
| 		   *SkillData.DisplayName.ToString()); | ||||
| } | ||||
| @@ -8,19 +8,27 @@ | ||||
| UENUM(BlueprintType) | ||||
| enum class ESkillNodeState : uint8 | ||||
| { | ||||
|     Locked      UMETA(DisplayName = "Locked"),         // Can't be purchased yet (missing prerequisites) | ||||
|     Available   UMETA(DisplayName = "Available"),      // Can be purchased | ||||
|     Selected    UMETA(DisplayName = "Selected"),       // Currently selected for purchase | ||||
|     Purchased   UMETA(DisplayName = "Purchased")       // Already owned | ||||
| 	// Can't be purchased yet (missing prerequisites) | ||||
| 	Locked UMETA(DisplayName = "Locked"), | ||||
|  | ||||
| 	// Can be purchased | ||||
| 	Available UMETA(DisplayName = "Available"), | ||||
|  | ||||
| 	// Currently selected for purchase | ||||
| 	Selected UMETA(DisplayName = "Selected"), | ||||
|  | ||||
| 	// Already owned | ||||
| 	Purchased UMETA(DisplayName = "Purchased") | ||||
| }; | ||||
|  | ||||
| // Struct for individual skill node data | ||||
| // Note: RowName in the DataTable serves as the unique SkillID | ||||
| USTRUCT(BlueprintType) | ||||
| struct FSkillNodeData : public FTableRowBase | ||||
| { | ||||
| 	GENERATED_BODY() | ||||
|  | ||||
| 	// RowName will be used as SkillID | ||||
|  | ||||
| 	// Display name for UI | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") | ||||
| 	FText DisplayName; | ||||
| @@ -37,31 +45,27 @@ struct FSkillNodeData : public FTableRowBase | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") | ||||
| 	int32 Cost; | ||||
|  | ||||
|     // Purchase order priority (lower = buy first, used when confirming multiple skills) | ||||
| 	// Purchase order priority | ||||
| 	// (lower = buy first, used when confirming multiple skills) | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") | ||||
| 	int32 PurchaseOrder; | ||||
|  | ||||
|     // Prerequisites - must have these skills first (RowNames of required skills) | ||||
| 	// Prerequisites - must have these skills first | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Skill") | ||||
| 	TArray<FName> Prerequisites; | ||||
|  | ||||
|     // Effects this skill provides (actual stat bonuses) | ||||
| 	// Effects this skill provides | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects") | ||||
| 	float HealthBonus; | ||||
|  | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects") | ||||
| 	float DamageBonus; | ||||
|  | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects") | ||||
| 	float SpeedBonus; | ||||
|  | ||||
|     // Percentage bonuses (multiplicative) | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects") | ||||
| 	float HealthBonusPercent; | ||||
|  | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects") | ||||
| 	float DamageBonusPercent; | ||||
|  | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects") | ||||
| 	float SpeedBonusPercent; | ||||
|  | ||||
| @@ -93,12 +97,5 @@ struct FSkillNodeRuntime | ||||
| 	UPROPERTY(BlueprintReadOnly, Category = "Skill") | ||||
| 	ESkillNodeState CurrentState; | ||||
|  | ||||
|     UPROPERTY(BlueprintReadOnly, Category = "Skill") | ||||
|     bool bIsSelected; | ||||
|  | ||||
|     FSkillNodeRuntime() | ||||
|     { | ||||
|         CurrentState = ESkillNodeState::Locked; | ||||
|         bIsSelected = false; | ||||
|     } | ||||
| 	FSkillNodeRuntime() { CurrentState = ESkillNodeState::Locked; } | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user