Syntax Souls

BSc Games Development Project Year 3

Description

Driving inspiration from the "get good" mentality, Syntax Souls takes on the Souls-like approach of a punishing action-rpg where you search the surrounding areas fighting off enemies until you are met by a challenging foe.

Game Engine

Unreal Engine 4 (4.27)

Used Languages

C++ / Blueprints

Platform

Windows (PC)

Genre

Action RPG

Year

2022

Play For Free

Poise

Poise is a hidden stat. It's an in-game statistic that increases your resistance to being staggered as an effect of taking hits from opponents
If the actor being attacked reaches 0 poise, they will be staggered. When the player/enemy has been staggered, they will be inable to move or attack.

void UHealthComponent::ApplyPoiseDamage(float value)
{
    // Return if the value is 0 or less
    if(value <= 0) return;
                          
    // Reset the PoiseResetCount
    PoiseResetCount = 0.0f;
    Poise = FMath::Clamp(Poise - value, 0.0f, PoiseMax);
                          
    // If Poise falls below 0, Stagger Owner unless already staggered
    if(Poise <= 0 && !isStaggered)
    {
        AActor* FOwner = GetOwner();
        OnPoiseBreak.Broadcast(FOwner);
        isStaggered = true;
        Poise = PoiseMax;
        PoiseResetCount = 0.0f;
    }
}

void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
                          
    if (Poise < PoiseMax)
    {
        PoiseResetCount += DeltaTime;
        if (PoiseResetCount >= PoiseResetTime)
        {
            PoiseResetCount = 0.0f;
            Poise = PoiseMax;
        }
    }
}

Target Locking

Target locking allows the player to lock onto their target, so that their attacks are directed towards the intended enemy. The player is also able to switch between targets within proximty.

void APlayerCharacter::LockTarget()
{
    // if target is locked, unlock it
    if(targetLocked)
    {
        targetLocked = false;
        bUseControllerRotationYaw = false;		
        GetCharacterMovement()->bOrientRotationToMovement = true;
        lockedOnTarget = nullptr;
    }
    // if target is not locked, lock it
    else
    {
        FVector StartLocation = GetActorLocation() + 300.f * MainCamera->GetForwardVector();
        FVector EndLocation = MainCamera->GetForwardVector() * 1000.0f + StartLocation;
                            
        TArray<AActor*> IgnoredActors;
        IgnoredActors.Add(this);
        TArray<FHitResult> HitArray;
                                  
        bool bHasHit = UKismetSystemLibrary::SphereTraceMulti(this, StartLocation, EndLocation, 300.f,
        UEngineTypes::ConvertToTraceType(ECC_Camera), false, IgnoredActors, DrawDebugType,
        HitArray, true, FLinearColor::Yellow, FLinearColor::Blue, 5.0f);
                                
        if(bHasHit)
        {
            for (const FHitResult HitResult : HitArray)
            {
                // if hitresult has the Target Component, then we have a target
                if(HitResult.GetActor()->FindComponentByClass<UTargetComponent>())
                {
                    UHealthComponent* targetHealth = HitResult.GetActor()->FindComponentByClass<UHealthComponent>();
                    if(targetHealth)
                    {
                        if(targetHealth->GetHealth() <= 0)
                        {
                            return;
                        }
                    }
                    lockedOnTarget = HitResult.GetActor();
                    targetLocked = true;
                    bUseControllerRotationYaw = true;
                    GetCharacterMovement()->bOrientRotationToMovement = false;
                    break;
                }
            }
        }
    }	
}
                            

void APlayerCharacter::SwitchTarget(bool isLeft, float Distance, float Radius)
{
    float offset = isLeft ? -1.0f : 1.0f;
                              
    FVector playerLeft = GetActorRightVector() * offset * Distance;
    FVector targetLocation = lockedOnTarget->GetActorLocation() + (Radius * 1.1f) * (GetActorRightVector() * offset);
    TArray<AActor*> IgnoredActors;
    IgnoredActors.Add(this);
    IgnoredActors.Add(lockedOnTarget);
    TArray<FHitResult> HitArray;
    bool bHasHit = UKismetSystemLibrary::SphereTraceMulti(this, targetLocation, targetLocation + playerLeft,
    Radius, UEngineTypes::ConvertToTraceType(ECC_Camera), false, IgnoredActors, DrawDebugType,
    HitArray, true, FLinearColor::Yellow, FLinearColor::Blue, 5.0f);
    if(bHasHit)
    {
        for (const FHitResult HitResult : HitArray)
        {
            if(HitResult.GetActor()->FindComponentByClass<UTargetComponent>())
            {
                UHealthComponent* targetHealth = HitResult.GetActor()->FindComponentByClass<UHealthComponent>();
                if(targetHealth)
                {
                    if(targetHealth->GetHealth() <= 0)
                    {
                        return;
                    }
                }
                lockedOnTarget = HitResult.GetActor();
                targetLocked = true;
                bUseControllerRotationYaw = true;
                GetCharacterMovement()->bOrientRotationToMovement = false;
                break;
            }
        }
    }
}