Reconnection
Empire, Resistance, and Identity on the Continent of Duns: A Turn-Based RPG of Colonial Conquest
Project Overview
Reconnection is a D&D-inspired turn-based RPG built in Unreal Engine 5, set on the continent of Duns. The eastern empire of Serontil is pushing westward, encroaching on the ancestral lands of two proud indigenous nations: Gelantis, a northwestern civilization with echoes of the Aztec and Incan peoples, and Ysgilati, a southwestern nation with roots reminiscent of the Iroquois and Cherokee. Players navigate this morally complex world of conquest, identity, survival, and resistance through tactical combat and narrative choice.
As Lead Engineer, I architected the entire combat system in C++, implementing dice-roll mechanics, stat-based calculations, and AI decision-making. The project was developed from August 2025 to February 2026 and is currently on hiatus until May 2026, when production will resume under a new title with a significantly expanded team.
Technical Highlights
D&D Combat System
Automated dice rolls, attack/defense calculations, and turn management
Turn-Based Mechanics
UTurnManager auto-discovers fighters, sorts by initiative, and drives the full round/turn loop via multicast delegates
Enemy AI System
Utility-scored AI weighs attack, heal, block, and buff actions each turn using health ratios, line-of-sight, and weapon type affinity
C++ Architecture
Clean, extensible parent classes with virtual functions
Buff System
Dynamic stat modifications for strategic depth
Dialogue Choices
Player-driven narrative with branching conversations
Code Showcase
Blueprint Integration & Designer Tools
The C++ AFighter and UEnemy classes are designed for Blueprint extensibility, allowing designers to create unique enemy behaviors, player abilities, and combat encounters without touching code. Key Blueprint-implementable functions include:
🤖 Enemy AI Behaviors
- ChooseAction() - Implement custom AI decision trees
- Conditional logic - Enemy evaluates player health, buffs, and position
- Action selection - Choose between Attack, Heal, Block, or special abilities
- Difficulty scaling - Adjust AI aggression and strategy per encounter
⚔️ Combat Actions
- Attack() - Custom attack animations and VFX triggers
- Heal() - Healing effects with particle systems
- Block() - Defensive stance animations
- Die() - Death sequences, loot drops, victory conditions
🎯 Event Delegates
- OnStartTurn - Trigger UI updates, camera effects
- OnEndTurn - Queue next fighter in initiative order
- OnHitAttack - Play hit reactions, damage numbers
- OnHitMiss - Miss animations, combat feedback
💬 Dialogue Integration
- Dynamic choices - Player dialogue affects combat stats
- Narrative branching - Choices influence enemy behavior
- Combat triggers - Dialogue can start/end encounters
- Character relationships - Track player decisions for story outcomes
AFighter Class - Combat System Foundation
The AFighter parent class implements all core combat mechanics including turn management, dice-roll calculations (d20 system), damage/healing, and buff systems. Designed with Blueprint integration for designer flexibility while maintaining C++ performance.
Fighter Base Class — Properties & Public Interface
// Base class for every combatant. Both player characters and enemies
// inherit from AFighter so UTurnManager can drive them identically.
UCLASS()
class RECONNECTION_API AFighter : public AActor
{
GENERATED_BODY()
public:
AFighter();
// Whether it is currently this fighter's turn.
// Set true by UTurnManager before calling StartTurn().
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stored Variables")
bool bIsTurn;
// Rolled once at combat start; UTurnManager sorts descending
// so the highest score acts first (classic D&D initiative order).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stored Variables")
int32 InitiativeScore;
// Sets bIsTurn = true and broadcasts the OnStartTurn delegate
// so Blueprint can respond with UI highlights, camera moves, etc.
UFUNCTION(BlueprintCallable, Category="Stored Functions")
void StartTurn();
// Sets bIsTurn = false and broadcasts OnEndTurn,
// which UTurnManager listens to in order to advance the queue.
UFUNCTION(BlueprintCallable, Category="Stored Functions")
void EndTurn();
// Routes a typed damage value to the target's ReceiveDamage().
// Type string ("Physical", "Magic", etc.) allows resistances later.
UFUNCTION(BlueprintCallable, Category="Stored Functions")
void SendDamage(float Damage, const FString& Type, AFighter* Target);
// Applies incoming damage. Virtual so UEnemy can override
// to cache LastDamageReceived for block utility scoring.
UFUNCTION(BlueprintCallable, Category="Stored Functions")
virtual void ReceiveDamage(float Damage, const FString& Type);
protected:
virtual void BeginPlay() override;
};
Turn Management & Combat Flow
Implements initiative-based turn order system with clear turn start/end demarcation. The turn management system allows for complex action queuing and supports both player and AI-controlled fighters.
Turn State Flow
// Constructor: initializes bIsTurn = false and InitiativeScore = 0.
// PrimaryActorTick is disabled — all event flow is delegate-driven.
AFighter::AFighter();
// Sets bIsTurn = true and logs entry.
// UTurnManager calls this after advancing CurrentTurnIndex.
void AFighter::StartTurn();
// Sets bIsTurn = false and logs exit.
// Broadcasts OnEndTurn so UTurnManager knows to call NextTurn().
void AFighter::EndTurn();
// Logs the outgoing damage type and amount,
// then calls Target->ReceiveDamage() to apply the value.
void AFighter::SendDamage(float Damage, const FString& Type, AFighter* Target);
// Base implementation logs received damage.
// UEnemy overrides this to also cache LastDamageReceived
// so GetBlockUtility() can score based on recent incoming hits.
void AFighter::ReceiveDamage(float Damage, const FString& Type);
D&D-Style Dice Roll System
From the original UFighter implementation: Authentic d20 attack rolls with modifiers, defense calculations, and random damage ranges. This creates unpredictable, strategic combat that feels like tabletop D&D.
Attack Resolution Logic
// Generates a hit value by rolling 1d20 and adding BaseAttack + AttackBuff.
// Higher result = better chance to overcome the target's defense.
int UFighter::RollToHit();
// Returns BaseDefense + DefenseBuff.
// The target's armor class equivalent — what an attacker must beat to land a hit.
float UFighter::GetDefense();
// Full attack action sequence:
// 1. Calls RollToHit() and compares against Target->GetDefense().
// 2. On a hit: calls RollDamage() for a randomized value (MinDamage..MaxDamage + DamageBuff),
// passes it to SendDamage(), then broadcasts OnHitAttack for Blueprint VFX/SFX hooks.
// 3. On a miss: broadcasts OnHitMiss for miss animations and feedback.
// 4. Always calls EndTurn() to return control to UTurnManager.
void UFighter::Attack(UFighter* Target);
// Returns a random float in [MinDamage, MaxDamage] plus DamageBuff.
// Called inside Attack() after a successful roll-to-hit check.
float UFighter::RollDamage();
// Blueprint-native event: applies incoming damage with reduction.
// 1. Subtracts DamageReduction from Damage for an effective hit value.
// 2. Clamps CurrentHealth to [0, MaxHealth].
// 3. If CurrentHealth reaches 0, calls Die() and broadcasts OnDeath.
void UFighter::ReceiveDamage_Implementation(float Damage);
Strategic Action System
Multiple combat actions beyond basic attacks: healing, blocking, and buff management. Each action has strategic tradeoffs, encouraging thoughtful decision-making during combat.
Heal, Block, and Buff Application
// Blueprint-native event: restores health by BaseHeal, clamped to MaxHealth.
// Immediately calls EndTurn() — healing costs the fighter's full action.
void UFighter::Heal_Implementation();
// Blueprint-native event: sets DamageReduction to BaseBlock + BlockBuff.
// This value is subtracted in ReceiveDamage_Implementation for the next hit received.
// Calls EndTurn() after committing the defensive stance.
void UFighter::Block_Implementation();
// Adds BuffAmount to the named stat's buff field.
// Stat keys: "Attack", "Damage", "Defense", "Block", "Heal".
// Buff values stack additively and are included in all relevant roll calculations.
// Complements RemoveBuff(), which decrements by the same amount when a buff expires.
void UFighter::AddBuff(float BuffAmount, const FString& stat);
void UFighter::RemoveBuff(float BuffAmount, const FString& stat);
UEnemy - Fighter Enemy Architecture
UEnemy inherits from UFighter and adds a full utility-based AI decision system.
Enemies track allies and opponents separately, evaluate weighted utility scores for every possible action,
and automatically choose the highest-value action each turn. Weapon type awareness (Melee, Ranged, Magic)
further biases the AI's attack preference, and all utility weights are EditAnywhere so designers
can tune behavior per enemy type without touching code.
Enemy Class — Architecture & Designer-Facing Properties
// Weapon type determines which attack utility scorer is preferred.
// Designers set this per enemy in the Details panel; no code change required.
UENUM(BlueprintType)
enum class EWeaponType : uint8 { Melee, Ranged, Magic };
UCLASS(ClassGroup=(Fighters), meta=(BlueprintSpawnableComponent))
class RECONNECTION_API UEnemy : public UFighter
{
GENERATED_BODY()
public:
// Overrides StartTurn() to immediately call ChooseAction() after setup.
virtual void StartTurn() override;
// Blueprint-implementable AI entry point. The C++ implementation
// evaluates each utility scorer and calls the highest-scoring action.
// Designers can override this in Blueprint to create fully custom behaviors.
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="Enemy|Combat")
void ChooseAction();
virtual void ChooseAction_Implementation();
// Builds the Allies/Enemies rosters from the full fighter list.
// Called once during UTurnManager::InitializeCombat().
UFUNCTION(BlueprintCallable, Category="Enemy|Setup")
void InitializeEnemy(const TArray<UFighter*>& AllFighters);
// Rebuilds rosters when a fighter joins or dies mid-combat.
UFUNCTION(BlueprintCallable, Category="Enemy|Setup")
void UpdateAlliesAndEnemies(const TArray<UFighter*>& AllFighters);
// Delegate receiver bound to UTurnManager's OnFighterJoined and OnFighterDeath.
// Looks up the active UTurnManager and triggers UpdateAlliesAndEnemies.
UFUNCTION(BlueprintCallable, Category="Enemy|Setup")
void OnFighterListChanged(UFighter* ChangedFighter);
// Overrides ReceiveDamage to cache LastDamageReceived before calling Super,
// making recent incoming damage available to GetBlockUtility().
virtual void ReceiveDamage(float Damage) override;
// --- Utility scorers (one per possible action) ---
// Each returns a float score; ChooseAction_Implementation picks the highest.
UFUNCTION(BlueprintCallable, Category="Enemy|Utility")
float GetAttackUtility(); // Aggression vs closest visible enemy
float GetMeleeUtility(); // Preferred weapon type affinity
float GetRangedUtility();
float GetMagicUtility();
float GetSelfHealUtility(); // Urgency based on own health ratio
float GetAllyHealUtility(); // Need based on most wounded visible ally
float GetBlockUtility(); // Reactivity based on last hit received
float GetBuffUtility(); // Reward for buffing when no active buffs exist
// All weights are EditAnywhere so designers tune behavior per-enemy
// in the Details panel without touching any C++.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
float AttackUtilityWeight;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
bool bHasMelee;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
bool bHasRanged;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
bool bHasMagic;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
float HealUtilityWeight;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
bool bHasSelfHeal;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
bool bHasAllyHeal;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
float BuffUtilityWeight;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
bool bHasBuff;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
float BlockUtilityWeight;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Enemy|Utility|Weight")
bool bHasBlock;
UFUNCTION(BlueprintCallable, Category="Enemy|StorageAccess")
UFighter* GetClosestEnemy();
UFUNCTION(BlueprintCallable, Category="Enemy|StorageAccess")
UFighter* GetLowestAlly();
private:
TArray<UFighter*> Allies;
TArray<UFighter*> Enemies;
float LastDamageReceived; // Cached for block utility
int ClosestEnemyIndex;
int LowestAllyIndex;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Enemy|Weapon",
meta=(AllowPrivateAccess="true"))
EWeaponType CurrentWeaponType = EWeaponType::Melee;
};
Enemy Roster Initialization and Faction Tracking
// Iterates every fighter in AllFighters (provided by UTurnManager).
// Checks whether each fighter's owner also carries a UEnemy component:
// - Same faction (UEnemy present): added to the Allies array.
// - Opposing faction (no UEnemy): added to the Enemies array.
// Skips self to avoid self-targeting.
void UEnemy::InitializeEnemy(const TArray<UFighter*>& AllFighters);
// Same logic as InitializeEnemy but clears both arrays first.
// Called mid-combat whenever a fighter joins or dies,
// ensuring utility scorers always work against a fresh roster.
void UEnemy::UpdateAlliesAndEnemies(const TArray<UFighter*>& AllFighters);
// Receives the UFighter* that changed (join or death) from UTurnManager delegates.
// Iterates world objects to find the active UTurnManager,
// then calls UpdateAlliesAndEnemies with its current Fighters array.
void UEnemy::OnFighterListChanged(UFighter* ChangedFighter);
// Calls Super::ReceiveDamage() for standard health reduction,
// then stores the Damage value in LastDamageReceived
// so GetBlockUtility() can score the block action based on recent pressure.
void UEnemy::ReceiveDamage(float Damage);
Utility AI Scoring Functions
// Attack utility — drives aggression toward the nearest visible enemy.
// 1. Finds the closest enemy by horizontal distance and records its index.
// 2. Returns 0 if no enemies are in range (for melee) or if no line-of-sight exists.
// 3. Scores via -ln(healthRatio) so near-dead targets produce exponentially
// higher values, making finishing moves feel natural.
// 4. Final score is multiplied by AttackUtilityWeight (designer-tunable).
float UEnemy::GetAttackUtility();
// Weapon affinity scalars — preferred weapon type scores 1.0;
// off-type variants score lower to bias the AI toward its equipped weapon.
// Returns 0 if the enemy doesn't possess that weapon type.
float UEnemy::GetMeleeUtility();
float UEnemy::GetRangedUtility();
float UEnemy::GetMagicUtility();
// Self-heal utility — exponential urgency scaled by own health ratio.
// Returns FLT_MAX (force-heal) when health drops to or below 10%,
// guaranteeing survival over any other action at critical thresholds.
float UEnemy::GetSelfHealUtility();
// Ally-heal utility — finds the most wounded visible ally and scores
// by their need using the same exponential curve as self-heal.
// LowestAllyIndex is stored so ChooseAction knows which ally to target.
float UEnemy::GetAllyHealUtility();
// Block utility — reactive defense scored on recent incoming damage.
// Uses exp(damageRatio * weight) - 1 so enemies who just took a heavy hit
// will prioritize blocking next turn proportionally to how hard they were hit.
float UEnemy::GetBlockUtility();
// Buff utility — rewards the first buff action highly (score = 1.0),
// then diminishes per already-active buff using 0.5 / BuffTracker.Num().
// Prevents enemies from wasting turns stacking redundant buffs.
float UEnemy::GetBuffUtility();
Comprehensive Stat System
Tracks 15+ combat statistics including initiative, health, damage ranges, attack bonuses, defense values, and buff modifiers. Provides a complete stat query system for UI and game logic.
Complete Stat Roster
// Returns all 15 combat stats in a flat array in this order:
// InitiativeScore, MaxHealth, CurrentHealth,
// MinDamage, MaxDamage, DamageBuff,
// BaseAttack, AttackBuff,
// BaseDefense, DefenseBuff,
// BaseBlock, BlockBuff,
// BaseHeal, HealBuff,
// DamageReduction
// Used by the UI to populate the stat panel without needing
// direct references to individual properties.
TArray<float> UFighter::GetAllStats();
// Blueprint-native event: routes outgoing damage to the target.
// Base implementation calls Target->ReceiveDamage(Damage) directly.
// Blueprint can override to insert hit effects, sounds, or damage-type logic.
void UFighter::SendDamage_Implementation(float Damage, UFighter* Target);
// Blueprint-native event: handles fighter death.
// C++ signals the event; Blueprint implements death animations,
// loot drops, VFX spawning, and any game-over state transitions.
void UFighter::Die_Implementation();
UTurnManager - Combat Orchestration
UTurnManager is an ActorComponent that owns the entire combat loop.
On BeginPlay it scans every actor in the world for UFighter components,
sorts them by initiative, binds to their death and end-turn delegates, and waits for a
StartCombat() call. Five Blueprint-assignable multicast delegates let UI, cameras,
and audio respond to every state change without any hard coupling.
Turn Manager — Delegates & Public Interface
// Five multicast delegates give Blueprint full observability over combat state.
// UI, cameras, audio, and narrative systems bind to these without any C++ changes.
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTurnChanged, UFighter*, CurrentFighter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRoundStarted);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCombatEnded);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFighterDeath, UFighter*, DeadFighter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFighterJoined, UFighter*, NewFighter);
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class RECONNECTION_API UTurnManager : public UActorComponent
{
GENERATED_BODY()
public:
UTurnManager();
// The sorted fighter roster — publicly readable so UEnemy can query it.
UPROPERTY(BlueprintReadOnly, Category="Turn Manager") TArray<UFighter*> Fighters;
UPROPERTY(BlueprintReadOnly, Category="Turn Manager") int32 CurrentTurnIndex;
UPROPERTY(BlueprintReadOnly, Category="Turn Manager") int32 CurrentRound;
UPROPERTY(BlueprintReadWrite, Category="Turn Manager") bool bCombatActive;
UPROPERTY(BlueprintReadOnly, Category="Turn Manager") int32 EnemiesLeft = 0;
// Set in Blueprint to control which level loads when all enemies die.
UPROPERTY(BlueprintReadWrite, Category="Turn Manager") FName NextLevel;
// Broadcast-assignable events — wire anything from Blueprint
UPROPERTY(BlueprintAssignable, Category="Turn Manager|Events") FOnTurnChanged OnTurnChanged;
UPROPERTY(BlueprintAssignable, Category="Turn Manager|Events") FOnRoundStarted OnRoundStarted;
UPROPERTY(BlueprintAssignable, Category="Turn Manager|Events") FOnCombatEnded OnCombatEnded;
UPROPERTY(BlueprintAssignable, Category="Turn Manager|Events") FOnFighterDeath OnFighterDeath;
UPROPERTY(BlueprintAssignable, Category="Turn Manager|Events") FOnFighterJoined OnFighterJoined;
// Scans the world for UFighter components, binds delegates, sorts by initiative.
UFUNCTION(BlueprintCallable, Category="Turn Manager") void InitializeCombat();
// Activates combat, resets state, and starts the first fighter's turn.
UFUNCTION(BlueprintCallable, Category="Turn Manager") void StartCombat();
// Advances CurrentTurnIndex, wraps at end of round, hands off to next fighter.
UFUNCTION(BlueprintCallable, Category="Turn Manager") void NextTurn();
UFUNCTION(BlueprintCallable, Category="Turn Manager") UFighter* GetCurrentFighter();
// Descending sort by InitiativeScore — highest acts first.
UFUNCTION(BlueprintCallable, Category="Turn Manager") void SortFightersByInitiative();
// Removes fighter, adjusts index, calls EndCombat() if only one side remains.
UFUNCTION(BlueprintCallable, Category="Turn Manager") void RemoveFighter(UFighter* Fighter);
UFUNCTION(BlueprintCallable, Category="Turn Manager") void EndCombat();
private:
// Bound to each fighter's OnDeath delegate during InitializeCombat().
UFUNCTION() void HandleFighterDeath(UFighter* DeadFighter);
// Bound to each fighter's OnEndTurn delegate — simply calls NextTurn().
UFUNCTION() void HandleFighterEndTurn(UFighter* Fighter);
};
Combat Initialization and Start Sequence
// BeginPlay calls InitializeCombat() automatically.
// StartCombat() is intentionally separate so Blueprint can delay the
// start with cutscenes, dialogue, or camera transitions.
void UTurnManager::BeginPlay();
// World scan phase:
// 1. Calls GetAllActorsOfClass to iterate every actor.
// 2. Checks each for a UFighter component; skips those without one.
// 3. Adds valid fighters to the Fighters array.
// 4. Binds each fighter's OnDeath and OnEndTurn delegates
// to HandleFighterDeath and HandleFighterEndTurn respectively.
// 5. If the actor also has a UEnemy component:
// - Calls InitializeEnemy() to build that enemy's ally/enemy rosters.
// - Binds OnFighterJoined and OnFighterDeath to the enemy's
// OnFighterListChanged so its rosters self-update mid-combat.
// - Increments EnemiesLeft.
// 6. Broadcasts OnFighterJoined for each fighter (UI can react).
// 7. Calls SortFightersByInitiative() to finalize turn order.
void UTurnManager::InitializeCombat();
// Activation phase:
// 1. Guards against an empty Fighters array.
// 2. Sets bCombatActive = true, CurrentTurnIndex = 0, CurrentRound = 1.
// 3. Broadcasts OnRoundStarted.
// 4. Clears any stale bIsTurn flags from all fighters.
// 5. Calls StartTurn() on the first fighter in sorted order
// and broadcasts OnTurnChanged.
void UTurnManager::StartCombat();
Turn Advancement and Initiative Sorting
// Advances the combat queue:
// 1. Guards against inactive combat or an empty roster.
// 2. Increments CurrentTurnIndex.
// 3. If the index exceeds the last slot, wraps to 0,
// increments CurrentRound, and broadcasts OnRoundStarted.
// 4. Clears lingering bIsTurn flags across all fighters.
// 5. Calls StartTurn() on the new CurrentFighter
// and broadcasts OnTurnChanged.
void UTurnManager::NextTurn();
// Returns Fighters[CurrentTurnIndex] if the index is valid, else nullptr.
UFighter* UTurnManager::GetCurrentFighter();
// Sorts Fighters descending by InitiativeScore using a lambda comparator.
// Ties are resolved by the natural array order (earlier discovery = higher priority).
// Called once at the end of InitializeCombat().
void UTurnManager::SortFightersByInitiative();
// Delegate receiver bound during InitializeCombat.
// Calls NextTurn() as long as bCombatActive is true.
void UTurnManager::HandleFighterEndTurn(UFighter* Fighter);
Fighter Removal, Victory, and Level Transition
// Safely removes a dead or retreating fighter from the sorted roster:
// 1. Finds the fighter's index; returns immediately if not found.
// 2. Removes the fighter from the array.
// 3. Adjusts CurrentTurnIndex to stay valid:
// - If removed index is before current: decrement index.
// - If removed index equals current and now out-of-bounds: wrap to 0.
// 4. If only one fighter (or zero) remain, calls EndCombat().
void UTurnManager::RemoveFighter(UFighter* Fighter);
// Sets bCombatActive = false and broadcasts OnCombatEnded.
// Blueprint listens to this event to show victory UI, trigger cutscenes, etc.
void UTurnManager::EndCombat();
// Bound to each fighter's OnDeath delegate during InitializeCombat().
// 1. Broadcasts OnFighterDeath (UEnemy.OnFighterListChanged listeners rebuild rosters).
// 2. Calls RemoveFighter() to clean up the roster and advance turn if needed.
// 3. If the dead fighter had a UEnemy component: decrements EnemiesLeft.
// When EnemiesLeft reaches 0, calls UGameplayStatics::OpenLevel with NextLevel
// to progress to the next combat encounter.
void UTurnManager::HandleFighterDeath(UFighter* DeadFighter);
Blueprint Showcases
Unreal Engine Blueprint graphs built on top of the C++ foundation, handling AI decision-making, movement validation, and input processing.
AI State Tree
The central thinking loop for every enemy. This Blueprint reads utility scores calculated in UEnemy.cpp and drives the State Tree that determines which action each enemy takes on their turn.
Enemy Brain (Part 1 of 2)
The Enemy Brain graph initializes State Tree thoughts and instructs each enemy how to act upon them, translating the utility AI system into concrete in-world decisions.
Enemy Brain (Part 2 of 2)
Continuation of the Enemy Brain graph, showing the downstream action execution after the State Tree has resolved which behavior scores highest for that turn.
Ghost Mover
A ghost mesh that pathfinds to the player's clicked destination and validates whether the target position is reachable within the player's movement constraints before committing the move.
Player Mouse Tracker
Line traces the mouse cursor on click to determine what was hit in the world, then routes that hit information back to Blueprint for downstream gameplay logic such as targeting and movement commands.
What's Next: A New Title, A Bigger Team
Reconnection is on hiatus from February 2026 until late May 2026, when full production resumes under a new title. The scope of what we are building has grown dramatically and deserves a name that reflects the story we are telling.
At GDC 2026, Producer Jadyn Englett and I recruited nearly 30 people to join the project. We spoke with Josh Labelle, Creative Director of Disney Dreamlight Valley, about managing creative vision and maintaining design coherence across a team of this scale. His insight on aligning a large, distributed team around a shared emotional core was invaluable as we prepare to scale up following the hiatus.
We are actively planning a meeting with indigenous American cultural representatives to ensure that the stories of Gelantis and Ysgilati are handled with honesty, sensitivity, and genuine respect. Drawing on inspirations from Mesoamerican and North American indigenous cultures carries real responsibility, and we are committed to telling these stories in a way that honors those perspectives while crafting something fantastical, singular, and imaginative.
We will have a table at UCF's Digital Media Workshop Showcase 2026, where we plan to debut a playable demo and recruit additional talented people to the team. Our goal is to grow the project further and bring Reconnection to a shippable state on Steam.
Team Credits
Developer Reflection
Building Reconnection taught me the importance of designing C++ systems that empower designers. By creating a robust combat foundation with Blueprint extensibility, our team could iterate rapidly on enemy behaviors and game balance while maintaining performant, clean code. Translating the unpredictability and strategy of tabletop D&D into automated combat was an incredibly rewarding challenge!