Combat mechanics, Level design and an intro Cutscene
* Added a cinematic on level start that shows the way ahead * The HUD and Player character are hidden and input disabled during the cutscene. Then reenabled after. * Various level design changes. * Added a damage multiplier to weapon rarities that's taken into account. * Changed stun chance calculation to make it depend on the damage inflicted and enemy health. To avoid constant stuns with the SMG. * Fixed input still working after player death. * Explosive barrels now make other barrels explode in chain.
This commit is contained in:
parent
e4ed6c309a
commit
08aff94716
BIN
Content/FantasyBundle/Maps/Demonstration_Castle.umap (Stored with Git LFS)
BIN
Content/FantasyBundle/Maps/Demonstration_Castle.umap (Stored with Git LFS)
Binary file not shown.
BIN
Content/FantasyBundle/Meshes/SM_Bridge01.uasset (Stored with Git LFS)
BIN
Content/FantasyBundle/Meshes/SM_Bridge01.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/FantasyBundle/Meshes/SM_Bridge02.uasset (Stored with Git LFS)
BIN
Content/FantasyBundle/Meshes/SM_Bridge02.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/FantasyBundle/Meshes/SM_Bridge03.uasset (Stored with Git LFS)
BIN
Content/FantasyBundle/Meshes/SM_Bridge03.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonKhaimera/Characters/Heroes/Khaimera/KhaimeraPlayerCharacter.uasset (Stored with Git LFS)
BIN
Content/ParagonKhaimera/Characters/Heroes/Khaimera/KhaimeraPlayerCharacter.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonLtBelica/Characters/Heroes/Belica/LtBelicaPlayerCharacter.uasset (Stored with Git LFS)
BIN
Content/ParagonLtBelica/Characters/Heroes/Belica/LtBelicaPlayerCharacter.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Global/MaterialFunctions/MF_CharacterEffects.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Global/MaterialFunctions/MF_CharacterEffects.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Global/MaterialFunctions/MF_DeathFade.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Global/MaterialFunctions/MF_DeathFade.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Global/MaterialFunctions/MF_HitFlash.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Global/MaterialFunctions/MF_HitFlash.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Leather/ML_Light_Leather_Brown2.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Leather/ML_Light_Leather_Brown2.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Leather/ML_ThinLeather01.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Leather/ML_ThinLeather01.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Metal/ML_DullPanelMetal.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Metal/ML_DullPanelMetal.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Metal/ML_blue_Metal.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Global/MaterialLayers/Metal/ML_blue_Metal.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Heroes/Revenant/Materials/MF_RevanantUltimate.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Heroes/Revenant/Materials/MF_RevanantUltimate.uasset (Stored with Git LFS)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/ParagonRevenant/Characters/Heroes/Revenant/Skins/RavenQuill/Materials/Layers/ML_Metal.uasset (Stored with Git LFS)
BIN
Content/ParagonRevenant/Characters/Heroes/Revenant/Skins/RavenQuill/Materials/Layers/ML_Metal.uasset (Stored with Git LFS)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/_Game/Character/Revenant/RevenantCharacterBP.uasset (Stored with Git LFS)
BIN
Content/_Game/Character/Revenant/RevenantCharacterBP.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/_Game/DataTable/ItemRarityDataTable.uasset (Stored with Git LFS)
BIN
Content/_Game/DataTable/ItemRarityDataTable.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/_Game/DataTable/WeaponDataTable.uasset (Stored with Git LFS)
BIN
Content/_Game/DataTable/WeaponDataTable.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/_Game/EnemyController/EnemyBehaviorTree.uasset (Stored with Git LFS)
BIN
Content/_Game/EnemyController/EnemyBehaviorTree.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/_Game/HUD/ShooterHUDBP.uasset (Stored with Git LFS)
BIN
Content/_Game/HUD/ShooterHUDBP.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/_Game/Health/HealthBP.uasset (Stored with Git LFS)
BIN
Content/_Game/Health/HealthBP.uasset (Stored with Git LFS)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/_Game/Weapons/BaseWeapon/BaseWeaponBP.uasset (Stored with Git LFS)
BIN
Content/_Game/Weapons/BaseWeapon/BaseWeaponBP.uasset (Stored with Git LFS)
Binary file not shown.
|
@ -30,7 +30,7 @@ AEnemy::AEnemy() :
|
|||
bCanHitReact(true),
|
||||
HitNumberDestroyTime(1.5f),
|
||||
bStunned(false),
|
||||
StunChance(.5f),
|
||||
StunChance(0.8f),
|
||||
AttackLFast(TEXT("AttackLFast")),
|
||||
AttackRFast(TEXT("AttackRFast")),
|
||||
AttackL(TEXT("AttackL")),
|
||||
|
@ -235,7 +235,7 @@ void AEnemy::CombatRangeOverlap(UPrimitiveComponent* OverlappedComponent, AActor
|
|||
|
||||
if (!EnemyController) return;
|
||||
EnemyController->GetBlackboardComponent()->SetValueAsBool(TEXT("InAttackRange"), true);
|
||||
//UE_LOG(LogTemp, Warning, TEXT("overlap player"));
|
||||
//UE_LOG(LogTemp, Warning, TEXT("overlap player: %d"), static_cast<int>(bInAttackRange));
|
||||
}
|
||||
|
||||
void AEnemy::CombatRangeEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
|
||||
|
@ -428,9 +428,12 @@ float AEnemy::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AC
|
|||
|
||||
ShowHealthBar();
|
||||
|
||||
// Determine actual Stun Chance based on health and damage inflicted
|
||||
float ActualStunChance = DamageInflicted / (Health / 10.f) * StunChance;
|
||||
|
||||
// Determine whether bullet hit stuns
|
||||
const float Stunned = FMath::FRandRange(0.f, 1.f);
|
||||
if (Stunned <= StunChance)
|
||||
if (Stunned <= ActualStunChance)
|
||||
{
|
||||
// Stun the Enemy
|
||||
PlayHitMontage(FName("HitReactFront"));
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
|
||||
// Sets default values
|
||||
AExplosive::AExplosive() :
|
||||
Damage(40.f)
|
||||
Damage(40.f),
|
||||
Exploded(false)
|
||||
{
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
@ -41,27 +42,53 @@ void AExplosive::Tick(float DeltaTime)
|
|||
|
||||
}
|
||||
|
||||
void AExplosive::BulletHit_Implementation(FHitResult HitResult, AActor* Shooter, AController* ShooterController)
|
||||
void AExplosive::Explode(AActor* Shooter, AController* ShooterController)
|
||||
{
|
||||
if (ExplosionSound)
|
||||
{
|
||||
UGameplayStatics::PlaySoundAtLocation(this, ExplosionSound, GetActorLocation());
|
||||
}
|
||||
Exploded = true;
|
||||
|
||||
if (ExplosionParticles)
|
||||
{
|
||||
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionParticles, HitResult.Location, FRotator(0.0), true);
|
||||
}
|
||||
if (ExplosionSound)
|
||||
{
|
||||
UGameplayStatics::PlaySoundAtLocation(this, ExplosionSound, GetActorLocation());
|
||||
}
|
||||
|
||||
// Apply explosive damage
|
||||
TArray<AActor*> OverlappingActors;
|
||||
GetOverlappingActors(OverlappingActors, ACharacter::StaticClass());
|
||||
if (ExplosionParticles)
|
||||
{
|
||||
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionParticles, GetActorLocation(), FRotator(0.0), true);
|
||||
}
|
||||
|
||||
for (auto Actor : OverlappingActors)
|
||||
{
|
||||
UGameplayStatics::ApplyDamage(Actor, Damage, ShooterController, Shooter, UDamageType::StaticClass());
|
||||
}
|
||||
// Apply explosive damage
|
||||
TArray<AActor*> OverlappingActors;
|
||||
GetOverlappingActors(OverlappingActors, ACharacter::StaticClass());
|
||||
|
||||
Destroy();
|
||||
for (auto Actor : OverlappingActors)
|
||||
{
|
||||
UGameplayStatics::ApplyDamage(Actor, Damage, ShooterController, Shooter, UDamageType::StaticClass());
|
||||
}
|
||||
|
||||
// Other explosives
|
||||
OverlappingActors.Empty();
|
||||
GetOverlappingActors(OverlappingActors, AExplosive::StaticClass());
|
||||
for (auto Actor : OverlappingActors)
|
||||
{
|
||||
AExplosive* Explosive = Cast<AExplosive>(Actor);
|
||||
if (Explosive && Explosive != this && !Explosive->Exploded)
|
||||
UGameplayStatics::ApplyDamage(Actor, Damage, ShooterController, Shooter, UDamageType::StaticClass());
|
||||
}
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void AExplosive::BulletHit_Implementation(FHitResult HitResult, AActor* Shooter, AController* ShooterController)
|
||||
{
|
||||
Explode(Shooter, ShooterController);
|
||||
}
|
||||
|
||||
float AExplosive::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator,
|
||||
AActor* DamageCauser)
|
||||
{
|
||||
Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
|
||||
|
||||
Explode(DamageCauser, EventInstigator);
|
||||
return DamageAmount;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ protected:
|
|||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
void Explode(AActor* Shooter, AController* ShooterController);
|
||||
private:
|
||||
|
||||
/** Particles to spawn when hit by bullets */
|
||||
|
@ -46,9 +47,14 @@ private:
|
|||
/** Damage amount for explosive */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true))
|
||||
float Damage;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Combat, meta = (AllowPrivateAccess = true))
|
||||
bool Exploded;
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
virtual void BulletHit_Implementation(FHitResult HitResult, AActor* Shooter, AController* ShooterController) override;
|
||||
|
||||
virtual float TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
|
||||
};
|
||||
|
|
|
@ -210,6 +210,7 @@ void AItem::OnConstruction(const FTransform& Transform)
|
|||
{
|
||||
GetItemMesh()->SetCustomDepthStencilValue(RarityRow->CustomDepthStencil);
|
||||
}
|
||||
DamageMultiplier = RarityRow->DamageMultiplier;
|
||||
|
||||
if (MaterialInstance)
|
||||
{
|
||||
|
|
|
@ -35,7 +35,7 @@ enum class EItemRarity : uint8
|
|||
UENUM(BlueprintType)
|
||||
enum class EItemState : uint8
|
||||
{
|
||||
EIS_Pickup UMETA(DisplayName = "Damaged"),
|
||||
EIS_Pickup UMETA(DisplayName = "Pickup"),
|
||||
EIS_EquipInterping UMETA(DisplayName = "EquipInterping"),
|
||||
EIS_PickedUp UMETA(DisplayName = "PickedUp"),
|
||||
EIS_Equipped UMETA(DisplayName = "Equipped"),
|
||||
|
@ -75,6 +75,9 @@ struct FItemRarityTable : public FTableRowBase
|
|||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 CustomDepthStencil;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
float DamageMultiplier;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
|
@ -305,6 +308,10 @@ private:
|
|||
/** Background icon for the inventory */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Rarity, meta = (AllowPrivateAccess = true))
|
||||
UTexture2D* IconBackground;
|
||||
|
||||
/** Damage multiplier applied on the weapon damage */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Rarity, meta = (AllowPrivateAccess = true))
|
||||
float DamageMultiplier;
|
||||
public:
|
||||
FORCEINLINE UWidgetComponent* GetPickupWidget() const { return PickupWidget; }
|
||||
|
||||
|
@ -349,6 +356,8 @@ public:
|
|||
FORCEINLINE void SetMaterialIndex(int32 Index) { MaterialIndex = Index; }
|
||||
FORCEINLINE int32 GetMaterialIndex() const { return MaterialIndex; }
|
||||
|
||||
FORCEINLINE float GetDamageMultiplier() const { return DamageMultiplier; }
|
||||
|
||||
/** Called from the AShooterCharacter class */
|
||||
void StartItemCurve(AShooterCharacter* Char, bool bForcePlaySound = false);
|
||||
|
||||
|
|
|
@ -61,8 +61,8 @@ AShooterCharacter::AShooterCharacter() :
|
|||
CameraInterpDistance(150.f),
|
||||
CameraInterpElevation(55.f),
|
||||
// Starting ammo amounts
|
||||
Starting9mmAmmo(85),
|
||||
StartingARAmmo(120),
|
||||
Starting9mmAmmo(15),
|
||||
StartingARAmmo(0),
|
||||
// Combat variables
|
||||
CombatState(ECombatState::ECS_Unoccupied),
|
||||
bCrouching(false),
|
||||
|
@ -238,6 +238,12 @@ void AShooterCharacter::Die()
|
|||
if (bDying) return;
|
||||
bDying = true;
|
||||
|
||||
APlayerController* PC = UGameplayStatics::GetPlayerController(this, 0);
|
||||
if (PC)
|
||||
{
|
||||
DisableInput(PC);
|
||||
}
|
||||
|
||||
if (!DeathMontage) return;
|
||||
|
||||
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
|
||||
|
@ -249,11 +255,6 @@ void AShooterCharacter::Die()
|
|||
void AShooterCharacter::FinishDeath()
|
||||
{
|
||||
GetMesh()->bPauseAnims = true;
|
||||
APlayerController* PC = UGameplayStatics::GetPlayerController(this, 0);
|
||||
if (PC)
|
||||
{
|
||||
DisableInput(PC);
|
||||
}
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
|
@ -691,6 +692,7 @@ void AShooterCharacter::FireWeapon()
|
|||
if (!WeaponHasAmmo())
|
||||
{
|
||||
ReloadWeapon();
|
||||
return;
|
||||
}
|
||||
|
||||
PlayFireSound();
|
||||
|
@ -788,12 +790,12 @@ void AShooterCharacter::SendBullet()
|
|||
float damageToApply;
|
||||
if (BeamHitResult.BoneName.ToString() == HitEnemy->GetHeadBone())
|
||||
{
|
||||
damageToApply = EquippedWeapon->GetHeadshotDamage();
|
||||
damageToApply = EquippedWeapon->GetHeadshotDamage() * EquippedWeapon->GetDamageMultiplier();
|
||||
HitEnemy->ShowHitNumber(damageToApply, BeamHitResult.Location, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
damageToApply = EquippedWeapon->GetDamage();
|
||||
damageToApply = EquippedWeapon->GetDamage() * EquippedWeapon->GetDamageMultiplier();
|
||||
HitEnemy->ShowHitNumber(damageToApply, BeamHitResult.Location, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,33 @@ void AShooterPlayerController::BeginPlay()
|
|||
if (HUDOverlay)
|
||||
{
|
||||
HUDOverlay->AddToViewport();
|
||||
HUDOverlay->SetVisibility(ESlateVisibility::Visible);
|
||||
HUDOverlay->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AShooterPlayerController::GameHasEnded(AActor* EndGameFocus, bool bIsWinner)
|
||||
{
|
||||
Super::GameHasEnded(EndGameFocus, bIsWinner);
|
||||
HUDOverlay->RemoveFromParent();
|
||||
|
||||
/*if (bIsWinner)
|
||||
{
|
||||
UUserWidget* WinScreen = CreateWidget(this, WinScreenClass);
|
||||
if (WinScreen != nullptr)
|
||||
{
|
||||
WinScreen->AddToViewport();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UUserWidget* LoseScreen = CreateWidget(this, LoseScreenClass);
|
||||
if (LoseScreen != nullptr)
|
||||
{
|
||||
LoseScreen->AddToViewport();
|
||||
}
|
||||
}
|
||||
|
||||
GetWorldTimerManager().SetTimer(RestartTimer, this, &APlayerController::RestartLevel, RestartDelay);*/
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ class SHOOTER_API AShooterPlayerController : public APlayerController
|
|||
GENERATED_BODY()
|
||||
public:
|
||||
AShooterPlayerController();
|
||||
virtual void GameHasEnded(class AActor* EndGameFocus = nullptr, bool bIsWinner = false) override;
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
|
Loading…
Reference in New Issue