Files
BallisticsDocs/Source/EasyBallistics/Private/EBMagazine.cpp
T
2025-07-04 03:26:03 -07:00

405 lines
7.1 KiB
C++

// Copyright 2016 Mookie. All Rights Reserved.
#include "EBMagazine.h"
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
UEBMagazine::UEBMagazine()
{
PrimaryComponentTick.bCanEverTick = false;
SetIsReplicatedByDefault(true);
// Initialize magazine state
CurrentCapacity = 0;
bIsEmpty = true;
bIsFull = false;
bIsJammed = false;
bBoltHeldOpen = false;
}
void UEBMagazine::BeginPlay()
{
Super::BeginPlay();
UpdateMagazineState();
}
void UEBMagazine::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UEBMagazine, StoredBullets);
DOREPLIFETIME(UEBMagazine, CurrentCapacity);
DOREPLIFETIME(UEBMagazine, bIsEmpty);
DOREPLIFETIME(UEBMagazine, bIsFull);
DOREPLIFETIME(UEBMagazine, bIsJammed);
DOREPLIFETIME(UEBMagazine, bBoltHeldOpen);
}
void UEBMagazine::LoadBullets(TArray<UEBBulletPropertiesAsset*> BulletTypes)
{
if (GetOwner()->HasAuthority())
{
ServerLoadBullets(BulletTypes);
}
else
{
ServerLoadBullets(BulletTypes);
}
}
void UEBMagazine::ServerLoadBullets_Implementation(const TArray<UEBBulletPropertiesAsset*>& BulletTypes)
{
if (bIsJammed)
{
return;
}
for (UEBBulletPropertiesAsset* BulletType : BulletTypes)
{
if (BulletType && CurrentCapacity < MagazineConfig.MaxCapacity)
{
StoredBullets.Add(BulletType);
CurrentCapacity++;
OnBulletLoaded.Broadcast(this, BulletType);
MulticastOnBulletLoaded(BulletType);
}
}
UpdateMagazineState();
}
void UEBMagazine::LoadSingleBullet(UEBBulletPropertiesAsset* BulletType)
{
if (GetOwner()->HasAuthority())
{
ServerLoadSingleBullet(BulletType);
}
else
{
ServerLoadSingleBullet(BulletType);
}
}
void UEBMagazine::ServerLoadSingleBullet_Implementation(UEBBulletPropertiesAsset* BulletType)
{
if (!BulletType || bIsJammed || bIsFull)
{
return;
}
StoredBullets.Add(BulletType);
CurrentCapacity++;
OnBulletLoaded.Broadcast(this, BulletType);
MulticastOnBulletLoaded(BulletType);
UpdateMagazineState();
}
UEBBulletPropertiesAsset* UEBMagazine::FeedNextBullet()
{
if (!GetOwner()->HasAuthority())
{
return nullptr;
}
if (!CanFeed())
{
return nullptr;
}
// Check for malfunction
if (CheckForMalfunction())
{
bIsJammed = true;
OnMagazineJammed.Broadcast(this);
MulticastOnMagazineJammed();
UpdateMagazineState();
return nullptr;
}
// Feed the bullet
UEBBulletPropertiesAsset* FedBullet = StoredBullets[0];
StoredBullets.RemoveAt(0);
CurrentCapacity--;
OnBulletFed.Broadcast(this, FedBullet);
MulticastOnBulletFed(FedBullet);
UpdateMagazineState();
// Check for bolt hold open
if (bIsEmpty && MagazineConfig.bLastRoundBoltHoldOpen)
{
ProcessBoltHoldOpen();
}
return FedBullet;
}
void UEBMagazine::UnloadAllBullets()
{
if (GetOwner()->HasAuthority())
{
ServerUnloadAllBullets();
}
else
{
ServerUnloadAllBullets();
}
}
void UEBMagazine::ServerUnloadAllBullets_Implementation()
{
StoredBullets.Empty();
CurrentCapacity = 0;
bBoltHeldOpen = false;
UpdateMagazineState();
}
UEBBulletPropertiesAsset* UEBMagazine::UnloadLastBullet()
{
if (!GetOwner()->HasAuthority())
{
return nullptr;
}
if (bIsEmpty)
{
return nullptr;
}
UEBBulletPropertiesAsset* UnloadedBullet = StoredBullets.Last();
StoredBullets.RemoveAt(StoredBullets.Num() - 1);
CurrentCapacity--;
UpdateMagazineState();
return UnloadedBullet;
}
void UEBMagazine::ClearJam()
{
if (GetOwner()->HasAuthority())
{
ServerClearJam();
}
else
{
ServerClearJam();
}
}
void UEBMagazine::ServerClearJam_Implementation()
{
if (bIsJammed)
{
bIsJammed = false;
UpdateMagazineState();
}
}
void UEBMagazine::ReleaseBoltHold()
{
if (GetOwner()->HasAuthority())
{
ServerReleaseBoltHold();
}
else
{
ServerReleaseBoltHold();
}
}
void UEBMagazine::ServerReleaseBoltHold_Implementation()
{
if (bBoltHeldOpen)
{
bBoltHeldOpen = false;
UpdateMagazineState();
}
}
void UEBMagazine::ForceJam()
{
if (GetOwner()->HasAuthority())
{
bIsJammed = true;
OnMagazineJammed.Broadcast(this);
MulticastOnMagazineJammed();
UpdateMagazineState();
}
}
int32 UEBMagazine::GetRemainingCapacity() const
{
return MagazineConfig.MaxCapacity - CurrentCapacity;
}
int32 UEBMagazine::GetCurrentRoundCount() const
{
return CurrentCapacity;
}
int32 UEBMagazine::GetMaxCapacity() const
{
return MagazineConfig.MaxCapacity;
}
float UEBMagazine::GetFillPercentage() const
{
if (MagazineConfig.MaxCapacity <= 0)
{
return 0.0f;
}
return (float)CurrentCapacity / (float)MagazineConfig.MaxCapacity;
}
UEBBulletPropertiesAsset* UEBMagazine::PeekNextBullet() const
{
if (bIsEmpty || StoredBullets.Num() == 0)
{
return nullptr;
}
return StoredBullets[0];
}
bool UEBMagazine::CanFeed() const
{
return !bIsEmpty && !bIsJammed && !bBoltHeldOpen && StoredBullets.Num() > 0;
}
bool UEBMagazine::NeedsReload() const
{
return bIsEmpty || CurrentCapacity < (MagazineConfig.MaxCapacity * 0.25f); // Need reload when below 25%
}
TArray<UEBBulletPropertiesAsset*> UEBMagazine::GetBulletTypesSummary() const
{
TArray<UEBBulletPropertiesAsset*> UniqueBulletTypes;
for (UEBBulletPropertiesAsset* BulletType : StoredBullets)
{
if (BulletType && !UniqueBulletTypes.Contains(BulletType))
{
UniqueBulletTypes.Add(BulletType);
}
}
return UniqueBulletTypes;
}
void UEBMagazine::UpdateMagazineState()
{
bool bOldEmpty = bIsEmpty;
bool bOldFull = bIsFull;
bIsEmpty = (CurrentCapacity == 0);
bIsFull = (CurrentCapacity >= MagazineConfig.MaxCapacity);
// Fire events for state changes
if (bIsEmpty && !bOldEmpty)
{
OnMagazineEmpty.Broadcast(this);
}
else if (bIsFull && !bOldFull)
{
OnMagazineFull.Broadcast(this);
}
}
bool UEBMagazine::CheckForMalfunction()
{
if (MagazineConfig.MalfunctionChance <= 0.0f)
{
return false;
}
float RandomValue = FMath::RandRange(0.0f, 1.0f);
return RandomValue <= MagazineConfig.MalfunctionChance;
}
void UEBMagazine::ProcessBoltHoldOpen()
{
if (!bBoltHeldOpen)
{
bBoltHeldOpen = true;
OnBoltHoldOpen.Broadcast(this);
MulticastOnBoltHoldOpen();
}
}
void UEBMagazine::OnRep_StoredBullets()
{
UpdateMagazineState();
}
void UEBMagazine::OnRep_CurrentCapacity()
{
UpdateMagazineState();
}
void UEBMagazine::OnRep_IsEmpty()
{
if (bIsEmpty)
{
OnMagazineEmpty.Broadcast(this);
}
}
void UEBMagazine::OnRep_IsFull()
{
if (bIsFull)
{
OnMagazineFull.Broadcast(this);
}
}
void UEBMagazine::OnRep_IsJammed()
{
if (bIsJammed)
{
OnMagazineJammed.Broadcast(this);
}
}
void UEBMagazine::OnRep_BoltHeldOpen()
{
if (bBoltHeldOpen)
{
OnBoltHoldOpen.Broadcast(this);
}
}
void UEBMagazine::MulticastOnBulletFed_Implementation(UEBBulletPropertiesAsset* BulletType)
{
if (!GetOwner()->HasAuthority())
{
OnBulletFed.Broadcast(this, BulletType);
}
}
void UEBMagazine::MulticastOnBulletLoaded_Implementation(UEBBulletPropertiesAsset* BulletType)
{
if (!GetOwner()->HasAuthority())
{
OnBulletLoaded.Broadcast(this, BulletType);
}
}
void UEBMagazine::MulticastOnMagazineJammed_Implementation()
{
if (!GetOwner()->HasAuthority())
{
OnMagazineJammed.Broadcast(this);
}
}
void UEBMagazine::MulticastOnBoltHoldOpen_Implementation()
{
if (!GetOwner()->HasAuthority())
{
OnBoltHoldOpen.Broadcast(this);
}
}