Advertisement
Guest User

Untitled

a guest
Oct 21st, 2014
264
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 32.66 KB | None | 0 0
  1. // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
  2.  
  3. #include "ShooterGame.h"
  4.  
  5. AShooterCharacter::AShooterCharacter(const class FPostConstructInitializeProperties& PCIP)
  6.     : Super(PCIP.SetDefaultSubobjectClass<UShooterCharacterMovement>(ACharacter::CharacterMovementComponentName))
  7. {
  8.     //Create The collision use sphere
  9.     CollectionSphere = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("CollectionSphere"));
  10.     CollectionSphere->AttachTo(RootComponent);
  11.     CollectionSphere->SetSphereRadius(200.f);
  12.  
  13.     Mesh1P = PCIP.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("PawnMesh1P"));
  14.     Mesh1P->AttachParent = CapsuleComponent;
  15.     Mesh1P->bOnlyOwnerSee = true;
  16.     Mesh1P->bOwnerNoSee = false;
  17.     Mesh1P->bCastDynamicShadow = false;
  18.     Mesh1P->bReceivesDecals = false;
  19.     Mesh1P->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered;
  20.     Mesh1P->PrimaryComponentTick.TickGroup = TG_PrePhysics;
  21.     Mesh1P->bChartDistanceFactor = false;
  22.     Mesh1P->SetCollisionObjectType(ECC_Pawn);
  23.     Mesh1P->SetCollisionEnabled(ECollisionEnabled::NoCollision);
  24.     Mesh1P->SetCollisionResponseToAllChannels(ECR_Ignore);
  25.  
  26.     Mesh->bOnlyOwnerSee = false;
  27.     Mesh->bOwnerNoSee = true;
  28.     Mesh->bReceivesDecals = false;
  29.     Mesh->SetCollisionObjectType(ECC_Pawn);
  30.     Mesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
  31.     Mesh->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Block);
  32.     Mesh->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
  33.     Mesh->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
  34.  
  35.     CapsuleComponent->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
  36.     CapsuleComponent->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
  37.     CapsuleComponent->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Ignore);
  38.  
  39.     TargetingSpeedModifier = 0.5f;
  40.     bIsTargeting = false;
  41.     RunningSpeedModifier = 1.5f;
  42.     bWantsToRun = false;
  43.     bWantsToFire = false;
  44.     LowHealthPercentage = 0.5f;
  45.  
  46.     BaseTurnRate = 45.f;
  47.     BaseLookUpRate = 45.f;
  48. }
  49.  
  50. void AShooterCharacter::PostInitializeComponents()
  51. {
  52.     Super::PostInitializeComponents();
  53.  
  54.     if (Role == ROLE_Authority)
  55.     {
  56.         Health = GetMaxHealth();
  57.         SpawnDefaultInventory();
  58.     }
  59.  
  60.     // set initial mesh visibility (3rd person view)
  61.     UpdatePawnMeshes();
  62.    
  63.     // create material instance for setting team colors (3rd person view)
  64.     for (int32 iMat = 0; iMat < Mesh->GetNumMaterials(); iMat++)
  65.     {
  66.         MeshMIDs.Add(Mesh->CreateAndSetMaterialInstanceDynamic(iMat));
  67.     }
  68.  
  69.     // play respawn effects
  70.     if (GetNetMode() != NM_DedicatedServer)
  71.     {
  72.         if (RespawnFX)
  73.         {
  74.             UGameplayStatics::SpawnEmitterAtLocation(this, RespawnFX, GetActorLocation(), GetActorRotation());
  75.         }
  76.  
  77.         if (RespawnSound)
  78.         {
  79.             UGameplayStatics::PlaySoundAtLocation(this, RespawnSound, GetActorLocation());
  80.         }
  81.     }
  82. }
  83.  
  84. void AShooterCharacter::Destroyed()
  85. {
  86.     Super::Destroyed();
  87.     DestroyInventory();
  88. }
  89.  
  90. void AShooterCharacter::PawnClientRestart()
  91. {
  92.     Super::PawnClientRestart();
  93.  
  94.     // switch mesh to 1st person view
  95.     UpdatePawnMeshes();
  96.  
  97.     // reattach weapon if needed
  98.     SetCurrentWeapon(CurrentWeapon);
  99.  
  100.     // set team colors for 1st person view
  101.     UMaterialInstanceDynamic* Mesh1PMID = Mesh1P->CreateAndSetMaterialInstanceDynamic(0);
  102.     UpdateTeamColors(Mesh1PMID);
  103. }
  104.  
  105. void AShooterCharacter::PossessedBy(class AController* InController)
  106. {
  107.     Super::PossessedBy(InController);
  108.  
  109.     // [server] as soon as PlayerState is assigned, set team colors of this pawn for local player
  110.     UpdateTeamColorsAllMIDs();
  111. }
  112.  
  113. void AShooterCharacter::OnRep_PlayerState()
  114. {
  115.     Super::OnRep_PlayerState();
  116.  
  117.     // [client] as soon as PlayerState is assigned, set team colors of this pawn for local player
  118.     if (PlayerState != NULL)
  119.     {
  120.         UpdateTeamColorsAllMIDs();
  121.     }
  122. }
  123.  
  124. FRotator AShooterCharacter::GetAimOffsets() const
  125. {
  126.     const FVector AimDirWS = GetBaseAimRotation().Vector();
  127.     const FVector AimDirLS = ActorToWorld().InverseTransformVectorNoScale(AimDirWS);
  128.     const FRotator AimRotLS = AimDirLS.Rotation();
  129.  
  130.     return AimRotLS;
  131. }
  132.  
  133. bool AShooterCharacter::IsEnemyFor(AController* TestPC) const
  134. {
  135.     if (TestPC == Controller || TestPC == NULL)
  136.     {
  137.         return false;
  138.     }
  139.  
  140.     AShooterPlayerState* TestPlayerState = Cast<AShooterPlayerState>(TestPC->PlayerState);
  141.     AShooterPlayerState* MyPlayerState = Cast<AShooterPlayerState>(PlayerState);
  142.  
  143.     bool bIsEnemy = true;
  144.     if (GetWorld()->GameState && GetWorld()->GameState->GameModeClass)
  145.     {
  146.         const AShooterGameMode* DefGame = GetWorld()->GameState->GameModeClass->GetDefaultObject<AShooterGameMode>();
  147.         if (DefGame && MyPlayerState && TestPlayerState)
  148.         {
  149.             bIsEnemy = DefGame->CanDealDamage(TestPlayerState, MyPlayerState);
  150.         }
  151.     }
  152.  
  153.     return bIsEnemy;
  154. }
  155.  
  156.  
  157. //////////////////////////////////////////////////////////////////////////
  158. // Meshes
  159.  
  160. void AShooterCharacter::UpdatePawnMeshes()
  161. {
  162.     bool const bFirstPerson = IsFirstPerson();
  163.  
  164.     Mesh1P->MeshComponentUpdateFlag = !bFirstPerson ? EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered : EMeshComponentUpdateFlag::AlwaysTickPoseAndRefreshBones;
  165.     Mesh1P->SetOwnerNoSee(!bFirstPerson);
  166.  
  167.     Mesh->MeshComponentUpdateFlag = bFirstPerson ? EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered : EMeshComponentUpdateFlag::AlwaysTickPoseAndRefreshBones;
  168.     Mesh->SetOwnerNoSee(bFirstPerson);
  169. }
  170.  
  171. void AShooterCharacter::UpdateTeamColors(UMaterialInstanceDynamic* UseMID)
  172. {
  173.     if (UseMID)
  174.     {
  175.         AShooterPlayerState* MyPlayerState = Cast<AShooterPlayerState>(PlayerState);
  176.         if (MyPlayerState != NULL)
  177.         {
  178.             float MaterialParam = (float)MyPlayerState->GetTeamNum();
  179.             UseMID->SetScalarParameterValue(TEXT("Team Color Index"), MaterialParam);
  180.         }
  181.     }
  182. }
  183.  
  184. void AShooterCharacter::OnCameraUpdate(const FVector& CameraLocation, const FRotator& CameraRotation)
  185. {
  186.     USkeletalMeshComponent* DefMesh1P = Cast<USkeletalMeshComponent>(GetClass()->GetDefaultSubobjectByName(TEXT("PawnMesh1P")));
  187.     const FMatrix DefMeshLS = FRotationTranslationMatrix(DefMesh1P->RelativeRotation, DefMesh1P->RelativeLocation);
  188.     const FMatrix LocalToWorld = ActorToWorld().ToMatrixWithScale();
  189.  
  190.     // Mesh rotating code expect uniform scale in LocalToWorld matrix
  191.  
  192.     const FRotator RotCameraPitch(CameraRotation.Pitch, 0.0f, 0.0f);
  193.     const FRotator RotCameraYaw(0.0f, CameraRotation.Yaw, 0.0f);
  194.  
  195.     const FMatrix LeveledCameraLS = FRotationTranslationMatrix(RotCameraYaw, CameraLocation) * LocalToWorld.Inverse();
  196.     const FMatrix PitchedCameraLS = FRotationMatrix(RotCameraPitch) * LeveledCameraLS;
  197.     const FMatrix MeshRelativeToCamera = DefMeshLS * LeveledCameraLS.Inverse();
  198.     const FMatrix PitchedMesh = MeshRelativeToCamera * PitchedCameraLS;
  199.  
  200.     Mesh1P->SetRelativeLocationAndRotation(PitchedMesh.GetOrigin(), PitchedMesh.Rotator());
  201. }
  202.  
  203.  
  204. //////////////////////////////////////////////////////////////////////////
  205. // Damage & death
  206.  
  207.  
  208. void AShooterCharacter::FellOutOfWorld(const class UDamageType& dmgType)
  209. {
  210.     Die(Health, FDamageEvent(dmgType.GetClass()), NULL, NULL);
  211. }
  212.  
  213. void AShooterCharacter::Suicide()
  214. {
  215.     KilledBy(this);
  216. }
  217.  
  218. void AShooterCharacter::KilledBy(APawn* EventInstigator)
  219. {
  220.     if (Role == ROLE_Authority && !bIsDying)
  221.     {
  222.         AController* Killer = NULL;
  223.         if (EventInstigator != NULL)
  224.         {
  225.             Killer = EventInstigator->Controller;
  226.             LastHitBy = NULL;
  227.         }
  228.  
  229.         Die(Health, FDamageEvent(UDamageType::StaticClass()), Killer, NULL);
  230.     }
  231. }
  232.  
  233.  
  234. float AShooterCharacter::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, class AActor* DamageCauser)
  235. {
  236.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  237.     if (MyPC && MyPC->HasGodMode())
  238.     {
  239.         return 0.f;
  240.     }
  241.  
  242.     if (Health <= 0.f)
  243.     {
  244.         return 0.f;
  245.     }
  246.  
  247.     // Modify based on game rules.
  248.     AShooterGameMode* const Game = GetWorld()->GetAuthGameMode<AShooterGameMode>();
  249.     Damage = Game ? Game->ModifyDamage(Damage, this, DamageEvent, EventInstigator, DamageCauser) : 0.f;
  250.  
  251.     const float ActualDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
  252.     if (ActualDamage > 0.f)
  253.     {
  254.         Health -= ActualDamage;
  255.         if (Health <= 0)
  256.         {
  257.             Die(ActualDamage, DamageEvent, EventInstigator, DamageCauser);
  258.         }
  259.         else
  260.         {
  261.             PlayHit(ActualDamage, DamageEvent, EventInstigator ? EventInstigator->GetPawn() : NULL, DamageCauser);
  262.         }
  263.  
  264.         MakeNoise(1.0f, EventInstigator ? EventInstigator->GetPawn() : this);
  265.     }
  266.  
  267.     return ActualDamage;
  268. }
  269.  
  270.  
  271. bool AShooterCharacter::CanDie(float KillingDamage, FDamageEvent const& DamageEvent, AController* Killer, AActor* DamageCauser) const
  272. {
  273.     if ( bIsDying                                       // already dying
  274.         || IsPendingKill()                              // already destroyed
  275.         || Role != ROLE_Authority                       // not authority
  276.         || GetWorld()->GetAuthGameMode() == NULL
  277.         || GetWorld()->GetAuthGameMode()->GetMatchState() == MatchState::LeavingMap)    // level transition occurring
  278.     {
  279.         return false;
  280.     }
  281.  
  282.     return true;
  283. }
  284.  
  285.  
  286. bool AShooterCharacter::Die(float KillingDamage, FDamageEvent const& DamageEvent, AController* Killer, AActor* DamageCauser)
  287. {
  288.     if (!CanDie(KillingDamage, DamageEvent, Killer, DamageCauser))
  289.     {
  290.         return false;
  291.     }
  292.  
  293.     Health = FMath::Min(0.0f, Health);
  294.  
  295.     // if this is an environmental death then refer to the previous killer so that they receive credit (knocked into lava pits, etc)
  296.     UDamageType const* const DamageType = DamageEvent.DamageTypeClass ? DamageEvent.DamageTypeClass->GetDefaultObject<UDamageType>() : GetDefault<UDamageType>();
  297.     Killer = GetDamageInstigator(Killer, *DamageType);
  298.  
  299.     AController* const KilledPlayer = (Controller != NULL) ? Controller : Cast<AController>(GetOwner());
  300.     GetWorld()->GetAuthGameMode<AShooterGameMode>()->Killed(Killer, KilledPlayer, this, DamageType);
  301.  
  302.     NetUpdateFrequency = GetDefault<AShooterCharacter>()->NetUpdateFrequency;
  303.     CharacterMovement->ForceReplicationUpdate();
  304.  
  305.     OnDeath(KillingDamage, DamageEvent, Killer ? Killer->GetPawn() : NULL, DamageCauser);
  306.     return true;
  307. }
  308.  
  309.  
  310. void AShooterCharacter::OnDeath(float KillingDamage, struct FDamageEvent const& DamageEvent, class APawn* PawnInstigator, class AActor* DamageCauser)
  311. {
  312.     if (bIsDying)
  313.     {
  314.         return;
  315.     }
  316.  
  317.     bReplicateMovement = false;
  318.     bTearOff = true;
  319.     bIsDying = true;
  320.  
  321.     if (Role == ROLE_Authority)
  322.     {
  323.         ReplicateHit(KillingDamage, DamageEvent, PawnInstigator, DamageCauser, true);  
  324.  
  325.         // play the force feedback effect on the client player controller
  326.         APlayerController* PC = Cast<APlayerController>(Controller);
  327.         if (PC && DamageEvent.DamageTypeClass)
  328.         {
  329.             UShooterDamageType *DamageType = Cast<UShooterDamageType>(DamageEvent.DamageTypeClass->GetDefaultObject());
  330.             if (DamageType && DamageType->KilledForceFeedback)
  331.             {
  332.                 PC->ClientPlayForceFeedback(DamageType->KilledForceFeedback, false, "Damage");
  333.             }
  334.         }
  335.     }
  336.  
  337.     // cannot use IsLocallyControlled here, because even local client's controller may be NULL here
  338.     if (GetNetMode() != NM_DedicatedServer && DeathSound && Mesh1P && Mesh1P->IsVisible())
  339.     {
  340.         UGameplayStatics::PlaySoundAtLocation(this, DeathSound, GetActorLocation());
  341.     }
  342.  
  343.     // remove all weapons
  344.     DestroyInventory();
  345.    
  346.     // switch back to 3rd person view
  347.     UpdatePawnMeshes();
  348.  
  349.     DetachFromControllerPendingDestroy();
  350.     StopAllAnimMontages();
  351.  
  352.     if (LowHealthWarningPlayer && LowHealthWarningPlayer->IsPlaying())
  353.     {
  354.         LowHealthWarningPlayer->Stop();
  355.     }
  356.  
  357.     if (RunLoopAC)
  358.     {
  359.         RunLoopAC->Stop();
  360.     }
  361.  
  362.     // disable collisions on capsule
  363.     CapsuleComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
  364.     CapsuleComponent->SetCollisionResponseToAllChannels(ECR_Ignore);
  365.  
  366.     if (Mesh)
  367.     {
  368.         static FName CollisionProfileName(TEXT("Ragdoll"));
  369.         Mesh->SetCollisionProfileName(CollisionProfileName);
  370.     }
  371.     SetActorEnableCollision(true);
  372.  
  373.     // Death anim
  374.     float DeathAnimDuration = PlayAnimMontage(DeathAnim);
  375.  
  376.     // Ragdoll
  377.     if (DeathAnimDuration > 0.f)
  378.     {
  379.         GetWorldTimerManager().SetTimer(this, &AShooterCharacter::SetRagdollPhysics, FMath::Min(0.1f, DeathAnimDuration), false);
  380.     }
  381.     else
  382.     {
  383.         SetRagdollPhysics();
  384.     }
  385. }
  386.  
  387. void AShooterCharacter::PlayHit(float DamageTaken, struct FDamageEvent const& DamageEvent, class APawn* PawnInstigator, class AActor* DamageCauser)
  388. {
  389.     if (Role == ROLE_Authority)
  390.     {
  391.         ReplicateHit(DamageTaken, DamageEvent, PawnInstigator, DamageCauser, false);
  392.  
  393.         // play the force feedback effect on the client player controller
  394.         APlayerController* PC = Cast<APlayerController>(Controller);
  395.         if (PC && DamageEvent.DamageTypeClass)
  396.         {
  397.             UShooterDamageType *DamageType = Cast<UShooterDamageType>(DamageEvent.DamageTypeClass->GetDefaultObject());
  398.             if (DamageType && DamageType->HitForceFeedback)
  399.             {
  400.                 PC->ClientPlayForceFeedback(DamageType->HitForceFeedback, false, "Damage");
  401.             }
  402.         }
  403.     }
  404.  
  405.     if (DamageTaken > 0.f)
  406.     {
  407.         ApplyDamageMomentum(DamageTaken, DamageEvent, PawnInstigator, DamageCauser);
  408.     }
  409.    
  410.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  411.     AShooterHUD* MyHUD = MyPC ? Cast<AShooterHUD>(MyPC->GetHUD()) : NULL;
  412.     if (MyHUD)
  413.     {
  414.         MyHUD->NotifyHit(DamageTaken, DamageEvent, PawnInstigator);
  415.     }
  416.  
  417.     if (PawnInstigator && PawnInstigator != this && PawnInstigator->IsLocallyControlled())
  418.     {
  419.         AShooterPlayerController* InstigatorPC = Cast<AShooterPlayerController>(PawnInstigator->Controller);
  420.         AShooterHUD* InstigatorHUD = InstigatorPC ? Cast<AShooterHUD>(InstigatorPC->GetHUD()) : NULL;
  421.         if (InstigatorHUD)
  422.         {
  423.             InstigatorHUD->NotifyEnemyHit();
  424.         }
  425.     }
  426. }
  427.  
  428.  
  429. void AShooterCharacter::SetRagdollPhysics()
  430. {
  431.     bool bInRagdoll = false;
  432.  
  433.     if (IsPendingKill())
  434.     {
  435.         bInRagdoll = false;
  436.     }
  437.     else if (!Mesh || !Mesh->GetPhysicsAsset())
  438.     {
  439.         bInRagdoll = false;
  440.     }
  441.     else
  442.     {
  443.         // initialize physics/etc
  444.         Mesh->SetAllBodiesSimulatePhysics(true);
  445.         Mesh->SetSimulatePhysics(true);
  446.         Mesh->WakeAllRigidBodies();
  447.         Mesh->bBlendPhysics = true;
  448.  
  449.         bInRagdoll = true;
  450.     }
  451.  
  452.     CharacterMovement->StopMovementImmediately();
  453.     CharacterMovement->DisableMovement();
  454.     CharacterMovement->SetComponentTickEnabled(false);
  455.  
  456.     if (!bInRagdoll)
  457.     {
  458.         // hide and set short lifespan
  459.         TurnOff();
  460.         SetActorHiddenInGame(true);
  461.         SetLifeSpan( 1.0f );
  462.     }
  463.     else
  464.     {
  465.         SetLifeSpan( 10.0f );
  466.     }
  467. }
  468.  
  469.  
  470.  
  471. void AShooterCharacter::ReplicateHit(float Damage, struct FDamageEvent const& DamageEvent, class APawn* PawnInstigator, class AActor* DamageCauser, bool bKilled)
  472. {
  473.     const float TimeoutTime = GetWorld()->GetTimeSeconds() + 0.5f;
  474.  
  475.     FDamageEvent const& LastDamageEvent = LastTakeHitInfo.GetDamageEvent();
  476.     if ((PawnInstigator == LastTakeHitInfo.PawnInstigator.Get()) && (LastDamageEvent.DamageTypeClass == LastTakeHitInfo.DamageTypeClass) && (LastTakeHitTimeTimeout == TimeoutTime))
  477.     {
  478.         // same frame damage
  479.         if (bKilled && LastTakeHitInfo.bKilled)
  480.         {
  481.             // Redundant death take hit, just ignore it
  482.             return;
  483.         }
  484.  
  485.         // otherwise, accumulate damage done this frame
  486.         Damage += LastTakeHitInfo.ActualDamage;
  487.     }
  488.  
  489.     LastTakeHitInfo.ActualDamage = Damage;
  490.     LastTakeHitInfo.PawnInstigator = Cast<AShooterCharacter>(PawnInstigator);
  491.     LastTakeHitInfo.DamageCauser = DamageCauser;
  492.     LastTakeHitInfo.SetDamageEvent(DamageEvent);       
  493.     LastTakeHitInfo.bKilled = bKilled;
  494.     LastTakeHitInfo.EnsureReplication();
  495.  
  496.     LastTakeHitTimeTimeout = TimeoutTime;
  497. }
  498.  
  499. void AShooterCharacter::OnRep_LastTakeHitInfo()
  500. {
  501.     if (LastTakeHitInfo.bKilled)
  502.     {
  503.         OnDeath(LastTakeHitInfo.ActualDamage, LastTakeHitInfo.GetDamageEvent(), LastTakeHitInfo.PawnInstigator.Get(), LastTakeHitInfo.DamageCauser.Get());
  504.     }
  505.     else
  506.     {
  507.         PlayHit(LastTakeHitInfo.ActualDamage, LastTakeHitInfo.GetDamageEvent(), LastTakeHitInfo.PawnInstigator.Get(), LastTakeHitInfo.DamageCauser.Get());
  508.     }
  509. }
  510.  
  511. //Pawn::PlayDying sets this lifespan, but when that function is called on client, dead pawn's role is still SimulatedProxy despite bTearOff being true.
  512. void AShooterCharacter::TornOff()
  513. {
  514.     SetLifeSpan(25.f);
  515. }
  516.  
  517.  
  518. //////////////////////////////////////////////////////////////////////////
  519. // Inventory
  520.  
  521. void AShooterCharacter::SpawnDefaultInventory()
  522. {
  523.     if (Role < ROLE_Authority)
  524.     {
  525.         return;
  526.     }
  527.  
  528.     int32 NumWeaponClasses = DefaultInventoryClasses.Num();
  529.     for (int32 i = 0; i < NumWeaponClasses; i++)
  530.     {
  531.         if (DefaultInventoryClasses[i])
  532.         {
  533.             FActorSpawnParameters SpawnInfo;
  534.             SpawnInfo.bNoCollisionFail = true;
  535.             AShooterWeapon* NewWeapon = GetWorld()->SpawnActor<AShooterWeapon>(DefaultInventoryClasses[i], SpawnInfo);
  536.             AddWeapon(NewWeapon);
  537.         }
  538.     }
  539.  
  540.     // equip first weapon in inventory
  541.     if (Inventory.Num() > 0)
  542.     {
  543.         EquipWeapon(Inventory[0]);
  544.     }
  545. }
  546.  
  547. void AShooterCharacter::DestroyInventory()
  548. {
  549.     if (Role < ROLE_Authority)
  550.     {
  551.         return;
  552.     }
  553.  
  554.     // remove all weapons from inventory and destroy them
  555.     for (int32 i = Inventory.Num() - 1; i >= 0; i--)
  556.     {
  557.         AShooterWeapon* Weapon = Inventory[i];
  558.         if (Weapon)
  559.         {
  560.             RemoveWeapon(Weapon);
  561.             Weapon->Destroy();
  562.         }
  563.     }
  564. }
  565.  
  566. void AShooterCharacter::AddWeapon(AShooterWeapon* Weapon)
  567. {
  568.     if (Weapon && Role == ROLE_Authority)
  569.     {
  570.         Weapon->OnEnterInventory(this);
  571.         Inventory.AddUnique(Weapon);
  572.     }
  573. }
  574.  
  575. void AShooterCharacter::RemoveWeapon(AShooterWeapon* Weapon)
  576. {
  577.     if (Weapon && Role == ROLE_Authority)
  578.     {
  579.         Weapon->OnLeaveInventory();
  580.         Inventory.RemoveSingle(Weapon);
  581.     }
  582. }
  583.  
  584. AShooterWeapon* AShooterCharacter::FindWeapon(TSubclassOf<AShooterWeapon> WeaponClass)
  585. {
  586.     for (int32 i = 0; i < Inventory.Num(); i++)
  587.     {
  588.         if (Inventory[i] && Inventory[i]->IsA(WeaponClass))
  589.         {
  590.             return Inventory[i];
  591.         }
  592.     }
  593.  
  594.     return NULL;
  595. }
  596.  
  597. void AShooterCharacter::EquipWeapon(AShooterWeapon* Weapon)
  598. {
  599.     if (Weapon)
  600.     {
  601.         if (Role == ROLE_Authority)
  602.         {
  603.             SetCurrentWeapon(Weapon);
  604.         }
  605.         else
  606.         {
  607.             ServerEquipWeapon(Weapon);
  608.         }
  609.     }
  610. }
  611.  
  612. bool AShooterCharacter::ServerEquipWeapon_Validate(AShooterWeapon* Weapon)
  613. {
  614.     return true;
  615. }
  616.  
  617. void AShooterCharacter::ServerEquipWeapon_Implementation(AShooterWeapon* Weapon)
  618. {
  619.     EquipWeapon(Weapon);
  620. }
  621.  
  622. void AShooterCharacter::OnRep_CurrentWeapon(AShooterWeapon* LastWeapon)
  623. {
  624.     SetCurrentWeapon(CurrentWeapon, LastWeapon);
  625. }
  626.  
  627. void AShooterCharacter::SetCurrentWeapon(class AShooterWeapon* NewWeapon, class AShooterWeapon* LastWeapon)
  628. {
  629.     AShooterWeapon* LocalLastWeapon = NULL;
  630.    
  631.     if (LastWeapon != NULL)
  632.     {
  633.         LocalLastWeapon = LastWeapon;
  634.     }
  635.     else if (NewWeapon != CurrentWeapon)
  636.     {
  637.         LocalLastWeapon = CurrentWeapon;
  638.     }
  639.  
  640.     // unequip previous
  641.     if (LocalLastWeapon)
  642.     {
  643.         LocalLastWeapon->OnUnEquip();
  644.     }
  645.  
  646.     CurrentWeapon = NewWeapon;
  647.  
  648.     // equip new one
  649.     if (NewWeapon)
  650.     {
  651.         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!
  652.         NewWeapon->OnEquip();
  653.     }
  654. }
  655.  
  656.  
  657. //////////////////////////////////////////////////////////////////////////
  658. // Weapon usage
  659.  
  660. void AShooterCharacter::StartWeaponFire()
  661. {
  662.     if (!bWantsToFire)
  663.     {
  664.         bWantsToFire = true;
  665.         if (CurrentWeapon)
  666.         {
  667.             CurrentWeapon->StartFire();
  668.         }
  669.     }
  670. }
  671.  
  672. void AShooterCharacter::StopWeaponFire()
  673. {
  674.     if (bWantsToFire)
  675.     {
  676.         bWantsToFire = false;
  677.         if (CurrentWeapon)
  678.         {
  679.             CurrentWeapon->StopFire();
  680.         }
  681.     }
  682. }
  683.  
  684. bool AShooterCharacter::CanFire() const
  685. {
  686.     return IsAlive();
  687. }
  688.  
  689. bool AShooterCharacter::CanReload() const
  690. {
  691.     return true;
  692. }
  693.  
  694. void AShooterCharacter::SetTargeting(bool bNewTargeting)
  695. {
  696.     bIsTargeting = bNewTargeting;
  697.  
  698.     if (TargetingSound)
  699.     {
  700.         UGameplayStatics::PlaySoundAttached(TargetingSound, GetRootComponent());
  701.     }
  702.  
  703.     if (Role < ROLE_Authority)
  704.     {
  705.         ServerSetTargeting(bNewTargeting);
  706.     }
  707. }
  708.  
  709. bool AShooterCharacter::ServerSetTargeting_Validate(bool bNewTargeting)
  710. {
  711.     return true;
  712. }
  713.  
  714. void AShooterCharacter::ServerSetTargeting_Implementation(bool bNewTargeting)
  715. {
  716.     SetTargeting(bNewTargeting);
  717. }
  718.  
  719. //////////////////////////////////////////////////////////////////////////
  720. // Movement
  721.  
  722. void AShooterCharacter::SetRunning(bool bNewRunning, bool bToggle)
  723. {
  724.     bWantsToRun = bNewRunning;
  725.     bWantsToRunToggled = bNewRunning && bToggle;
  726.  
  727.     if (Role < ROLE_Authority)
  728.     {
  729.         ServerSetRunning(bNewRunning, bToggle);
  730.     }
  731.  
  732.     UpdateRunSounds(bNewRunning);
  733. }
  734.  
  735. bool AShooterCharacter::ServerSetRunning_Validate(bool bNewRunning, bool bToggle)
  736. {
  737.     return true;
  738. }
  739.  
  740. void AShooterCharacter::ServerSetRunning_Implementation(bool bNewRunning, bool bToggle)
  741. {
  742.     SetRunning(bNewRunning, bToggle);
  743. }
  744.  
  745. void AShooterCharacter::UpdateRunSounds(bool bNewRunning)
  746. {
  747.     if (bNewRunning)
  748.     {
  749.         if (!RunLoopAC && RunLoopSound)
  750.         {
  751.             RunLoopAC = UGameplayStatics::PlaySoundAttached(RunLoopSound, GetRootComponent());
  752.             if (RunLoopAC)
  753.             {
  754.                 RunLoopAC->bAutoDestroy = false;
  755.             }
  756.            
  757.         }
  758.         else if (RunLoopAC)
  759.         {
  760.             RunLoopAC->Play();
  761.         }
  762.     }
  763.     else
  764.     {
  765.         if (RunLoopAC)
  766.         {
  767.             RunLoopAC->Stop();
  768.         }
  769.  
  770.         if (RunStopSound)
  771.         {
  772.             UGameplayStatics::PlaySoundAttached(RunStopSound, GetRootComponent());
  773.         }
  774.     }
  775. }
  776.  
  777. //////////////////////////////////////////////////////////////////////////
  778. // Animations
  779.  
  780. float AShooterCharacter::PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
  781. {
  782.     USkeletalMeshComponent* UseMesh = GetPawnMesh();
  783.     if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance)
  784.     {
  785.         return UseMesh->AnimScriptInstance->Montage_Play(AnimMontage, InPlayRate);
  786.     }
  787.  
  788.     return 0.0f;
  789. }
  790.  
  791. void AShooterCharacter::StopAnimMontage(class UAnimMontage* AnimMontage)
  792. {
  793.     USkeletalMeshComponent* UseMesh = GetPawnMesh();
  794.     if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance &&
  795.         UseMesh->AnimScriptInstance->Montage_IsPlaying(AnimMontage))
  796.     {
  797.         UseMesh->AnimScriptInstance->Montage_Stop(AnimMontage->BlendOutTime);
  798.     }
  799. }
  800.  
  801. void AShooterCharacter::StopAllAnimMontages()
  802. {
  803.     USkeletalMeshComponent* UseMesh = GetPawnMesh();
  804.     if (UseMesh && UseMesh->AnimScriptInstance)
  805.     {
  806.         UseMesh->AnimScriptInstance->Montage_Stop(0.0f);
  807.     }
  808. }
  809.  
  810.  
  811. //////////////////////////////////////////////////////////////////////////
  812. // Input
  813.  
  814. void AShooterCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
  815. {
  816.     check(InputComponent);
  817.     InputComponent->BindAxis("MoveForward", this, &AShooterCharacter::MoveForward);
  818.     InputComponent->BindAxis("MoveRight", this, &AShooterCharacter::MoveRight);
  819.     InputComponent->BindAxis("MoveUp", this, &AShooterCharacter::MoveUp);
  820.     InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
  821.     InputComponent->BindAxis("TurnRate", this, &AShooterCharacter::TurnAtRate);
  822.     InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
  823.     InputComponent->BindAxis("LookUpRate", this, &AShooterCharacter::LookUpAtRate);
  824.  
  825.     InputComponent->BindAction("Fire", IE_Pressed, this, &AShooterCharacter::OnStartFire);
  826.     InputComponent->BindAction("Fire", IE_Released, this, &AShooterCharacter::OnStopFire);
  827.  
  828.     InputComponent->BindAction("Targeting", IE_Pressed, this, &AShooterCharacter::OnStartTargeting);
  829.     InputComponent->BindAction("Targeting", IE_Released, this, &AShooterCharacter::OnStopTargeting);
  830.  
  831.     InputComponent->BindAction("NextWeapon", IE_Pressed, this, &AShooterCharacter::OnNextWeapon);
  832.     InputComponent->BindAction("PrevWeapon", IE_Pressed, this, &AShooterCharacter::OnPrevWeapon);
  833.  
  834.     InputComponent->BindAction("Reload", IE_Pressed, this, &AShooterCharacter::OnReload);
  835.  
  836.     InputComponent->BindAction("Jump", IE_Pressed, this, &AShooterCharacter::OnStartJump);
  837.     InputComponent->BindAction("Jump", IE_Released, this, &AShooterCharacter::OnStopJump);
  838.  
  839.     InputComponent->BindAction("Run", IE_Pressed, this, &AShooterCharacter::OnStartRunning);
  840.     InputComponent->BindAction("RunToggle", IE_Pressed, this, &AShooterCharacter::OnStartRunningToggle);
  841.     InputComponent->BindAction("Run", IE_Released, this, &AShooterCharacter::OnStopRunning);
  842.  
  843.     InputComponent->BindAction("Use", IE_Pressed, this, &AShooterCharacter::UseItem);  
  844.     InputComponent->BindAction("Use", IE_Repeat, this, &AShooterCharacter::UseItem);
  845. }
  846.  
  847.  
  848.  
  849.  
  850. void AShooterCharacter::MoveForward(float Val)
  851. {
  852.     if (Controller && Val != 0.f)
  853.     {
  854.         // Limit pitch when walking or falling
  855.         const bool bLimitRotation = (CharacterMovement->IsMovingOnGround() || CharacterMovement->IsFalling());
  856.         const FRotator Rotation = bLimitRotation ? GetActorRotation() : Controller->GetControlRotation();
  857.         const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis( EAxis::X );
  858.         AddMovementInput(Direction, Val);
  859.     }
  860. }
  861.  
  862. void AShooterCharacter::MoveRight(float Val)
  863. {
  864.     if (Val != 0.f)
  865.     {
  866.         const FRotator Rotation = GetActorRotation();
  867.         const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis( EAxis::Y );
  868.         AddMovementInput(Direction, Val);
  869.     }
  870. }
  871.  
  872. void AShooterCharacter::MoveUp(float Val)
  873. {
  874.     if (Val != 0.f)
  875.     {
  876.         // Not when walking or falling.
  877.         if (CharacterMovement->IsMovingOnGround() || CharacterMovement->IsFalling())
  878.         {
  879.             return;
  880.         }
  881.  
  882.         AddMovementInput(FVector::UpVector, Val);
  883.     }
  884. }
  885.  
  886. void AShooterCharacter::TurnAtRate(float Val)
  887. {
  888.     // calculate delta for this frame from the rate information
  889.     AddControllerYawInput(Val * BaseTurnRate * GetWorld()->GetDeltaSeconds());
  890. }
  891.  
  892. void AShooterCharacter::LookUpAtRate(float Val)
  893. {
  894.     // calculate delta for this frame from the rate information
  895.     AddControllerPitchInput(Val * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
  896. }
  897.  
  898. void AShooterCharacter::OnStartFire()
  899. {
  900.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  901.     if (MyPC && MyPC->IsGameInputAllowed())
  902.     {
  903.         if (IsRunning())
  904.         {
  905.             SetRunning(false, false);
  906.         }
  907.         StartWeaponFire();
  908.     }
  909. }
  910.  
  911. void AShooterCharacter::OnStopFire()
  912. {
  913.     StopWeaponFire();
  914. }
  915.  
  916. void AShooterCharacter::OnStartTargeting()
  917. {
  918.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  919.     if (MyPC && MyPC->IsGameInputAllowed())
  920.     {
  921.         if (IsRunning())
  922.         {
  923.             SetRunning(false, false);
  924.         }
  925.         SetTargeting(true);
  926.     }
  927. }
  928.  
  929. void AShooterCharacter::OnStopTargeting()
  930. {
  931.     SetTargeting(false);
  932. }
  933.  
  934. void AShooterCharacter::OnNextWeapon()
  935. {
  936.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  937.     if (MyPC && MyPC->IsGameInputAllowed())
  938.     {
  939.         if (Inventory.Num() >= 2 && (CurrentWeapon == NULL || CurrentWeapon->GetCurrentState() != EWeaponState::Equipping))
  940.         {
  941.             const int32 CurrentWeaponIdx = Inventory.IndexOfByKey(CurrentWeapon);
  942.             AShooterWeapon* NextWeapon = Inventory[(CurrentWeaponIdx + 1) % Inventory.Num()];
  943.             EquipWeapon(NextWeapon);
  944.         }
  945.     }
  946. }
  947.  
  948. void AShooterCharacter::OnPrevWeapon()
  949. {
  950.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  951.     if (MyPC && MyPC->IsGameInputAllowed())
  952.     {
  953.         if (Inventory.Num() >= 2 && (CurrentWeapon == NULL || CurrentWeapon->GetCurrentState() != EWeaponState::Equipping))
  954.         {
  955.             const int32 CurrentWeaponIdx = Inventory.IndexOfByKey(CurrentWeapon);
  956.             AShooterWeapon* PrevWeapon = Inventory[(CurrentWeaponIdx - 1 + Inventory.Num()) % Inventory.Num()];
  957.             EquipWeapon(PrevWeapon);
  958.         }
  959.     }
  960. }
  961.  
  962. void AShooterCharacter::OnReload()
  963. {
  964.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  965.     if (MyPC && MyPC->IsGameInputAllowed())
  966.     {
  967.         if (CurrentWeapon)
  968.         {
  969.             CurrentWeapon->StartReload();
  970.         }
  971.     }
  972. }
  973.  
  974. void AShooterCharacter::OnStartRunning()
  975. {
  976.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  977.     if (MyPC && MyPC->IsGameInputAllowed())
  978.     {
  979.         if (IsTargeting())
  980.         {
  981.             SetTargeting(false);
  982.         }
  983.         StopWeaponFire();
  984.         SetRunning(true, false);
  985.     }
  986. }
  987.  
  988. void AShooterCharacter::OnStartRunningToggle()
  989. {
  990.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  991.     if (MyPC && MyPC->IsGameInputAllowed())
  992.     {
  993.         if (IsTargeting())
  994.         {
  995.             SetTargeting(false);
  996.         }
  997.         StopWeaponFire();
  998.         SetRunning(true, true);
  999.     }
  1000. }
  1001.  
  1002. void AShooterCharacter::OnStopRunning()
  1003. {
  1004.     SetRunning(false, false);
  1005. }
  1006.  
  1007. bool AShooterCharacter::IsRunning() const
  1008. {  
  1009.     if (!CharacterMovement)
  1010.     {
  1011.         return false;
  1012.     }
  1013.    
  1014.     return (bWantsToRun || bWantsToRunToggled) && !GetVelocity().IsZero() && (GetVelocity().SafeNormal2D() | GetActorRotation().Vector()) > -0.1;
  1015. }
  1016.  
  1017. void AShooterCharacter::Tick(float DeltaSeconds)
  1018. {
  1019.     Super::Tick(DeltaSeconds);
  1020.  
  1021.     if (bWantsToRunToggled && !IsRunning())
  1022.     {
  1023.         SetRunning(false, false);
  1024.     }
  1025.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  1026.     if (MyPC && MyPC->HasHealthRegen())
  1027.     {
  1028.         if (this->Health < this->GetMaxHealth())
  1029.         {
  1030.             this->Health +=  5 * DeltaSeconds;
  1031.             if (Health > this->GetMaxHealth())
  1032.             {
  1033.                 Health = this->GetMaxHealth();
  1034.             }
  1035.         }
  1036.     }
  1037.    
  1038.     if (LowHealthSound && GEngine->UseSound())
  1039.     {
  1040.         if ((this->Health > 0 && this->Health < this->GetMaxHealth() * LowHealthPercentage) && (!LowHealthWarningPlayer || !LowHealthWarningPlayer->IsPlaying()))
  1041.         {
  1042.             LowHealthWarningPlayer = UGameplayStatics::PlaySoundAttached(LowHealthSound, GetRootComponent(),
  1043.                 NAME_None, FVector(ForceInit), EAttachLocation::KeepRelativeOffset, true);
  1044.             LowHealthWarningPlayer->SetVolumeMultiplier(0.0f);
  1045.         }
  1046.         else if ((this->Health > this->GetMaxHealth() * LowHealthPercentage || this->Health < 0) && LowHealthWarningPlayer && LowHealthWarningPlayer->IsPlaying())
  1047.         {
  1048.             LowHealthWarningPlayer->Stop();
  1049.         }
  1050.         if (LowHealthWarningPlayer && LowHealthWarningPlayer->IsPlaying())
  1051.         {
  1052.             const float MinVolume = 0.3f;
  1053.             const float VolumeMultiplier = (1.0f - (this->Health / (this->GetMaxHealth() * LowHealthPercentage)));
  1054.             LowHealthWarningPlayer->SetVolumeMultiplier(MinVolume + (1.0f - MinVolume) * VolumeMultiplier);
  1055.         }
  1056.     }
  1057. }
  1058.  
  1059. void AShooterCharacter::OnStartJump()
  1060. {
  1061.     AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller);
  1062.     if (MyPC && MyPC->IsGameInputAllowed())
  1063.     {
  1064.         bPressedJump = true;
  1065.     }
  1066. }
  1067.  
  1068. void AShooterCharacter::OnStopJump()
  1069. {
  1070.     bPressedJump = false;
  1071. }
  1072.  
  1073. //////////////////////////////////////////////////////////////////////////
  1074. // Replication
  1075.  
  1076. void AShooterCharacter::PreReplication( IRepChangedPropertyTracker & ChangedPropertyTracker )
  1077. {
  1078.     Super::PreReplication( ChangedPropertyTracker );
  1079.  
  1080.     // 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
  1081.     DOREPLIFETIME_ACTIVE_OVERRIDE( AShooterCharacter, LastTakeHitInfo, GetWorld() && GetWorld()->GetTimeSeconds() < LastTakeHitTimeTimeout );
  1082. }
  1083.  
  1084. void AShooterCharacter::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
  1085. {
  1086.     Super::GetLifetimeReplicatedProps( OutLifetimeProps );
  1087.  
  1088.     // only to local owner: weapon change requests are locally instigated, other clients don't need it
  1089.     DOREPLIFETIME_CONDITION( AShooterCharacter, Inventory,          COND_OwnerOnly );
  1090.  
  1091.     // everyone except local owner: flag change is locally instigated
  1092.     DOREPLIFETIME_CONDITION( AShooterCharacter, bIsTargeting,       COND_SkipOwner );
  1093.     DOREPLIFETIME_CONDITION( AShooterCharacter, bWantsToRun,        COND_SkipOwner );
  1094.  
  1095.     DOREPLIFETIME_CONDITION( AShooterCharacter, LastTakeHitInfo,    COND_Custom );
  1096.  
  1097.     // everyone
  1098.     DOREPLIFETIME( AShooterCharacter, CurrentWeapon );
  1099.     DOREPLIFETIME( AShooterCharacter, Health );
  1100. }
  1101.  
  1102. AShooterWeapon* AShooterCharacter::GetWeapon() const
  1103. {
  1104.     return CurrentWeapon;
  1105. }
  1106.  
  1107. int32 AShooterCharacter::GetInventoryCount() const
  1108. {
  1109.     return Inventory.Num();
  1110. }
  1111.  
  1112. AShooterWeapon* AShooterCharacter::GetInventoryWeapon(int32 index) const
  1113. {
  1114.     return Inventory[index];
  1115. }
  1116.  
  1117. USkeletalMeshComponent* AShooterCharacter::GetPawnMesh() const
  1118. {
  1119.     return IsFirstPerson() ? Mesh1P : Mesh;
  1120. }
  1121.  
  1122. USkeletalMeshComponent* AShooterCharacter::GetSpecifcPawnMesh( bool WantFirstPerson ) const
  1123. {
  1124.     return WantFirstPerson == true  ? Mesh1P : Mesh;
  1125. }
  1126.  
  1127. FName AShooterCharacter::GetWeaponAttachPoint() const
  1128. {
  1129.     return WeaponAttachPoint;
  1130. }
  1131.  
  1132. float AShooterCharacter::GetTargetingSpeedModifier() const
  1133. {
  1134.     return TargetingSpeedModifier;
  1135. }
  1136.  
  1137. bool AShooterCharacter::IsTargeting() const
  1138. {
  1139.     return bIsTargeting;
  1140. }
  1141.  
  1142. float AShooterCharacter::GetRunningSpeedModifier() const
  1143. {
  1144.     return RunningSpeedModifier;
  1145. }
  1146.  
  1147. bool AShooterCharacter::IsFiring() const
  1148. {
  1149.     return bWantsToFire;
  1150. };
  1151.  
  1152. bool AShooterCharacter::IsFirstPerson() const
  1153. {
  1154.     return IsAlive() && Controller && Controller->IsLocalPlayerController();
  1155. }
  1156.  
  1157. int32 AShooterCharacter::GetMaxHealth() const
  1158. {
  1159.     return GetClass()->GetDefaultObject<AShooterCharacter>()->Health;
  1160. }
  1161.  
  1162. bool AShooterCharacter::IsAlive() const
  1163. {
  1164.     return Health > 0;
  1165. }
  1166.  
  1167. float AShooterCharacter::GetLowHealthPercentage() const
  1168. {
  1169.     return LowHealthPercentage;
  1170. }
  1171.  
  1172. void AShooterCharacter::UpdateTeamColorsAllMIDs()
  1173. {
  1174.     for (int32 i = 0; i < MeshMIDs.Num(); ++i)
  1175.     {
  1176.         UpdateTeamColors(MeshMIDs[i]);
  1177.     }
  1178. }
  1179.  
  1180. void AShooterCharacter::UseItem()
  1181. {
  1182.     /* Get all overlapping actors */
  1183.     TArray<AActor*> CollectedActors;
  1184.     CollectionSphere->GetOverlappingActors(CollectedActors);
  1185.  
  1186.     /* Iterate through all overlapping actors*/
  1187.     for (int32 iCollected = 0; iCollected < CollectedActors.Num(); ++iCollected)
  1188.     {
  1189.         AUsableButton* const button = Cast<AUsableButton>(CollectedActors[iCollected]); // check if the overlapping actor is a usable button
  1190.  
  1191.         if (button && !button->IsPendingKill() && button->bIsUsable)
  1192.         {
  1193.             /* If the actor is a button and it is usable and not likely to be killed then go ahead and use it */
  1194.             UseUsableButton(button);
  1195.         }
  1196.     }
  1197. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement