Reconnection
D&D-Inspired Turn-Based Combat Meets Stunning Visuals
Project Overview
Reconnection is a D&D-inspired turn-based fighting game built in Unreal Engine, combining tactical combat with beautiful visuals and dynamic narrative choices. As Lead Programmer, I architected the entire combat system in C++, implementing dice-roll mechanics, stat-based calculations, and AI decision-making that brings tabletop RPG combat to life.
Technical Highlights
D&D Combat System
Automated dice rolls, attack/defense calculations, and turn management
Turn-Based Mechanics
Initiative-based turn order with strategic action choices
Enemy AI System
Blueprint-extensible AI behavior for dynamic opponents
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.
AFighter. h - Header Definition
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AFighter.generated.h"
UCLASS()
class RECONNECTION_API AFighter : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AFighter();
//Boolean to check if it's this fighter's turn
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stored Variables")
bool bIsTurn;
//Initiative score for turn order
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stored Variables")
int32 InitiativeScore;
//Function to start this fighter's turn
UFUNCTION(BlueprintCallable, Category="Stored Functions")
void StartTurn();
//Function to end this fighter's turn
UFUNCTION(BlueprintCallable, Category="Stored Functions")
void EndTurn();
UFUNCTION(BlueprintCallable, Category = "Stored Functions")
void SendDamage(float Damage, const FString& Type, AFighter *Target);
UFUNCTION(BlueprintCallable, Category = "Stored Functions")
virtual void ReceiveDamage(float Damage, const FString& Type);
protected:
// Called when the game starts or when spawned
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.
AFighter.cpp - Turn System Implementation
// Fill out your copyright notice in the Description page of Project Settings.
#include "AFighter.h"
void AFighter::BeginPlay()
{
Super::BeginPlay();
}
// Sets default values
AFighter::AFighter()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick. bCanEverTick = false;
bIsTurn = false;
InitiativeScore = 0;
}
void AFighter::StartTurn()
{
bIsTurn = true;
UE_LOG(LogTemp, Warning, TEXT("Turn started"));
}
void AFighter::EndTurn()
{
bIsTurn = false;
UE_LOG(LogTemp, Warning, TEXT("Turn ended"));
}
void AFighter:: SendDamage(float Damage, const FString& Type, AFighter *Target)
{
UE_LOG(LogTemp, Warning, TEXT("Sending %f %s damage to target"), Damage, *Type);
}
void AFighter::ReceiveDamage(float Damage, const FString& Type)
{
UE_LOG(LogTemp, Warning, TEXT("Received %f %s damage from source"), Damage, *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.
UFighter.cpp - Attack & Defense Mechanics
// D20 Attack Roll System
int UFighter::RollToHit()
{
int Roll = FMath::RandRange(1, 20); // d20 roll
return Roll + BaseAttack + AttackBuff;
}
float UFighter::GetDefense()
{
return BaseDefense + DefenseBuff;
}
void UFighter:: Attack(UFighter *Target)
{
// Roll to hit vs target defense (classic D&D mechanic)
if (RollToHit() >= Target->GetDefense())
{
SendDamage(RollDamage(), Target);
OnHitAttack. Broadcast(Target);
}
else
{
OnHitMiss.Broadcast(Target);
}
EndTurn();
}
float UFighter::RollDamage()
{
// Random damage range with buffs
float Damage = FMath::RandRange(MinDamage, MaxDamage) + DamageBuff;
return Damage;
}
void UFighter:: ReceiveDamage_Implementation(float Damage)
{
// Apply damage with reduction/armor
CurrentHealth = FMath::Clamp(CurrentHealth - (Damage - DamageReduction), 0.0f, MaxHealth);
if (CurrentHealth <= 0)
{
Die();
UE_LOG(LogTemp, Warning, TEXT("Fighter has been defeated"));
}
}
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.
UFighter.cpp - Heal, Block, & Buff Systems
// Healing Action
void UFighter::Heal_Implementation()
{
CurrentHealth = FMath::Clamp(CurrentHealth + BaseHeal, 0.0f, MaxHealth);
EndTurn();
}
// Blocking/Defensive Action
void UFighter::Block_Implementation()
{
DamageReduction = BaseBlock + BlockBuff;
EndTurn();
}
// Dynamic Buff System
void UFighter::AddBuff(float BuffAmount, const FString& stat)
{
if (stat == "Attack")
{
AttackBuff += BuffAmount;
}
else if (stat == "Damage")
{
DamageBuff += BuffAmount;
}
else if (stat == "Defense")
{
DefenseBuff += BuffAmount;
}
else if (stat == "Block")
{
BlockBuff += BuffAmount;
}
else if (stat == "Heal")
{
HealBuff += BuffAmount;
}
}
void UFighter:: RemoveBuff(float BuffAmount, const FString& stat)
{
if (stat == "Attack")
AttackBuff -= BuffAmount;
else if (stat == "Damage")
DamageBuff -= BuffAmount;
else if (stat == "Defense")
DefenseBuff -= BuffAmount;
else if (stat == "Block")
BlockBuff -= BuffAmount;
else if (stat == "Heal")
HealBuff -= BuffAmount;
}
Enemy AI Architecture
Blueprint-extensible AI system that inherits from UFighter. Designers can implement custom AI behaviors in Blueprints while the C++ foundation handles combat mechanics. This separation of concerns enables rapid iteration.
UEnemy.cpp - AI Decision Making
#include "UEnemy.h"
void UEnemy::StartTurn()
{
Super::StartTurn();
// Automatically trigger AI decision when turn starts
ChooseAction();
}
// Blueprint-implementable function for AI logic
void UEnemy::ChooseAction_Implementation()
{
// Non-blueprint implementation does nothing
// Designers implement this in Blueprint to define enemy behavior:
// - Analyze player health, buffs, position
// - Choose between Attack, Heal, Block, Special abilities
// - Execute chosen action
}
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.
UFighter.cpp - Stat Management
TArray<float> UFighter::GetAllStats()
{
TArray<float> Stats;
Stats.Add(InitiativeScore);
Stats.Add(MaxHealth);
Stats.Add(CurrentHealth);
Stats.Add(MinDamage);
Stats.Add(MaxDamage);
Stats.Add(DamageBuff);
Stats.Add(BaseAttack);
Stats.Add(AttackBuff);
Stats.Add(BaseDefense);
Stats.Add(DefenseBuff);
Stats.Add(BaseBlock);
Stats.Add(BlockBuff);
Stats.Add(BaseHeal);
Stats.Add(HealBuff);
Stats.Add(DamageReduction);
return Stats;
}
// Example stat usage in combat
void UFighter::SendDamage_Implementation(float Damage, UFighter* Target)
{
Target->ReceiveDamage(Damage);
}
void UFighter::Die_Implementation()
{
// Handle death logic here in Blueprint
// Can trigger animations, VFX, game over states, etc.
}
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!