almost implemented attack (using projectiles)

pull/6/head
m4xxx1m 2 years ago
parent ab6ba0a53e
commit b2d6fadf2b

@ -32,6 +32,9 @@ public:
UPROPERTY(EditAnywhere)
float LinearWidth = 50.0f;
UPROPERTY(EditAnywhere)
float Speed = 1500.0f;
// Called every frame
// virtual void TickComponent(float DeltaTime,

@ -62,6 +62,10 @@ AMyPlayerState *AMyGameState::PlayerNotInTurn() const {
return GetMyPlayerState(!CurrentPlayerTurn);
}
TArray<ATrooper *> AMyGameState::GetTroopers() const {
return Troopers;
}
void AMyGameState::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty> &OutLifetimeProps) const {
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

@ -31,8 +31,9 @@ public:
UFUNCTION(BlueprintPure)
AMyPlayerState *PlayerNotInTurn() const;
auto GetMyPlayerState(uint8 PlayerIndex) const;
UFUNCTION()
TArray<ATrooper *> GetTroopers() const;
private:
UPROPERTY(Replicated)
@ -41,4 +42,5 @@ private:
UPROPERTY(Replicated)
uint8 CurrentPlayerTurn{0};
auto GetMyPlayerState(uint8 PlayerIndex) const;
};

