diff --git a/Content/Legumix/Spawner/DT_WavePreset.uasset b/Content/Legumix/Spawner/DT_WavePreset.uasset new file mode 100644 index 0000000..69cfca0 --- /dev/null +++ b/Content/Legumix/Spawner/DT_WavePreset.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4dc980d10370e54247d6d2b0bd1cd609110484abbbff020f22414b7261c5777 +size 2889 diff --git a/Content/Legumix/Spawner/LVL_TestSpawn.umap b/Content/Legumix/Spawner/LVL_TestSpawn.umap index 9fd5345..babf3e9 100644 --- a/Content/Legumix/Spawner/LVL_TestSpawn.umap +++ b/Content/Legumix/Spawner/LVL_TestSpawn.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c66f6a8227ba03137a1a9a32451b8be1b400d73f51ce06f8c532dc0aee38fd0 -size 79804 +oid sha256:bcb5b01b681fff1a03e31833c07c4f3f7d7201fc37d0f0216cbc4714a96d1526 +size 81157 diff --git a/Source/LegumeMix/Private/Enemy/LMEnemyRatio.cpp b/Source/LegumeMix/Private/Enemy/LMEnemyRatio.cpp new file mode 100644 index 0000000..3647fa0 --- /dev/null +++ b/Source/LegumeMix/Private/Enemy/LMEnemyRatio.cpp @@ -0,0 +1,3 @@ +#include "Enemy/LMEnemyRatio.h" +#include "Enemy/LMEnemy.h" + diff --git a/Source/LegumeMix/Private/Enemy/LMWaveStructure.cpp b/Source/LegumeMix/Private/Enemy/LMWaveStructure.cpp new file mode 100644 index 0000000..f724e7b --- /dev/null +++ b/Source/LegumeMix/Private/Enemy/LMWaveStructure.cpp @@ -0,0 +1 @@ +#include "LMWaveStructure.h" diff --git a/Source/LegumeMix/Private/LMSpawnPosition.cpp b/Source/LegumeMix/Private/LMSpawnPosition.cpp index 0715e59..6aaa7ec 100644 --- a/Source/LegumeMix/Private/LMSpawnPosition.cpp +++ b/Source/LegumeMix/Private/LMSpawnPosition.cpp @@ -16,7 +16,7 @@ ALMSpawnPosition::ALMSpawnPosition() bool ALMSpawnPosition::CanSpawn() const { - return !IsPlayerVisible(); + return !IsPlayerVisible() && !IsOnCooldow; } // Called when the game starts or when spawned @@ -30,7 +30,7 @@ void ALMSpawnPosition::BeginPlay() void ALMSpawnPosition::Tick(float DeltaTime) { Super::Tick(DeltaTime); - UE_LOG(LogTemp, Warning, TEXT("CanSpawn: %s"), CanSpawn() ? TEXT("true") : TEXT("false")); + //UE_LOG(LogTemp, Warning, TEXT("CanSpawn: %s"), CanSpawn() ? TEXT("true") : TEXT("false")); } bool ALMSpawnPosition::IsPlayerVisible() const @@ -54,15 +54,27 @@ bool ALMSpawnPosition::IsPlayerVisible() const ); // Debug trace - DrawDebugLine(GetWorld(), Start, End, bHit ? FColor::Red : FColor::Green, false, 2.0f); + //DrawDebugLine(GetWorld(), Start, End, bHit ? FColor::Red : FColor::Green, false, 2.0f); if (bHit) { - UE_LOG(LogTemp, Warning, TEXT("LineTrace Hit: %s"), *HitResult.GetActor()->GetName()); + //UE_LOG(LogTemp, Warning, TEXT("LineTrace Hit: %s"), *HitResult.GetActor()->GetName()); return HitResult.GetActor() == Player; // Retourne true uniquement si l'acteur touché est le joueur } - UE_LOG(LogTemp, Warning, TEXT("LineTrace: No Hit")); + //UE_LOG(LogTemp, Warning, TEXT("LineTrace: No Hit")); return false; // Aucun obstacle, le joueur est visible } +void ALMSpawnPosition::StarCooldown() +{ + IsOnCooldow = true; + GetWorld()->GetTimerManager().SetTimer(ProcessCooldown, this, &ALMSpawnPosition::EndCooldown, TimerCooldown, false); +} + +void ALMSpawnPosition::EndCooldown() +{ + IsOnCooldow = false; +} + + diff --git a/Source/LegumeMix/Private/LMWaveManager.cpp b/Source/LegumeMix/Private/LMWaveManager.cpp index 85e8155..c2228b2 100644 --- a/Source/LegumeMix/Private/LMWaveManager.cpp +++ b/Source/LegumeMix/Private/LMWaveManager.cpp @@ -1,6 +1,9 @@ // Fill out your copyright notice in the Description page of Project Settings. #include "LMWaveManager.h" + +#include "IPropertyTable.h" +#include "Components/CapsuleComponent.h" #include "Enemy/LMEnemy.h" // Sets default values @@ -18,16 +21,40 @@ void ALMWaveManager::BeginPlay() StartWave(); } -void ALMWaveManager::SpawnEnemy(ALMSpawnPosition* spawnPosition, TSubclassOf enemyToSpawn) +void ALMWaveManager::SpawnEnemy(ALMSpawnPosition* spawnPosition) { - //Spawn sur une position - ALMEnemy* tempEnemy = GetWorld()->SpawnActor(enemyToSpawn, spawnPosition->GetActorLocation(), FRotator::ZeroRotator); + int RandomIndex = FMath::RandRange(0, AllEnemyType.Num()-1); + while(EnemiesPerType[RandomIndex] <= 0 ) + { + RandomIndex = FMath::RandRange(0, AllEnemyType.Num()-1); + } + //Choisis un ennemis à spawn random + TSubclassOf enemyToSpawn = AllEnemyType[RandomIndex]; + //Spawn sur une position + FActorSpawnParameters SpawnParameters; + SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + ALMEnemy* tempEnemy = GetWorld()->SpawnActor(enemyToSpawn, spawnPosition->GetActorLocation(), FRotator::ZeroRotator); + + if (!tempEnemy) + { + UE_LOG(LogTemp, Error, TEXT("Failed to spawn enemy!")); + return; + } + spawnPosition->StarCooldown(); + float tailleCapsule = tempEnemy->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); + FVector newPosition = tempEnemy->GetActorLocation(); + tempEnemy->SetActorLocation(FVector(newPosition.X,newPosition.Y,newPosition.Z + tailleCapsule)); + //ajoute l'ennemis à la liste des ennemis vivants EnemyAliveList.Add(tempEnemy); - + //bind la method ennemydead à la death de l'ennemis - tempEnemy->OnEnemyDeath.AddUniqueDynamic(this, &ALMWaveManager::EnemyDead); + tempEnemy->OnEnemyDeath.AddDynamic(this, &ALMWaveManager::EnemyDead); + UE_LOG(LogTemp, Error, TEXT("Succes to spawn and bind on death method")); + + EnemySpawned++; + EnemiesPerType[RandomIndex]--; } void ALMWaveManager::EnemyDead(ALMEnemy* enemyToRemoveFromLife) @@ -39,8 +66,15 @@ void ALMWaveManager::EnemyDead(ALMEnemy* enemyToRemoveFromLife) bool ALMWaveManager::RemainsEnemyToSpawn() { + int TotalEnemies = 0; + + for (int EnemyCount : EnemiesPerType) + { + TotalEnemies += EnemyCount; + } + bool stillToSpawn = TotalEnemies !=0; bool no = EnemyAliveList.Num() < MaxEnemyInstantiate; - return EnemyNumberInWave != EnemySpawned && no; + return EnemyNumberInWave != EnemySpawned && no && stillToSpawn; } void ALMWaveManager::CheckForSpawnerOK() @@ -55,6 +89,54 @@ void ALMWaveManager::CheckForSpawnerOK() } } +void ALMWaveManager::GetRandomDataWaveRow() +{ + TArray RowNames = WaveDatePreset->GetRowNames(); + int RandomIndex = FMath::RandRange(0, RowNames.Num() - 1);; + + FName WaveName = RowNames[RandomIndex]; + + CurrentWaveName = WaveName.ToString(); + FLMWaveStructure* InfoWaveRow = WaveDatePreset->FindRow(WaveName, ""); + + //InternalEnemyRatio = InfoWaveRow->WaveComposition; + EnemyNumberInWave = InfoWaveRow->EnemyCount; + MaxEnemyInstantiate = InfoWaveRow->MaxEnemyCount; + + for (const FLMEnemyRatio& EnemyRatio : InfoWaveRow->WaveComposition) + { + // Ajoute chaque EnemyType à la liste + AllEnemyType.Add(EnemyRatio.EnemyType); + } + + // Récupère les ratios (NbEnemy) et calcule leur somme + int32 TotalRatio = 0; + for (const FLMEnemyRatio& EnemyRatio : InfoWaveRow->WaveComposition) + { + TotalRatio += EnemyRatio.NbEnemy; + } + + // Sécurité : Si le total est 0, évite la division par zéro + if (TotalRatio == 0) return; + + // Répartit les ennemis proportionnellement + int32 RemainingEnemies = EnemyNumberInWave; + for (const FLMEnemyRatio& EnemyRatio : InfoWaveRow->WaveComposition) + { + // Calcul proportionnel arrondi + int32 Count = FMath::RoundToInt((float(EnemyRatio.NbEnemy) / TotalRatio) * EnemyNumberInWave); + + EnemiesPerType.Add(Count); + RemainingEnemies -= Count; + } + + // Ajuste les arrondis pour respecter exactement EnemyNumberInWave + for (int32 i = 0; i < RemainingEnemies; ++i) + { + EnemiesPerType[i % EnemiesPerType.Num()]++; + } +} + // Called every frame void ALMWaveManager::Tick(float DeltaTime) { @@ -66,7 +148,7 @@ void ALMWaveManager::Tick(float DeltaTime) { //Choose spawnpoint to spawn int32 RandomIndex = FMath::RandRange(0, SpawnPositionsOK.Num() - 1); - SpawnEnemy(SpawnPositionsOK[RandomIndex],TypeOfEnemy); + SpawnEnemy(SpawnPositionsOK[RandomIndex]); EnemySpawned++; } } @@ -81,5 +163,6 @@ void ALMWaveManager::Tick(float DeltaTime) void ALMWaveManager::StartWave() { - + GetRandomDataWaveRow(); + EnemySpawned = 0; } diff --git a/Source/LegumeMix/Public/Enemy/LMEnemyRatio.h b/Source/LegumeMix/Public/Enemy/LMEnemyRatio.h new file mode 100644 index 0000000..8995697 --- /dev/null +++ b/Source/LegumeMix/Public/Enemy/LMEnemyRatio.h @@ -0,0 +1,17 @@ +#pragma once + +#include "LMEnemyRatio.generated.h" + + +class ALMEnemy; + +USTRUCT(BlueprintType) +struct FLMEnemyRatio : public FTableRowBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Legumix") + TSubclassOf EnemyType; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Legumix", meta = (ClampMin = 0)) + int NbEnemy; +}; diff --git a/Source/LegumeMix/Public/LMSpawnPosition.h b/Source/LegumeMix/Public/LMSpawnPosition.h index b3dd41e..3a118c3 100644 --- a/Source/LegumeMix/Public/LMSpawnPosition.h +++ b/Source/LegumeMix/Public/LMSpawnPosition.h @@ -17,15 +17,22 @@ public: // Méthode pour vérifier si le spawn est possible bool CanSpawn() const; - + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") + float TimerCooldown = 1.5; + void StarCooldown(); + protected: // Called when the game starts or when spawned virtual void BeginPlay() override; - + + virtual void EndCooldown(); + public: // Called every frame virtual void Tick(float DeltaTime) override; private: bool IsPlayerVisible() const; + FTimerHandle ProcessCooldown; + bool IsOnCooldow; }; diff --git a/Source/LegumeMix/Public/LMWaveManager.h b/Source/LegumeMix/Public/LMWaveManager.h index 48fb0c9..83ec789 100644 --- a/Source/LegumeMix/Public/LMWaveManager.h +++ b/Source/LegumeMix/Public/LMWaveManager.h @@ -5,6 +5,7 @@ #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "LMSpawnPosition.h" +#include "LMWaveStructure.h" #include "LMWaveManager.generated.h" class ALMEnemy; @@ -17,34 +18,45 @@ class LEGUMEMIX_API ALMWaveManager : public AActor public: // Sets default values for this actor's properties ALMWaveManager(); - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") - TArray EnemyAliveList; // Liste des ennemis + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "Legumix") + FString CurrentWaveName; // Name of the current wave + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") TArray SpawnPositionsList; // Liste des ennemis + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "Legumix") + TArray EnemyAliveList; // Liste des ennemis - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "Legumix") int EnemyNumberInWave; // Nombre d'ennemis dans la wave - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "Legumix") int MaxEnemyInstantiate; // Nombre d'ennemis max spawnés - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") - TSubclassOf TypeOfEnemy; // Type d'ennemis to spawn - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") float BreakTime; // Nombre d'ennemis dans la wave + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"), Category = "Legumix") + UDataTable* WaveDatePreset; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Legumix") + TArray> AllEnemyType; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Legumix") + TArray EnemiesPerType; + protected: // Called when the game starts or when spawned virtual void BeginPlay() override; virtual void StartWave(); - virtual void SpawnEnemy(ALMSpawnPosition* spawnPosition, TSubclassOf enemyToSpawn); + virtual void SpawnEnemy(ALMSpawnPosition* spawnPosition); UFUNCTION() virtual void EnemyDead(ALMEnemy* enemyToRemoveFromLife); virtual bool RemainsEnemyToSpawn(); virtual void CheckForSpawnerOK(); - + + virtual void GetRandomDataWaveRow(); public: // Called every frame @@ -52,7 +64,7 @@ public: private: TArray SpawnPositionsOK; - int EnemySpawned; + int EnemySpawned = 0; FTimerHandle BreakTimer; }; diff --git a/Source/LegumeMix/Public/LMWaveStructure.h b/Source/LegumeMix/Public/LMWaveStructure.h new file mode 100644 index 0000000..6da4520 --- /dev/null +++ b/Source/LegumeMix/Public/LMWaveStructure.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Enemy/LMEnemyRatio.h" +#include "LMWaveStructure.generated.h" + +USTRUCT(BlueprintType) +struct FLMWaveStructure : public FTableRowBase +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Legumix") + TArray WaveComposition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Legumix", meta = (ClampMin = 0)) + int EnemyCount; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Legumix", meta = (ClampMin = 0)) + int MaxEnemyCount; +};