Created by using Claude, Gemini, and ChatGPT to put together something using my collection of documents over the years.
BALLHALLA: CROSSOVER TACTICS - CONSOLIDATED TECHNICAL IMPLEMENTATION GUIDE
(Final Expanded Version - March 30, 2025)
Table of Contents
1. Game Overview & Core Concepts
- Genre: Tactical Turn-Based RPG meets Basketball.
- Platform: PC/Mobile.
- Target Age: 13+.
- Logline: After a crushing defeat, a ragtag basketball team is transported to Ballhalla, a magical realm where basketball is life and death. They must master card-based abilities, recruit defeated foes, and explore themed realms to challenge the usurper Mizio, restore order, and find the missing G.O.A.T.
- Core Gameplay: Command 5 ballers on a 9x12 grid (half-court). Use actions (Move, Fake, Talk, Pass, Screen, Shoot) within a 24-second shot clock (3s/action). Chain actions to execute Plays. Manage Stamina and build Hype to unlock powerful abilities. Customize team using a card-based system for ballers, plays, and equipment. Explore an overworld with 8 unique themed Sub-Realms. Features roguelike progression with autosaving and run-based resets.
2. Game Manager (AGameManager
)
- Purpose: Central controller managing overall game state, transitions between states (Overworld, Match, Menu, etc.), and holding references to other core managers. Orchestrates the main game flow and handles Save/Load operations.
- Code:
GameManager.h
/GameManager.cpp
// GameManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
// #include "BattleManager.h" // Forward declare or include needed headers
// #include "GridManager.h"
// #include "TurnManager.h"
// #include "OverworldManager.h"
// #include "TeamManager.h"
// #include "MatchManager.h"
// #include "EssenceManager.h"
// #include "ProgressionManager.h"
// #include "DeckManager.h"
// #include "CardCollectionManager.h" // Placeholder
#include "GameManager.generated.h"
UENUM(BlueprintType)
enum class EGameState : uint8
{
MainMenu UMETA(DisplayName = "Main Menu"),
Overworld UMETA(DisplayName = "Overworld"),
// CourtSelection UMETA(DisplayName = "Court Selection"), // May be part of TeamSetup
TeamSetup UMETA(DisplayName = "Team Setup"), // Deck/Roster adjustment pre-match
Match UMETA(DisplayName = "Match"), // Active gameplay
Results UMETA(DisplayName = "Results"), // Post-match screen
GameOver UMETA(DisplayName = "Game Over") // Run ended
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameStateChangedSignature, EGameState, NewState);
/**
* Main game manager that controls overall game flow and manager access.
*/
UCLASS()
class BALLHALLA_API AGameManager : public AActor
{
GENERATED_BODY()
public:
AGameManager();
virtual void BeginPlay() override;
// Current game state
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Game State")
EGameState CurrentGameState;
// Core Manager References (Ensure these are set up, e.g., via spawning or finding)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class ABattleManager* BattleManager;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class AGridManager* GridManager;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class ATurnManager* TurnManager;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class AOverworldManager* OverworldManager;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class AMatchManager* MatchManager;
// Component Managers (May be components on GameManager or PlayerState/GameState)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class UEssenceManager* EssenceManager;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class UProgressionManager* ProgressionManager;
// Card Management Components (Potentially on Player State)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class UDeckManager* PlayerDeckManager; // Manages in-match deck
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Managers")
class UCardCollectionManager* PlayerCardCollectionManager; // Manages persistent run collection
// State transitions
UFUNCTION(BlueprintCallable, Category = "Game State")
void TransitionToState(EGameState NewState);
// State entry/exit handlers (Called by TransitionToState)
protected:
virtual void HandleMainMenuState();
virtual void HandleOverworldState();
// virtual void HandleCourtSelectionState();
virtual void HandleTeamSetupState();
virtual void HandleMatchState();
virtual void HandleResultsState();
virtual void HandleGameOverState();
public:
// Game flow
UFUNCTION(BlueprintCallable, Category = "Game Flow")
void StartNewGame(); // Initializes a fresh run (resets facilities, run progress)
UFUNCTION(BlueprintCallable, Category = "Game Flow")
void SaveGame(); // Triggers autosave logic for current run (See Section 21)
UFUNCTION(BlueprintCallable, Category = "Game Flow")
void LoadGame(); // Loads game from autosave for current run (See Section 21)
// Meta Progression Save/Load potentially handled separately or via Profile system
// Events
UPROPERTY(BlueprintAssignable, Category = "Game State|Events")
FOnGameStateChangedSignature OnGameStateChanged;
};
3. Grid System (AGridManager
, UGridCell
)
- Purpose: Manages the 9x12 court grid, cell data, spatial queries (range, AoE), and coordinate conversions.
- Code:
GridManager.h/.cpp
,GridCell.h/.cpp
// GridCell.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "GridCell.generated.h"
UENUM(BlueprintType)
enum class ECourtArea : uint8
{
Paint UMETA(DisplayName = "Paint"), // 1-3 squares from rim
Midrange UMETA(DisplayName = "Midrange"), // 4-7 squares from rim
ThreePoint UMETA(DisplayName = "ThreePoint"), // 7-9 squares from rim
Deep UMETA(DisplayName = "Deep") // 9-12 squares from rim
};
/** Represents a single cell in the basketball court grid */
UCLASS(BlueprintType)
class BALLHALLA_API UGridCell : public UObject
{
GENERATED_BODY()
public:
UGridCell();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Grid")
FIntPoint Coordinates; // X, Y on the grid
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Grid") // ReadWrite to allow setting/clearing
TWeakObjectPtr<AActor> OccupyingActor; // Use TWeakObjectPtr to avoid circular refs
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Grid")
ECourtArea AreaType; // Pre-calculated area type
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Grid")
float DistanceFromRim; // Pre-calculated distance
// World location equivalent of the cell center
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Grid")
FVector WorldLocation;
UFUNCTION(BlueprintCallable, Category = "Grid")
bool IsOccupied() const;
UFUNCTION(BlueprintCallable, Category = "Grid")
bool CanBeOccupied() const; // Add logic (e.g., not occupied, not obstacle)
UFUNCTION(BlueprintCallable, Category = "Grid")
void SetOccupant(AActor* NewOccupant);
UFUNCTION(BlueprintCallable, Category = "Grid")
void ClearOccupant();
};
// GridManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GridCell.h" // Include GridCell header
#include "GridManager.generated.h"
/** Manages the 9x12 basketball court grid */
UCLASS()
class BALLHALLA_API AGridManager : public AActor
{
GENERATED_BODY()
public:
AGridManager();
virtual void BeginPlay() override;
// Grid dimensions
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Grid Config")
int32 Width = 9;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Grid Config")
int32 Height = 12;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Grid Config")
float CellSize = 100.0f; // World units per cell
// Rim location in grid coordinates (configurable)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Grid Config")
FIntPoint RimCoordinates = FIntPoint(4, 0); // Example: Center of back row
protected:
// Grid data storage (Use flat array for cache efficiency)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Grid")
TArray<UGridCell*> GridCells;
private:
// Initialize grid cells, calculate distances, areas, world locations
void InitializeGrid();
void SetupCourtAreas(); // Assign AreaType based on DistanceFromRim
void CalculateDistancesFromRim(); // Calculate DistanceFromRim for each cell
public:
// Get cell object at coordinates
UFUNCTION(BlueprintCallable, Category = "Grid Query")
UGridCell* GetCellAt(int32 X, int32 Y) const;
UFUNCTION(BlueprintCallable, Category = "Grid Query")
UGridCell* GetCellAtCoord(const FIntPoint& Coords) const;
// Get cells within Manhattan distance range
UFUNCTION(BlueprintCallable, Category = "Grid Query")
TArray<UGridCell*> GetCellsInRange(const UGridCell* Origin, int32 Range) const;
// Get cells affected by an Area of Effect pattern relative to a target cell
UFUNCTION(BlueprintCallable, Category = "Grid Query")
TArray<UGridCell*> GetAreaOfEffect(const UGridCell* Target, const TArray<FIntPoint>& Pattern) const;
// Convert grid coordinates to world location (center of cell)
UFUNCTION(BlueprintCallable, Category = "Grid Conversion")
FVector GridToWorldLocation(const FIntPoint& GridCoords) const;
// Convert world location to grid coordinates
UFUNCTION(BlueprintCallable, Category = "Grid Conversion")
FIntPoint WorldToGridLocation(const FVector& WorldLocation) const;
// Calculate shooting modifier based on cell properties (e.g., distance, area type)
UFUNCTION(BlueprintCallable, Category = "Grid Gameplay")
float CalculateShootingModifier(const UGridCell* FromCell) const;
// Helper to check if coordinates are valid
UFUNCTION(BlueprintPure, Category = "Grid Query")
bool IsValidCoordinate(const FIntPoint& Coords) const;
// Pathfinding (Simple BFS suitable for this grid size)
UFUNCTION(BlueprintCallable, Category = "Grid Pathfinding")
TArray<UGridCell*> FindPath(const UGridCell* StartCell, const UGridCell* EndCell) const;
};
(Implementation Notes for Grid System: Pre-calculate DistanceFromRim
, AreaType
, and WorldLocation
during InitializeGrid
for efficiency. Use Manhattan distance for range calculations unless diagonal movement is explicitly allowed.)
4. Turn Manager (ATurnManager
)
- Purpose: Manages the current game phase (Offense/Defense), shot clock, quarter progression, and phase transitions.
- Code:
TurnManager.h/.cpp
// TurnManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TurnManager.generated.h"
UENUM(BlueprintType)
enum class EGamePhase : uint8
{
PreMatch UMETA(DisplayName = "Pre-Match"), // Setup phase
Offense UMETA(DisplayName = "Offense"),
Defense UMETA(DisplayName = "Defense"), // AI simulates offense here
Timeout UMETA(DisplayName = "Timeout"),
Halftime UMETA(DisplayName = "Halftime"),
PostMatch UMETA(DisplayName = "Post-Match"), // Results/Rewards
GameOver UMETA(DisplayName = "Game Over") // End of run
};
// Add delegates for events if needed by UI or other systems
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPhaseChangedSignature, EGamePhase, NewPhase);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnShotClockUpdatedSignature, int32, TimeRemaining);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnQuarterChangedSignature, int32, NewQuarter);
/** Manages turns, shot clock, and game phases within a match */
UCLASS()
class BALLHALLA_API ATurnManager : public AActor
{
GENERATED_BODY()
public:
ATurnManager();
virtual void BeginPlay() override;
// Removed Tick if updates are event-driven
// --- Configuration ---
UPROPERTY(EditDefaultsOnly, Category = "Turn Config")
int32 MaxShotClockTime = 24;
UPROPERTY(EditDefaultsOnly, Category = "Turn Config")
int32 TimePerAction = 3;
// --- State ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Turn State")
EGamePhase CurrentPhase;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Turn State")
int32 CurrentShotClockTime;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Turn State")
int32 CurrentQuarter = 1;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Turn State")
bool bIsPlayerTeamOffense = true; // Tracks which team has offensive possession
// --- Phase Management ---
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void StartMatch(bool bPlayerStartsOnDefense = false); // Initialize for match start
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void StartNextPhase(); // Core logic to transition (e.g., Offense -> Defense -> Offense...)
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void StartOffensePhase();
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void StartDefensePhase();
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void StartTimeoutPhase();
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void EndTimeoutPhase();
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void StartHalftimePhase();
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void EndHalftimePhase();
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void StartPostMatchPhase();
UFUNCTION(BlueprintCallable, Category = "Turn Flow")
void EndMatch(); // Sets state to GameOver or triggers results
// --- Shot Clock ---
UFUNCTION(BlueprintCallable, Category = "Turn Actions")
void ConsumeActionTime(); // Decreases shot clock by TimePerAction
UFUNCTION(BlueprintCallable, Category = "Turn State")
bool IsShotClockExpired() const;
UFUNCTION(BlueprintCallable, Category = "Turn Actions")
void ResetShotClock();
// --- Event Handling (Called by BattleManager/MatchManager) ---
UFUNCTION(BlueprintCallable, Category = "Turn Events")
void HandlePossessionChange(); // Switches bIsPlayerTeamOffense, resets clock, starts next phase
UFUNCTION(BlueprintCallable, Category = "Turn Events")
void HandleShotAttemptOutcome(bool bMadeShot, bool bIsBlocked); // Determines next step based on shot result
UFUNCTION(BlueprintCallable, Category = "Turn Events")
void HandleTurnoverOccurred(bool bIsShotClockViolation); // Ends offense phase due to turnover
UFUNCTION(BlueprintCallable, Category = "Turn Events")
void HandleEndOfQuarter(); // Logic before incrementing quarter
UFUNCTION(BlueprintCallable, Category = "Turn Events")
void HandleEndOfHalf(); // Triggers halftime
// --- Event Dispatchers ---
UPROPERTY(BlueprintAssignable, Category = "Turn|Events")
FOnPhaseChangedSignature OnPhaseChanged;
UPROPERTY(BlueprintAssignable, Category = "Turn|Events")
FOnShotClockUpdatedSignature OnShotClockUpdated;
UPROPERTY(BlueprintAssignable, Category = "Turn|Events")
FOnQuarterChangedSignature OnQuarterChanged;
private:
// Helper to change phase and broadcast event
void SetPhase(EGamePhase NewPhase);
void UpdateShotClock(int32 NewTime);
void AdvanceQuarter();
};
(Implementation Notes for Turn Manager: Ensure phase transitions correctly handle quarter/halftime logic and interact with MatchManager
.)
5. Baller Character (ABallerCharacter
)
- Purpose: Base class for all player-controlled and AI ballers. Contains stats, resources, state, abilities, and action implementations.
- Code:
BallerCharacter.h/.cpp
// BallerCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameplayTagContainer.h" // Include for Synergy/Status Tags
#include "GridCell.h" // Include GridCell
// Include headers for Ability, EquipmentCard if needed
#include "BallerCharacter.generated.h"
// Forward Declarations
class UGridCell;
class UAbility;
class UEquipmentCard;
class ATeamManager; // For team reference
class UStatusManagerComponent; // For Status Effects
// Define Enums (ensure globally accessible or within appropriate scope)
UENUM(BlueprintType) enum class EPosition : uint8 { PointGuard, ShootingGuard, SmallForward, PowerForward, Center };
UENUM(BlueprintType) enum class ERank : uint8 { Rookie, Starter, Veteran, AllStar, MVP, HallOfFamer };
UENUM(BlueprintType) enum class EEssenceType : uint8 { None, Werewolf, Voodoo, Witch, Beast, Ocean, Police, TreePeople, Vampire };
UENUM(BlueprintType) enum class EActionType : uint8 { Move, Fake, Talk, Shoot, Pass, Screen, HypeAbility }; // Basic Types
UENUM(BlueprintType) enum class EMoveType : uint8 { Cut, Drive, Juke, Pick };
UENUM(BlueprintType) enum class EFakeType : uint8 { Crossover, BallFake, TripleThreat, PostUp };
UENUM(BlueprintType) enum class ETalkType : uint8 { Playcall, TrashTalk, Leadership, ISO };
UENUM(BlueprintType) enum class EHypeLevel : uint8 { None, Low, Medium, High, Max };
USTRUCT(BlueprintType)
struct FBallerStats // Structure to hold baller stats for easier management
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float MaxStamina = 100.0f;
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "Stats")
float CurrentStamina = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float MaxHype = 100.0f;
UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "Stats")
float CurrentHype = 0.0f;
// --- Core Basketball Stats ---
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Offense")
float TwoPointPercentage = 50.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Offense")
float ThreePointPercentage = 35.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Offense")
float PassRating = 50.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Offense")
float OffensiveRating = 50.0f; // General offensive capability
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Physical")
float DribbleRating = 50.0f; // Handle
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Physical")
float SpeedRating = 50.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Physical")
float Movement = 4.0f; // Grid cells per move action base
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Defense")
float ReboundRating = 50.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Defense")
float BlockRating = 50.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Defense")
float DefensiveRating = 50.0f; // General defensive capability
// Gravity - Influences AI behavior (See Section 5.1)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats|Special")
float NaturalGravity = 3.0f;
// Gameplay Tags inherent to this baller's role/archetype (for Synergy System)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats|Tags")
FGameplayTagContainer InherentTags;
};
/** Base class for all basketball players (ballers) */
UCLASS()
class BALLHALLA_API ABallerCharacter : public ACharacter
{
GENERATED_BODY()
public:
ABallerCharacter();
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
// --- Identity & Role ---
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Baller|Identity")
FString BallerName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Baller|Identity", meta=(ToolTip="Unique ID for save games and tracking"))
FString UniqueID; // Needed for XP tracking / SaveGame persistence
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Baller|Identity")
EPosition Position;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Baller|Identity", SaveGame) // Rank persists
ERank Rank = ERank::Rookie;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Baller|Identity", SaveGame) // Essence empowerment persists
EEssenceType CurrentEssenceType = EEssenceType::None;
// --- Stats ---
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Baller|Stats", meta = (ShowOnlyInnerProperties))
FBallerStats BaseStats; // Holds the core, unmodified stats
// --- Status Effects ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Baller|Status", meta=(ToolTip="Manages temporary conditions affecting this baller"))
UStatusManagerComponent* StatusManager; // Component manages active conditions (See Section 5.2)
// --- State ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Baller|State")
UGridCell* CurrentCell; // Cell the baller occupies
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Baller|State") // ReadWrite for BattleManager to set
bool bHasBall;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Baller|State")
bool bIsExhausted; // True if CurrentStamina reaches zero
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Baller|State")
TWeakObjectPtr<ATeamManager> OwningTeam; // Reference to the team manager
// --- Gameplay Actions (Called by BattleManager) ---
// These functions contain the core logic for executing an action
virtual bool PerformMove(UGridCell* TargetCell, EMoveType MoveType);
virtual bool PerformFake(ABallerCharacter* Target, EFakeType FakeType);
virtual bool PerformTalk(ETalkType TalkType);
virtual bool PerformShoot(UGridCell* TargetCell); // TargetCell is usually basket cell based on current position
virtual bool PerformPass(ABallerCharacter* TargetReceiver);
virtual bool PerformScreen(UGridCell* TargetCell);
virtual bool PerformHypeAbility(int32 AbilityIndex /* or Ability Ptr */, const struct FAbilityTargetData& TargetData); // Define FAbilityTargetData
// --- Resource Management ---
UFUNCTION(BlueprintCallable, Category = "Baller|Resources")
bool ConsumeStamina(float Amount); // Returns false if not enough stamina (unless forced)
UFUNCTION(BlueprintCallable, Category = "Baller|Resources")
void GainStamina(float Amount);
UFUNCTION(BlueprintCallable, Category = "Baller|Resources")
void GainHype(float Amount);
UFUNCTION(BlueprintCallable, Category = "Baller|Resources")
bool ConsumeHype(float Amount); // Returns false if not enough hype
// --- State Checks ---
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Baller|State")
bool CanPerformAction(FGameplayTag ActionTag) const; // Checks if exhausted, affected by status effects (using tags) etc.
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Baller|State")
EHypeLevel GetCurrentHypeLevel() const; // Translates CurrentHype value to Enum
// --- Grid Interaction ---
UFUNCTION(BlueprintCallable, Category = "Baller|Grid")
void SetCurrentCell(UGridCell* NewCell);
// --- Ability & Equipment Management ---
// TODO: Define how abilities/equipment are stored and accessed (Potentially via Components)
// UPROPERTY(SaveGame) TArray<UEquipmentCard*> EquippedItems; // Needs reference to Card Data Assets
// --- Natural Gravity ---
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Baller|Stats")
float GetEffectiveGravity() const; // Calculates current gravity based on state (See Section 5.1)
// --- Stat Calculation ---
// Function to get the final value of a stat considering base, modifiers, status effects etc.
// UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Baller|Stats")
// float GetCurrentStatValue(EStatType StatToGet) const;
// --- Gameplay Tags ---
// UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Baller|Tags")
// FGameplayTagContainer GetCurrentGameplayTags() const; // Combine inherent + status effect tags
protected:
// Called on initialization or when Rank/Position/Essence changes
virtual void ApplyPositionModifiers(); // Adjust stats based on Position enum
virtual void ApplyEssenceModifiers(); // Adjust stats/grant abilities based on CurrentEssenceType
virtual void ApplyRankModifiers(); // Adjust stats/grant abilities based on Rank enum
// Update exhaustion state based on stamina
virtual void UpdateExhaustionState();
// Recalculate derived stats whenever base stats or modifiers change
virtual void RecalculateDerivedStats();
};
5.1. Natural Gravity Mechanic
- Purpose: Represents the defensive attention an offensive baller commands, influencing AI behavior.
- Core Effects:
- AI Targeting: Higher
EffectiveGravity
increases AI priority for guarding assignments/switches. - AI Help Positioning: Nearby AI defenders guarding lower-gravity players may "cheat" slightly towards high-gravity players (subtle effect).
- AI Behavior Trigger: High
EffectiveGravity
on ball-handler is the main trigger for AI consideringDoubleTeam
.
- AI Targeting: Higher
- Calculation:
EffectiveGravity = (BaseNaturalGravity + LocationBonus) * BallPossessionModifier * HypeModifier
.BaseNaturalGravity
: FromFBallerStats
.LocationBonus
: (Placeholder) Bonus in optimal scoring areas.BallPossessionModifier
: (Placeholder) Increases the longer baller holds the ball.HypeModifier
: (Placeholder) Increases with baller'sCurrentHype
.
- Interactions: Closer defenders increase shot contest difficulty. Screens can impede defenders pulled by gravity and temporarily increase screener's perceived gravity to that defender.
5.2. Status Effect / Condition System
- Purpose: To manage temporary or persistent effects applied to a baller that alter their stats, available actions, or passive behavior. Conditions add strategic depth, enabling combos, counterplay, and tactical team-building by modifying baller capabilities dynamically.
- Core Structure (
FConditionEffect
): Conditions should be defined using a struct, likely managed via Data Assets (UConditionDataAsset
inheriting fromUDataAsset
containing anFConditionEffect
struct) for easy creation and referencing.USTRUCT(BlueprintType) struct FConditionEffect { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition") FName ConditionName; // Unique identifier (e.g., "Slowed", "Hexed", "Exhausted") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition") FString Description; // Tooltip explanation UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition", meta=(ToolTip="Turns remaining. Decremented per full turn cycle (Off+Def). -1 for persistent/conditional removal.")) int32 DurationTurns; // Modifiers applied while condition is active UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition") TArray<FStatModifier> StatModifiers; // Reference the unified FStatModifier struct // Restrictions imposed by the condition using Gameplay Tags UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition", meta=(ToolTip="Gameplay Tags identifying actions blocked by this condition.")) FGameplayTagContainer BlockedActionTags; // e.g., Blocks tags like "Action.Ability", "Action.Move" UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition") bool bIsBuff; // True for positive effects, False for negative (UI/visual distinction) UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition") TSoftObjectPtr<UTexture2D> Icon; // For display on HUD/UI elements // Optional: Add particle system references for visual effects // UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Condition") // TSoftObjectPtr<UParticleSystem> ConditionVFX; };
- Management Component (
UStatusManagerComponent
): Recommended approach is an Actor Component attached toABallerCharacter
. Responsibilities:ApplyCondition(UConditionDataAsset* ConditionData)
: Adds a condition. Handles stacking rules (e.g., refresh duration, block application if immune, stack intensity).RemoveCondition(FName ConditionName)
: Removes a specific condition instance.TickConditions(EGamePhase CurrentPhase)
: Called by the Turn Manager or Battle Manager at appropriate intervals (e.g., end of a full turn cycle) to decrementDurationTurns
and remove expired conditions.GetCurrentStatModifiers() const
: Aggregates allStatModifiers
from active conditions.IsActionBlocked(FGameplayTag ActionTag) const
: Checks if any active condition'sBlockedActionTags
match the tag of the action being attempted.- Event dispatchers for UI updates when conditions are applied/removed/tick.
- Example Conditions (Illustrative):
- Exhausted: (Debuff, Applied at 0 Stamina)
DurationTurns = -1
(until Stamina > 0).StatModifiers
: Significant negative Shooting%, Move Range.BlockedActionTags
:Action.Ability
,Action.Hype
. - Slowed: (Debuff)
DurationTurns = 2
(e.g., 1 full Quarter).StatModifiers
:-2 Movement
. Source: Crossovers, Ice Abilities. - Hexed: (Debuff)
DurationTurns = 3
.StatModifiers
: Applies random stat reduction modifier dynamically each turn (requires custom logic). Source: Witch Abilities. - Hydrated: (Buff)
DurationTurns = 2
.StatModifiers
:-50% StaminaCostMultiplier
(requires custom stat mod handling). Source: Water Bottle Card. - Shooter's Touch: (Buff)
DurationTurns = -1
(Match duration).StatModifiers
:+10% ShootingPercentage
. Source: Equipment Card. - Icy Veins: (Buff)
DurationTurns = -1
. Conditional Stat Modifier (requires custom logic checking shot clock). Source: Card/Ability.
- Exhausted: (Debuff, Applied at 0 Stamina)
- Application Sources: Actions (specific outcomes), Abilities, Card effects, Equipment (passive or active), Overworld events.
- Integration: Requires modification of stat calculation logic in
ABallerCharacter
to account for modifiers from theUStatusManagerComponent
. Action validation logic needs to checkBlockedActionTags
. UI needs to display active conditions clearly. - MVP Status: Post-MVP. The core loop functions without complex status effects, though the existing
bIsExhausted
state serves as a basic condition.
6. Team Manager (ATeamManager
)
- Purpose: Manages a single team (player or AI), including their roster of active and benched ballers, team-wide effects, and who currently possesses the ball within the team.
- Code:
TeamManager.h/.cpp
// TeamManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BallerCharacter.h" // Include BallerCharacter header
// Include headers for FStatModifier if needed
#include "TeamManager.generated.h"
// Forward declarations
struct FStatModifier; // Use the unified name
/** Manages a team of ballers (Player or Enemy) */
UCLASS()
class BALLHALLA_API ATeamManager : public AActor
{
GENERATED_BODY()
public:
ATeamManager();
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override; // For updating temporary effects
// --- Team Identity ---
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Team Config")
FString TeamName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Team Config")
UTexture2D* TeamLogo;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Team Config")
bool bIsPlayerTeam = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Team Config")
EEssenceType TeamEssence = EEssenceType::None; // Primary essence type for AI teams
// --- Roster ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Team Roster")
TArray<ABallerCharacter*> ActiveBallers; // Max 5 on court
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Team Roster")
TArray<ABallerCharacter*> BenchedBallers; // Available substitutes
// --- State ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Team State")
TWeakObjectPtr<ABallerCharacter> BallerWithBall; // Current baller holding the ball
// --- Roster Management ---
UFUNCTION(BlueprintCallable, Category = "Team Management")
bool AddBallerToRoster(ABallerCharacter* Baller, bool bStartActive = false);
UFUNCTION(BlueprintCallable, Category = "Team Management")
bool RemoveBallerFromRoster(ABallerCharacter* Baller);
UFUNCTION(BlueprintCallable, Category = "Team Management")
bool SubstituteBaller(ABallerCharacter* BallerToRemove, ABallerCharacter* BallerToAdd); // Swaps between Active/Bench
// --- Gameplay ---
UFUNCTION(BlueprintCallable, Category = "Team Gameplay")
ABallerCharacter* GetBallerWithBall() const;
UFUNCTION(BlueprintCallable, Category = "Team Gameplay")
void SetBallerWithBall(ABallerCharacter* Baller); // Nullptr if no one has ball
UFUNCTION(BlueprintCallable, Category = "Team Gameplay")
void ClearBallPossession();
UFUNCTION(BlueprintCallable, Category = "Team Gameplay")
float GetTeamHype() const; // Calculates combined hype from ActiveBallers
UFUNCTION(BlueprintCallable, Category = "Team Gameplay")
void ApplyTeamStatModifier(const FStatModifier& Modifier); // Apply temporary buff/debuff to all active ballers
UFUNCTION(BlueprintCallable, Category = "Team Gameplay")
void UpdateTemporaryEffects(float DeltaTime); // Decrement duration of modifiers
private:
// Store active temporary modifiers affecting the whole team
UPROPERTY()
TArray<FStatModifier> ActiveTeamModifiers;
};
7. Battle Manager (ABattleManager
)
- Purpose: Orchestrates the actions within a single offensive or defensive turn. Handles player input validation, calls actions on
ABallerCharacter
, resolves action outcomes (like shooting percentages, turnovers), and interacts withATurnManager
to update game state. Owns the Action History. - Code:
BattleManager.h/.cpp
// BattleManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameplayTagContainer.h" // For Turnover Reasons
// #include "TurnManager.h" // Forward declare needed managers
// #include "GridManager.h"
// #include "TeamManager.h"
// #include "BallerCharacter.h"
#include "BattleManager.generated.h"
// Enums EActionType, EFakeType, ETalkType, EMoveType defined previously
// Structure to hold parameters for various actions
USTRUCT(BlueprintType)
struct FActionParams // Define needed parameters for all action types
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite) TWeakObjectPtr<UGridCell> TargetCell;
UPROPERTY(EditAnywhere, BlueprintReadWrite) TWeakObjectPtr<ABallerCharacter> TargetBaller;
UPROPERTY(EditAnywhere, BlueprintReadWrite) EFakeType FakeType = EFakeType::Crossover;
UPROPERTY(EditAnywhere, BlueprintReadWrite) ETalkType TalkType = ETalkType::Playcall;
UPROPERTY(EditAnywhere, BlueprintReadWrite) EMoveType MoveType = EMoveType::Cut;
// Add params for Ability Index/Data if abilities are handled here
// FAbilityTargetData TargetData; // If needed
};
// Structure to record action history
USTRUCT(BlueprintType)
struct FActionRecord
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite) TWeakObjectPtr<ABallerCharacter> ActingBaller;
UPROPERTY(EditAnywhere, BlueprintReadWrite) EActionType ActionType;
UPROPERTY(EditAnywhere, BlueprintReadWrite) FActionParams Params;
UPROPERTY(EditAnywhere, BlueprintReadWrite) float Timestamp; // Game time action occurred
UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bWasSuccessful; // Did the action succeed
};
/** Manages the core basketball battle gameplay logic and action resolution */
UCLASS()
class BALLHALLA_API ABattleManager : public AActor
{
GENERATED_BODY()
public:
ABattleManager();
virtual void BeginPlay() override;
// --- Manager References ---
UPROPERTY() TWeakObjectPtr<ATurnManager> TurnManager;
UPROPERTY() TWeakObjectPtr<AGridManager> GridManager;
UPROPERTY() TWeakObjectPtr<AMatchManager> MatchManager; // For reporting score changes etc.
UPROPERTY() TWeakObjectPtr<ATeamManager> PlayerTeam;
UPROPERTY() TWeakObjectPtr<ATeamManager> EnemyTeam;
// Potentially references to PlaySystemManager, HypeActionManager etc.
// --- Action Handling (Player Input Driven) ---
// Called by PlayerController or UI based on player input
UFUNCTION(BlueprintCallable, Category = "Battle Actions")
bool RequestBallerAction(ABallerCharacter* Baller, EActionType ActionType, FActionParams Params);
private:
// --- Action Resolution (Internal Logic) ---
// These functions are called internally by RequestBallerAction after validation
bool ExecuteMoveAction(ABallerCharacter* Mover, UGridCell* TargetCell, EMoveType MoveType);
bool ExecuteShootAction(ABallerCharacter* Shooter); // Calculates chance, resolves hit/miss
bool ExecutePassAction(ABallerCharacter* Passer, ABallerCharacter* Receiver); // Calculates chance, resolves completion/turnover
bool ExecuteFakeAction(ABallerCharacter* Faker, ABallerCharacter* Target, EFakeType FakeType); // Target is defender being faked
bool ExecuteTalkAction(ABallerCharacter* Talker, ETalkType TalkType);
bool ExecuteScreenAction(ABallerCharacter* Screener, UGridCell* TargetCell); // TargetCell is where screen is set
// bool ExecuteHypeAbilityAction(...);
// --- Gameplay Logic ---
UFUNCTION(BlueprintCallable, Category = "Battle Logic")
float CalculateShootingPercentage(ABallerCharacter* Shooter, const UGridCell* FromCell);
UFUNCTION(BlueprintCallable, Category = "Battle Logic")
void ResolveShot(ABallerCharacter* Shooter, bool bWasBlocked); // Determines Make/Miss based on percentage
UFUNCTION(BlueprintCallable, Category = "Battle Logic")
void HandleRebound(); // Initiates the rebound contest (See Section 7.1)
// Use Gameplay Tags for Turnover Reasons for flexibility
UFUNCTION(BlueprintCallable, Category = "Battle Logic")
void TriggerTurnover(ABallerCharacter* LosingBaller, FGameplayTag TurnoverReason); // Notifies TurnManager (See Section 7.2)
UFUNCTION(BlueprintCallable, Category = "Battle Logic")
void AddPointsToScore(bool bToPlayerTeam, int32 Points); // Notifies MatchManager
// --- Action History ---
// BattleManager owns the definitive history for replay/analysis/play checking
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Battle History")
TArray<FActionRecord> ActionHistory;
UFUNCTION(BlueprintCallable, Category = "Battle History")
void RecordAction(ABallerCharacter* Baller, EActionType ActionType, const FActionParams& Params, bool bSuccess);
UFUNCTION(BlueprintCallable, Category = "Battle History")
TArray<FActionRecord> GetRecentActions(int32 Count = 5) const;
// Helper function to get the correct team manager based on baller affiliation
ATeamManager* GetTeamForBaller(ABallerCharacter* Baller) const;
};
7.1. Rebounding Logic
- Purpose: Determines possession after a missed shot.
- Core Mechanic: Probabilistic contest based on
ReboundScore
calculated for participants within 2-3 cells of the basket. - Score Factors:
ReboundRating
, Proximity Bonus, Box Out Modifier (Penalty if opponent between baller & basket), Role Bonus (C/PF), Stamina Penalty, Hype/Ability/Equipment Mods. - Participants: Includes nearby players and the original shooter.
- Resolution: Weighted random roll based on scores. Tie-breaker: Higher Hype wins; if tied, Defender wins.
- Outcomes:
- Offensive Rebound: Offense continues, shot clock doesn't reset, Hype gain (extra if shooter rebounds).
- Defensive Rebound: Offense ends, defense gains minimal Points, potential Fast Break bonus. Phase transition occurs.
7.2. Turnover Causes
- Purpose: Lists conditions that end the offensive phase prematurely.
- Causes:
- Shot Clock Violation: Clock reaches 0.
- Defensive Rebound: Defense secures rebound after missed shot.
- Pass Interception (Steal): Defender successfully intercepts pass. Chance based on Passer/Receiver/Defender ratings, distance, Hype difference, Passer Stamina.
- On-Ball Steal (Swipe): Defender successfully swipes ball from dribbler. Costs defender Stamina. Chance based on Dribbler/Defender ratings, Stamina, Hype. Risky for defense.
- Failed Offensive Actions: Fakes/Drives can fail and lose ball if user has low Stamina/Rating vs high-rated Defender.
- Defensive Pressure (Traps): Being Double Teamed increases turnover chance on subsequent actions (Pass/Fake).
- Exclusions (for now): Offensive Fouls, Out of Bounds.
8. Card System (UCardBase
, UBallerCard
, UPlayCard
, UEquipmentCard
)
- Purpose: Defines the structure for different card types. Use Data Assets (
UDataAsset
orUPrimaryDataAsset
) for defining specific cards. - Code Structure:
FStatModifier
: Define struct centrally (see Section 11).UCardBase
(Base Data Asset): Contains common properties: Name, Description, Rarity (Enum: Common, Uncommon, Rare, Epic, Legendary), CardType (Enum: Baller, Play, Equipment), CardImage, EssenceType (Enum),FGameplayTagContainer
GameplayTags. Maybe virtualGetDescriptionText()
for dynamic text.UBallerCardData
(Derived Data Asset): ContainsFBallerStats
template,TSubclassOf<ABallerCharacter>
for base class, list of inherent startingTSoftObjectPtr<UAbility>
or Ability Tags. Functionality: Defines baller data template.UPlayCardData
(Derived Data Asset): Contains sequence ofFPlayAction
structs (defining ActionType, Position requirement, etc.),TArray<FStatModifier>
success bonuses,DifficultyRating
,TArray<EPosition>
requirements. Functionality: Defines play data template.UEquipmentCardData
(Derived Data Asset): ContainsbIsPassive
,TArray<FStatModifier>
passive bonuses,TSoftObjectPtr<UAbility>
granted active ability (if any). Functionality: Defines equipment data template.
(Implementation Notes: UBallerCardData
defines templates; actual ABallerCharacter
instances are managed by ATeamManager
. Play/Equipment effects applied via BattleManager
or components.)
8.1. Card Synergy / Tag System (Placeholder)
- Purpose: Facilitates deeper strategy via tags on cards/abilities/ballers defining relationships and enabling synergistic effects.
- Scope & Implementation: Use Unreal Engine's Gameplay Tag system (
FGameplayTag
,FGameplayTagContainer
). Define a hierarchy in Project Settings (e.g.,Essence.Werewolf
,Action.Shoot
,Role.PG
,Effect.Buff
,Condition.Slowed
,Combo.Starter
). AddFGameplayTagContainer
properties to relevant data assets (UCardBase
,UAbility
definitions) and potentially toFBallerStats
orABallerCharacter
. Implement game logic checks for tags within abilities/effects based on querying tags (HasMatchingGameplayTag
, etc.). - Example Synergies: Play Card bonus if handler has
Role.PG
tag; Ability deals extra damage if target hasCondition.Slowed
tag; Equipment bonus if deck has 3+Essence.Police
cards. - Integration: Requires adding Tag properties to data structures. Logic implementation within specific abilities/effects. UI for Deckbuilding/Collection could allow filtering/sorting by tags.
- MVP Status: Post-MVP.
9. Deck Manager (UDeckManager
)
- Purpose: Manages the active deck state during a single match (Draw, Hand, Discard). Initializes from the player's collection based on pre-match selection. Likely an Actor Component on Player State.
- Code:
DeckManager.h/.cpp
// DeckManager.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CardBase.h" // Use Data Asset Ptrs or similar ref type
#include "DeckManager.generated.h"
/** Manages card deck, hand, and discard for a match. */
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BALLHALLA_API UDeckManager : public UActorComponent
{
GENERATED_BODY()
public:
UDeckManager();
virtual void BeginPlay() override;
// --- Deck Configuration ---
UPROPERTY(EditDefaultsOnly, Category = "Deck Rules")
int32 MaxDeckSize = 10;
// UPROPERTY(EditDefaultsOnly, Category = "Deck Rules")
// int32 MaxBallerCardsInDeck = 3; // CONFIRM: This rule likely applies to Collection, not Match Deck
UPROPERTY(EditDefaultsOnly, Category = "Deck Rules")
int32 StartingHandSize = 5;
UPROPERTY(EditDefaultsOnly, Category = "Deck Rules")
int32 HalftimeDrawAmount = 5;
// --- Card Piles (Match State) ---
// Store references to the Data Assets representing the cards
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Deck State")
TArray<UCardBase*> DrawPile; // Should be TArray> or similar
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Deck State")
TArray<UCardBase*> HandCards;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Deck State")
TArray<UCardBase*> DiscardPile;
// --- Deck Management ---
// Initialize with specific list of Card Data Assets from CollectionManager
UFUNCTION(BlueprintCallable, Category = "Deck Management")
void InitializeDeck(const TArray<UCardBase*>& PlayerSelectedDeck); // Pass Data Asset refs
UFUNCTION(BlueprintCallable, Category = "Deck Management")
void ShuffleDrawPile();
UFUNCTION(BlueprintCallable, Category = "Deck Management")
TArray<UCardBase*> DrawCards(int32 Amount); // Returns drawn cards (Data Asset refs)
UFUNCTION(BlueprintCallable, Category = "Deck Management")
void DiscardCardFromHand(UCardBase* Card); // Pass Data Asset ref
UFUNCTION(BlueprintCallable, Category = "Deck Management")
void DiscardHand();
UFUNCTION(BlueprintCallable, Category = "Deck Management")
void AddCardToDiscard(UCardBase* Card); // Pass Data Asset ref
// Called when draw pile is empty
UFUNCTION(BlueprintCallable, Category = "Deck Management")
void ReshuffleDiscardIntoDrawPile();
// Reset state for a new match
UFUNCTION(BlueprintCallable, Category = "Deck Management")
void ResetDeck();
};
(Self-Correction: Clarified MaxBallerCardsInDeck
rule needs review, as Ballers are usually roster elements, not playable cards in the deck. Updated card pile types to reflect storing Data Asset references.)
10. Card Collection Manager
- Purpose: Manages the player's persistent collection of all acquired Play and Equipment card types across a single roguelike run. This is the source pool for constructing the 10-card match deck via the
UDeckManager
. Baller acquisition/roster is managed separately byATeamManager
. - Scope & Structure: Actor Component (
UCardCollectionManager
) onPlayerState
. Stores collection data usingTMap<TSoftObjectPtr<UCardBase>, int32>
(Card Data Asset -> Count). Provides functions:AddCard
,RemoveCard
,DestroyCard
,GetCardCount
,GetUniqueOwnedCardTypes
. - Card Destruction: Triggered via UI. Removes card instance permanently for the current run. Grants Neutral Essence based on rarity via
UEssenceManager
. - Integration: Saved/loaded via run's SaveGame. Provides data to Deckbuilding UI &
UDeckManager
. Interacts withUEssenceManager
. Populated byMatchManager::DistributeRewards
. - MVP Status: Required if deck customization is MVP.
- Code:
CardCollectionManager.h/.cpp
(LikelyUActorComponent
onPlayerState
or similar persistent object).
// CardCollectionManager.h (Conceptual)
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
// #include "CardBase.h" // Reference Data Assets
#include "CardCollectionManager.generated.h"
// Forward declarations
class UCardBase;
class UEssenceManager;
USTRUCT(BlueprintType)
struct FCardCollectionEntry
{
GENERATED_BODY()
// Reference to the card's Data Asset
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame) // Mark relevant fields for SaveGame
TSoftObjectPtr<UCardBase> CardDataAsset;
// Count if multiple copies can be owned
UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame)
int32 Count = 0;
};
/** Manages the player's persistent card collection for the current run */
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BALLHALLA_API UCardCollectionManager : public UActorComponent
{
GENERATED_BODY()
public:
UCardCollectionManager();
virtual void BeginPlay() override;
// The collection, saved/loaded per run
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Collection", SaveGame)
TArray<FCardCollectionEntry> CardCollection;
// Reference needed for destruction logic
UPROPERTY() TWeakObjectPtr<UEssenceManager> EssenceManager;
UFUNCTION(BlueprintCallable, Category = "Collection")
void AddCardToCollection(UCardBase* CardData); // Adds one instance
// Destroys a card instance permanently for the run, grants essence
UFUNCTION(BlueprintCallable, Category = "Collection")
bool DestroyCardInCollection(UCardBase* CardData);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Collection")
int32 GetCardCount(UCardBase* CardData) const;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Collection")
TArray<UCardBase*> GetAvailableCardsForDeckbuilding() const; // Returns list of unique card types owned
// Function to load/save collection data via SaveGame system
void LoadCollection(/* From SaveGame data */);
void PopulateSaveData(/* To SaveGame data */);
private:
int32 GetEssenceFromCardDestruction(UCardBase* CardData); // Define value based on rarity etc.
};
11. Ability System (UAbility
, UActionAbility
, UHypeAbility
)
- Purpose: Defines structure and logic for special actions. Use Data Assets (
UAbilityData
) or a component-based approach. - Code Structure:
FStatModifier
(Central Struct):StatName
(FName/Enum),ModifierAmount
,Duration
(Turns),bIsPercentage
,bStacks
.UAbilityBase
(Base Data Asset/Object): Name, Description, Icon, Costs (Stamina, Hype), Cooldown (Turns), TargetingType (Enum), AreaPattern (TArray<FIntPoint>
),FGameplayTagContainer
AbilityTags. VirtualCanActivate(Actor)
,Activate(Actor, TargetData)
.UActionAbilityData
(Derived):EActionType
,Subtype
(e.g., EMoveType),TArray<FStatModifier>
applied on use.Activate
callsBattleManager
action.UHypeAbilityData
(Derived):EEssenceType
requirement,HypeThreshold
.CanActivate
checks Hype.Activate
implements special effect.UEssenceAbilityData
(Derived): Tied to specific Essence type. Can be passive (applied constantly if baller has essence) or active.
// FStatModifier.h (Define where appropriate)
#pragma once
#include "CoreMinimal.h"
#include "FStatModifier.generated.h"
USTRUCT(BlueprintType)
struct FStatModifier
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite) FName StatName; // Target stat (use Enum or FName for safety)
UPROPERTY(EditAnywhere, BlueprintReadWrite) float ModifierAmount;
UPROPERTY(EditAnywhere, BlueprintReadWrite) int32 Duration = 1; // In turns or seconds? Define unit.
UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bIsPercentage = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bStacks = false; // Can multiple instances stack?
FStatModifier() : StatName(NAME_None), ModifierAmount(0.0f), Duration(1), bIsPercentage(false), bStacks(false)
;
12. Essence System (UEssenceManager
)
- Purpose: Manages Colored (thematic) and Neutral (upgrade currency) Essence. Handles acquisition, spending, and potentially Baller Empowerment. Component on GameState or similar.
- Code:
EssenceManager.h/.cpp
// EssenceManager.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BallerCharacter.h" // Include EEssenceType
#include "EssenceManager.generated.h"
// Forward declarations
class UBallerCard;
class UEquipmentCard;
class UAbility;
/** Manages essence collection, storage, and usage */
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BALLHALLA_API UEssenceManager : public UActorComponent
{
GENERATED_BODY()
public:
UEssenceManager();
virtual void BeginPlay() override;
// Player's current Essence inventory (Persisted via SaveGame)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Essence", SaveGame)
TMap<EEssenceType, int32> ColoredEssenceInventory;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Essence", SaveGame)
int32 NeutralEssenceAmount = 0;
// --- Essence Management ---
UFUNCTION(BlueprintCallable, Category = "Essence")
void AddColoredEssence(EEssenceType Type, int32 Amount);
UFUNCTION(BlueprintCallable, Category = "Essence")
bool ConsumeColoredEssence(EEssenceType Type, int32 Amount);
UFUNCTION(BlueprintCallable, Category = "Essence")
int32 GetColoredEssenceAmount(EEssenceType Type) const;
UFUNCTION(BlueprintCallable, Category = "Essence")
void AddNeutralEssence(int32 Amount);
UFUNCTION(BlueprintCallable, Category = "Essence")
bool ConsumeNeutralEssence(int32 Amount); // Returns true if successful
UFUNCTION(BlueprintCallable, Category = "Essence")
int32 GetNeutralEssenceAmount() const;
// --- Gameplay Integration ---
// Empower baller - Applies temporary (e.g., one match) essence effects. Cost/Source TBD.
UFUNCTION(BlueprintCallable, Category = "Essence")
bool EmpowerBaller(ABallerCharacter* Baller, EEssenceType Type, bool bIsTemporary = true);
// Ability Upgrades - Uses Neutral Essence
// UFUNCTION(BlueprintCallable, Category = "Essence")
// bool CanAffordUpgrade(UAbility* AbilityToUpgrade) const; // Needs cost definition on Ability
// UFUNCTION(BlueprintCallable, Category = "Essence")
// bool PurchaseAbilityUpgrade(UAbility* AbilityToUpgrade); // Consumes Neutral Essence
// Function to load/save essence data via SaveGame system
void LoadEssence(/* From SaveGame data */);
void PopulateSaveData(/* To SaveGame data */);
};
(Implementation Notes: Define the 8 Essence Types: Werewolf, Voodoo, Witch, Beast, Ocean, Police, Tree People, Vampire and their general thematic effects/associated abilities. Clarify mechanics/cost/persistence of `EmpowerBaller`.)
13. Overworld System (AOverworldManager
, ASubRealm
, AOverworldObject
)
- Purpose: Manages player navigation between Sub-Realms (potentially on a node-based map), interactions with objects (Courts, Items, NPCs), and discovering content outside of matches.
- Code:
OverworldManager.h/.cpp
,SubRealm.h/.cpp
,OverworldObject.h/.cpp
// OverworldManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "OverworldManager.generated.h"
// Forward Declarations
class ASubRealm;
class AOverworldObject;
/** Manages the overworld navigation and interactions */
UCLASS()
class BALLHALLA_API AOverworldManager : public AActor
{
GENERATED_BODY()
public:
AOverworldManager();
virtual void BeginPlay() override;
// List of all potential SubRealms in the game
UPROPERTY(EditDefaultsOnly, Category = "Overworld Config")
TArray<TSubclassOf<ASubRealm>> AllSubRealmClasses; // Or load from data asset
// Current state (Run specific - Saved/Loaded)
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Overworld State", SaveGame)
TArray<FName> UnlockedSubRealmKeys; // Identifiers for unlocked realms
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Overworld State", SaveGame)
FName CurrentSubRealmKey; // Identifier for player's current location
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Overworld State")
ASubRealm* CurrentSubRealmActor; // Spawned actor instance for current realm
// Functions for managing state and travel
UFUNCTION(BlueprintCallable, Category = "Overworld")
void TravelToSubRealm(FName TargetRealmKey);
UFUNCTION(BlueprintCallable, Category = "Overworld")
void InteractWithObject(AOverworldObject* Object); // Called by player controller
UFUNCTION(BlueprintCallable, Category = "Overworld")
void UnlockSubRealm(FName RealmKey);
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Overworld")
bool IsSubRealmUnlocked(FName RealmKey) const;
// Function to load/save Overworld state via SaveGame system
void LoadOverworldState(/* From SaveGame data */);
void PopulateSaveData(/* To SaveGame data */);
private:
void LoadSubRealmLevel(FName RealmKey); // Handles level streaming / loading
};
// SubRealm.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BallerCharacter.h" // For EEssenceType
#include "SubRealm.generated.h"
/** Represents a themed area in the overworld */
UCLASS()
class BALLHALLA_API ASubRealm : public AActor
{
GENERATED_BODY()
public:
ASubRealm();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sub-Realm")
FName RealmKey; // Unique identifier used by OverworldManager
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sub-Realm")
FString RealmName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sub-Realm")
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sub-Realm")
EEssenceType RealmEssence;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sub-Realm")
int32 DifficultyLevel = 1; // Base difficulty
// Define potential contents (spawn points, encounter triggers, etc.)
// UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Sub-Realm")
// TArray Courts;
// UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Sub-Realm")
// TArray FreeAgents;
// UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Sub-Realm")
// TArray Items;
};
// OverworldObject.h (Abstract Base)
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "OverworldObject.generated.h"
/** Base class for all interactable objects in the overworld */
UCLASS(Abstract)
class BALLHALLA_API AOverworldObject : public AActor
{
GENERATED_BODY()
public:
AOverworldObject();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Overworld Object")
FString ObjectName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Overworld Object")
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Overworld Object")
UTexture2D* Icon;
// Called when player interacts
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Overworld Object")
void OnInteract(APlayerController* InteractingController);
virtual void OnInteract_Implementation(APlayerController* InteractingController);
// Check if interaction is possible
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Overworld Object")
bool CanInteract(APlayerController* InteractingController);
virtual bool CanInteract_Implementation(APlayerController* InteractingController);
};
// Derived classes: ACourtEntrance, AFreeAgentCharacter, AItemPickup, ADraftScout, AGameTapePickup, etc.
13.1. Random Overworld Events (Placeholder)
- Purpose: Enhance roguelike variance during Overworld exploration via random encounters/choices.
- Scope & Implementation: Define trigger conditions (% chance, node types). Use Data Assets (
UOverworldEventDataAsset
) for event structure (Text, Choices[FEventChoice
], Outcomes[FEventOutcome
]). Use Data Tables for encounter lists per Sub-Realm.AOverworldManager
handles triggering, UI display, choice resolution, and outcome execution via other managers. - MVP Status: Post-MVP.
14. Progression System (UProgressionManager
)
- Purpose: Manages persistent *run-based* progression: Baller Rank/XP and Facility Upgrades. Component on GameState or similar. Resets at start of new run.
- Code:
ProgressionManager.h/.cpp
// ProgressionManager.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BallerCharacter.h" // Include ERank
#include "ProgressionManager.generated.h"
// Forward declarations
class UEssenceManager;
UENUM(BlueprintType)
enum class EFacilityType : uint8 {
Concession UMETA(DisplayName = "Concession Stand"),
Merchandise UMETA(DisplayName = "Merchandise Mart"),
Coaching UMETA(DisplayName = "Coaching Staff"),
FrontOffice UMETA(DisplayName = "Front Office"),
LockerRoom UMETA(DisplayName = "Locker Room")
};
/** Manages run-based progression: Baller XP/Rank and Facility Upgrades */
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BALLHALLA_API UProgressionManager : public UActorComponent
{
GENERATED_BODY()
public:
UProgressionManager();
virtual void BeginPlay() override;
// --- Dependencies ---
UPROPERTY() TWeakObjectPtr<UEssenceManager> EssenceManager; // Needed for upgrade costs
// --- Facility Levels (Run Specific - Saved/Loaded via SaveGame) ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Progression|Facilities", SaveGame)
TMap<EFacilityType, int32> FacilityLevels;
// Map to store current XP for each baller (Run Specific - Saved/Loaded)
// Key: Baller UniqueID (FString or FName)
UPROPERTY(VisibleAnywhere, Category = "Progression|Baller", SaveGame)
TMap<FString, int32> BallerExperience;
// --- Configuration ---
UPROPERTY(EditDefaultsOnly, Category = "Progression|Config")
int32 MaxFacilityLevel = 5;
// Define XP requirements per rank (e.g., Rookie->Starter = 100XP, Starter->Veteran=250XP)
UPROPERTY(EditDefaultsOnly, Category = "Progression|Config")
TMap<ERank, int32> RankExperienceRequirements;
// Define Facility Upgrade Costs (Neutral Essence) - Array index corresponds to level upgrading TO (Index 0 = cost to level 1->2)
// UPROPERTY(EditDefaultsOnly, Category = "Progression|Config")
// TMap<EFacilityType, TArray<int32>> FacilityUpgradeCosts;
// --- Run Initialization ---
UFUNCTION(BlueprintCallable, Category = "Progression")
void InitializeForNewRun(); // Resets Levels and XP maps to default (Level 1, 0 XP)
// --- Baller Progression ---
UFUNCTION(BlueprintCallable, Category = "Progression|Baller")
void AddExperienceToBaller(const FString& BallerUniqueID, ABallerCharacter* BallerInstance, int32 Amount); // Handles XP gain and checks for Rank Up
private:
UFUNCTION() // Internal helper
bool CheckAndPromoteBallerRank(const FString& BallerUniqueID, ABallerCharacter* BallerInstance);
public:
// --- Facility Management ---
UFUNCTION(BlueprintCallable, Category = "Progression|Facilities")
bool UpgradeFacility(EFacilityType Type); // Checks cost, consumes Neutral essence, increments level
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Progression|Facilities")
int32 GetFacilityLevel(EFacilityType Type) const;
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Progression|Facilities")
int32 GetFacilityUpgradeCost(EFacilityType Type) const; // Cost for *next* level
// --- Facility Bonuses (Calculated based on levels) ---
// These functions return the current bonus provided by facility levels
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Progression|Bonuses")
float GetHypeGenerationBonus() const; // From Concession Stand
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Progression|Bonuses")
float GetStaminaRegenBonus() const; // From Coaching
// ... other facility bonus getters ...
// Functions to load/save progression data via SaveGame system
void LoadProgression(/* From SaveGame data */);
void PopulateSaveData(/* To SaveGame data */);
};
(Implementation Notes: Define specific XP curves and Facility upgrade costs/effects.)
14.1. Post-Match Reward System
- Purpose: Distributes rewards after winning a match. Triggered by
MatchManager
. - Rewards:
- XP: Awarded to participating ballers (full share for active, reduced for bench/exhausted). Scales with opponent difficulty. Handled via
UProgressionManager
. - Colored Essence: 1 unit matching defeated team's type. Unlocks potential. Handled via
UEssenceManager
. - Neutral Essence: Variable amount based on opponent difficulty. Used for upgrades. Handled via
UEssenceManager
. - Card Rewards: Player chooses 1 from several offered cards (Play/Equipment). Pool influenced by context. Added via
UCardCollectionManager
. Requires UI.
- XP: Awarded to participating ballers (full share for active, reduced for bench/exhausted). Scales with opponent difficulty. Handled via
- Logic Flow (Summary in
MatchManager::DistributeRewards
): Verify win -> Determine opponent details -> Calculate & Distribute XP -> Add Colored Essence -> Calculate & Add Neutral Essence -> Trigger Card Reward UI & process choice.
15. Play System (UPlaySystemManager
, UPlayData
)
- Purpose: Manages the calling and execution tracking of basketball plays initiated via Play Cards. Provides bonuses upon successful execution.
- Code:
PlaySystemManager.h/.cpp
(Likely Actor Component),PlayData.h/.cpp
(Data Asset)
// PlayData.h (Use Data Asset)
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h" // For tags if used
#include "BallerCharacter.h" // For Enums
#include "AbilitySystem/FStatModifier.h" // Assuming path
#include "PlayData.generated.h"
USTRUCT(BlueprintType)
struct FPlayActionStep // Defines one step in a play's sequence
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
EActionType ActionType;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(EditCondition="bRequirePosition"))
EPosition RequiredPosition = EPosition::PointGuard; // Optional: Specific position must perform
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bRequirePosition = false;
// Optional: Define target requirements (e.g., pass to Center, screen near basket)
// UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTagContainer TargetRequirementTags;
};
/** Data asset defining a single basketball play */
UCLASS()
class BALLHALLA_API UPlayData : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play") FString PlayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play") FText Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play") TSoftObjectPtr<UTexture2D> DiagramImage;
// Required sequence of actions
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play") TArray<FPlayActionStep> RequiredActions;
// Stat modifiers applied to team/actors on successful execution
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play") TArray<FStatModifier> SuccessModifiers;
// Difficulty rating (influences AI usage, maybe cost)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play", meta = (ClampMin = "1", ClampMax = "10"))
int32 DifficultyRating = 5;
// Positions required on the court for this play to be viable
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play") TArray<EPosition> RequiredPositions;
// Gameplay Tags associated with this play (for synergy etc.)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play") FGameplayTagContainer PlayTags;
// Function to check if a sequence of recent actions matches this play
bool CheckActionSequence(const TArray<struct FActionRecord>& Actions) const;
};
// PlaySystemManager.h (Likely Actor Component on BattleManager or TeamManager)
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
// Include PlayData, FActionRecord
#include "PlaySystemManager.generated.h"
// Forward Declarations
class UPlayData;
struct FActionRecord;
class ABallerCharacter;
/** Manages active plays and checks for successful execution */
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BALLHALLA_API UPlaySystemManager : public UActorComponent
{
GENERATED_BODY()
public:
UPlaySystemManager();
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
// Play currently called by the player/AI
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Play System")
UPlayData* CurrentCalledPlay;
// Reference to the action history source (likely BattleManager)
// UPROPERTY() TWeakObjectPtr BattleManagerRef;
// Call a play (from Play Card)
UFUNCTION(BlueprintCallable, Category = "Play System")
void CallPlay(UPlayData* PlayToCall);
// Cancel the currently called play
UFUNCTION(BlueprintCallable, Category = "Play System")
void CancelCurrentPlay();
// Check if the called play was successfully executed based on recent actions
UFUNCTION(BlueprintCallable, Category = "Play System")
void CheckPlayExecution(); // Called after each action is recorded
private:
// Apply bonuses if execution successful
void ApplyPlayBonuses(UPlayData* Play);
// Handle success/failure feedback or state changes
void HandleSuccessfulPlay(UPlayData* Play);
void HandleFailedPlay(UPlayData* Play);
// Internal state tracking if needed (e.g., current step index)
};
(Implementation Notes: UPlaySystemManager
needs access to BattleManager::ActionHistory
. CheckPlayExecution
compares recent history against UPlayData::RequiredActions
. ApplyPlayBonuses
applies SuccessModifiers
.)
16. Hype Action System (UHypeActionManager
, UHypeActionData
)
- Purpose: Manages the selection and execution of enhanced "Hype Actions" when a baller's Hype level is high. Includes logic for critical hits triggering Hype Actions.
- Code:
HypeActionManager.h/.cpp
(Likely Actor Component),HypeActionData.h/.cpp
(Data Asset)
// HypeActionData.h (Use Data Asset)
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "BallerCharacter.h" // For Enums EActionType, EHypeLevel
// #include "AbilityBase.h" // Reference ability types
#include "HypeActionData.generated.h"
// Forward Declaration
class UAbilityBase; // Or specific Ability Data Asset type
/** Data asset defining hype variations for a base action */
UCLASS()
class BALLHALLA_API UHypeActionData : public UDataAsset
{
GENERATED_BODY()
public:
// The base action this data corresponds to
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hype Action")
EActionType BaseActionType;
// Define the ability/action variation for each hype level
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hype Action", meta=(DisplayName="Normal Action (Low Hype)"))
TSoftObjectPtr<UAbilityBase> NormalAction; // Pointer to Ability Data Asset
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hype Action", meta=(DisplayName="Hype Action (Medium Hype)"))
TSoftObjectPtr<UAbilityBase> HypeAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hype Action", meta=(DisplayName="Super Hype Action (High Hype)"))
TSoftObjectPtr<UAbilityBase> SuperHypeAction;
// Chance (0-100) for a 'Critical Hit' - using a higher-tier action than current hype allows
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hype Action", meta = (ClampMin = "0", ClampMax = "100"))
float CriticalHitChance = 5.0f;
// Get the appropriate ability data asset based on hype level
UFUNCTION(BlueprintCallable, Category = "Hype Action")
UAbilityBase* GetActionForHypeLevel(EHypeLevel HypeLevel) const;
};
// HypeActionManager.h (Likely Actor Component on BattleManager or accessible globally)
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BallerCharacter.h" // For Enums
#include "HypeActionManager.generated.h"
// Forward Declarations
class UHypeActionData;
class ABallerCharacter;
class UAbilityBase;
/** Manages hype-enhanced actions and critical hits */
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class BALLHALLA_API UHypeActionManager : public UActorComponent
{
GENERATED_BODY()
public:
UHypeActionManager();
virtual void BeginPlay() override;
// Map base action types to their hype data assets (Populated from config/registry)
UPROPERTY(EditDefaultsOnly, Category = "Hype System")
TMap<EActionType, UHypeActionData*> HypeActionMap;
// Get the Hype-modified Ability/Action to use based on baller's state and base action type
UFUNCTION(BlueprintCallable, Category = "Hype System")
UAbilityBase* GetAppropriateAction(ABallerCharacter* Baller, EActionType BaseActionType);
// Check if a critical hit should occur for a given action
UFUNCTION(BlueprintCallable, Category = "Hype System")
bool ShouldTriggerCriticalHit(ABallerCharacter* Baller, EActionType BaseActionType);
// Potentially trigger the critical hit effect (e.g., modify action outcome or use higher-tier ability)
// UFUNCTION(BlueprintCallable, Category = "Hype System")
// void TriggerCriticalHitEffect(ABallerCharacter* Baller, EActionType BaseActionType);
private:
// Helper to get the specific UHypeActionData for a base action type
UHypeActionData* GetHypeDataForAction(EActionType BaseActionType) const;
};
(Implementation Notes: UHypeActionManager::GetAppropriateAction
selects Ability based on BaseActionType
and current EHypeLevel
. Critical Hit logic checks chance and potentially returns a higher-tier ability than expected for the current Hype level.)
17. AI Defense System (ADefensiveAIController
)
- Purpose: Controls enemy baller behavior during player's offense. Uses utility scores (
CalculateDefensiveUtility
) to choose actions (Guard, Switch, Swipe, DoubleTeam, TrashTalk). Influenced by Natural Gravity. - Code:
DefensiveAIController.h/.cpp
// DefensiveAIController.h
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
// Include necessary headers: BallerCharacter, GridCell
#include "DefensiveAIController.generated.h"
UENUM(BlueprintType)
enum class EDefensiveAction : uint8
{
Guard UMETA(DisplayName = "Guard"),
Switch UMETA(DisplayName = "Switch"),
Swipe UMETA(DisplayName = "Swipe"),
DoubleTeam UMETA(DisplayName = "Double Team"),
TrashTalk UMETA(DisplayName = "Trash Talk"),
HelpDefense UMETA(DisplayName = "Help Defense") // Subtle repositioning
};
/** AI controller for defensive players */
UCLASS()
class BALLHALLA_API ADefensiveAIController : public AAIController
{
GENERATED_BODY()
public:
ADefensiveAIController();
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override; // For decision timing
// Assign the primary offensive player this AI should focus on
UFUNCTION(BlueprintCallable, Category = "Defensive AI")
void AssignDefensiveTarget(ABallerCharacter* NewTarget);
// Core decision loop, called periodically
UFUNCTION(BlueprintCallable, Category = "Defensive AI")
void DecideNextAction();
protected:
// Calculates the utility score for performing a given action in the current context
UFUNCTION(BlueprintNativeEvent, Category = "Defensive AI")
float CalculateDefensiveUtility(EDefensiveAction Action);
virtual float CalculateDefensiveUtility_Implementation(EDefensiveAction Action);
// Chooses the best action based on utility scores
UFUNCTION(BlueprintCallable, Category = "Defensive AI")
EDefensiveAction ChooseBestDefensiveAction();
// Executes the chosen action
UFUNCTION(BlueprintCallable, Category = "Defensive AI")
void PerformDefensiveAction(EDefensiveAction Action);
// Specific action logic implementations
virtual void ExecuteGuardAction();
virtual void ExecuteSwitchAction(); // Needs logic to find suitable switch target/partner
virtual void ExecuteSwipeAction(); // Calls BattleManager potentially
virtual void ExecuteDoubleTeamAction(); // Needs coordination with another AI/TeamManager
virtual void ExecuteTrashTalkAction(); // Calls BattleManager potentially
virtual void ExecuteHelpDefenseAction(); // Adjusts position slightly towards high gravity threat
// React to specific offensive moves made by the target or nearby players
UFUNCTION(BlueprintCallable, Category = "Defensive AI")
void ReactToOffensiveMove(ABallerCharacter* OffensivePlayer, EActionType Action);
private:
UPROPERTY() TWeakObjectPtr<ABallerCharacter> AssignedTarget;
UPROPERTY() TWeakObjectPtr<ABallerCharacter> ControlledDefender; // The Pawn this controller possesses
UPROPERTY() EDefensiveAction CurrentAction;
UPROPERTY() float DecisionCooldownTimer = 0.0f;
UPROPERTY(EditDefaultsOnly, Category = "Defensive AI") float DecisionInterval = 1.0f; // Time between decisions
// Target for double teaming, if active
UPROPERTY() TWeakObjectPtr<ABallerCharacter> DoubleTeamTarget;
};
(Implementation Notes: Fix UseTrashTalk
typo if present in original implementation. Integrate Natural Gravity calculations into utility scores and targeting, especially for Guard, HelpDefense, and DoubleTeam actions.)
18. AI Offensive System
- Purpose: Controls enemy baller behavior during their own offensive phase.
- Core Logic: Utility-based decision making (
CalculateOffensiveUtility
). Considers Baller Capabilities, Court Position, Defensive Pressure, Teammate Status, Game State, Resources (Stamina/Hype), Active Plays, and Team Style Bias. - Actions: Implements logic for choosing Move targets, Pass targets (risk/reward), when to Shoot (based on % chance, clock), when to Fake/Talk/Screen, and when/how to use Abilities/Hype.
- Play Calling: AI has a small playbook (2-4 Play Cards per style), evaluates calling plays, prioritizes executing called plays, may abandon if disrupted.
- Difficulty Scaling: Adjusts complexity of utility calculation, play usage, resource management.
- Code: Requires
OffensiveAIController
or integration intoTeamManager
/BattleManager
AI turn logic. Requires definingCalculateOffensiveUtility
and corresponding action execution functions similar to the Defensive AI.
19. Player-Directed AI Strategy (Placeholder)
- Purpose: (Potential Post-MVP Feature) To allow players a degree of influence over their *own team's* AI behavior during the **Defensive Phase**, enhancing strategic control.
- Scope & Implementation: Define simple strategy options (e.g., Stance: Aggressive/Balanced/Conservative; Focus: Perimeter/Paint/Ball). Store player choice (per-run or persistent). Modify
ADefensiveAIController
utility calculations based on selected strategy. Requires UI for setting strategy. - MVP Status: Post-MVP.
20. Match Manager (AMatchManager
)
- Purpose: Manages the overall flow of a single match: teams involved, score, quarters, halftime, timeouts, overtime conditions, and triggering reward distribution.
- Code:
MatchManager.h/.cpp
// MatchManager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
// Include/Forward Declare Managers: BattleManager, TurnManager, TeamManager
#include "MatchManager.generated.h"
UENUM(BlueprintType)
enum class ETeam : uint8 { None, Player, Enemy, Tie }; // For determining winner
// Add Delegates if needed (OnMatchStarted, OnMatchEnded, OnScoreChanged)
/** Manages the state and flow of a single basketball match */
UCLASS()
class BALLHALLA_API AMatchManager : public AActor
{
GENERATED_BODY()
public:
AMatchManager();
virtual void BeginPlay() override;
// --- Manager References ---
UPROPERTY() TWeakObjectPtr<ABattleManager> BattleManager;
UPROPERTY() TWeakObjectPtr<ATurnManager> TurnManager;
UPROPERTY() TWeakObjectPtr<AGameManager> GameManager; // To report match end
// --- Match Participants ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Match Teams")
ATeamManager* PlayerTeam; // Assign these when match is set up
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Match Teams")
ATeamManager* EnemyTeam; // Assign these when match is set up
// --- Match State ---
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Match State")
int32 PlayerScore = 0;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Match State")
int32 EnemyScore = 0;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Match State") // RW to allow decrementing
int32 PlayerTimeoutsRemaining = 2; // Add logic to enforce this limit
// UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Match State")
// int32 EnemyTimeoutsRemaining = 2; // If AI uses timeouts
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Match State")
bool bIsOvertime = false;
// --- Match Flow Control ---
UFUNCTION(BlueprintCallable, Category = "Match Flow")
void SetupMatch(ATeamManager* InPlayerTeam, ATeamManager* InEnemyTeam); // Initialize teams
UFUNCTION(BlueprintCallable, Category = "Match Flow")
void StartMatch(); // Begins the first quarter via TurnManager
UFUNCTION(BlueprintCallable, Category = "Match Flow") // Called by TurnManager usually
void EndMatch(); // Determines winner, triggers rewards via DistributeRewards
UFUNCTION(BlueprintCallable, Category = "Match Flow")
bool CanCallTimeout(bool bIsPlayerTeam) const;
UFUNCTION(BlueprintCallable, Category = "Match Flow")
void CallTimeout(bool bIsPlayerTeam); // Decrements count, notifies TurnManager
UFUNCTION(BlueprintCallable, Category = "Match Flow")
void StartOvertime(); // Called if score tied after 4 quarters
// --- Gameplay Interaction (Called by BattleManager) ---
UFUNCTION(BlueprintCallable, Category = "Match Gameplay")
void AddPoints(ETeam Team, int32 Points); // Updates score
// --- Results & Rewards ---
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Match Results")
ETeam GetWinningTeam() const;
UFUNCTION(BlueprintCallable, Category = "Match Results")
void DistributeRewards(); // Contains logic for XP, Essence, Cards (See Section 14.1)
private:
// Helper to check if overtime is needed
bool ShouldGoToOvertime() const;
// Potentially internal helpers for reward calculation if logic is complex
};
(Implementation Notes: Needs logic for timeout limits/enforcement. DistributeRewards
function details are in Section 14.1.)
21. Save/Load System
- Purpose: Handles automatic saving/loading of the current roguelike run's progress via a single slot.
- Triggers: Autosaves on Overworld Travel (between Sub-Realms/major locations), Post-Match (Win), potentially major progression events. No saving during match.
- Saved Data: Team Roster (Baller ID, Rank, potentially persistent Essence/Equipment), Card Collection (Owned Play/Equipment types & counts), Essence Inventory (Colored/Neutral), Facility Levels, Baller XP Map, Current Overworld Location, Unlocked Realms/Paths, Persistent World State Flags.
- Implementation: Use
USaveGame
object (UBallhallaSaveGame
). Logic resides inAGameManager::SaveGame/LoadGame
. Handle new game if no save exists. Single slot name (e.g., "BallhallaAutosaveSlot").
22. Player Profile / Meta Progression
- Purpose: Tracks achievements and unlocks persisting *between* runs (meta-progression). Separate from the single-run autosave.
- Facility Reset Policy: All Facility levels managed by
UProgressionManager
**reset to Level 1** at the start of each new run. - Scope (Post-MVP): Define permanent unlocks (New Cards, Playstyles, Events, Starting Bonuses). Store in
UPlayerProfile
(separate save file). Unlocks triggered by achievements. Affects content generation/options in future runs. - Playstyle Cards: Unique cards unlocked via meta-progression, selected pre-run to grant team-wide effects (e.g., "7 Seconds or Less," "Twin Towers").
- MVP Status: Post-MVP for unlocks. Facility Reset policy is MVP relevant.
23. Minimum Viable Product (MVP) Definition
- Core Loop: Grid Movement -> Turn/Shot Clock -> Basic Actions (Move, Pass, Shoot) -> Stamina/Hype Resources -> Basic Make/Miss -> Basic AI Defense Sim -> Quarter Progression -> Win/Loss.
- Includes: Functional core systems (Grid, Turn, Baller Character, Resource, Battle, Match, Team systems at their core level). Minimal AI Defense & simulated AI Offense. Baller Cards for initial roster. Basic UI for selection and state display.
- Excludes (Initially): Advanced AI (Offense/Defense details), Full Card System (Plays, Equipment, In-Match Draw/Play), Advanced Hype/Play/Essence/Status Effects, Overworld, Progression (XP/Facilities), Narrative, Audio, Gravity, Complex Rebounding/Turnovers, Detailed Balancing/Polish, Meta Progression.
24. Implementation Notes & Best Practices
- Data Assets: Use for static definitions (Cards, Abilities, Conditions, Plays, etc.).
- Grid Array: Use flat 1D
TArray
forGridCells
. - Pre-calculation: Calculate static grid data (
DistanceFromRim
,AreaType
) on init. - Structs: Use for grouping stats (
FBallerStats
), effects (FStatModifier
,FConditionEffect
). - Weak Pointers: Use
TWeakObjectPtr
for inter-object references to prevent cycles. - Components: Use
UActorComponent
for modular logic/state (UStatusManagerComponent
,UDeckManager
,UCardCollectionManager
,UEssenceManager
,UProgressionManager
). - Event-Driven: Use Delegates for manager communication.
- Clear Ownership: Define state ownership clearly (e.g.,
BattleManager
ownsActionHistory
). - Placeholders: Mark values needing tuning clearly.
- Gameplay Tags: Use Unreal's Gameplay Tag system for Conditions, Synergies, Action Blocking.
25. Known Design Gaps / Areas for Future Definition
- Stat Balancing & Economy Formulas: Precise values for XP curves, Essence costs, ability costs, AI weights, rebound factors, pass/steal rates, shot formulas, reward scaling, Card Destruction Essence return. (Status: ⚠️ Placeholder)
- UI/UX Design Documentation: Mockups, wireframes, interaction flows, asset requirements, style guides for all interfaces. (Status: ❌ Missing)
- Detailed Ability/Card/Condition/Playstyle Effects:** Specific implementation logic & balancing for *all* unique items. (Status: ⚠️ Placeholder)
- Narrative Implementation:** Technical design for dialogue display, cutscenes, story progression flags. (Status: ❌ Missing)
- Audio Design:** List of required SFX, music, VO needs. (Status: ❌ Missing)
- Condition System Details:** Finalized list, effects, durations, stacking rules, interactions. (Status: 📝 Expanded, Needs Detail)
- Card Synergy/Tag Details:** Finalized tag hierarchy & specific interaction logic. (Status: 📝 Expanded, Needs Detail)
- Random Overworld Event Details:** Specific event content, tables, logic, UI. (Status: 📝 Expanded, Needs Detail)
- Card Collection Implementation:** Final design details for UI interaction, sorting/filtering. (Status: 📝 Expanded, Needs Detail)
- Meta Progression Details:** Specific unlockables, trigger conditions, UI. (Status: 📝 Expanded, Needs Detail)
- Player-Directed AI Strategy Design:** Specifics (Post-MVP). (Status: 📝 Expanded, Needs Detail)
- Deck/Timeout Limit Enforcement Logic:** Specific implementation details for UI/gameplay checks. (Status: ⚠️ Implementation Detail Needed)
- Baller Card Deck Rule:** Clarify `MaxBallerCardsInDeck` rule - likely applies to Roster Size / Collection rules, not Match Deck. (Status: ⚠️ Needs Clarification)