@ -15,6 +15,10 @@ void AMyPlayerState::BeginPlay() {
Super::BeginPlay();
}
auto AMyPlayerState::GetMyGameState() const {
return Cast<AMyGameState>(GetWorld()->GetGameState());
}
void AMyPlayerState::SetPlayerIndex(uint8 NewPlayerIndex) {
PlayerIndex = NewPlayerIndex;
}
@ -40,11 +44,30 @@ void AMyPlayerState::MoveTrooper_Implementation(ATrooper *Trooper,
}
}
// void AMyPlayerState::Attack_Implementation(ATrooper *Attacker,
// FVector Location,
// int ActionIndex) {
// if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) {
// Attacker->Attack(ActionIndex);
// } else {
// GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red,
// FString::Printf(
// TEXT(
// "Out of radius or not enough Action Points!")));
// }
// }
void AMyPlayerState::Attack_Implementation(ATrooper *Attacker,
FVector Location,
int ActionIndex) {
int ActionIndex,
const TArray<ATrooper *> &Troopers) {
if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) {
Attacker->Attack(ActionIndex);
Attacker->Attack(ActionIndex, Location);
// for (const auto Trooper : Troopers) {
// if (Attacker->GetPlayerIndex() != Trooper->GetPlayerIndex()) {
// Trooper->TakeDamage(Attacker->GetAbility(ActionIndex)->Damage);
// }
// }
} else {
GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red,
FString::Printf(
@ -55,7 +78,7 @@ void AMyPlayerState::Attack_Implementation(ATrooper *Attacker,
void AMyPlayerState::CycleTurns() const {
if (bIsMyTurn) {
Cast<AMyGameState>(GetWorld()->GetGameState())->CycleTurns();
GetMyGameState()->CycleTurns();
}
}
@ -73,6 +96,7 @@ void AMyPlayerState::SetMyTurn(bool bMyTurn) {
}
}
void AMyPlayerState::StartTurn_Implementation() {
SetMyTurn(true);
UE_LOG(LogTemp, Warning, TEXT("Your turn, %d"), PlayerIndex);
@ -122,7 +146,7 @@ void AMyPlayerState::OnPlayerAction(const FHitResult &HitResult) {
// ATTACK! ATTACK!
UE_LOG(LogTemp, Warning, TEXT("Do attack"));
Attack(SelectedTrooper, NewlySelectedLocation,
CurrentAction);
CurrentAction, GetMyGameState()->GetTroopers());
SelectedTrooper->SetSelection(false, CurrentAction);
SelectedTrooper = nullptr;
break;
@ -145,7 +169,7 @@ void AMyPlayerState::SetCurrentAction_Implementation(int Action) {
CurrentAction = Action;
if (SelectedTrooper) {
SelectedTrooper->UpdateSelectionRadius(Action);
}
}
UE_LOG(LogTemp, Warning, TEXT("SetCurrentAction: %d on Player Controller "
"with index %d"), CurrentAction, PlayerIndex);
}

@ -12,6 +12,7 @@
UCLASS()
class TURNBASEDTUTORIAL_API AMyPlayerState : public APlayerState {
GENERATED_BODY()
public:
AMyPlayerState();
@ -19,7 +20,7 @@ public:
UFUNCTION(Client, Reliable)
void StartTurn();
UFUNCTION(Client, Reliable, BlueprintCallable)
void EndTurn();
@ -27,7 +28,10 @@ public:
void MoveTrooper(ATrooper *Trooper, FVector Location);
UFUNCTION(Server, Reliable)
void Attack(ATrooper *Attacker, FVector Location, int ActionIndex);
void Attack(ATrooper *Attacker,
FVector Location,
int ActionIndex,
const TArray<ATrooper *> &Troopers);
UFUNCTION()
void CycleTurns() const;
@ -40,7 +44,7 @@ public:
UFUNCTION(BlueprintCallable, Client, Reliable)
void SetCurrentAction(int Action);
UFUNCTION(BlueprintCallable)
uint8 GetPlayerIndex();
@ -53,16 +57,18 @@ public:
private:
UPROPERTY(Replicated)
uint8 PlayerIndex;
UFUNCTION()
void SetMyTurn(bool bMyTurn);
UPROPERTY(Replicated)
bool bIsMyTurn;
UPROPERTY(Replicated)
int CurrentAction = 0;
UPROPERTY(Replicated)
ATrooper *SelectedTrooper;
auto GetMyGameState() const;
};

@ -0,0 +1,64 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyProjectile.h"
#include "Net/UnrealNetwork.h"
#include "VisualLogger/VisualLoggerCustomVersion.h"
AMyProjectile::AMyProjectile() {
// if (!CollisionComponent) {
// CollisionComponent = CreateDefaultSubobject<USphereComponent>(
// TEXT("SphereComponent"));
// }
// RootComponent = CollisionComponent;
if (!ProjectileMeshComponent) {
ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(
TEXT("ProjectileMeshComponent"));
static ConstructorHelpers::FObjectFinder<UStaticMesh> Mesh(
TEXT(
"StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));
if (Mesh.Succeeded()) {
ProjectileMeshComponent->SetStaticMesh(Mesh.Object);
RootComponent = ProjectileMeshComponent;
}
}
if (!ProjectileMovementComponent) {
ProjectileMovementComponent = CreateDefaultSubobject<
UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
ProjectileMovementComponent->SetUpdatedComponent(
ProjectileMeshComponent);
ProjectileMovementComponent->InitialSpeed = 1000.0f;
ProjectileMovementComponent->MaxSpeed = 1000.0f;
ProjectileMovementComponent->ProjectileGravityScale = 0.0f;
}
InitialLifeSpan = 2.0f;
}
void AMyProjectile::Initialize(const UAbility *Ability,
uint8 playerIndex,
float PathLength) {
ProjectileMovementComponent->InitialSpeed =
ProjectileMovementComponent->MaxSpeed = Ability->Speed;
Damage = Ability->Damage;
float Scale = Ability->LinearWidth / 100;
ProjectileMeshComponent->SetWorldScale3D({Scale, Scale, Scale});
PlayerIndex = playerIndex;
SetLifeSpan(PathLength / Ability->Speed);
}
void AMyProjectile::Shoot(FVector From, FVector To) const {
ProjectileMovementComponent->Velocity =
(To - From).GetSafeNormal() * ProjectileMovementComponent->InitialSpeed;
}
void AMyProjectile::BeginPlay() {
Super::BeginPlay();
}
void AMyProjectile::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty> &OutLifetimeProps) const {
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyProjectile, Damage);
}

@ -0,0 +1,40 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Ability.h"
#include "Components/SphereComponent.h"
#include "GameFramework/Actor.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "MyProjectile.generated.h"
UCLASS()
class TURNBASEDTUTORIAL_API AMyProjectile : public AActor {
GENERATED_BODY()
public:
AMyProjectile();
void Initialize(const UAbility *Ability, uint8 playerIndex, float PathLength);
void Shoot(FVector From, FVector To) const;
protected:
UPROPERTY(Replicated)
float Damage;
UPROPERTY(Replicated)
uint8 PlayerIndex = -1;
// UPROPERTY(EditAnywhere)
// USphereComponent *CollisionComponent;
UPROPERTY(EditAnywhere)
UStaticMeshComponent *ProjectileMeshComponent;
UPROPERTY(VisibleAnywhere)
UProjectileMovementComponent *ProjectileMovementComponent;
virtual void BeginPlay() override;
};

@ -2,7 +2,7 @@
#include <Kismet/GameplayStatics.h>
#include "HealthBar.h"
#include "MyPlayerState.h"
#include "MyProjectile.h"
#include "Components/WidgetComponent.h"
#include "Net/UnrealNetwork.h"
@ -32,7 +32,6 @@ ATrooper::ATrooper()
if (MeshToUse.Object) {
SelectionStaticMesh->SetStaticMesh(MeshToUse.Object);
SelectionStaticMesh->SetStaticMesh(MeshToUse.Object);
}
// SelectionStaticMesh->SetRelativeTransform(FTransform({1000,1000,100}, {0, 0, 0}), false,
// nullptr, ETeleportType::TeleportPhysics);
@ -71,6 +70,7 @@ void ATrooper::Initialize(uint8 const NewPlayerIndex,
PlayerIndex = NewPlayerIndex;
bIsMoving = false;
AttackPlayedTime = 0.0f;
TakingDamagePlayedTime = 0.0f;
CurrentLocation = SpawnLocation;
Id = NewId;
}
@ -78,11 +78,24 @@ void ATrooper::Initialize(uint8 const NewPlayerIndex,
void ATrooper::Tick(float const DeltaTime) {
if (bIsAttacking) {
AttackPlayedTime += DeltaTime;
if (bIsWaitingForFire && AttackPlayedTime >= FireAfterTime) {
FireProjectile();
CurrentAbilityIndex = -1;
CurrentAbilityDestination = {};
bIsWaitingForFire = false;
}
if (AttackPlayedTime >= AttackDuration) {
AttackPlayedTime = 0.0f;
bIsAttacking = false;
}
}
if (bIsTakingDamage) {
TakingDamagePlayedTime += DeltaTime;
if (TakingDamagePlayedTime >= TakingDamageDuration) {
TakingDamagePlayedTime = 0.0f;
bIsTakingDamage = false;
}
}
if (bIsMoving) {
FVector PositionVector = (TargetLocation - CurrentLocation);
PositionVector.Normalize();
@ -133,6 +146,11 @@ void ATrooper::GetLifetimeReplicatedProps(
DOREPLIFETIME(ATrooper, ActionPoints);
DOREPLIFETIME(ATrooper, HealthWidgetComponent);
DOREPLIFETIME(ATrooper, AttackPlayedTime);
DOREPLIFETIME(ATrooper, TakingDamagePlayedTime);
DOREPLIFETIME(ATrooper, bIsTakingDamage);
DOREPLIFETIME(ATrooper, bIsDead);
DOREPLIFETIME(ATrooper, CurrentAbilityIndex);
DOREPLIFETIME(ATrooper, CurrentAbilityDestination);
}
uint8 ATrooper::GetPlayerIndex() const {
@ -221,19 +239,89 @@ UAbility *ATrooper::GetAbility(int AbilityIndex) const {
}
}
float ATrooper::GetAnimationValue() {
bool ATrooper::TakeDamage(float Damage) {
HitPoints = FMath::Max<float>(0, HitPoints - Damage);
if (HitPoints == 0) {
bIsDead = true;
return true;
}
bIsTakingDamage = true;
return false;
}
TSubclassOf<AMyProjectile> ATrooper::GetProjectileClass(
uint8 AbilityIndex) const {
switch (AbilityIndex) {
case 1:
return AttackProjectileClass;
case 2:
return SpecialProjectileClass;
default:
return AMyProjectile::StaticClass();
}
}
void ATrooper::FireProjectile_Implementation() {
if (!this || !this->IsValidLowLevel())
return;
FTransform SpawnTransform(
(CurrentAbilityDestination - CurrentLocation).Rotation());
const FVector TransformedVector = SpawnTransform.TransformVector(
{GetAbility(CurrentAbilityIndex)->LinearWidth / 2 + 50.0f, 0.0f,
88.0f});
const FVector SpawnLocation = CurrentLocation + TransformedVector;
SpawnTransform.SetLocation(SpawnLocation);
FActorSpawnParameters SpawnParameters;
SpawnParameters.Owner = this;
SpawnParameters.Instigator = GetInstigator();
SpawnParameters.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AMyProjectile *Projectile = GetWorld()->SpawnActor<AMyProjectile>(
GetProjectileClass(CurrentAbilityIndex), SpawnTransform,
SpawnParameters);
Projectile->Initialize(GetAbility(CurrentAbilityIndex), CurrentAbilityIndex,
(CurrentAbilityDestination - SpawnLocation).
Size());
Projectile->Shoot(SpawnLocation, CurrentAbilityDestination);
}
int ATrooper::GetAnimationValue() {
if (bIsDead) {
return 4;
}
if (bIsTakingDamage) {
return 3;
}
if (bIsAttacking) {
return -100.0f;
return 2;
}
if (bIsMoving) {
return 100.0f;
return 1;
}
return 0.0f;
return 0;
}
void ATrooper::Attack(int abilityIndex) {
void ATrooper::Attack(int AbilityIndex, FVector ToLocation) {
bIsAttacking = true;
ActionPoints -= GetAbility(abilityIndex)->ActionCost;
bIsWaitingForFire = true;
ActionPoints -= GetAbility(AbilityIndex)->ActionCost;
CurrentAbilityIndex = AbilityIndex;
ToLocation.Z = 88.0f;
CurrentAbilityDestination = ToLocation;
// FTransform SpawnTransform((ToLocation - CurrentLocation).Rotation());
// SpawnTransform.SetLocation(
// CurrentLocation + SpawnTransform.TransformVector(
// {GetAbility(AbilityIndex)->LinearWidth/2 + 50.0f, 0.0f, 0.0f})
// );
// FActorSpawnParameters SpawnParameters;
// SpawnParameters.Owner = this;
// SpawnParameters.Instigator = GetInstigator();
// SpawnParameters.SpawnCollisionHandlingOverride =
// ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// AMyProjectile *Projectile = GetWorld()->SpawnActor<AMyProjectile>(
// GetProjectileClass(AbilityIndex), SpawnTransform, SpawnParameters);
// Projectile->Initialize(GetAbility(AbilityIndex));
// Projectile->Shoot(CurrentLocation, ToLocation);
}
bool ATrooper::CheckMoveCorrectness(const FVector newPos) const {

@ -36,10 +36,10 @@ public:
FVector GetLocation() const;
UFUNCTION(BlueprintCallable)
float GetAnimationValue();
int GetAnimationValue();
UFUNCTION()
void Attack(int abilityIndex);
void Attack(int AbilityIndex, FVector ToLocation);
UFUNCTION()
float GetActionRadius(int action) const;
@ -65,6 +65,9 @@ public:
UFUNCTION()
UAbility *GetAbility(int AbilityIndex) const;
UFUNCTION()
bool TakeDamage(float Damage);
protected:
constexpr static float PIXELS_IN_RADIUS = 50;
@ -79,6 +82,24 @@ protected:
UPROPERTY(EditAnywhere)
UAbility *SpecialAbility;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AMyProjectile> AttackProjectileClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AMyProjectile> SpecialProjectileClass;
UFUNCTION()
TSubclassOf<AMyProjectile> GetProjectileClass(uint8 AbilityIndex) const;
UFUNCTION(Server, Reliable)
void FireProjectile();
UPROPERTY(Replicated)
uint8 CurrentAbilityIndex = -1;
UPROPERTY(Replicated)
FVector CurrentAbilityDestination = {};
UPROPERTY(EditAnywhere)
float Speed = 300.0f;
@ -97,7 +118,7 @@ protected:
UPROPERTY(Replicated)
float ActionPoints;
UPROPERTY(Replicated)
bool bIsAttacking = false;
@ -106,6 +127,22 @@ protected:
const float AttackDuration = 1.16667f;
const float FireAfterTime = 0.6f;
UPROPERTY(Replicated)
bool bIsWaitingForFire = false;
UPROPERTY(Replicated)
bool bIsTakingDamage = false;
UPROPERTY(Replicated)
float TakingDamagePlayedTime;
const float TakingDamageDuration = 1.46667f;
UPROPERTY(Replicated)
bool bIsDead = false;
virtual void BeginPlay() override;
virtual void Tick(float const DeltaTime) override;

Loading…
Cancel
Save