Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
- #include "ShooterGame.h"
- AShooterCharacter::AShooterCharacter(const class FPostConstructInitializeProperties& PCIP)
- : Super(PCIP.SetDefaultSubobjectClass<UShooterCharacterMovement>(ACharacter::CharacterMovementComponentName))
- {
- //Create The collision use sphere
- CollectionSphere = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("CollectionSphere"));
- CollectionSphere->AttachTo(RootComponent);
- CollectionSphere->SetSphereRadius(200.f);
- Mesh1P = PCIP.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("PawnMesh1P"));
- Mesh1P->AttachParent = CapsuleComponent;
- Mesh1P->bOnlyOwnerSee = true;
- Mesh1P->bOwnerNoSee = false;
- Mesh1P->bCastDynamicShadow = false;
- Mesh1P->bReceivesDecals = false;
- Mesh1P->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered;
- Mesh1P->PrimaryComponentTick.TickGroup = TG_PrePhysics;
- Mesh1P->bChartDistanceFactor = false;
- Mesh1P->SetCollisionObjectType(ECC_Pawn);
- Mesh1P->SetCollisionEnabled(ECollisionEnabled::NoCollision);
- Mesh1P->SetCollisionResponseToAllChannels(ECR_Ignore);
- Mesh->bOnlyOwnerSee = false;
- Mesh->bOwnerNoSee = true;
- Mesh->bReceivesDecals = false;
- Mesh->SetCollisionObjectType(ECC_Pawn);
- Mesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
- Mesh->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Block);
- Mesh->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
- Mesh->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
- CapsuleComponent->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
- CapsuleComponent->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
- CapsuleComponent->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Ignore);
- TargetingSpeedModifier = 0.5f;
- bIsTargeting = false;
- RunningSpeedModifier = 1.5f;
- bWantsToRun = false;
- bWantsToFire = false;
- LowHealthPercentage = 0.5f;
- BaseTurnRate = 45.f;
- BaseLookUpRate = 45.f;
- }
- void AShooterCharacter::PostInitializeComponents()
- {
- Super::PostInitializeComponents();
- if (Role == ROLE_Authority)
- {
- Health = GetMaxHealth();
- SpawnDefaultInventory();
- }
- // set initial mesh visibility (3rd person view)
- UpdatePawnMeshes();
- // create material instance for setting team colors (3rd person view)
- for (int32 iMat = 0; iMat < Mesh->GetNumMaterials(); iMat++)
- {
- MeshMIDs.Add(Mesh->CreateAndSetMaterialInstanceDynamic(iMat));
- }
- // play respawn effects
- if (GetNetMode() != NM_DedicatedServer)
- {
- if (RespawnFX)
- {
- UGameplayStatics::SpawnEmitterAtLocation(this, RespawnFX, GetActorLocation(), GetActorRotation());
- }
- if (RespawnSound)
- {
- UGameplayStatics::PlaySoundAtLocation(this, RespawnSound, GetActorLocation());
- }
- }
- }
- void AShooterCharacter::Destroyed()
- {
- Super::Destroyed();
- DestroyInventory();
- }
- void AShooterCharacter::PawnClientRestart()
- {
- Super::PawnClientRestart();
- // switch mesh to 1st person view
- UpdatePawnMeshes();
- // reattach weapon if needed
- SetCurrentWeapon(CurrentWeapon);
- // set team colors for 1st person view
- UMaterialInstanceDynamic* Mesh1PMID = Mesh1P->CreateAndSetMaterialInstanceDynamic(0);
- UpdateTeamColors(Mesh1PMID);
- }
- void AShooterCharacter::PossessedBy(class AController* InController)
- {
- Super::PossessedBy(InController);
- // [server] as soon as PlayerState is assigned, set team colors of this pawn for local player
- UpdateTeamColorsAllMIDs();
- }
- void AShooterCharacter::OnRep_PlayerState()
- {
- Super::OnRep_PlayerState();
- // [client] as soon as PlayerState is assigned, set team colors of this pawn for local player
- if (PlayerState != NULL)
- {
- UpdateTeamColorsAllMIDs();
- }
- }
- FRotator AShooterCharacter::GetAimOffsets() const
- {
- const FVector AimDirWS = GetBaseAimRotation().Vector();
- const FVector AimDirLS = ActorToWorld().InverseTransformVectorNoScale(AimDirWS);
- const FRotator AimRotLS = AimDirLS.Rotation();
- return AimRotLS;
- }
- bool AShooterCharacter::IsEnemyFor(AController* TestPC) const
- {
- if (TestPC == Controller || TestPC == NULL)
- {
- return false;
- }
- AShooterPlayerState* TestPlayerState = Cast<AShooterPlayerState>(TestPC->PlayerState);
- AShooterPlayerState* MyPlayerState = Cast<AShooterPlayerState>(PlayerState);
- bool bIsEnemy = true;
- if (GetWorld()->GameState && GetWorld()->GameState->GameModeClass)
- {
- const AShooterGameMode* DefGame = GetWorld()->GameState->GameModeClass->GetDefaultObject<AShooterGameMode>();
- if (DefGame && MyPlayerState && TestPlayerState)
- {
- bIsEnemy = DefGame->CanDealDamage(TestPlayerState, MyPlayerState);
- }
- }
- return bIsEnemy;
- }
- //////////////////////////////////////////////////////////////////////////
- // Meshes
- void AShooterCharacter::UpdatePawnMeshes()
- {
- bool const bFirstPerson = IsFirstPerson();
- Mesh1P->MeshComponentUpdateFlag = !bFirstPerson ? EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered : EMeshComponentUpdateFlag::AlwaysTickPoseAndRefreshBones;
- Mesh1P->SetOwnerNoSee(!bFirstPerson);
- Mesh->MeshComponentUpdateFlag = bFirstPerson ? EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered : EMeshComponentUpdateFlag::AlwaysTickPoseAndRefreshBones;
- Mesh->SetOwnerNoSee(bFirstPerson);
- }
- void AShooterCharacter::UpdateTeamColors(UMaterialInstanceDynamic* UseMID)
- {
- if (UseMID)
- {
- AShooterPlayerState* MyPlayerState = Cast<AShooterPlayerState>(PlayerState);
- if (MyPlayerState != NULL)
- {
- float MaterialParam = (float)MyPlayerState->GetTeamNum();
- UseMID->SetScalarParameterValue(TEXT("Team Color Index"), MaterialParam);
- }
- }
- }
- void AShooterCharacter::OnCameraUpdate(const FVector& CameraLocation, const FRotator& CameraRotation)
- {
- USkeletalMeshComponent* DefMesh1P = Cast<USkeletalMeshComponent>(GetClass()->GetDefaultSubobjectByName(TEXT("PawnMesh1P")));
- const FMatrix DefMeshLS = FRotationTranslationMatrix(DefMesh1P->RelativeRotation, DefMesh1P->RelativeLocation);
- const FMatrix LocalToWorld = ActorToWorld().ToMatrixWithScale();
- // Mesh rotating code expect uniform scale in LocalToWorld matrix
- const FRotator RotCameraPitch(CameraRotation.Pitch, 0.0f, 0.0f);
- const FRotator RotCameraYaw(0.0f, CameraRotation.Yaw, 0.0f);
- const FMatrix LeveledCameraLS = FRotationTranslationMatrix(RotCameraYaw, CameraLocation) * LocalToWorld.Inverse();
- const FMatrix PitchedCameraLS = FRotationMatrix(RotCameraPitch) * LeveledCameraLS;
- const FMatrix MeshRelativeToCamera = DefMeshLS * LeveledCameraLS.Inverse();
- const FMatrix PitchedMesh = MeshRelativeToCamera * PitchedCameraLS;
- Mesh1P->SetRelativeLocationAndRotation(PitchedMesh.GetOrigin(), PitchedMesh.Rotator());
- }
- //////////////////////////////////////////////////////////////////////////
- // Damage & death
- void AShooterCharacter::FellOutOfWorld(const class UDamageType& dmgType)
- {
- Die(Health, FDamageEvent(dmgType.GetClass()), NULL, NULL);
- }
- void AShooterCharacter::Suicide()
- {
- KilledBy(this);
- }
- void AShooterCharacter::KilledBy(APawn* EventInstigator)
- {
- if (Role == ROLE_Authority && !bIsDying)
- {
- AController* Killer = NULL;
- if (EventInstigator != NULL)
- {
- Killer = EventInstigator->Controller;
- LastHitBy = NULL;
- }
- Die(Health, FDamageEvent(UDamageType::StaticClass()), Killer, NULL);
- }
- }
- float AShooterCharacter::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, class AActor* DamageCauser)
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->HasGodMode())
- {
- return 0.f;
- }
- if (Health <= 0.f)
- {
- return 0.f;
- }
- // Modify based on game rules.
- AShooterGameMode* const Game = GetWorld()->GetAuthGameMode<AShooterGameMode>();
- Damage = Game ? Game->ModifyDamage(Damage, this, DamageEvent, EventInstigator, DamageCauser) : 0.f;
- const float ActualDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
- if (ActualDamage > 0.f)
- {
- Health -= ActualDamage;
- if (Health <= 0)
- {
- Die(ActualDamage, DamageEvent, EventInstigator, DamageCauser);
- }
- else
- {
- PlayHit(ActualDamage, DamageEvent, EventInstigator ? EventInstigator->GetPawn() : NULL, DamageCauser);
- }
- MakeNoise(1.0f, EventInstigator ? EventInstigator->GetPawn() : this);
- }
- return ActualDamage;
- }
- bool AShooterCharacter::CanDie(float KillingDamage, FDamageEvent const& DamageEvent, AController* Killer, AActor* DamageCauser) const
- {
- if ( bIsDying // already dying
- || IsPendingKill() // already destroyed
- || Role != ROLE_Authority // not authority
- || GetWorld()->GetAuthGameMode() == NULL
- || GetWorld()->GetAuthGameMode()->GetMatchState() == MatchState::LeavingMap) // level transition occurring
- {
- return false;
- }
- return true;
- }
- bool AShooterCharacter::Die(float KillingDamage, FDamageEvent const& DamageEvent, AController* Killer, AActor* DamageCauser)
- {
- if (!CanDie(KillingDamage, DamageEvent, Killer, DamageCauser))
- {
- return false;
- }
- Health = FMath::Min(0.0f, Health);
- // if this is an environmental death then refer to the previous killer so that they receive credit (knocked into lava pits, etc)
- UDamageType const* const DamageType = DamageEvent.DamageTypeClass ? DamageEvent.DamageTypeClass->GetDefaultObject<UDamageType>() : GetDefault<UDamageType>();
- Killer = GetDamageInstigator(Killer, *DamageType);
- AController* const KilledPlayer = (Controller != NULL) ? Controller : Cast<AController>(GetOwner());
- GetWorld()->GetAuthGameMode<AShooterGameMode>()->Killed(Killer, KilledPlayer, this, DamageType);
- NetUpdateFrequency = GetDefault<AShooterCharacter>()->NetUpdateFrequency;
- CharacterMovement->ForceReplicationUpdate();
- OnDeath(KillingDamage, DamageEvent, Killer ? Killer->GetPawn() : NULL, DamageCauser);
- return true;
- }
- void AShooterCharacter::OnDeath(float KillingDamage, struct FDamageEvent const& DamageEvent, class APawn* PawnInstigator, class AActor* DamageCauser)
- {
- if (bIsDying)
- {
- return;
- }
- bReplicateMovement = false;
- bTearOff = true;
- bIsDying = true;
- if (Role == ROLE_Authority)
- {
- ReplicateHit(KillingDamage, DamageEvent, PawnInstigator, DamageCauser, true);
- // play the force feedback effect on the client player controller
- APlayerController* PC = Cast<APlayerController>(Controller);
- if (PC && DamageEvent.DamageTypeClass)
- {
- UShooterDamageType *DamageType = Cast<UShooterDamageType>(DamageEvent.DamageTypeClass->GetDefaultObject());
- if (DamageType && DamageType->KilledForceFeedback)
- {
- PC->ClientPlayForceFeedback(DamageType->KilledForceFeedback, false, "Damage");
- }
- }
- }
- // cannot use IsLocallyControlled here, because even local client's controller may be NULL here
- if (GetNetMode() != NM_DedicatedServer && DeathSound && Mesh1P && Mesh1P->IsVisible())
- {
- UGameplayStatics::PlaySoundAtLocation(this, DeathSound, GetActorLocation());
- }
- // remove all weapons
- DestroyInventory();
- // switch back to 3rd person view
- UpdatePawnMeshes();
- DetachFromControllerPendingDestroy();
- StopAllAnimMontages();
- if (LowHealthWarningPlayer && LowHealthWarningPlayer->IsPlaying())
- {
- LowHealthWarningPlayer->Stop();
- }
- if (RunLoopAC)
- {
- RunLoopAC->Stop();
- }
- // disable collisions on capsule
- CapsuleComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
- CapsuleComponent->SetCollisionResponseToAllChannels(ECR_Ignore);
- if (Mesh)
- {
- static FName CollisionProfileName(TEXT("Ragdoll"));
- Mesh->SetCollisionProfileName(CollisionProfileName);
- }
- SetActorEnableCollision(true);
- // Death anim
- float DeathAnimDuration = PlayAnimMontage(DeathAnim);
- // Ragdoll
- if (DeathAnimDuration > 0.f)
- {
- GetWorldTimerManager().SetTimer(this, &AShooterCharacter::SetRagdollPhysics, FMath::Min(0.1f, DeathAnimDuration), false);
- }
- else
- {
- SetRagdollPhysics();
- }
- }
- void AShooterCharacter::PlayHit(float DamageTaken, struct FDamageEvent const& DamageEvent, class APawn* PawnInstigator, class AActor* DamageCauser)
- {
- if (Role == ROLE_Authority)
- {
- ReplicateHit(DamageTaken, DamageEvent, PawnInstigator, DamageCauser, false);
- // play the force feedback effect on the client player controller
- APlayerController* PC = Cast<APlayerController>(Controller);
- if (PC && DamageEvent.DamageTypeClass)
- {
- UShooterDamageType *DamageType = Cast<UShooterDamageType>(DamageEvent.DamageTypeClass->GetDefaultObject());
- if (DamageType && DamageType->HitForceFeedback)
- {
- PC->ClientPlayForceFeedback(DamageType->HitForceFeedback, false, "Damage");
- }
- }
- }
- if (DamageTaken > 0.f)
- {
- ApplyDamageMomentum(DamageTaken, DamageEvent, PawnInstigator, DamageCauser);
- }
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- AShooterHUD* MyHUD = MyPC ? Cast<AShooterHUD>(MyPC->GetHUD()) : NULL;
- if (MyHUD)
- {
- MyHUD->NotifyHit(DamageTaken, DamageEvent, PawnInstigator);
- }
- if (PawnInstigator && PawnInstigator != this && PawnInstigator->IsLocallyControlled())
- {
- AShooterPlayerController* InstigatorPC = Cast<AShooterPlayerController>(PawnInstigator->Controller);
- AShooterHUD* InstigatorHUD = InstigatorPC ? Cast<AShooterHUD>(InstigatorPC->GetHUD()) : NULL;
- if (InstigatorHUD)
- {
- InstigatorHUD->NotifyEnemyHit();
- }
- }
- }
- void AShooterCharacter::SetRagdollPhysics()
- {
- bool bInRagdoll = false;
- if (IsPendingKill())
- {
- bInRagdoll = false;
- }
- else if (!Mesh || !Mesh->GetPhysicsAsset())
- {
- bInRagdoll = false;
- }
- else
- {
- // initialize physics/etc
- Mesh->SetAllBodiesSimulatePhysics(true);
- Mesh->SetSimulatePhysics(true);
- Mesh->WakeAllRigidBodies();
- Mesh->bBlendPhysics = true;
- bInRagdoll = true;
- }
- CharacterMovement->StopMovementImmediately();
- CharacterMovement->DisableMovement();
- CharacterMovement->SetComponentTickEnabled(false);
- if (!bInRagdoll)
- {
- // hide and set short lifespan
- TurnOff();
- SetActorHiddenInGame(true);
- SetLifeSpan( 1.0f );
- }
- else
- {
- SetLifeSpan( 10.0f );
- }
- }
- void AShooterCharacter::ReplicateHit(float Damage, struct FDamageEvent const& DamageEvent, class APawn* PawnInstigator, class AActor* DamageCauser, bool bKilled)
- {
- const float TimeoutTime = GetWorld()->GetTimeSeconds() + 0.5f;
- FDamageEvent const& LastDamageEvent = LastTakeHitInfo.GetDamageEvent();
- if ((PawnInstigator == LastTakeHitInfo.PawnInstigator.Get()) && (LastDamageEvent.DamageTypeClass == LastTakeHitInfo.DamageTypeClass) && (LastTakeHitTimeTimeout == TimeoutTime))
- {
- // same frame damage
- if (bKilled && LastTakeHitInfo.bKilled)
- {
- // Redundant death take hit, just ignore it
- return;
- }
- // otherwise, accumulate damage done this frame
- Damage += LastTakeHitInfo.ActualDamage;
- }
- LastTakeHitInfo.ActualDamage = Damage;
- LastTakeHitInfo.PawnInstigator = Cast<AShooterCharacter>(PawnInstigator);
- LastTakeHitInfo.DamageCauser = DamageCauser;
- LastTakeHitInfo.SetDamageEvent(DamageEvent);
- LastTakeHitInfo.bKilled = bKilled;
- LastTakeHitInfo.EnsureReplication();
- LastTakeHitTimeTimeout = TimeoutTime;
- }
- void AShooterCharacter::OnRep_LastTakeHitInfo()
- {
- if (LastTakeHitInfo.bKilled)
- {
- OnDeath(LastTakeHitInfo.ActualDamage, LastTakeHitInfo.GetDamageEvent(), LastTakeHitInfo.PawnInstigator.Get(), LastTakeHitInfo.DamageCauser.Get());
- }
- else
- {
- PlayHit(LastTakeHitInfo.ActualDamage, LastTakeHitInfo.GetDamageEvent(), LastTakeHitInfo.PawnInstigator.Get(), LastTakeHitInfo.DamageCauser.Get());
- }
- }
- //Pawn::PlayDying sets this lifespan, but when that function is called on client, dead pawn's role is still SimulatedProxy despite bTearOff being true.
- void AShooterCharacter::TornOff()
- {
- SetLifeSpan(25.f);
- }
- //////////////////////////////////////////////////////////////////////////
- // Inventory
- void AShooterCharacter::SpawnDefaultInventory()
- {
- if (Role < ROLE_Authority)
- {
- return;
- }
- int32 NumWeaponClasses = DefaultInventoryClasses.Num();
- for (int32 i = 0; i < NumWeaponClasses; i++)
- {
- if (DefaultInventoryClasses[i])
- {
- FActorSpawnParameters SpawnInfo;
- SpawnInfo.bNoCollisionFail = true;
- AShooterWeapon* NewWeapon = GetWorld()->SpawnActor<AShooterWeapon>(DefaultInventoryClasses[i], SpawnInfo);
- AddWeapon(NewWeapon);
- }
- }
- // equip first weapon in inventory
- if (Inventory.Num() > 0)
- {
- EquipWeapon(Inventory[0]);
- }
- }
- void AShooterCharacter::DestroyInventory()
- {
- if (Role < ROLE_Authority)
- {
- return;
- }
- // remove all weapons from inventory and destroy them
- for (int32 i = Inventory.Num() - 1; i >= 0; i--)
- {
- AShooterWeapon* Weapon = Inventory[i];
- if (Weapon)
- {
- RemoveWeapon(Weapon);
- Weapon->Destroy();
- }
- }
- }
- void AShooterCharacter::AddWeapon(AShooterWeapon* Weapon)
- {
- if (Weapon && Role == ROLE_Authority)
- {
- Weapon->OnEnterInventory(this);
- Inventory.AddUnique(Weapon);
- }
- }
- void AShooterCharacter::RemoveWeapon(AShooterWeapon* Weapon)
- {
- if (Weapon && Role == ROLE_Authority)
- {
- Weapon->OnLeaveInventory();
- Inventory.RemoveSingle(Weapon);
- }
- }
- AShooterWeapon* AShooterCharacter::FindWeapon(TSubclassOf<AShooterWeapon> WeaponClass)
- {
- for (int32 i = 0; i < Inventory.Num(); i++)
- {
- if (Inventory[i] && Inventory[i]->IsA(WeaponClass))
- {
- return Inventory[i];
- }
- }
- return NULL;
- }
- void AShooterCharacter::EquipWeapon(AShooterWeapon* Weapon)
- {
- if (Weapon)
- {
- if (Role == ROLE_Authority)
- {
- SetCurrentWeapon(Weapon);
- }
- else
- {
- ServerEquipWeapon(Weapon);
- }
- }
- }
- bool AShooterCharacter::ServerEquipWeapon_Validate(AShooterWeapon* Weapon)
- {
- return true;
- }
- void AShooterCharacter::ServerEquipWeapon_Implementation(AShooterWeapon* Weapon)
- {
- EquipWeapon(Weapon);
- }
- void AShooterCharacter::OnRep_CurrentWeapon(AShooterWeapon* LastWeapon)
- {
- SetCurrentWeapon(CurrentWeapon, LastWeapon);
- }
- void AShooterCharacter::SetCurrentWeapon(class AShooterWeapon* NewWeapon, class AShooterWeapon* LastWeapon)
- {
- AShooterWeapon* LocalLastWeapon = NULL;
- if (LastWeapon != NULL)
- {
- LocalLastWeapon = LastWeapon;
- }
- else if (NewWeapon != CurrentWeapon)
- {
- LocalLastWeapon = CurrentWeapon;
- }
- // unequip previous
- if (LocalLastWeapon)
- {
- LocalLastWeapon->OnUnEquip();
- }
- CurrentWeapon = NewWeapon;
- // equip new one
- if (NewWeapon)
- {
- NewWeapon->SetOwningPawn(this); // Make sure weapon's MyPawn is pointing back to us. During replication, we can't guarantee APawn::CurrentWeapon will rep after AWeapon::MyPawn!
- NewWeapon->OnEquip();
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Weapon usage
- void AShooterCharacter::StartWeaponFire()
- {
- if (!bWantsToFire)
- {
- bWantsToFire = true;
- if (CurrentWeapon)
- {
- CurrentWeapon->StartFire();
- }
- }
- }
- void AShooterCharacter::StopWeaponFire()
- {
- if (bWantsToFire)
- {
- bWantsToFire = false;
- if (CurrentWeapon)
- {
- CurrentWeapon->StopFire();
- }
- }
- }
- bool AShooterCharacter::CanFire() const
- {
- return IsAlive();
- }
- bool AShooterCharacter::CanReload() const
- {
- return true;
- }
- void AShooterCharacter::SetTargeting(bool bNewTargeting)
- {
- bIsTargeting = bNewTargeting;
- if (TargetingSound)
- {
- UGameplayStatics::PlaySoundAttached(TargetingSound, GetRootComponent());
- }
- if (Role < ROLE_Authority)
- {
- ServerSetTargeting(bNewTargeting);
- }
- }
- bool AShooterCharacter::ServerSetTargeting_Validate(bool bNewTargeting)
- {
- return true;
- }
- void AShooterCharacter::ServerSetTargeting_Implementation(bool bNewTargeting)
- {
- SetTargeting(bNewTargeting);
- }
- //////////////////////////////////////////////////////////////////////////
- // Movement
- void AShooterCharacter::SetRunning(bool bNewRunning, bool bToggle)
- {
- bWantsToRun = bNewRunning;
- bWantsToRunToggled = bNewRunning && bToggle;
- if (Role < ROLE_Authority)
- {
- ServerSetRunning(bNewRunning, bToggle);
- }
- UpdateRunSounds(bNewRunning);
- }
- bool AShooterCharacter::ServerSetRunning_Validate(bool bNewRunning, bool bToggle)
- {
- return true;
- }
- void AShooterCharacter::ServerSetRunning_Implementation(bool bNewRunning, bool bToggle)
- {
- SetRunning(bNewRunning, bToggle);
- }
- void AShooterCharacter::UpdateRunSounds(bool bNewRunning)
- {
- if (bNewRunning)
- {
- if (!RunLoopAC && RunLoopSound)
- {
- RunLoopAC = UGameplayStatics::PlaySoundAttached(RunLoopSound, GetRootComponent());
- if (RunLoopAC)
- {
- RunLoopAC->bAutoDestroy = false;
- }
- }
- else if (RunLoopAC)
- {
- RunLoopAC->Play();
- }
- }
- else
- {
- if (RunLoopAC)
- {
- RunLoopAC->Stop();
- }
- if (RunStopSound)
- {
- UGameplayStatics::PlaySoundAttached(RunStopSound, GetRootComponent());
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Animations
- float AShooterCharacter::PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
- {
- USkeletalMeshComponent* UseMesh = GetPawnMesh();
- if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance)
- {
- return UseMesh->AnimScriptInstance->Montage_Play(AnimMontage, InPlayRate);
- }
- return 0.0f;
- }
- void AShooterCharacter::StopAnimMontage(class UAnimMontage* AnimMontage)
- {
- USkeletalMeshComponent* UseMesh = GetPawnMesh();
- if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance &&
- UseMesh->AnimScriptInstance->Montage_IsPlaying(AnimMontage))
- {
- UseMesh->AnimScriptInstance->Montage_Stop(AnimMontage->BlendOutTime);
- }
- }
- void AShooterCharacter::StopAllAnimMontages()
- {
- USkeletalMeshComponent* UseMesh = GetPawnMesh();
- if (UseMesh && UseMesh->AnimScriptInstance)
- {
- UseMesh->AnimScriptInstance->Montage_Stop(0.0f);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Input
- void AShooterCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
- {
- check(InputComponent);
- InputComponent->BindAxis("MoveForward", this, &AShooterCharacter::MoveForward);
- InputComponent->BindAxis("MoveRight", this, &AShooterCharacter::MoveRight);
- InputComponent->BindAxis("MoveUp", this, &AShooterCharacter::MoveUp);
- InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
- InputComponent->BindAxis("TurnRate", this, &AShooterCharacter::TurnAtRate);
- InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
- InputComponent->BindAxis("LookUpRate", this, &AShooterCharacter::LookUpAtRate);
- InputComponent->BindAction("Fire", IE_Pressed, this, &AShooterCharacter::OnStartFire);
- InputComponent->BindAction("Fire", IE_Released, this, &AShooterCharacter::OnStopFire);
- InputComponent->BindAction("Targeting", IE_Pressed, this, &AShooterCharacter::OnStartTargeting);
- InputComponent->BindAction("Targeting", IE_Released, this, &AShooterCharacter::OnStopTargeting);
- InputComponent->BindAction("NextWeapon", IE_Pressed, this, &AShooterCharacter::OnNextWeapon);
- InputComponent->BindAction("PrevWeapon", IE_Pressed, this, &AShooterCharacter::OnPrevWeapon);
- InputComponent->BindAction("Reload", IE_Pressed, this, &AShooterCharacter::OnReload);
- InputComponent->BindAction("Jump", IE_Pressed, this, &AShooterCharacter::OnStartJump);
- InputComponent->BindAction("Jump", IE_Released, this, &AShooterCharacter::OnStopJump);
- InputComponent->BindAction("Run", IE_Pressed, this, &AShooterCharacter::OnStartRunning);
- InputComponent->BindAction("RunToggle", IE_Pressed, this, &AShooterCharacter::OnStartRunningToggle);
- InputComponent->BindAction("Run", IE_Released, this, &AShooterCharacter::OnStopRunning);
- InputComponent->BindAction("Use", IE_Pressed, this, &AShooterCharacter::UseItem);
- InputComponent->BindAction("Use", IE_Repeat, this, &AShooterCharacter::UseItem);
- }
- void AShooterCharacter::MoveForward(float Val)
- {
- if (Controller && Val != 0.f)
- {
- // Limit pitch when walking or falling
- const bool bLimitRotation = (CharacterMovement->IsMovingOnGround() || CharacterMovement->IsFalling());
- const FRotator Rotation = bLimitRotation ? GetActorRotation() : Controller->GetControlRotation();
- const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis( EAxis::X );
- AddMovementInput(Direction, Val);
- }
- }
- void AShooterCharacter::MoveRight(float Val)
- {
- if (Val != 0.f)
- {
- const FRotator Rotation = GetActorRotation();
- const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis( EAxis::Y );
- AddMovementInput(Direction, Val);
- }
- }
- void AShooterCharacter::MoveUp(float Val)
- {
- if (Val != 0.f)
- {
- // Not when walking or falling.
- if (CharacterMovement->IsMovingOnGround() || CharacterMovement->IsFalling())
- {
- return;
- }
- AddMovementInput(FVector::UpVector, Val);
- }
- }
- void AShooterCharacter::TurnAtRate(float Val)
- {
- // calculate delta for this frame from the rate information
- AddControllerYawInput(Val * BaseTurnRate * GetWorld()->GetDeltaSeconds());
- }
- void AShooterCharacter::LookUpAtRate(float Val)
- {
- // calculate delta for this frame from the rate information
- AddControllerPitchInput(Val * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
- }
- void AShooterCharacter::OnStartFire()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- if (IsRunning())
- {
- SetRunning(false, false);
- }
- StartWeaponFire();
- }
- }
- void AShooterCharacter::OnStopFire()
- {
- StopWeaponFire();
- }
- void AShooterCharacter::OnStartTargeting()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- if (IsRunning())
- {
- SetRunning(false, false);
- }
- SetTargeting(true);
- }
- }
- void AShooterCharacter::OnStopTargeting()
- {
- SetTargeting(false);
- }
- void AShooterCharacter::OnNextWeapon()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- if (Inventory.Num() >= 2 && (CurrentWeapon == NULL || CurrentWeapon->GetCurrentState() != EWeaponState::Equipping))
- {
- const int32 CurrentWeaponIdx = Inventory.IndexOfByKey(CurrentWeapon);
- AShooterWeapon* NextWeapon = Inventory[(CurrentWeaponIdx + 1) % Inventory.Num()];
- EquipWeapon(NextWeapon);
- }
- }
- }
- void AShooterCharacter::OnPrevWeapon()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- if (Inventory.Num() >= 2 && (CurrentWeapon == NULL || CurrentWeapon->GetCurrentState() != EWeaponState::Equipping))
- {
- const int32 CurrentWeaponIdx = Inventory.IndexOfByKey(CurrentWeapon);
- AShooterWeapon* PrevWeapon = Inventory[(CurrentWeaponIdx - 1 + Inventory.Num()) % Inventory.Num()];
- EquipWeapon(PrevWeapon);
- }
- }
- }
- void AShooterCharacter::OnReload()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- if (CurrentWeapon)
- {
- CurrentWeapon->StartReload();
- }
- }
- }
- void AShooterCharacter::OnStartRunning()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- if (IsTargeting())
- {
- SetTargeting(false);
- }
- StopWeaponFire();
- SetRunning(true, false);
- }
- }
- void AShooterCharacter::OnStartRunningToggle()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- if (IsTargeting())
- {
- SetTargeting(false);
- }
- StopWeaponFire();
- SetRunning(true, true);
- }
- }
- void AShooterCharacter::OnStopRunning()
- {
- SetRunning(false, false);
- }
- bool AShooterCharacter::IsRunning() const
- {
- if (!CharacterMovement)
- {
- return false;
- }
- return (bWantsToRun || bWantsToRunToggled) && !GetVelocity().IsZero() && (GetVelocity().SafeNormal2D() | GetActorRotation().Vector()) > -0.1;
- }
- void AShooterCharacter::Tick(float DeltaSeconds)
- {
- Super::Tick(DeltaSeconds);
- if (bWantsToRunToggled && !IsRunning())
- {
- SetRunning(false, false);
- }
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->HasHealthRegen())
- {
- if (this->Health < this->GetMaxHealth())
- {
- this->Health += 5 * DeltaSeconds;
- if (Health > this->GetMaxHealth())
- {
- Health = this->GetMaxHealth();
- }
- }
- }
- if (LowHealthSound && GEngine->UseSound())
- {
- if ((this->Health > 0 && this->Health < this->GetMaxHealth() * LowHealthPercentage) && (!LowHealthWarningPlayer || !LowHealthWarningPlayer->IsPlaying()))
- {
- LowHealthWarningPlayer = UGameplayStatics::PlaySoundAttached(LowHealthSound, GetRootComponent(),
- NAME_None, FVector(ForceInit), EAttachLocation::KeepRelativeOffset, true);
- LowHealthWarningPlayer->SetVolumeMultiplier(0.0f);
- }
- else if ((this->Health > this->GetMaxHealth() * LowHealthPercentage || this->Health < 0) && LowHealthWarningPlayer && LowHealthWarningPlayer->IsPlaying())
- {
- LowHealthWarningPlayer->Stop();
- }
- if (LowHealthWarningPlayer && LowHealthWarningPlayer->IsPlaying())
- {
- const float MinVolume = 0.3f;
- const float VolumeMultiplier = (1.0f - (this->Health / (this->GetMaxHealth() * LowHealthPercentage)));
- LowHealthWarningPlayer->SetVolumeMultiplier(MinVolume + (1.0f - MinVolume) * VolumeMultiplier);
- }
- }
- }
- void AShooterCharacter::OnStartJump()
- {
- AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
- if (MyPC && MyPC->IsGameInputAllowed())
- {
- bPressedJump = true;
- }
- }
- void AShooterCharacter::OnStopJump()
- {
- bPressedJump = false;
- }
- //////////////////////////////////////////////////////////////////////////
- // Replication
- void AShooterCharacter::PreReplication( IRepChangedPropertyTracker & ChangedPropertyTracker )
- {
- Super::PreReplication( ChangedPropertyTracker );
- // Only replicate this property for a short duration after it changes so join in progress players don't get spammed with fx when joining late
- DOREPLIFETIME_ACTIVE_OVERRIDE( AShooterCharacter, LastTakeHitInfo, GetWorld() && GetWorld()->GetTimeSeconds() < LastTakeHitTimeTimeout );
- }
- void AShooterCharacter::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
- {
- Super::GetLifetimeReplicatedProps( OutLifetimeProps );
- // only to local owner: weapon change requests are locally instigated, other clients don't need it
- DOREPLIFETIME_CONDITION( AShooterCharacter, Inventory, COND_OwnerOnly );
- // everyone except local owner: flag change is locally instigated
- DOREPLIFETIME_CONDITION( AShooterCharacter, bIsTargeting, COND_SkipOwner );
- DOREPLIFETIME_CONDITION( AShooterCharacter, bWantsToRun, COND_SkipOwner );
- DOREPLIFETIME_CONDITION( AShooterCharacter, LastTakeHitInfo, COND_Custom );
- // everyone
- DOREPLIFETIME( AShooterCharacter, CurrentWeapon );
- DOREPLIFETIME( AShooterCharacter, Health );
- }
- AShooterWeapon* AShooterCharacter::GetWeapon() const
- {
- return CurrentWeapon;
- }
- int32 AShooterCharacter::GetInventoryCount() const
- {
- return Inventory.Num();
- }
- AShooterWeapon* AShooterCharacter::GetInventoryWeapon(int32 index) const
- {
- return Inventory[index];
- }
- USkeletalMeshComponent* AShooterCharacter::GetPawnMesh() const
- {
- return IsFirstPerson() ? Mesh1P : Mesh;
- }
- USkeletalMeshComponent* AShooterCharacter::GetSpecifcPawnMesh( bool WantFirstPerson ) const
- {
- return WantFirstPerson == true ? Mesh1P : Mesh;
- }
- FName AShooterCharacter::GetWeaponAttachPoint() const
- {
- return WeaponAttachPoint;
- }
- float AShooterCharacter::GetTargetingSpeedModifier() const
- {
- return TargetingSpeedModifier;
- }
- bool AShooterCharacter::IsTargeting() const
- {
- return bIsTargeting;
- }
- float AShooterCharacter::GetRunningSpeedModifier() const
- {
- return RunningSpeedModifier;
- }
- bool AShooterCharacter::IsFiring() const
- {
- return bWantsToFire;
- };
- bool AShooterCharacter::IsFirstPerson() const
- {
- return IsAlive() && Controller && Controller->IsLocalPlayerController();
- }
- int32 AShooterCharacter::GetMaxHealth() const
- {
- return GetClass()->GetDefaultObject<AShooterCharacter>()->Health;
- }
- bool AShooterCharacter::IsAlive() const
- {
- return Health > 0;
- }
- float AShooterCharacter::GetLowHealthPercentage() const
- {
- return LowHealthPercentage;
- }
- void AShooterCharacter::UpdateTeamColorsAllMIDs()
- {
- for (int32 i = 0; i < MeshMIDs.Num(); ++i)
- {
- UpdateTeamColors(MeshMIDs[i]);
- }
- }
- void AShooterCharacter::UseItem()
- {
- /* Get all overlapping actors */
- TArray<AActor*> CollectedActors;
- CollectionSphere->GetOverlappingActors(CollectedActors);
- /* Iterate through all overlapping actors*/
- for (int32 iCollected = 0; iCollected < CollectedActors.Num(); ++iCollected)
- {
- AUsableButton* const button = Cast<AUsableButton>(CollectedActors[iCollected]); // check if the overlapping actor is a usable button
- if (button && !button->IsPendingKill() && button->bIsUsable)
- {
- /* If the actor is a button and it is usable and not likely to be killed then go ahead and use it */
- UseUsableButton(button);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement