(Feat) Add Animation Fps Charater
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "SurvivalStatsComponent.h"
|
||||
|
||||
#include "Engine/World.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
USurvivalStatsComponent::USurvivalStatsComponent()
|
||||
{
|
||||
// Pas de Tick : tout passe par un timer a basse frequence.
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
SetIsReplicatedByDefault(true);
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
// COND_OwnerOnly ici, contrairement a l'inventaire : ces jauges n'existent
|
||||
// que sur un pawn, qui appartient bien a une connexion -- la condition
|
||||
// fonctionne donc. Et la faim d'un coequipier ne regarde personne d'autre
|
||||
// que lui. Le jour ou une barre de vie s'affichera au-dessus des allies,
|
||||
// c'est Health seul qu'il faudra ouvrir a tous.
|
||||
DOREPLIFETIME_CONDITION(USurvivalStatsComponent, Health, COND_OwnerOnly);
|
||||
DOREPLIFETIME_CONDITION(USurvivalStatsComponent, Hunger, COND_OwnerOnly);
|
||||
DOREPLIFETIME_CONDITION(USurvivalStatsComponent, Thirst, COND_OwnerOnly);
|
||||
DOREPLIFETIME_CONDITION(USurvivalStatsComponent, bDead, COND_OwnerOnly);
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::OnRep_Stats()
|
||||
{
|
||||
OnStatsChanged.Broadcast();
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::OnRep_Dead()
|
||||
{
|
||||
OnStatsChanged.Broadcast();
|
||||
|
||||
// bDead ne repasse jamais a vrai sans un nouveau pawn : apres un respawn
|
||||
// c'est un AUTRE composant qui arrive, plein et vivant. Pas de risque de
|
||||
// rediffuser une mort deja traitee.
|
||||
if (bDead)
|
||||
{
|
||||
OnDied.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Un client ne simule rien : il recevra les valeurs par replication.
|
||||
// Sans cette garde, chaque client ferait descendre SA copie des jauges en
|
||||
// plus de celle du serveur, et les barres sauteraient a chaque OnRep --
|
||||
// un scintillement qu'on chercherait longtemps.
|
||||
const AActor* Owner = GetOwner();
|
||||
if (!Owner || !Owner->HasAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Health = MaxHealth;
|
||||
Hunger = MaxHunger;
|
||||
Thirst = MaxThirst;
|
||||
bDead = false;
|
||||
|
||||
if (UWorld* World = GetWorld())
|
||||
{
|
||||
World->GetTimerManager().SetTimer(
|
||||
UpdateTimerHandle, this, &USurvivalStatsComponent::UpdateStats,
|
||||
FMath::Max(UpdateInterval, 0.05f), /*bLoop=*/true);
|
||||
}
|
||||
|
||||
OnStatsChanged.Broadcast();
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
// Un timer laisse en vie apres la destruction du composant rappellerait
|
||||
// une methode sur un objet mort.
|
||||
if (UWorld* World = GetWorld())
|
||||
{
|
||||
World->GetTimerManager().ClearTimer(UpdateTimerHandle);
|
||||
}
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::UpdateStats()
|
||||
{
|
||||
if (bDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Les vitesses sont par minute, l'intervalle en secondes.
|
||||
const float Minutes = FMath::Max(UpdateInterval, 0.05f) / 60.f;
|
||||
|
||||
Hunger = FMath::Max(0.f, Hunger - HungerDecayPerMinute * Minutes);
|
||||
Thirst = FMath::Max(0.f, Thirst - ThirstDecayPerMinute * Minutes);
|
||||
|
||||
// Faim ET soif a zero cumulent leurs degats : mourir de soif pendant
|
||||
// qu'on meurt de faim doit aller plus vite.
|
||||
float Damage = 0.f;
|
||||
if (Hunger <= 0.f)
|
||||
{
|
||||
Damage += StarvationDamagePerMinute * Minutes;
|
||||
}
|
||||
if (Thirst <= 0.f)
|
||||
{
|
||||
Damage += DehydrationDamagePerMinute * Minutes;
|
||||
}
|
||||
|
||||
if (Damage > 0.f)
|
||||
{
|
||||
Health = FMath::Max(0.f, Health - Damage);
|
||||
}
|
||||
|
||||
OnStatsChanged.Broadcast();
|
||||
|
||||
if (Health <= 0.f && !bDead)
|
||||
{
|
||||
bDead = true;
|
||||
|
||||
if (UWorld* World = GetWorld())
|
||||
{
|
||||
World->GetTimerManager().ClearTimer(UpdateTimerHandle);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("USurvivalStatsComponent : %s est mort."), *GetNameSafe(GetOwner()));
|
||||
OnDied.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::ApplyRestore(float HealthAmount, float HungerAmount, float ThirstAmount)
|
||||
{
|
||||
// Manger se decide sur le serveur (AFpsPlayer::Server_UseItem). Une
|
||||
// restauration jouee en local remonterait la barre du client, que le
|
||||
// prochain OnRep ferait redescendre : un clignotement pour rien.
|
||||
const AActor* Owner = GetOwner();
|
||||
if (!Owner || !Owner->HasAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Health = FMath::Clamp(Health + HealthAmount, 0.f, MaxHealth);
|
||||
Hunger = FMath::Clamp(Hunger + HungerAmount, 0.f, MaxHunger);
|
||||
Thirst = FMath::Clamp(Thirst + ThirstAmount, 0.f, MaxThirst);
|
||||
|
||||
OnStatsChanged.Broadcast();
|
||||
}
|
||||
|
||||
void USurvivalStatsComponent::ApplyDamage(float Amount)
|
||||
{
|
||||
// Meme regle que ApplyRestore : les degats appartiennent a l'autorite.
|
||||
const AActor* Owner = GetOwner();
|
||||
if (!Owner || !Owner->HasAuthority())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bDead || Amount <= 0.f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Health = FMath::Max(0.f, Health - Amount);
|
||||
OnStatsChanged.Broadcast();
|
||||
|
||||
if (Health <= 0.f)
|
||||
{
|
||||
bDead = true;
|
||||
|
||||
if (UWorld* World = GetWorld())
|
||||
{
|
||||
World->GetTimerManager().ClearTimer(UpdateTimerHandle);
|
||||
}
|
||||
|
||||
OnDied.Broadcast();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user