0% found this document useful (0 votes)
128 views

C++ UAnimInstance Examples

The document provides an example of C++ code for a UAnimInstance class. It includes a character class with methods for movement, firing, and playing animations. When firing, it gets the animation instance and calls Montage_Play to play the firing animation.

Uploaded by

Nenad
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
128 views

C++ UAnimInstance Examples

The document provides an example of C++ code for a UAnimInstance class. It includes a character class with methods for movement, firing, and playing animations. When firing, it gets the animation instance and calls Montage_Play to play the firing animation.

Uploaded by

Nenad
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 101

C++ UAnimInstance

UAnimInstance::Montage_Play Examples

These are the top rated real world C++ (Cpp) examples of UAnimInstance::Montage_Play extracted from open source projects. You can rate examples to help us
improve the quality of examples.

Link: https://cpp.hotexamples.com/examples/-/UAnimInstance/Montage_Play/cpp-uaniminstance-montage_play-method-examples.html

EXAMPLE #1

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "MobileOpenCV.h"
#include "MobileOpenCVCharacter.h"
#include "MobileOpenCVProjectile.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AMobileOpenCVCharacter

AMobileOpenCVCharacter::AMobileOpenCVCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

//////////////////////////////////////////////////////////////////////////
// Input

void AMobileOpenCVCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMobileOpenCVCharacter::TouchStarted);


if( EnableTouchscreenMovement(InputComponent) == false )
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AMobileOpenCVCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &AMobileOpenCVCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AMobileOpenCVCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AMobileOpenCVCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AMobileOpenCVCharacter::LookUpAtRate);
}

void AMobileOpenCVCharacter::OnFire()
{
// try and fire a projectile
if (ProjectileClass != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle pos ition
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World != NULL)
{
// spawn the projectile at the muzzle
World->SpawnActor<AMobileOpenCVProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if(FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if(AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

void AMobileOpenCVCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if( TouchItem.bIsPressed == true )
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AMobileOpenCVCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if( ( FingerIndex == TouchItem.FingerIndex ) && (TouchItem.bMoved == false) )
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void AMobileOpenCVCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && ( TouchItem.FingerIndex==FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D( MoveDelta.X, MoveDelta.Y) / ScreenSize;

if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AMobileOpenCVCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AMobileOpenCVCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AMobileOpenCVCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AMobileOpenCVCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool AMobileOpenCVCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if(FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch )
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMobileOpenCVCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AMobileOpenCVCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AMobileOpenCVCharacter::TouchUpdate);
}
return bResult;
}
EXAMPLE #2

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "MurphysLaw.h"
#include "MurphysLawCharacter.h"
#include "../Network/MurphysLawPlayerState.h"
#include "../Weapon/MurphysLawBaseWeapon.h"
#include "../HUD/MurphysLawHUDWidget.h"
#include "../Components/MurphysLawInventoryComponent.h"
#include "../Menu/MurphysLawInGameMenu.h"
#include "../Network/MurphysLawPlayerController.h"
#include "../AI/MurphysLawAIController.h"
#include "WidgetComponent.h"
#include "Blueprint/UserWidget.h"
#include "Animation/AnimInstance.h"
#include "UnrealNetwork.h"
#include "../DamageZone/MurphysLawDamageZone.h"
#include "../Environnement/ExplosiveBarrel/MurphysLawExplosiveBarrel.h"
#include "MurphysLawNameplateWidget.h"
#include "Kismet/KismetMathLibrary.h"
#include "../Network/MurphysLawGameMode.h"
#include "../Network/MurphysLawGameState.h"

#include <MurphysLaw/Interface/MurphysLawIController.h>
#include <MurphysLaw/Utils/MurphysLawUtils.h>

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AMurphysLawCharacter

const float AMurphysLawCharacter::DefaultAimFactor = 90.0f;


const FName AMurphysLawCharacter::MATERIAL_PARAM_TEAM_COLOR_CLOTHES("TeamClothesColor");
const FName AMurphysLawCharacter::MATERIAL_PARAM_TEAM_COLOR_MASK("TeamMaskColor");
const float AMurphysLawCharacter::ROTATION_RATE_HUMAN(360.f);
const float AMurphysLawCharacter::ROTATION_RATE_BOT(160.f);
const float AMurphysLawCharacter::FACTOR_HEADSHOT(3.f);
const float AMurphysLawCharacter::FACTOR_CHESTSHOT(1.5f);
const FString AMurphysLawCharacter::SOCKET_HEAD = "Head";
const FString AMurphysLawCharacter::SOCKET_SPINE = "Spine1";

AMurphysLawCharacter::AMurphysLawCharacter()
{
// 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;

// Set size for collision capsule


GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;
// Create a SceneComponent
SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
SceneComponent->AttachParent = GetCapsuleComponent();
SceneComponent->RelativeLocation = FVector(-20, 0, 120);

// Create a CharacterNameplate
CharacterNameplate = CreateDefaultSubobject<UWidgetComponent>(TEXT("CharacterNameplate"));
CharacterNameplate->AttachParent = SceneComponent;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

GetMesh()->SetCollisionObjectType(ECC_PhysicsBody);
GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
GetMesh()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Ignore);
GetMesh()->SetCollisionResponseToAllChannels(ECR_Block);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)

// Set base health level for the character


MaxHealth = 100.f;
Dead = false;
IsRunning = false;

// Set the Index so the character starts with no weapon


CurrentWeaponIndex = NO_WEAPON_VALUE;

// Creates the inventory component to store the weapons of the character


Inventory = CreateDefaultSubobject<UMurphysLawInventoryComponent>(TEXT("InventoryComponent"));
checkf(Inventory != nullptr, TEXT("Inventory has not been initialized correctly"));

// Movement related
IsCrouched = false;
GetCharacterMovement()->GetNavAgentPropertiesRef().bCanCrouch = true;
ConfigureMovement(false); // Configure as human to see human related values in editor

// AI and possessing settings from APawm


AIControllerClass = nullptr; // Should be a subclass of AMurphysLawAIController
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
AutoPossessPlayer = EAutoReceiveInput::Disabled;

// Spawning settings
SpawnCollisionHandlingMethod = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;

// Try to load the sound of switching weapon


static ConstructorHelpers::FObjectFinder<USoundBase> SwitchWeaponSound(TEXT("SoundWave'/Game/MurphysLawRessources/Weapons/S_Switch_Weapon.S_Switch_Weapon'"));
SwitchingWeaponSound = nullptr;
if (SwitchWeaponSound.Succeeded())
{
SwitchingWeaponSound = SwitchWeaponSound.Object;
}

// Sets up the stamina info


MaxStamina = 100.f;
RunningStaminaDecayRate = 0.4f;
JumpStaminaDecayAmount = 7.5f;
StaminaRegenerationRate = 0.07f;

// Team color flags


ValidTeamBodyMeshColor = false;
ValidTeamMaskMeshColor = false;
}

// Indicates to the server what properties of the object to replicate on the clients
void AMurphysLawCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME(AMurphysLawCharacter, CurrentWeaponIndex);
DOREPLIFETIME(AMurphysLawCharacter, CurrentHealth);
DOREPLIFETIME(AMurphysLawCharacter, Dead);
DOREPLIFETIME(AMurphysLawCharacter, TeamBodyMeshColor);
DOREPLIFETIME(AMurphysLawCharacter, TeamMaskMeshColor);
}

void AMurphysLawCharacter::BeginPlay()
{
Super::BeginPlay();

// AI settings ressources
if (AIControllerClass == nullptr) ShowWarning("No AIController class assigned to MurphysLawCharacter");

if (SwitchingWeaponSound == nullptr) ShowWarning("MurphysLawCharacter - Unable to load the SwitchingWeaponSound");

// If the InventoryComponent's BeginPlay has not been called yet, we call it


if (!Inventory->HasBegunPlay())
{
Inventory->BeginPlay();
}

EquipFirstWeapon();

// Set the current health of the character here in case the value has been overriden in a subclass
CurrentHealth = MaxHealth;

auto NameplateWidget = Cast<UMurphysLawNameplateWidget>(CharacterNameplate->GetUserWidgetObject());


if (NameplateWidget)
{
NameplateWidget->SetCharacter(this);
}

// Sets the current stamina level to the maximum


CurrentStamina = MaxStamina;
}

// Called when game ends


void AMurphysLawCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);

// Clear the inventory


if (Inventory->HasBegunPlay()) Inventory->EndPlay(EndPlayReason);
}

// Called when the character is possessed by a new controller


void AMurphysLawCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);

if (NewController != nullptr)
{
// Update settings of movement component to allow "natural" movement for bots
const bool ConfigureAsBot = NewController->IsA<AAIController>();
ConfigureMovement(ConfigureAsBot);
}
}

/** Configure character movement with human or bot specific settings */


void AMurphysLawCharacter::ConfigureMovement(const bool ConfigureForBot)
{
if (ConfigureForBot)
{ // Bot movement configuration

// Allow looking in different direction than movement


bUseControllerRotationYaw = true;
GetCharacterMovement()->bOrientRotationToMovement = false;

// Change left/right rotation speed


GetCharacterMovement()->RotationRate.Yaw = ROTATION_RATE_BOT;
}
else
{ // Human movement configuration

// Allow looking in different direction than movement


bUseControllerRotationYaw = true;
GetCharacterMovement()->bOrientRotationToMovement = false;

// Change left/right rotation speed


GetCharacterMovement()->RotationRate.Yaw = ROTATION_RATE_HUMAN;
}
}

/** Defines a world-space point where an ai should look


Since the gun is located in the center of the body, it is a good focal point */
AActor* AMurphysLawCharacter::GetFocalPoint() const { return GetEquippedWeapon(); }

void AMurphysLawCharacter::Tick(float DeltaSeconds)


{
Super::Tick(DeltaSeconds);

if (FirstPersonCameraComponent != nullptr)
{
// Gets the camera angle for the mini-map (to rotate it as the player rotates the player)
Bearing = FirstPersonCameraComponent->GetComponentRotation().Yaw;
}

// Calculate the current regeneration rate according on character's movement


const bool IsMoving = GetVelocity().Size() != 0.f;
const float CurrentRegenerationRate = IsMoving ? StaminaRegenerationRate : StaminaRegenerationRate * 2;

// Raise the level of stamina overtime


UpdateStaminaLevel(CurrentRegenerationRate * DeltaSeconds * GetMaxStaminaLevel());

// If the character is not moving, we deactivate his running so he doesn't lose stamina
if (!IsMoving)
{
SetIsRunning(false);
}

// But lower the stamina if the player is running


if (IsRunning)
{
UpdateStaminaLevel(-RunningStaminaDecayRate * DeltaSeconds * GetMaxStaminaLevel());

// If the character is out of breath, it stops running


if (GetCurrentStaminaLevel() <= 0.f)
{
SetIsRunning(false);
}
}
if (this->GetMovementComponent()->IsFalling())
{
SetIsInAir(true);
HighestZ = FMath::Max(HighestZ, GetActorLocation().Z);
}
else
{
float DeltaZ = (HighestZ - GetActorLocation().Z) / 10.f;
//On ne veut pas de dégât pour les petits sauts...
//Pour calculer le dégât fait en sautant de la tour
if (DeltaZ > 100)
{
FHitResult HitResult;
float Damage = DeltaZ * 0.35f;
FVector HurtDirection = GetActorLocation();
FPointDamageEvent CollisionDamageEvent(Damage, HitResult, HurtDirection, UDamageType::StaticClass());

if (Role == ROLE_Authority)
{
TakeDamage(Damage, CollisionDamageEvent, GetController(), this);
}
}

//Pour calculer le dégât fait en sautant du balcon


else if (DeltaZ > 30)
{
FHitResult HitResult;
float Damage = DeltaZ * 0.5f;
FVector HurtDirection = GetActorLocation();
FPointDamageEvent CollisionDamageEvent(Damage, HitResult, HurtDirection, UDamageType::StaticClass());

if (Role == ROLE_Authority)
{
TakeDamage(Damage, CollisionDamageEvent, GetController(), this);
}
}

HighestZ = 0;
SetIsInAir(false);
}
/** [PS] DO NOT REMOVE - Trying to make it work */

//ACharacter* myCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);


//if (myCharacter) //Maybe check character
//{
// FVector charLocation = myCharacter->GetActorLocation();
// FVector sceneLocation = SceneComponent->GetComponentLocation();
//
// FRotator PlayerRot = UKismetMathLibrary::FindLookAtRotation(sceneLocation, charLocation);
// float X, Y, Z;

// UKismetMathLibrary::BreakRotator(PlayerRot, X, Y, Z);
// FRotator Result = UKismetMathLibrary::MakeRotator(0.f, 0.f, Z);
// //UKismetMathLibrary::BreakRotIntoAxes(PlayerRot, X, Y, Z);
//
// SceneComponent->SetWorldLocationAndRotation(sceneLocation, Result);
// /*SceneComponent->SetWorldRotation(FRotationMatrix::MakeFromXY(X, Y).ToQuat());*/
//}
}

//////////////////////////////////////////////////////////////////////////
// Input

bool AMurphysLawCharacter::IsInAir() const


{
return InAir;
}

void AMurphysLawCharacter::SetIsInAir(bool isInAir)


{
InAir = isInAir;
}

void AMurphysLawCharacter::Aim()
{
if (HasWeaponEquipped())
{
FirstPersonCameraComponent->FieldOfView = GetEquippedWeapon()->AimFactor;
IsCharacterAiming = true;
}
}

void AMurphysLawCharacter::StopAiming()
{
if (HasWeaponEquipped())
{
FirstPersonCameraComponent->FieldOfView = DefaultAimFactor;
IsCharacterAiming = false;
}
}

void AMurphysLawCharacter::MoveForward(float Value)


{
if (Value != 0.0f && CanPlayerMove())
{
// If the character is going backward, he can't be running
if (Value < 0.f)
{
SetIsRunning(false);
}

// add movement in that direction


AddMovementInput(GetActorForwardVector(), Value);
}
}

void AMurphysLawCharacter::MoveRight(float Value)


{
if (Value != 0.0f && CanPlayerMove())
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AMurphysLawCharacter::Run()
{
SetIsRunning(true);
}

void AMurphysLawCharacter::StopRunning()
{
SetIsRunning(false);
}
///////////////////////////////////////////////////////////////////////////

#pragma region Health functions

// Reports current health level of the character


float AMurphysLawCharacter::GetCurrentHealthLevel() const { return CurrentHealth; }

// Reports maximum health level of the character


float AMurphysLawCharacter::GetMaxHealthLevel() const { return MaxHealth; }

// Increase the health of the character


void AMurphysLawCharacter::ReceiveHealAmount(const float HealingAmount)
{
checkf(HealingAmount >= 0, TEXT("HealingAmount needs to be higher than zero"));
CurrentHealth = FMath::Min(CurrentHealth + HealingAmount, MaxHealth);
}

#pragma endregion

// Refills ammos for the current equipped weapon


void AMurphysLawCharacter::ReceiveAmmo(const int32 NumberOfAmmo)
{
GetEquippedWeapon()->AddAmmoInInventory(NumberOfAmmo);

// If the equipped weapon is empty when picking up ammos, it auto-reloads


if (ShouldReload())
{
Reload();
}
}

// Refills ammos for the collected weapon or collects it if character didn't have it yet
void AMurphysLawCharacter::CollectWeapon(class AMurphysLawBaseWeapon* Weapon)
{
Inventory->CollectWeapon(Weapon);

// If the equipped weapon is empty when picking up the weapon and it's the same, it auto-reloads
if (HasWeaponEquipped()
&& GetEquippedWeapon()->IsOfSameType(Weapon)
&& ShouldReload())
{
Reload();
}
}

// Makes the character die


void AMurphysLawCharacter::Die()
{
Dead = true;

IMurphysLawIController* PlayerController = Cast<IMurphysLawIController>(Controller);


if (PlayerController)
{
PlayerController->OnKilled(TimeToRespawn);
}

// Disable collisions for the actor as he's dead


SetActorEnableCollision(false);

// Hide the current weapon of the player before destroying it


if (HasWeaponEquipped())
{
GetEquippedWeapon()->SetActorHiddenInGame(true);
Inventory->GetFullMeshWeapon(CurrentWeaponIndex)->SetActorHiddenInGame(true);
}
}

bool AMurphysLawCharacter::CanPlayerMove()
{
bool CanMove = true;
AMurphysLawBaseWeapon* CurrentWeapon = GetEquippedWeapon();
if (CurrentWeapon)
CanMove = !(IsCrouched && CurrentWeapon->IsReloading);

return CanMove;
}

// Makes the character live again


void AMurphysLawCharacter::Relive()
{
Dead = false;
CurrentHealth = MaxHealth;
Inventory->Reinitialize();
EquipFirstWeapon();

// Enable collisions for the actor as he's alive


SetActorEnableCollision(true);
}

// Executed when the Dead variable is replicated


void AMurphysLawCharacter::OnRep_Dead()
{
// When the character is revived by the server, we reinitialize his inventory
if (!Dead)
{
Inventory->Reinitialize();
EquipFirstWeapon();

// Show HUD
auto PlayerController = Cast<AMurphysLawPlayerController>(GetController());
if(PlayerController != nullptr)
PlayerController->ChangeHUDVisibility(ESlateVisibility::Visible);
}
}

void AMurphysLawCharacter::EquipFirstWeapon()
{
CurrentWeaponIndex = NO_WEAPON_VALUE;
// Determines the current weapon of the character
for (int32 i = 0; i < Inventory->NumberOfWeaponInInventory && CurrentWeaponIndex == NO_WEAPON_VALUE; ++i)
{
AMurphysLawBaseWeapon* Weapon = Inventory->GetWeapon(i);
if (Weapon != nullptr)
{
// The first available weapon in the inventory become the current weapon
CurrentWeaponIndex = i;
Weapon->SetActorHiddenInGame(false);
Inventory->GetFullMeshWeapon(i)->SetActorHiddenInGame(false);
}
}
}

// Reports if the character is dead


bool AMurphysLawCharacter::IsDead() const
{
return Dead;
}

#pragma region Shooting and bullet collision

void AMurphysLawCharacter::Fire()
{
// check if we have a weapon equipped
if (HasWeaponEquipped())
{
// if the weapon has been able to fire
if (GetEquippedWeapon()->Fire(this))
{
// Stop the character from running
SetIsRunning(false);

// try and play a firing animation if specified


if (FireAnimation != nullptr)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != nullptr)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

// check for bullet collisions


ComputeBulletCollisions();
}

// We reload the weapon if it is empty and we have bullets left in our inventory
if (ShouldReload())
{
Reload();
}
}
}

// Check for bullet collisions


void AMurphysLawCharacter::ComputeBulletCollisions()
{
// Only look for pawns
FCollisionObjectQueryParams CollisionObjectQueryParams;
CollisionObjectQueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_PhysicsBody);
CollisionObjectQueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_Destructible);
CollisionObjectQueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_WorldStatic);

// Remove self from query potential results since we are the first to collide with the ray
FCollisionQueryParams RayQueryParams;
RayQueryParams.AddIgnoredActor(this);

// Ray starting coordinates


const FVector CollisionRayStart = GetFirstPersonCameraComponent()->GetComponentLocation();
const FVector CollisionRayInitialDirection = GetFirstPersonCameraComponent()->GetComponentTransform().GetRotation().GetAxisX();

// Group damage of touched objects together


const float MaxFragmentDeviationRadian = FMath::DegreesToRadians(GetEquippedWeapon()->GetMaxFragmentDeviationAngle(IsCharacterAiming));

bool AtLeastOneHit = false;

// Trace lines to detect pawn


for (int32 i = 0; i < GetEquippedWeapon()->GetNumberOfEmittedFragments(); ++i)
{
// Ray ending coordinates
const FVector CollisionRayAngledDirection = FMath::VRandCone(CollisionRayInitialDirection, MaxFragmentDeviationRadian);
const FVector CollisionRayEnd = CollisionRayStart + (CollisionRayAngledDirection * GetEquippedWeapon()->GetMaxTravelDistanceOfBullet());

FHitResult CollisionResult;
bool HasHit = GetWorld()->LineTraceSingleByObjectType(CollisionResult, CollisionRayStart, CollisionRayEnd, CollisionObjectQueryParams, RayQueryParams);

if (HasHit)
{
// Simple damage amount considering the distance to the target depending on the bone hit
float DeliveredDamage = GetDeliveredDamage(CollisionResult);

FPointDamageEvent CollisionDamageEvent(DeliveredDamage, CollisionResult, CollisionRayAngledDirection, UDamageType::StaticClass());

// If the actor we hit is a hittable actor and an enemy, we have at least one hit so we'll show the Hit Marker
if (IsHittableActor(CollisionResult.GetActor()) && !MurphysLawUtils::IsInSameTeam(CollisionResult.GetActor(), this))
{
AtLeastOneHit = true;
}

if (Role == ROLE_Authority)
{
CollisionResult.GetActor()->TakeDamage(DeliveredDamage, CollisionDamageEvent, GetController(), this);
}
else
{
Server_TransferDamage(CollisionResult.GetActor(), DeliveredDamage, CollisionDamageEvent, GetController(), this);
}
}
}

// If there was at least one hit, we show the HitMarker


if (AtLeastOneHit)
{
auto MyController = Cast<AMurphysLawPlayerController>(GetController());
if (MyController != nullptr && MyController->GetHUDInstance() != nullptr)
{
MyController->GetHUDInstance()->ShowHitMarker();
}
}
}

float AMurphysLawCharacter::GetDeliveredDamage(const FHitResult& CollisionResult) const


{
float DeliveredDamage = GetEquippedWeapon()->ComputeCollisionDamage(CollisionResult.Distance);
FString BoneName = CollisionResult.BoneName.ToString();

if (BoneName == SOCKET_HEAD)
DeliveredDamage *= FACTOR_HEADSHOT;
else if (BoneName == SOCKET_SPINE)
DeliveredDamage *= FACTOR_CHESTSHOT;

return DeliveredDamage;
}

bool AMurphysLawCharacter::Server_TransferDamage_Validate(class AActor * DamagedActor, const float DamageAmount, struct FDamageEvent const & DamageEvent, class AController *
EventInstigator, class AActor * DamageCauser) { return true; }
void AMurphysLawCharacter::Server_TransferDamage_Implementation(class AActor * DamagedActor, const float DamageAmount, struct FDamageEvent const & DamageEvent, class AController *
EventInstigator, class AActor * DamageCauser)
{
DamagedActor->TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}

float AMurphysLawCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const & DamageEvent, class AController * EventInstigator, AActor * DamageCauser)
{
float ActualDamage = 0.f;

if (CurrentHealth > 0.f)


{
if (Role == ROLE_Authority)
{
ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

// If the player actually took damage


if (ActualDamage > 0.f)
{
// If the character has a HUD, we show the damages on it
auto MyController = Cast<AMurphysLawPlayerController>(GetController());
if (MyController != nullptr)
{
// We start by getting best info on the hit
FVector ImpulseDirection;
FHitResult Hit;
DamageEvent.GetBestHitInfo(this, DamageCauser, Hit, ImpulseDirection);

// We calculate the vector from the character to the damage causer


FVector2D HitVector = FVector2D(FRotationMatrix(GetControlRotation()).InverseTransformVector(-ImpulseDirection));
HitVector.Normalize();

// We compute the vector representing the ForwardVector


FVector2D StraightVector = FVector2D(1.f, 0.f);
StraightVector.Normalize();

// Finally, we calculate the angle where the hit came from


float Angle = UKismetMathLibrary::DegAcos(FVector2D::DotProduct(StraightVector, HitVector));

// The angle ranges from -180.f to 180.f


Angle = HitVector.Y < 0.f ? -Angle : Angle;

// Dispatch to the controller


MyController->ShowDamage(Angle);
}
}
}
else
{
// Let the server do it
Server_TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}
}

return ActualDamage;
}

void AMurphysLawCharacter::OnReceiveAnyDamage(float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser)
{
// If it was friendly fire, we do not damage our teammate, except if it is from an explosive
if (IsFriendlyFire(InstigatedBy) && !DamageCausedByExplosive(DamageCauser))
{
return;
}

CurrentHealth = FMath::Max(CurrentHealth - Damage, 0.f);

if (CurrentHealth <= 0.f)


{
UpdateStatsOnKill(InstigatedBy, DamageCauser);
Die();
}
}

bool AMurphysLawCharacter::Server_TakeDamage_Validate(float DamageAmount, struct FDamageEvent const & DamageEvent, class AController * EventInstigator, AActor * DamageCauser) { return
true; }
void AMurphysLawCharacter::Server_TakeDamage_Implementation(float DamageAmount, struct FDamageEvent const & DamageEvent, class AController * EventInstigator, AActor * DamageCauser)
{
TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}

// Update the statistics of players involved in the death


void AMurphysLawCharacter::UpdateStatsOnKill(AController* InstigatedBy, AActor* DamageCauser)
{
AMurphysLawGameState* GameState = GetWorld()->GetGameState<AMurphysLawGameState>();
FString DeathMessage = "";
// Try to cast the DamageCauser to DamageZone to see if the player committed suicide
auto DamageZone = Cast<AMurphysLawDamageZone>(DamageCauser);

// Does the player committed suicide?


if (InstigatedBy == GetController() || DamageCausedByDamageZone(DamageCauser))
{
InstigatedBy = GetController();
DeathMessage = FString::Printf(TEXT("%s committed suicide."), *GetHumanReadableName());
if (GetPlayerState())
GetPlayerState()->IncrementNbDeaths();
if (GameState)
GameState->PlayerCommitedSuicide(GetPlayerState()->GetTeam() == AMurphysLawGameMode::TEAM_A);
}
else if (IsFriendlyFire(InstigatedBy) && DamageCausedByExplosive(DamageCauser))
{
// Or was he killed by a teammate because of explosion?
DeathMessage = FString::Printf(TEXT("%s was killed by a teammate."), *GetHumanReadableName());
if (GetPlayerState())
GetPlayerState()->IncrementNbDeaths();

if (GameState)
GameState->PlayerKilledTeammate(GetPlayerState()->GetTeam() == AMurphysLawGameMode::TEAM_A);
}
else
{
// If the player was killed by somebody else
if (InstigatedBy)
{
DeathMessage = FString::Printf(TEXT("%s was killed by %s"), *GetHumanReadableName(), *InstigatedBy->GetHumanReadableName());

// Increment the number of kills of the other player


auto OtherPlayerState = Cast<AMurphysLawPlayerState>(InstigatedBy->PlayerState);
if (OtherPlayerState)
OtherPlayerState->IncrementNbKills();

// And increment the number of deaths of the current player


if (GetPlayerState())
GetPlayerState()->IncrementNbDeaths();

if (GameState)
{
if (GetPlayerState())
{
GameState->PlayerWasKilled(OtherPlayerState->GetTeam() == AMurphysLawGameMode::TEAM_A);
}
else
ShowError("PlayerState is null");
}
else
ShowError("GameState is null");
}
}

AMurphysLawGameMode* GameMode = Cast<AMurphysLawGameMode>(GetWorld()->GetAuthGameMode());


if (GameMode && DeathMessage != "")
GameMode->SendDeathMessage(Cast<AMurphysLawPlayerController>(InstigatedBy), DeathMessage);
}

// Returns true if the other actor is an explosive barrel, otherwise false


bool AMurphysLawCharacter::DamageCausedByExplosive(class AActor* OtherActor) const
{
return Cast<AMurphysLawExplosiveBarrel>(OtherActor) != nullptr;
}

// Returns true if the other actor is an damage zone, false otherwise


bool AMurphysLawCharacter::DamageCausedByDamageZone(class AActor* OtherActor) const
{
return Cast<AMurphysLawDamageZone>(OtherActor) != nullptr;
}

// Returns true if the controller inflicting the damage is in the same team, false otherwise
bool AMurphysLawCharacter::IsFriendlyFire(class AController* OtherController) const
{
// If the controller of the actor that hit me is a player controller
auto OtherPlayerController = Cast<AMurphysLawPlayerController>(OtherController);
if (OtherPlayerController && OtherPlayerController != GetController())
{
// And if we both have a player state
auto OtherPlayerState = Cast<AMurphysLawPlayerState>(OtherPlayerController->PlayerState);
if (OtherPlayerState && GetPlayerState())
{
// We check if we are on the same team
return OtherPlayerState->GetTeam() == GetPlayerState()->GetTeam();
}
}

return false;
}

#pragma endregion

// Called when the player press the Reload key


void AMurphysLawCharacter::Reload()
{
// Checks if we have a weapon equipped
if (HasWeaponEquipped())
{
// If the weapon has been able to reload
if (GetEquippedWeapon()->Reload())
{
SetIsRunning(false);
}
}
}

// Reports the reference to the current weapon of the character


AMurphysLawBaseWeapon* AMurphysLawCharacter::GetEquippedWeapon() const
{
return Inventory->GetWeapon(CurrentWeaponIndex);
}

// Reports if the character has a weapon equipped


bool AMurphysLawCharacter::HasWeaponEquipped() const
{
return CurrentWeaponIndex != NO_WEAPON_VALUE && GetEquippedWeapon() != nullptr;
}

// Called when the player press a key to change his character's weapon
void AMurphysLawCharacter::EquipWeapon(int32 Index)
{
// Checks if the index passed in parameter is within the boundaries of our Inventory
if (Index >= Inventory->NumberOfWeaponInInventory) return;

// Don't try to re-equip the weapon we already have in hand


if (Index == CurrentWeaponIndex) return;
// Allows the character to change weapon even if the current weapon is reloading
GetEquippedWeapon()->IsReloading = false;

// Plays a sound when switching weapon if available


if (SwitchingWeaponSound != nullptr)
{
UGameplayStatics::PlaySoundAtLocation(this, SwitchingWeaponSound, GetActorLocation());
}

// If the server modifies the value of the property, it is sent to the clients automatically
if (Role == ROLE_Authority)
{
Server_ChangeCurrentWeapon_Implementation(Index);
}
else
{
// If a client wants to modify the value, it has to send a request to the server
Server_ChangeCurrentWeapon(Index);
}
}

// Replicates the current weapon index


bool AMurphysLawCharacter::Server_ChangeCurrentWeapon_Validate(int32 Index) { return true; }
void AMurphysLawCharacter::Server_ChangeCurrentWeapon_Implementation(int32 Index)
{
int32 OldIndex = CurrentWeaponIndex;
CurrentWeaponIndex = Index;
OnRep_CurrentWeaponIndex(OldIndex);
}

// Executed when CurrentWeaponIndex is replicated


void AMurphysLawCharacter::OnRep_CurrentWeaponIndex(int32 OldIndex)
{
auto OldWeapon = Inventory->GetWeapon(OldIndex);
auto OldFullMeshWeapon = Inventory->GetFullMeshWeapon(OldIndex);
auto NewFullMeshWeapon = Inventory->GetFullMeshWeapon(CurrentWeaponIndex);

// Hide the old weapons


if (OldWeapon != nullptr) OldWeapon->SetActorHiddenInGame(true);
if (OldFullMeshWeapon != nullptr) OldFullMeshWeapon->SetActorHiddenInGame(true);

// And show the new ones


if (HasWeaponEquipped()) GetEquippedWeapon()->SetActorHiddenInGame(false);
if (NewFullMeshWeapon != nullptr) NewFullMeshWeapon->SetActorHiddenInGame(false);
}

void AMurphysLawCharacter::ToggleCrouch()
{
// If the character is not crouched
if (CanCrouch())
{
IsCrouched = true;
Crouch();

// Stop the player from running


SetIsRunning(false);
}
else
{
IsCrouched = false;
UnCrouch();
}
}
// Gets the player state casted to MurphysLawPlayerState
AMurphysLawPlayerState* AMurphysLawCharacter::GetPlayerState() const
{
return Cast<AMurphysLawPlayerState>(PlayerState);
}

// Returns true if the weapon hold by the character should be reloaded, false otherwise
bool AMurphysLawCharacter::ShouldReload() const
{
return HasWeaponEquipped()
&& !GetEquippedWeapon()->IsReloading
&& GetEquippedWeapon()->GetNumberOfAmmoLeftInMagazine() <= 0
&& GetEquippedWeapon()->GetNumberOfAmmoLeftInInventory() > 0;
}

void AMurphysLawCharacter::SetMeshTeamColorTint(const MurphysLawTeamColor& TeamColor)


{
TeamBodyMeshColor = TeamColor.GetPrimary();
TeamMaskMeshColor = TeamColor.GetDarker();

if (Role == ROLE_Authority)
{
ValidTeamBodyMeshColor = true;
ValidTeamMaskMeshColor = true;
ApplyMeshTeamColor();
}
}

void AMurphysLawCharacter::OnRep_TeamBodyMeshColor()
{
ValidTeamBodyMeshColor = true;
ApplyMeshTeamColor();
}

void AMurphysLawCharacter::OnRep_TeamMaskMeshColor()
{
ValidTeamMaskMeshColor = true;
ApplyMeshTeamColor();
}

void AMurphysLawCharacter::ApplyMeshTeamColor()
{
// Put color on meshes
if (ValidTeamBodyMeshColor && ValidTeamMaskMeshColor)
{
/* Get references to mesh material to be able to color them. */
UMaterialInstanceDynamic* ShaderMeshBody = GetMesh()->CreateDynamicMaterialInstance(0);
UMaterialInstanceDynamic* ShaderMeshArms = GetMesh1P()->CreateDynamicMaterialInstance(0);
checkf(ShaderMeshBody != nullptr && ShaderMeshArms != nullptr, TEXT("Unable to find first material on character"));

ShaderMeshArms->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_CLOTHES, TeamBodyMeshColor);
ShaderMeshArms->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_MASK, TeamMaskMeshColor);
ShaderMeshBody->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_CLOTHES, TeamBodyMeshColor);
ShaderMeshBody->SetVectorParameterValue(MATERIAL_PARAM_TEAM_COLOR_MASK, TeamMaskMeshColor);
}
}

// Called when the character jumps


void AMurphysLawCharacter::Jump()
{
// Only jump when the character has enough stamina or isn't already in the air
if (GetCurrentStaminaLevel() - JumpStaminaDecayAmount <= 0.f
|| IsInAir()) return;

// Stop the character from running first


SetIsRunning(false);

Super::Jump();

UpdateStaminaLevel(-JumpStaminaDecayAmount);
}

// Changes the IsRunning state


void AMurphysLawCharacter::SetIsRunning(bool NewValue)
{
IsRunning = NewValue;

// If the character starts running, we change its speed


if (NewValue)
{
if (!CanCrouch()) UnCrouch();
GetCharacterMovement()->MaxWalkSpeed = RUN_SPEED;
}
else
{
GetCharacterMovement()->MaxWalkSpeed = WALK_SPEED;
}
}

// Reports the current stamina level


float AMurphysLawCharacter::GetCurrentStaminaLevel() const { return CurrentStamina; }

// Reports the maximum stamina level


float AMurphysLawCharacter::GetMaxStaminaLevel() const { return MaxStamina; }

// Function to update the character's stamina


void AMurphysLawCharacter::UpdateStaminaLevel(float StaminaChange)
{
// Change the stamina level itself
CurrentStamina += StaminaChange;

// Make sure the value stays between 0 and its maximum value
CurrentStamina = UKismetMathLibrary::FClamp(GetCurrentStaminaLevel(), 0.f, GetMaxStaminaLevel());
}

// Determines if an actor is an hittable actor


bool AMurphysLawCharacter::IsHittableActor(AActor* OtherActor)
{
return OtherActor != nullptr
&& (OtherActor->IsA<AMurphysLawCharacter>()
|| OtherActor->IsA<AMurphysLawExplosiveBarrel>());
}

// Called from TeleportTo() when teleport succeeds


void AMurphysLawCharacter::TeleportSucceeded(bool bIsATest)
{
Super::TeleportSucceeded(bIsATest);

// To make sure all the weapons are teleported aswell as the character
Inventory->AttachAllWeaponsToOwner();
}
EXAMPLE #3

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "FP_FirstPerson.h"
#include "FP_FirstPersonCharacter.h"
#include "Animation/AnimInstance.h"

static FName WeaponFireTraceIdent = FName(TEXT("WeaponTrace"));


#define COLLISION_WEAPON ECC_GameTraceChannel1

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AFP_FirstPersonCharacter

AFP_FirstPersonCharacter::AFP_FirstPersonCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

WeaponRange = 5000.0f;
WeaponDamage = 500000.0f;

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

//////////////////////////////////////////////////////////////////////////
// Input

void AFP_FirstPersonCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

// Bind jump events


InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

InputComponent->BindAction("Fire", IE_Pressed, this, &AFP_FirstPersonCharacter::OnFire);


TryEnableTouchscreenMovement(InputComponent);

// Bind movement events


InputComponent->BindAxis("MoveForward", this, &AFP_FirstPersonCharacter::MoveForward);
InputComponent->BindAxis("MoveRight", this, &AFP_FirstPersonCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AFP_FirstPersonCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AFP_FirstPersonCharacter::LookUpAtRate);
}

void AFP_FirstPersonCharacter::OnFire()
{
// Play a sound if there is one
if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if(FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if(AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

// Now send a trace from the end of our gun to see if we should hit anything
APlayerController* PlayerController = Cast<APlayerController>(GetController());

// Calculate the direction of fire and the start location for trace
FVector CamLoc;
FRotator CamRot;
PlayerController->GetPlayerViewPoint(CamLoc, CamRot);
const FVector ShootDir = CamRot.Vector();

FVector StartTrace = FVector::ZeroVector;


if (PlayerController)
{
FRotator UnusedRot;
PlayerController->GetPlayerViewPoint(StartTrace, UnusedRot);

// Adjust trace so there is nothing blocking the ray between the camera and the pawn, and calculate distance from adjusted start
StartTrace = StartTrace + ShootDir * ((GetActorLocation() - StartTrace) | ShootDir);
}

// Calculate endpoint of trace


const FVector EndTrace = StartTrace + ShootDir * WeaponRange;
// Check for impact
const FHitResult Impact = WeaponTrace(StartTrace, EndTrace);

// Deal with impact


AActor* DamagedActor = Impact.GetActor();
UPrimitiveComponent* DamagedComponent = Impact.GetComponent();

// If we hit an actor, with a component that is simulating physics, apply an impulse


if ((DamagedActor != NULL) && (DamagedActor != this) && (DamagedComponent != NULL) && DamagedComponent->IsSimulatingPhysics())
{
DamagedComponent->AddImpulseAtLocation(ShootDir*WeaponDamage, Impact.Location);
}
}

void AFP_FirstPersonCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// If touch is already pressed check the index. If it is not the same as the current touch assume a second touch and thus we want to fire
if (TouchItem.bIsPressed == true)
{
if( TouchItem.FingerIndex != FingerIndex)
{
OnFire();
}
}
else
{
// Cache the finger index and touch location and flag we are processing a touch
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}
}

void AFP_FirstPersonCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// If we didnt record the start event do nothing, or this is a different index
if((TouchItem.bIsPressed == false) || ( TouchItem.FingerIndex != FingerIndex) )
{
return;
}

// If the index matches the start index and we didn't process any movement we assume we want to fire
if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
{
OnFire();
}

// Flag we are no longer processing the touch event


TouchItem.bIsPressed = false;
}

void AFP_FirstPersonCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// If we are processing a touch event and this index matches the initial touch event process movement
if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}

void AFP_FirstPersonCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// Add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AFP_FirstPersonCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// Add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AFP_FirstPersonCharacter::TurnAtRate(float Rate)


{
// Calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AFP_FirstPersonCharacter::LookUpAtRate(float Rate)


{
// Calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

FHitResult AFP_FirstPersonCharacter::WeaponTrace(const FVector& StartTrace, const FVector& EndTrace) const


{
// Perform trace to retrieve hit info
FCollisionQueryParams TraceParams(WeaponFireTraceIdent, true, Instigator);
TraceParams.bTraceAsyncScene = true;
TraceParams.bReturnPhysicalMaterial = true;

FHitResult Hit(ForceInit);
GetWorld()->LineTraceSingleByChannel(Hit, StartTrace, EndTrace, COLLISION_WEAPON, TraceParams);

return Hit;
}

void AFP_FirstPersonCharacter::TryEnableTouchscreenMovement(UInputComponent* InputComponent)


{
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AFP_FirstPersonCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AFP_FirstPersonCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AFP_FirstPersonCharacter::TouchUpdate);
}

EXAMPLE #4

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

#include "ShaderPluginDemo.h"
#include "ShaderPluginDemoCharacter.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "../Public/HighResScreenshot.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AShaderPluginDemoCharacter

AShaderPluginDemoCharacter::AShaderPluginDemoCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(-39.56f, 1.75f, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

static ConstructorHelpers::FObjectFinder<UTextureRenderTarget2D> TextureFinderL(


TEXT("TextureRenderTarget2D'/Game/FirstPerson/Textures/TargetL.TargetL'"));
RenderTargetL = TextureFinderL.Object;

static ConstructorHelpers::FObjectFinder<UTextureRenderTarget2D> TextureFinderR(


TEXT("TextureRenderTarget2D'/Game/FirstPerson/Textures/TargetR.TargetR'"));
RenderTargetR = TextureFinderR.Object;

/*RenderTargetL = CreateDefaultSubobject<UTextureRenderTarget2D>(TEXT("RenderTarget_L"));
RenderTargetL->InitAutoFormat(1024, 1024);
RenderTargetL->AddressX = TA_Wrap;
RenderTargetL->AddressY = TA_Wrap;*/

/*RenderTargetR = CreateDefaultSubobject<UTextureRenderTarget2D>(TEXT("RenderTarget_R"));
RenderTargetR->InitAutoFormat(1024, 1024);
RenderTargetR->AddressX = TA_Wrap;
RenderTargetR->AddressY = TA_Wrap;*/
StereoCameraComponentL = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("StereoCameraComponent_L"));
//StereoCameraComponentL->FOVAngle = 90.f;
StereoCameraComponentL->AttachParent = GetCapsuleComponent();
StereoCameraComponentL->RelativeLocation = FVector(0.f, 0.f, 70.f); // Position the camera
StereoCameraComponentL->TextureTarget = RenderTargetL;
StereoCameraComponentL->bCaptureEveryFrame = false;

StereoCameraComponentR = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("StereoCameraComponent_R"));
StereoCameraComponentR->AttachParent = GetCapsuleComponent();
StereoCameraComponentR->RelativeLocation = FVector(0.f, 30.f, 70.f); // Position the camera
StereoCameraComponentR->TextureTarget = RenderTargetR;
StereoCameraComponentR->bCaptureEveryFrame = false;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
Mesh1P->RelativeRotation = FRotator(1.9f, -19.19f, 5.2f);
Mesh1P->RelativeLocation = FVector(-0.5f, -4.4f, -155.7f);

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
//FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
FP_MuzzleLocation->AttachTo(FP_Gun);
FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)

//ShaderPluginDemo variables
EndColorBuildup = 0;
EndColorBuildupDirection = 1;
PixelShaderTopLeftColor = FColor::Green;
ComputeShaderSimulationSpeed = 1.0;
ComputeShaderBlend = 0.5f;
ComputeShaderBlendScalar = 0;
TotalElapsedTime = 0;
}

//Since we need the featurelevel, we need to create the shaders from beginplay, and not in the ctor.
void AShaderPluginDemoCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true); //Attach gun mesh component to Skeleton, doing it here because the skelton is
not yet created in the constructor

PixelShading = new FPixelShaderUsageExample(PixelShaderTopLeftColor, GetWorld()->Scene->GetFeatureLevel());


ComputeShading = new FComputeShaderUsageExample(ComputeShaderSimulationSpeed, 1024, 1024, GetWorld()->Scene->GetFeatureLevel());
}
//Do not forget cleanup :)
void AShaderPluginDemoCharacter::BeginDestroy()
{
Super::BeginDestroy();

if (PixelShading)
{
delete PixelShading;
}
if (ComputeShading)
{
delete ComputeShading;
}
}

//Saving functions
void AShaderPluginDemoCharacter::SavePixelShaderOutput()
{
PixelShading->Save();
}
void AShaderPluginDemoCharacter::SaveComputeShaderOutput()
{
ComputeShading->Save();
}

void AShaderPluginDemoCharacter::ModifyComputeShaderBlend(float NewScalar)


{
ComputeShaderBlendScalar = NewScalar;
}

void AShaderPluginDemoCharacter::Tick(float DeltaSeconds)


{
Super::Tick(DeltaSeconds);

TotalElapsedTime += DeltaSeconds;

if (PixelShading)
{
EndColorBuildup = FMath::Clamp(EndColorBuildup + DeltaSeconds * EndColorBuildupDirection, 0.0f, 1.0f);
if (EndColorBuildup >= 1.0 || EndColorBuildup <= 0)
{
EndColorBuildupDirection *= -1;
}

FTexture2DRHIRef InputTexture = NULL;

if (ComputeShading)
{
ComputeShading->ExecuteComputeShader(TotalElapsedTime);
InputTexture = ComputeShading->GetTexture(); //This is the output texture from the compute shader that we will pass to the pixel shader.
}

ComputeShaderBlend = FMath::Clamp(ComputeShaderBlend + ComputeShaderBlendScalar * DeltaSeconds, 0.0f, 1.0f);


PixelShading->ExecutePixelShader(RenderTarget, InputTexture, FColor(EndColorBuildup * 255, 0, 0, 255), ComputeShaderBlend);
}

//save screenshot to disk


SaveRenderTargetToDisk(RenderTargetL, "screenshotL1.png",false);
SaveRenderTargetToDisk(RenderTargetR, "screenshotR1.png",false);
}

void AShaderPluginDemoCharacter::SaveRenderTargetToDisk(UTextureRenderTarget2D* InRenderTarget, FString Filename, bool debug)


{
FTextureRenderTargetResource* RTResource = InRenderTarget->GameThread_GetRenderTargetResource();

FReadSurfaceDataFlags ReadPixelFlags(RCM_UNorm);
ReadPixelFlags.SetLinearToGamma(true);
TArray<FColor> OutBMP;
RTResource->ReadPixels(OutBMP, ReadPixelFlags);

FIntRect SourceRect;

FIntPoint DestSize(InRenderTarget->GetSurfaceWidth(), InRenderTarget->GetSurfaceHeight());

FString ResultPath;
FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig();
if (HighResScreenshotConfig.SaveImage(FPaths::GameSavedDir() + TEXT("Screenshots/") + Filename, OutBMP, DestSize, &ResultPath) && debug) {
UE_LOG(LogFPChar, Warning, TEXT("Screenshot saved to: %s"), *ResultPath);
}

void AShaderPluginDemoCharacter::OnFire()
{
//Try to set a texture to the object we hit!
FHitResult HitResult;
FVector StartLocation = FirstPersonCameraComponent->GetComponentLocation();
FRotator Direction = FirstPersonCameraComponent->GetComponentRotation();
FVector EndLocation = StartLocation + Direction.Vector() * 10000;
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(this);

if (GetWorld()->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_Visibility, QueryParams))


{
TArray<UStaticMeshComponent*> StaticMeshComponents = TArray<UStaticMeshComponent*>();
AActor* HitActor = HitResult.GetActor();

if (NULL != HitActor)
{
HitActor->GetComponents<UStaticMeshComponent>(StaticMeshComponents);
for (int32 i = 0; i < StaticMeshComponents.Num(); i++)
{
UStaticMeshComponent* CurrentStaticMeshPtr = StaticMeshComponents[i];
CurrentStaticMeshPtr->SetMaterial(0, MaterialToApplyToClickedObject);
UMaterialInstanceDynamic* MID = CurrentStaticMeshPtr->CreateAndSetMaterialInstanceDynamic(0);
UTexture* CastedRenderTarget = Cast<UTexture>(RenderTarget);
MID->SetTextureParameterValue("InputTexture", CastedRenderTarget);
}
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}
}

void AShaderPluginDemoCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

//ShaderPluginDemo Specific input mappings


InputComponent->BindAction("SavePixelShaderOutput", IE_Pressed, this, &AShaderPluginDemoCharacter::SavePixelShaderOutput);
InputComponent->BindAction("SaveComputeShaderOutput", IE_Pressed, this, &AShaderPluginDemoCharacter::SaveComputeShaderOutput);
InputComponent->BindAxis("ComputeShaderBlend", this, &AShaderPluginDemoCharacter::ModifyComputeShaderBlend);
//ShaderPluginDemo Specific input mappings

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AShaderPluginDemoCharacter::TouchStarted);


if (EnableTouchscreenMovement(InputComponent) == false)
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AShaderPluginDemoCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &AShaderPluginDemoCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AShaderPluginDemoCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AShaderPluginDemoCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AShaderPluginDemoCharacter::LookUpAtRate);
}

void AShaderPluginDemoCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == true)
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AShaderPluginDemoCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
{
OnFire();
}
TouchItem.bIsPressed = false;
}
void AShaderPluginDemoCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)
{
if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
if (FMath::Abs(ScaledDelta.X) >= 4.0 / ScreenSize.X)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (FMath::Abs(ScaledDelta.Y) >= 4.0 / ScreenSize.Y)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y * BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AShaderPluginDemoCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AShaderPluginDemoCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AShaderPluginDemoCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AShaderPluginDemoCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
bool AShaderPluginDemoCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)
{
bool bResult = false;
if (FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch)
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AShaderPluginDemoCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AShaderPluginDemoCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AShaderPluginDemoCharacter::TouchUpdate);
}
return bResult;
}

EXAMPLE #5

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

#include "Totem.h"
#include "TotemCharacter.h"
#include "TotemProjectile.h"
#include "Animation/AnimInstance.h"
#include "Engine.h"
#include "Cyclone.h"
#include "QuakeAttack.h"
#include "UnrealNetwork.h"
#include "TotemPlayerState.h"
#include "TotemPlayerController.h"
#include "TotemGameMode.h"
#include "TerrainManager.h"
#include "TotemGameState.h"

//////////////////////////////////////////////////////////////////////////
// ATotemCharacter

ATotemCharacter::ATotemCharacter(const FObjectInitializer& ObjectInitializer)


: Super(ObjectInitializer), BaseTurnRate(45.0f), BaseLookUpRate(45.0f), PlayerHealth(100.0f), ProjectileDamage(10.0f), TimeBetweenShots(0.5f), ShotPressed(false),
ShotFired(false), TimeSinceAttack(0.0f), CanRapidFire(false), MoveSpeedScale(1.0f), StrafeSpeedScale(1.0f), PreviousHit(), bInvincible(false),
CurrentAbility(nullptr), bCanMove(true), AttachCyclone(nullptr), NumAbilities(0), bFirstSpawn(true), bBeingKnockedBack(false), TimeRestoreFromKnockedBack(3.0f),
TimeSinceKnockedBack(-1.0f), DamageScale(1.0f), bAcceptMoveInput(true), DistanceFromPlayer(0.0f), FirstTick(true), ThirdPersonAbilityCamDistance(550.0f),
ThirdPersonDeathCamDistance(700.0f), bClickedThisFrame(false),
bDeathCam(false), bDeathEffectFired(false), DeathCamAngle(20), InstigKillTimer(5.0f), TimeSinceHit(0.0f)
{
Instigator = this; //seem instigator automagically be himself, but for safe, I assign again

// Set size for collision capsule


GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

//Create CameraBoom for Third person Camera


CameraBoom = ObjectInitializer.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("CameraBoom"));
CameraBoom->AttachParent = GetCapsuleComponent();
CameraBoom->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
CameraBoom->TargetArmLength = 0;
CameraBoom->bUsePawnControlRotation = true;
// Create a CameraComponent
FirstPersonCameraComponent = ObjectInitializer.CreateDefaultSubobject<UCameraComponent>(this, TEXT("FirstPersonCamera"));
//FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->AttachParent = CameraBoom;
FirstPersonCameraComponent->RelativeLocation = FVector(20, 0, 0); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a CameraComponent
PlayerBody = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("PlayerBody"));
PlayerBody->AttachParent = GetCapsuleComponent();
PlayerBody->RelativeLocation = FVector(0, 0, 0); // Position the body

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true); // only the owning player will see this mesh
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->RelativeLocation = FVector(0.f, 0.f, -150.f);
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

//bReplicates = true;

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)

//Allow the actor to ( anywhere


PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = true;
PrimaryActorTick.bAllowTickOnDedicatedServer = true;
SetActorTickEnabled(true);

Metrics::CreateInstance();

//Death state
bIsDead = false;
TotalDeadBodyDuration = 5.0f;
CurrentDeadBodyDuration = -1.0f;

Team = ETeam::NO_TEAM;
bCanBasicAttack = false;
bCanUseAbility = false;
bAcceptMoveInput = false;
bReborn = true;
TotalRebornDuration = 0.1f;

bAcceptCameraChange = true;

bThirdPersonCamLerp = false;
bThirdPersonDeathCamLerp = false;

bRightButtonDownThisFrame = false;

TotemCharacterMoveState = ETotemCharacterMoveState::NoMove;

NormalWalkSpeed = 600.0f; //Just a random number


SprintSpeed = 1200.0f;
DeathDissolvePlayAfterTimer = 0.733f;
}

void ATotemCharacter::BeginPlay()
{
Super::BeginPlay();
NormalWalkSpeed = GetCharacterMovement()->MaxWalkSpeed;
}

void ATotemCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const


{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);//very important! Make sure not ignore parent stuff about replication
DOREPLIFETIME(ATotemCharacter, PlayerHealth);
DOREPLIFETIME(ATotemCharacter, bCanMove);
DOREPLIFETIME(ATotemCharacter, bBeingKnockedBack);
DOREPLIFETIME(ATotemCharacter, bIsDead);
DOREPLIFETIME(ATotemCharacter, TotalDeadBodyDuration);
DOREPLIFETIME(ATotemCharacter, Team);
DOREPLIFETIME(ATotemCharacter, bCanBasicAttack);
DOREPLIFETIME(ATotemCharacter, bCanUseAbility);
DOREPLIFETIME(ATotemCharacter, bReborn);
DOREPLIFETIME(ATotemCharacter, bThirdPersonCamLerp);
DOREPLIFETIME(ATotemCharacter, bThirdPersonDeathCamLerp);
DOREPLIFETIME(ATotemCharacter, bAcceptCameraChange);
DOREPLIFETIME(ATotemCharacter, TotemCharacterMoveState);
DOREPLIFETIME(ATotemCharacter, bDeathEffectFired);
DOREPLIFETIME(ATotemCharacter, DamageScale);
}

void ATotemCharacter::ServerSpawnProjectile_Implementation(const FVector& SpawnLocation, const FRotator& SpawnRotation, AActor* TheOwner, APawn* TheInstigator)
{
//if (GEngine)
//{
// GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Should appear only on server!"));
//}
UWorld* const World = GetWorld();
if (World != NULL)
{
//to add owner and instigator information
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = TheOwner;
SpawnParams.Instigator = TheInstigator;
if (ProjectileClass)
{
FVector Offset = RootComponent->GetComponentRotation().Vector();
Offset.Z = 0.0f;
Offset.Normalize();
Offset *= DistanceFromPlayer;
World->SpawnActor<ATotemProjectile>(ProjectileClass, SpawnLocation + Offset, SpawnRotation, SpawnParams);
ATotemPlayerState *state = nullptr;
if (Cast<ATotemPlayerController>(Controller))
{
state = Cast<ATotemPlayerState>(Cast<ATotemPlayerController>(Controller)->PlayerState);
}

if (state)
{
#if WRITE_METRICS
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
std::string attack;
if (Cast<AEarthShaman>(this))
{
attack = "Rock fist";
}
else if (Cast<AWindShaman>(this))
{
attack = "Sonic bolt";
}
else
{
attack = "Nothing?";
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, attack);

#endif
}
}
else if (FireProjectileClass)
{
AFireProjectile* shotProjectile = World->SpawnActor<AFireProjectile>(FireProjectileClass, SpawnLocation, SpawnRotation, SpawnParams);
}
}
}

bool ATotemCharacter::ServerSpawnProjectile_Validate(const FVector& SpawnLocation, const FRotator& SpawnRotation, AActor* TheOwner, APawn* TheInstigator)
{
return true;
}

//////////////////////////////////////////////////////////////////////////
// Input

void ATotemCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

//InputComponent->BindAction("Jump", IE_Pressed, this, &ATotemCharacter::Jump);


//InputComponent->BindAction("Jump", IE_Released, this, &ATotemCharacter::StopJumping);

//AEarthShaman* shaman = Cast<AEarthShaman>(this);


//if (shaman)
//{
// InputComponent->BindAction("Fire", IE_Pressed, shaman, &AEarthShaman::OnShotDown);
// InputComponent->BindAction("Fire", IE_Released, shaman, &AEarthShaman::OnShotUp);
//}
//else
//{
// InputComponent->BindAction("Fire", IE_Pressed, this, &ATotemCharacter::OnShotDown);
// InputComponent->BindAction("Fire", IE_Released, this, &ATotemCharacter::OnShotUp);
//}
//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ATotemCharacter::TouchStarted);

//InputComponent->BindAction("Ability", IE_Pressed, this, &ATotemCharacter::RightDown);


//InputComponent->BindAction("Ability", IE_Released, this, &ATotemCharacter::RightUp);
//InputComponent->BindAction("ScrollUp", IE_Pressed, this, &ATotemCharacter::ScrollUp);
//InputComponent->BindAction("ScrollDown", IE_Pressed, this, &ATotemCharacter::ScrollDown);
InputComponent->BindAction("ToggleDeathCam", IE_Pressed, this, &ATotemCharacter::ToggleDebugDeathCam);

//InputComponent->BindAxis("MoveForward", this, &ATotemCharacter::MoveForward);


//InputComponent->BindAxis("MoveRight", this, &ATotemCharacter::MoveRight);

//// We have 2 versions of the rotation bindings to handle different kinds of devices differently
//// "turn" handles devices that provide an absolute delta, such as a mouse.
//// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
//InputComponent->BindAxis("Turn", this, &ATotemCharacter::AddControllerYawInput);
//InputComponent->BindAxis("TurnRate", this, &ATotemCharacter::TurnAtRate);
//InputComponent->BindAxis("LookUp", this, &ATotemCharacter::AddControllerPitchInput);
//InputComponent->BindAxis("LookUpRate", this, &ATotemCharacter::LookUpAtRate);

void ATotemCharacter::ToggleDebugDeathCam()
{
bDeathCam = !bDeathCam;

if (bDeathCam)
{
bThirdPersonCamLerp = true;
ThirdPersonCamDistance = ThirdPersonDeathCamDistance;
}
else
{
bThirdPersonCamLerp = false;
ThirdPersonCamDistance = 0.0f;
}
}

void ATotemCharacter::ReduceHealth(float damage, ATotemPlayerController* player, AbilityType type, FName abilityName)


{
if (PlayerHealth > 0 && HasAuthority() && !bInvincible)
{
PlayerHealth -= damage * DamageScale;

ATotemCharacter* character = nullptr;

if (type != AbilityType::NONE && type != AbilityType::End && player)


{
character = Cast<ATotemCharacter>(player->AcknowledgedPawn);
PreviousHit.controller = player;
PreviousHit.name = abilityName;
PreviousHit.type = type;
TimeSinceHit = InstigKillTimer;
}
else if (PlayerHealth <= 0)
{
#if WRITE_METRICS
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Died in lava"), std::string(), std::string(),
std::string("Probably"));
state->NumOfDie += 1;
}
#endif
}

if (PlayerHealth <= 0)
{
PlayerHealth = 0;
if (character)
{
#if WRITE_METRICS
if (type != AbilityType::NONE && type != AbilityType::End)
{
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
ATotemPlayerState* attackerState = Cast<ATotemPlayerState>(player->PlayerState);
if (state && attackerState)
{
std::string team;
std::string attackerTeam;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
switch (attackerState->Team)
{
case ETeam::RED_TEAM:
attackerTeam = "Red";
break;
case ETeam::BLUE_TEAM:
attackerTeam = "Blue";
break;
default:
attackerTeam = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(attackerState->MyName))), attackerTeam,
std::string("Killed"), std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Using: " + std::string(TCHAR_TO_UTF8(*(abilityName.ToString())))));
// Player statistics part
state->NumOfDie += 1;
// Prevent count friendly kill
if (team != attackerTeam)
{
attackerState->NumOfKill += 1;
}
}
}
}
#endif
ATotemPlayerController* controller = Cast<ATotemPlayerController>(GetController());
if (controller && controller != player)
{
character->DealtDamageTo(nullptr, controller, player, true);
}
}
else if (PreviousHit.controller)
{
////Tell controller to count the kill
//ATotemCharacter* character = Cast<ATotemCharacter>(PreviousHit.controller->GetPawn());
//if (character)
//{
// ATotemPlayerController* controller = Cast<ATotemPlayerController>(GetController());
// if (controller)
// {
// character->DealtDamageTo(nullptr, controller, PreviousHit.controller, true);
// }
//}
//else
//{
// //TODO: update score on the controller if previous hit player died in the meantime
//}
DeathToLava(PreviousHit.controller, PreviousHit.type);
// Add kill and dead statistics, hopefully it will work
ATotemPlayerState* AttackerState = Cast<ATotemPlayerState>(PreviousHit.controller->PlayerState);

if (Controller)
{
ATotemPlayerController* VictimController = Cast<ATotemPlayerController>(Controller);
if (VictimController)
{
ATotemPlayerState* VictimState = Cast<ATotemPlayerState>(Controller->PlayerState);
if (VictimState)
{
VictimState->NumOfDie += 1;
if (AttackerState)
{
if (AttackerState->Team != VictimState->Team)
{
AttackerState += 1;
}
}
}
}
}

#if WRITE_METRICS
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
ATotemPlayerState* attackerState = Cast<ATotemPlayerState>(PreviousHit.controller->PlayerState);
if (state && attackerState)
{
std::string team;
std::string attackerTeam;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
switch (attackerState->Team)
{
case ETeam::RED_TEAM:
attackerTeam = "Red";
break;
case ETeam::BLUE_TEAM:
attackerTeam = "Blue";
break;
default:
attackerTeam = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(attackerState->MyName))), attackerTeam, std::string("Killed"),
std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Using: " + std::string(TCHAR_TO_UTF8(*(abilityName.ToString())))));
}
}
#endif
}
KillPlayer();
}
else if (character)
{
ATotemPlayerController* controller = Cast<ATotemPlayerController>(GetController());
if (controller)
{
character->DealtDamageTo(nullptr, controller, player, false);
}
}
}

if (!bInvincible )
{
OnDamageTaken(player);

DamageTakenBy(type, abilityName);
}
}

void ATotemCharacter::Tick(float DeltaTime)


{
Super::Tick(DeltaTime);

if (TimeSinceHit > 0.0f)


{
TimeSinceHit -= DeltaTime;
}
else
{
PreviousHit.controller = nullptr;
PreviousHit.type = AbilityType::NONE;
PreviousHit.name = "";
}

if (bThirdPersonDeathCamLerp)
{
if (Controller)
{
Controller->SetControlRotation(FRotator(-1.0f * DeathCamAngle, 0, 0));
}
}
if (bThirdPersonCamLerp || bThirdPersonDeathCamLerp )
{
CameraBoom->TargetArmLength = FMath::Lerp<float>(CameraBoom->TargetArmLength, ThirdPersonCamDistance, DeltaTime*4.0f);
}
else
{
CameraBoom->TargetArmLength = FMath::Lerp<float>(CameraBoom->TargetArmLength, 0, DeltaTime*4.0f);
}

if (bIsDead && (ThirdPersonCamDistance!= ThirdPersonDeathCamDistance))


{
ThirdPersonCamDistance = ThirdPersonDeathCamDistance;
}

if ( ((FMath::Abs(CameraBoom->TargetArmLength - ThirdPersonCamDistance) < 0.5f) && bIsDead && bThirdPersonDeathCamLerp))


{
ATotemPlayerController *PC = Cast<ATotemPlayerController>(Controller);
if (PC)
{
if (HasAuthority())
{
PC->HandleDead(FVector(0, 0, 0), FRotator(0, 0, 0));
}
bThirdPersonDeathCamLerp = false;
}
}

if (bDeathEffectFired && RayCastOnGround() )


{
FireMaterialEffect();
bDeathEffectFired = false;
}

if ((CameraBoom->TargetArmLength > 0.5f) && (!bIsShamanVisible))


{
if (GEngine->GetGamePlayer(GetWorld(), 0)->PlayerController == Controller)
{
MakeSelfVisible();
bIsShamanVisible = true;
}
}

if ((CameraBoom->TargetArmLength <= 0.5f) && (bIsShamanVisible))


{
if (GEngine->GetGamePlayer(GetWorld(), 0)->PlayerController == Controller)
{
MakeSelfInvisible();
bIsShamanVisible = false;
}
}

if (Controller && FirstTick)


{
if (GEngine->GetGamePlayer(GetWorld(), 0)->PlayerController == Controller)
{
MakeSelfInvisible();
bIsShamanVisible = false;
}
FirstTick = false;
}

ATotemPlayerState *TotemPlayerState = Cast<ATotemPlayerState>(PlayerState);


if (TotemPlayerState && bFirstSpawn)
{
SetActorLocation(TotemPlayerState->StartingLocation);
SetActorRotation(TotemPlayerState->StartingRotation);
bFirstSpawn = false;
}

if (bReborn && HasAuthority())


{
TotalRebornDuration -= DeltaTime;
if (TotalRebornDuration < 0.0f)
{
bReborn = false;
ExitReborn();
}
}

if (HasAuthority())
{
if (bBeingKnockedBack)
{
TimeSinceKnockedBack -= DeltaTime;
if (TimeSinceKnockedBack < 0.0f)
{
bBeingKnockedBack = false;
}
}
#if (WRITE_METRICS > 1)
if (DeltaTime >= .025)
{
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if(control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
if(state)
{
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Frame rate hit"),
std::string(std::to_string(DeltaTime) + "ms"), std::string(std::to_string(1.0f/DeltaTime) + "fps"));
}
}
}
#endif
}

//Keep track of how long has passed since the last attack
TimeSinceAttack += DeltaTime;
ShotPressedDuration += DeltaTime;

//If shot is pressed and enough time has been pressed


if (ShotPressed && TimeSinceAttack >= TimeBetweenShots)
{
//And you either can rapid fire or no shot has been fired since the shot button was pressed
if (CanRapidFire || !ShotFired)
{
OnFire();
ShotFired = true;
TimeSinceAttack = 0.0f;
}
}
else if (!ShotPressed)
{
ShotPressedDuration = 0;
}

//If get hit by cyclone, then the shaman cannnot move


if (HasAuthority())
{
TArray<AActor* > OverlappedActors;
TArray<ACyclone* > OverlappedCyclones;
GetOverlappingActors(OverlappedActors);
for (int32 i = 0; i < OverlappedActors.Num(); i++)
{
ACyclone* Cyclone = Cast<ACyclone>(OverlappedActors[i]);
if (Cyclone && !Cyclone->IsPendingKill())
{
//overlap myself doesn't count
if (Cyclone->GetOwner() != GetController())
{
OverlappedCyclones.Add(Cyclone);
}
}
}

if (OverlappedCyclones.Num() == 0)
{
//Do this to prevent bCanMove be replictated every frame
if (!bCanMove)
{
bCanMove = true;
}
AttachCyclone = nullptr;
}
else if (OverlappedCyclones.Num() > 0)
{
if (bCanMove)
{
bCanMove = false;
}
if (AttachCyclone)
{
FVector CycloneVelocity = AttachCyclone->ProjectileMovement->Velocity;
ATotemPlayerController* own = Cast<ATotemPlayerController>(AttachCyclone->GetOwner());
if (own)
{
ReduceHealth(AttachCyclone->Damage * DeltaTime, own, AbilityType::WIND, FName(TEXT("Cyclone"))); //every second reduce Damage
amount of health
}
LaunchCharacter(CycloneVelocity, true, true);
}
//CharacterMovement->Velocity = CycloneVelocity;
}
}

//temporary solution, maybe later we need more fancy stuff.


if (bIsDead && HasAuthority())
{
CurrentDeadBodyDuration -= DeltaTime;
if (CurrentDeadBodyDuration <= 0)
{
Destroy();
}
}

if (bClickedThisFrame)
{
bClickedThisFrame = false;
if (Controller)
{
APlayerController* control = Cast<APlayerController>(Controller);
if (control)
{
if (!control->IsInputKeyDown(EKeys::LeftMouseButton))
{
LeftUp();
}
}
}
}

if (bRightButtonDownThisFrame)
{
bRightButtonDownThisFrame = false;
if (Controller)
{
APlayerController* control = Cast<APlayerController>(Controller);
if (control)
{
if (!control->IsInputKeyDown(EKeys::RightMouseButton))
{
RightUp();
}
}
}
}

//CheckShiftKey();

//This function will be only execute on server side


void ATotemCharacter::KillPlayer()
{
UWorld* const World = GetWorld();
if (World)
{
ATotemGameState* TotemGameState = Cast<ATotemGameState>(World->GetGameState());
if (PlayerHealth <= 0 && HasAuthority() && TotemGameState->CurrentState == ETotemGameState::Playing)
{
//ATotemPlayerController *PC = Cast<ATotemPlayerController>(Controller);
//if (PC)
//{
// // later delete this
// //FVector loc = FirstPersonCameraComponent->GetComponentLocation();
// //FRotator rot = FirstPersonCameraComponent->GetComponentRotation();
// FVector loc;
// FRotator rot;
// //PC->HandleDead(loc, rot);

// bThirdPersonCamLerp = true;
// //PC->HandleDead(loc, rot)
//}
bThirdPersonDeathCamLerp = true;
ThirdPersonCamDistance = ThirdPersonDeathCamDistance;
ATotemPlayerController *PC = Cast<ATotemPlayerController>(Controller);

if (PC)
{

PC->DisableInput(PC);
}

CurrentDeadBodyDuration = TotalDeadBodyDuration;
bIsDead = true;
EnterDeath();
GetWorld()->GetTimerManager().SetTimer(mDeathDissolveEffectTimerHandle, this, &ATotemCharacter::CallBlueprintEventForMaterial,
DeathDissolvePlayAfterTimer, false);
//don't destory pawn himself, will cause weird
//Problem. But I don't know why...
//Destroy();
}

}
}

void ATotemCharacter::OnFire()
{
//only server can spawn projectile theoretically
// try and fire a projectile
if (ProjectileClass != NULL || FireProjectileClass != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle pos ition
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World != NULL)
{
//to add owner and instigator information
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = Cast<ATotemPlayerController>(Controller);
SpawnParams.Instigator = Instigator;
//When the server function is called by serve, it also execute.
ServerSpawnProjectile(SpawnLocation, SpawnRotation, SpawnParams.Owner, SpawnParams.Instigator);
// spawn the projectile at the muzzle single player version
//World->SpawnActor<ATotemProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, SpawnParams);
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

}
void ATotemCharacter::OnShotDown()
{
ShotPressed = true;
ShotFired = false;
ShotDownMetrics();
}

void ATotemCharacter::ShotDownMetrics_Implementation()
{
#if WRITE_METRICS

ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);


if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
if (state)
{
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
std::string attack;
if (Cast<AFireShaman>(this))
{
attack = "Flamethrower start";
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, attack);
}
}
}
#endif
}

bool ATotemCharacter::ShotDownMetrics_Validate()
{
return true;
}

void ATotemCharacter::OnShotUp()
{
ShotPressed = false;
ShotUpMetrics();
}

void ATotemCharacter::ShotUpMetrics_Implementation()
{
#if WRITE_METRICS
if (Cast<AFireShaman>(this))
{
ATotemPlayerController* control = Cast<ATotemPlayerController>(Controller);
if (control)
{
ATotemPlayerState* state = Cast<ATotemPlayerState>(control->PlayerState);
if (state)
{
std::string team;
switch (state->Team)
{
case ETeam::RED_TEAM:
team = "Red";
break;
case ETeam::BLUE_TEAM:
team = "Blue";
break;
default:
team = "None";
break;
}
Metrics::WriteToFile(std::string(TCHAR_TO_UTF8(*(state->MyName))), team, std::string("Flamethrower end"));
}
}
}
#endif
}

bool ATotemCharacter::ShotUpMetrics_Validate()
{
return true;
}

void ATotemCharacter::TouchStarted(const ETouchIndex::Type FingerIndex, const FVector Location)


{
// only fire for first finger down
if (FingerIndex == 0)
{
OnFire();
}
}

void ATotemCharacter::MoveForward(float Value)


{
if (Value != 0.0f && bCanMove && bAcceptMoveInput)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value * MoveSpeedScale);
}
}

void ATotemCharacter::MoveRight(float Value)


{
if (Value != 0.0f && bCanMove && bAcceptMoveInput)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value * StrafeSpeedScale);
}
}

void ATotemCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void ATotemCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

void ATotemCharacter::SetCurrentAbility(class BaseAbility* ability)


{
CurrentAbility = ability;
}

void ATotemCharacter::RightUp()
{

void ATotemCharacter::RightDown()
{
bRightButtonDownThisFrame = true;
}

void ATotemCharacter::ScrollUp()
{
if (CurrentAbility != nullptr) CurrentAbility->ScrollUp();
}

void ATotemCharacter::ScrollDown()
{
if (CurrentAbility != nullptr) CurrentAbility->ScrollDown();
}

int ATotemCharacter::CurrentAbilityIndex()
{
if (CurrentAbility != nullptr)
{
return CurrentAbility->GetIndex();
}
else
{
return -1;
}
}

void ATotemCharacter::AddAbility(BaseAbility* newAbility)


{
newAbility->SetIndex(NumAbilities);
if (CurrentAbility == nullptr)
{
CurrentAbility = newAbility;
CurrentAbility->SetNextAbility(CurrentAbility);
CurrentAbility->SetPreviousAbility(CurrentAbility);
++NumAbilities;
}
else
{
newAbility->SetNextAbility(CurrentAbility);
newAbility->SetPreviousAbility(CurrentAbility->GetPreviousAbility());
CurrentAbility->GetPreviousAbility()->SetNextAbility(newAbility);
CurrentAbility->SetPreviousAbility(newAbility);
++NumAbilities;
}
}

void ATotemCharacter::BeginDestroy()
{
while (NumAbilities > 0)
{
BaseAbility* DestroyAbility = CurrentAbility;
CurrentAbility = CurrentAbility->GetNextAbility();
delete DestroyAbility;
--NumAbilities;
}
Super::BeginDestroy();
}

void ATotemCharacter::Jump()
{
//ActInAir();
Super::Jump();
}

//This only called on the server.


void ATotemCharacter::PossessedBy(AController * NewController)
{
Super::PossessedBy(NewController);
ATotemPlayerState* TotemPlayerState = Cast<ATotemPlayerState>(PlayerState);
if (TotemPlayerState)
{
StartingLocation = TotemPlayerState->StartingLocation;
StartingRotation = TotemPlayerState->StartingRotation;
ATotemPlayerState* TotemPlayerState = Cast<ATotemPlayerState>(PlayerState);
if (TotemPlayerState)
{
TotemPlayerState->SetCurrentState(ETotemPlayState::Playing);
}
Team = TotemPlayerState->Team;
}
}

void ATotemCharacter::SetAbility(int32 index)


{
int count = 0;
if (index < NumAbilities && !CurrentAbility->isRightClickDown())
{
while (index != CurrentAbilityIndex())
{
ScrollUp();
count++;
if (count > NumAbilities)
{
break;
}
}
}
}

float ATotemCharacter::CooldownLeftOnAbility(int32 AbilityIndex)


{
if (AbilityIndex >= NumAbilities)
{
return -1.0f;
}

BaseAbility* Ability = CurrentAbility;

while (AbilityIndex != Ability->GetIndex())


{
Ability = Ability->GetNextAbility();
}

return Ability->CooldownLeft();
}

float ATotemCharacter::CooldownPercentRemaining(int32 AbilityIndex)


{
if (AbilityIndex >= NumAbilities)
{
return -1.0f;
}

BaseAbility* Ability = CurrentAbility;

while (AbilityIndex != Ability->GetIndex())


{
Ability = Ability->GetNextAbility();
}

return (float)Ability->CooldownLeft() / (float)Ability->GetCooldown();


}

float ATotemCharacter::AbilityCooldown(int32 AbilityIndex)


{
if (AbilityIndex >= NumAbilities)
{
return -1.0f;
}

BaseAbility* Ability = CurrentAbility;

while (AbilityIndex != Ability->GetIndex())


{
Ability = Ability->GetNextAbility();
}

return Ability->GetCooldown();
}

void ATotemCharacter::ClientPlaySound_Implementation(USoundBase* Sound, float distToPlay, float VolumeMultiplier, float PitchMultiplier, float StartTime, USoundAttenuation*
AttenuationSettings)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("ClientPlayExplosionSound is triggered!"));
}
for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
if ((*Iterator)->IsLocalPlayerController())
{
ATotemCharacter* MyCharacter = Cast<ATotemCharacter>((*Iterator)->GetPawn());
if (MyCharacter)
{
//FVector use local variable store actor location
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Local player is found!"));
}
if ((GetActorLocation() - MyCharacter->GetActorLocation()).Size() <= distToPlay)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Dist check succeed!"));
}
UGameplayStatics::PlaySoundAtLocation(this, Sound, GetActorLocation(), VolumeMultiplier, PitchMultiplier, StartTime,
AttenuationSettings);
}

}
break;
}
}
}
void ATotemCharacter::ShrineUnlocked(FString name)
{
#if WRITE_METRICS
Metrics::WriteToFile(std::string(), std::string(), std::string(TCHAR_TO_UTF8(*name) + std::string(" shrine unlocked")));
#endif
}

void ATotemCharacter::ShrineCaptured(FString name, FString team)


{
#if WRITE_METRICS
Metrics::WriteToFile(std::string(), TCHAR_TO_UTF8(*team), std::string(std::string("captured ") + TCHAR_TO_UTF8(*name) + std::string(" shrine")));
#endif
}

//
void ATotemCharacter::EnterDeath()
{
ClientSetIfAcceptMoveInput(false);
bCanBasicAttack = false;
bCanUseAbility = false;
bAcceptCameraChange = false;
GetCharacterMovement()->Velocity = FVector(0.0f);
}

void ATotemCharacter::ExitReborn()
{
ClientSetIfAcceptMoveInput(true);
bCanBasicAttack = true;
bCanUseAbility = true;
}

void ATotemCharacter::ClientSetIfAcceptMoveInput_Implementation(bool flag)


{
bAcceptMoveInput = flag;
}

//
//void ATotemCharacter::ActInAir_Implementation()
//{
//
//}
//
//bool ATotemCharacter::ActInAir_Validate()
//{
// return true;
//}

void ATotemCharacter::DestroyTerrain(FVector location, float radius, UWorld* world)


{

TActorIterator<ATotemCharacter> characterIterator(world);

if (characterIterator && (*characterIterator))


{
(*characterIterator)->ClientDestroyTerrain(location, radius);
}
}

void ATotemCharacter::ClientDestroyTerrain_Implementation(FVector location, float radius)


{
//UE_LOG(LogTemp, Warning, TEXT("ClientDestroyTerrain_Implementation (FireBall): %s, radius : %f"), *location.ToString(), radius);

for (TActorIterator<ATerrainManager> ActorItr(GetWorld()); ActorItr; ++ActorItr)


{
ATerrainManager * voxelVolume = (ATerrainManager*)(*ActorItr);

voxelVolume->EditVolume(location, radius, false);


//voxelVolume->ExecAddMesh(SpawnLocation, 10.0f);
}
}

void ATotemCharacter::AddControllerYawInput(float Val)


{
if (bAcceptCameraChange)
{
Super::AddControllerYawInput(Val);
}

void ATotemCharacter::AddControllerPitchInput(float Val)


{
if (bAcceptCameraChange)
{
Super::AddControllerPitchInput(Val);
}
}

void ATotemCharacter::ToggleSpectator()
{
if (Controller)
{
ATotemPlayerController* TotemPlayerController = Cast<ATotemPlayerController>(Controller);
if (TotemPlayerController)
{
TotemPlayerController->ServerSetMyShaman(EPlayerType::SPECTATE_SHAMAN);
ATotemPlayerState* TotemPlayerState = Cast<ATotemPlayerState>(TotemPlayerController->PlayerState);
if (TotemPlayerState)
{
TotemPlayerState->bDisplayUI = false;
}
}
}
}

void ATotemCharacter::LeftDown()
{
bClickedThisFrame = true;
}

void ATotemCharacter::LeftUp()
{

bool ATotemCharacter::Invincible()
{
return bInvincible;
}

void ATotemCharacter::SetInvincible(bool invince)


{
bInvincible = invince;
bCanBasicAttack = !invince;
bCanUseAbility = !invince;
}
void ATotemCharacter::SetTotemCharacterMoveState(ETotemCharacterMoveState NewState)
{
TotemCharacterMoveExit(TotemCharacterMoveState);
TotemCharacterMoveState = NewState;
ServerSyncTotemCharacterMoveState(NewState);
TotemCharacterMoveEnter(NewState);
}

void ATotemCharacter::ServerSyncTotemCharacterMoveState_Implementation(ETotemCharacterMoveState NewState)


{
TotemCharacterMoveState = NewState;
//OnRep_TotemCharacterMoveState();
ClientsSwitchSpeed(NewState);
}

bool ATotemCharacter::ServerSyncTotemCharacterMoveState_Validate(ETotemCharacterMoveState NewState)


{
return true;
}

void ATotemCharacter::ClientsSwitchSpeed_Implementation(ETotemCharacterMoveState NewState)


{
switch (TotemCharacterMoveState)
{
case ETotemCharacterMoveState::NoMove:
{
GetCharacterMovement()->MaxWalkSpeed = NormalWalkSpeed;
break;
}
case ETotemCharacterMoveState::Sprint:
{
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
break;
}
}
}

void ATotemCharacter::OnRep_TotemCharacterMoveState()
{
// change speed here GetCharacterMovement()->MaxWalkSpeed = NormalMaxSpeed;
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Unreal API Shit!"));
}
switch (TotemCharacterMoveState)
{
case ETotemCharacterMoveState::NoMove:
{
GetCharacterMovement()->MaxWalkSpeed = NormalWalkSpeed;
break;
}
case ETotemCharacterMoveState::Sprint:
{
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
break;
}
}

void ATotemCharacter::TotemCharacterMoveEnter(ETotemCharacterMoveState NewState)


{
switch (NewState)
{
case ETotemCharacterMoveState::NoMove:
{
#if PRINT_LOG
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("No Sprint!"));
}
break;
#endif
}
case ETotemCharacterMoveState::Sprint:
{
#if PRINT_LOG
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Sprint!"));
}
break;
#endif
}
}
}

void ATotemCharacter::TotemCharacterMoveExit(ETotemCharacterMoveState OldState)


{
switch (OldState)
{
case ETotemCharacterMoveState::NoMove:
break;
case ETotemCharacterMoveState::Sprint:
break;
}
}

void ATotemCharacter::CheckShiftKey()
{
//For Sprint, designed only execute on owning client
if (Controller)
{
if (Controller->IsLocalPlayerController())
{
APlayerController* PlayerController = Cast<APlayerController>(Controller);
if (PlayerController)
{
if (PlayerController->IsInputKeyDown(EKeys::LeftShift))
{
// if no move state && on the ground
if (TotemCharacterMoveState == ETotemCharacterMoveState::NoMove && GetCharacterMovement()->IsWalking())
{
SetTotemCharacterMoveState(ETotemCharacterMoveState::Sprint);
}
}
else
{
if (TotemCharacterMoveState == ETotemCharacterMoveState::Sprint)
{
SetTotemCharacterMoveState(ETotemCharacterMoveState::NoMove);
}
}
}
}
}
}
bool ATotemCharacter::RayCastOnGround()
{
UWorld *world = GetWorld();

FVector StartTrace = GetActorLocation();


FVector EndTrace = StartTrace - FVector(0, 0, 150);

if (world)
{
FHitResult Hit(ForceInit);
FCollisionQueryParams collisionParams(FName(TEXT("EARTH_SHAMAN_TRACE")), true, this);
world->LineTraceSingle(Hit, StartTrace, EndTrace, ECC_WorldStatic, collisionParams);

if (Hit.bBlockingHit && Hit.GetActor())


{
//DrawDebugLine(world, StartTrace, Hit.ImpactPoint, FColor::Green, false, 5.0f);
return true;
}
}
//DrawDebugLine(world, StartTrace, EndTrace, FColor::Red, false, 5.0f);

return false;
}

void ATotemCharacter::CallBlueprintEventForMaterial()
{

bDeathEffectFired = true;
}

EXAMPLE #6

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

#include "GameWeapons.h"
#include "States/GWWeaponState.h"
#include "Tracing/GWTraceBase.h"
#include "Tracing/GWTraceRangedWeapon.h"

#include "Ammo/GWAmmo.h"

#include "IGISkeletalMesh.h"

#include "Net/UnrealNetwork.h"

#include "GWWeaponRanged.h"

AGWWeaponRanged::AGWWeaponRanged(const FObjectInitializer& ObjectInitializer)


: Super(ObjectInitializer)
{
bReplicates = true;

PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = false;
PrimaryActorTick.bAllowTickOnDedicatedServer = true;
PrimaryActorTick.bRunOnAnyThread = true;
PrimaryActorTick.TickGroup = ETickingGroup::TG_DuringPhysics;
SetTickGroup(ETickingGroup::TG_DuringPhysics);

bIsWeaponFiring = false;

ReloadEndCount = 0;
}
void AGWWeaponRanged::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
if (bTraceEveryTick && bIsWeaponFiring && TargetingMethod)
{
TargetingMethod->SingleLineTraceSetHitLocation();
if (Role < ROLE_Authority || GetNetMode() == ENetMode::NM_Standalone)
OnWeaponFiring(HitInfo.Origin, HitInfo.HitLocation);
}
}
void AGWWeaponRanged::BeginPlay()
{
CurrentState = ActiveState;
RemaningAmmo = MaximumAmmo;
RemainingMagazineAmmo = MagazineSize;
CurrentSpread = BaseSpread;
CurrentHorizontalRecoil = RecoilConfig.HorizontalRecoilBase;
CurrentVerticalRecoil = RecoilConfig.VerticalRecoilBase;

if (TargetingMethod)
{
TargetingMethod->SetRange(Range);
TargetingMethod->SetCurrentSpread(CurrentSpread);
TargetingMethod->Initialize();
}

if (TypeOfAmmo)
{
CurrentAmmo = NewObject<UGWAmmo>(TypeOfAmmo);
}
}
void AGWWeaponRanged::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME_CONDITION(AGWWeaponRanged, RemainingMagazineAmmo, COND_OwnerOnly);


DOREPLIFETIME_CONDITION(AGWWeaponRanged, RemaningAmmo, COND_OwnerOnly);
DOREPLIFETIME(AGWWeaponRanged, ReloadEndCount);
DOREPLIFETIME(AGWWeaponRanged, ReloadBeginCount);

void AGWWeaponRanged::InitializeWeapon()
{
Super::InitializeWeapon();
}
void AGWWeaponRanged::InputPressed()
{
BeginFire();
}
void AGWWeaponRanged::InputReleased()
{
EndFire();
}

void AGWWeaponRanged::InputReload()
{
BeginReload();
}
void AGWWeaponRanged::FireWeapon(const FHitResult& TargetIn, float DamageIn, APawn* InstigatorIn)
{
if (CurrentAmmo)
{
CurrentAmmo->ApplyDamage(TargetIn, DamageIn, InstigatorIn);
}
}
void AGWWeaponRanged::ShootWeapon()
{
if (!CheckIfHaveAmmo())
{
CurrentState->EndActionSequence();
return;
}
SubtractAmmo();
CalculateCurrentWeaponSpread();

//if (Role == ROLE_Authority)


//{
if (TargetingMethod)
TargetingMethod->Execute();

OnShoot();
//}
if (GetNetMode() == ENetMode::NM_Standalone || Role < ROLE_Authority)
OnRep_HitInfo();
}
void AGWWeaponRanged::ActionEnd()
{

//activate states
void AGWWeaponRanged::BeginFire()
{
//if (Role < ROLE_Authority)
//{
// if (!CheckIfHaveAmmo())
// return;

// ServerBeginFire();
// bIsWeaponFiring = true;
// //start shooting also on onwing client, to provide better feedback.
// CurrentState->BeginActionSequence();
// //OnRep_HitInfo();

// GetWorldTimerManager().ClearTimer(ReduceSpreadOverTimeTimerHandle);
// //if (TargetingMethod)
// // TargetingMethod->Execute();
//}
//else
//{
if (!CheckIfHaveAmmo())
{
CurrentState->EndActionSequence();
return;
}
GetWorldTimerManager().ClearTimer(ReduceSpreadOverTimeTimerHandle);
bIsWeaponFiring = true;
CurrentState->BeginActionSequence();
//}
}
void AGWWeaponRanged::ServerBeginFire_Implementation()
{
BeginFire();
}
bool AGWWeaponRanged::ServerBeginFire_Validate()
{
return true;
}

void AGWWeaponRanged::EndFire()
{
//if (Role < ROLE_Authority)
//{
// bIsWeaponFiring = false;
// CurrentState->EndActionSequence();

// GetWorldTimerManager().SetTimer(ReduceSpreadOverTimeTimerHandle, this, &AGWWeaponRanged::ReduceSpreadOverTime, 0.1, true);


// ServerEndFire();
//}
//else
//{
bIsWeaponFiring = false;
GetWorldTimerManager().SetTimer(ReduceSpreadOverTimeTimerHandle, this, &AGWWeaponRanged::ReduceSpreadOverTime, 0.1, true);
CurrentState->EndActionSequence();
OnFireEnd();
//}
}
void AGWWeaponRanged::ServerEndFire_Implementation()
{
EndFire();
}
bool AGWWeaponRanged::ServerEndFire_Validate()
{
return true;
}

void AGWWeaponRanged::BeginReload()
{
if (Role < ROLE_Authority)
{
OnRep_ReloadBeign();
ServerBeginReload();
}
else
{
ReloadBeginCount++;
GotoState(ReloadState);
if (GetNetMode() == ENetMode::NM_Standalone)
OnRep_ReloadBeign();
}
}
void AGWWeaponRanged::ServerBeginReload_Implementation()
{
BeginReload();
}
bool AGWWeaponRanged::ServerBeginReload_Validate()
{
return true;
}

void AGWWeaponRanged::CalculateReloadAmmo()
{
float NeededAmmo = MagazineSize - RemainingMagazineAmmo;
if (NeededAmmo <= RemainingMagazineAmmo)
return;
RemaningAmmo -= NeededAmmo;
RemainingMagazineAmmo += NeededAmmo;
}

void AGWWeaponRanged::EndReload()
{
if (Role < ROLE_Authority)
{
if (!CheckIfCanReload())
return;
CalculateReloadAmmo();
ServerEndReload();
}
else
{
/*
Impelemtnsome reload mechanic..
1. Where is ammo coming from ?
2. How it is setup ?
3. Does weapon have it's own magazine max ammo count, or is it stored externally ?
*/
if (!CheckIfCanReload())
return;
CalculateReloadAmmo();
ReloadEndCount++;
if (GetNetMode() == ENetMode::NM_Standalone)
OnRep_ReloadEnd();
}
}
void AGWWeaponRanged::ServerEndReload_Implementation()
{
EndReload();
}
bool AGWWeaponRanged::ServerEndReload_Validate()
{
return true;
}

void AGWWeaponRanged::OnRep_Shooting()
{
//OnWeaponFiring();
}

void AGWWeaponRanged::OnRep_ReloadBeign()
{
OnReloadBegin();
if (ReloadAnimation)
{
IIGISkeletalMesh* skelMeshInt = Cast<IIGISkeletalMesh>(Instigator);
if (!skelMeshInt)
return;

UAnimInstance* AnimInst = skelMeshInt->GetMasterSkeletalMesh()->GetAnimInstance();


if (!AnimInst)
return;
float montageLenght = ReloadAnimation->CalculateSequenceLength();

float multiplier = montageLenght / ReloadTime;

AnimInst->Montage_Play(ReloadAnimation, multiplier);
}
}

void AGWWeaponRanged::OnRep_ReloadEnd()
{
OnReloadEnd();
}

void AGWWeaponRanged::OnRep_HitInfo()
{
OnWeaponFiring(HitInfo.Origin, HitInfo.HitLocation);
}

bool AGWWeaponRanged::CheckIfHaveAmmo()
{
if (RemainingMagazineAmmo > 0)
return true;
else
return false;
}
void AGWWeaponRanged::SubtractAmmo()
{
RemainingMagazineAmmo -= AmmoCost;
}
bool AGWWeaponRanged::CheckIfCanReload()
{
if (RemainingMagazineAmmo >= MagazineSize)
return false;
else
return true;
}

void AGWWeaponRanged::CalculateCurrentWeaponSpread()
{
CurrentSpread = CurrentSpread * SpreadMultiplier;
CurrentHorizontalRecoil = CurrentHorizontalRecoil * RecoilConfig.HorizontalRecoilMultiplier;
CurrentVerticalRecoil = CurrentVerticalRecoil * RecoilConfig.VerticalRecoilMultiplier;
if (CurrentHorizontalRecoil > RecoilConfig.HorizontalRecoilMaximum)
{
CurrentHorizontalRecoil = RecoilConfig.HorizontalRecoilMaximum;
}
if (CurrentVerticalRecoil > RecoilConfig.VerticalRecoilMaximum)
{
CurrentVerticalRecoil = RecoilConfig.VerticalRecoilMaximum;
}
if (CurrentSpread > MaximumSpread)
{
CurrentSpread = MaximumSpread;
}
OnCurrentWeaponSpread.Broadcast(CurrentSpread);
if (TargetingMethod)
{
TargetingMethod->SetCurrentSpread(CurrentSpread);
}
}
void AGWWeaponRanged::ReduceSpreadOverTime()
{
CurrentSpread -= SpreadReduce;
CurrentHorizontalRecoil -= 1;
CurrentVerticalRecoil -= 1;
if (CurrentSpread <= BaseSpread)
{
CurrentSpread = BaseSpread;
GetWorldTimerManager().ClearTimer(ReduceSpreadOverTimeTimerHandle);
}
}
EXAMPLE #7

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "UDKPresentation.h"
#include "UDKPresentationCharacter.h"
#include "UDKPresentationProjectile.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AUDKPresentationCharacter

AUDKPresentationCharacter::AUDKPresentationCharacter()
{
maxJump = 1;
currentJump = 0;
maxAmmo = 0;
ammo = maxAmmo;
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Create a gun mesh component


FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

void AUDKPresentationCharacter::UpdateAmmo()
{
if (CharacterMovement->IsMovingOnGround()) {
ammo = maxAmmo;
}
}

void AUDKPresentationCharacter::MoreAmmo()
{
maxAmmo++;
}

int AUDKPresentationCharacter::GetMaxAmmo()
{
return maxAmmo;
}

int AUDKPresentationCharacter::GetAmmo()
{
if (CharacterMovement->IsMovingOnGround()) {
ammo = maxAmmo;
}
return ammo;
}

void AUDKPresentationCharacter::Jump()
{
if (CharacterMovement->IsMovingOnGround()) {
currentJump = 0;
bPressedJump = true;
JumpKeyHoldTime = 0.0f;
ammo = maxAmmo;
}
else {
DoubleJump();
}
}

void AUDKPresentationCharacter::DoubleJump() {
if (currentJump < maxJump) {
CharacterMovement->Velocity.Z = 0;
CharacterMovement->Velocity += FVector(0, 0, 500);
currentJump++;
}
}

void AUDKPresentationCharacter::StopJumping()
{
bPressedJump = false;
JumpKeyHoldTime = 0.0f;
}

//////////////////////////////////////////////////////////////////////////
// Input

void AUDKPresentationCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

InputComponent->BindAction("Jump", IE_Pressed, this, &AUDKPresentationCharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &AUDKPresentationCharacter::StopJumping);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AUDKPresentationCharacter::TouchStarted);


if( EnableTouchscreenMovement(InputComponent) == false )
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AUDKPresentationCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &AUDKPresentationCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AUDKPresentationCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AUDKPresentationCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AUDKPresentationCharacter::LookUpAtRate);
}

void AUDKPresentationCharacter::OnFire()
{
if (CharacterMovement->IsMovingOnGround()) {
ammo = maxAmmo;
}
if (ammo <= 0) return;
// try and fire a projectile
if (ProjectileClass != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle pos ition
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

ammo--;
CharacterMovement->Velocity = GetControlRotation().Vector()*(-1000) + 0.5f * CharacterMovement->Velocity;

UWorld* const World = GetWorld();


if (World != NULL)
{
// spawn the projectile at the muzzle
World->SpawnActor<AUDKPresentationProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
}
}

// try and play the sound if specified


if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if(FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if(AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

void AUDKPresentationCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if( TouchItem.bIsPressed == true )
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AUDKPresentationCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if( ( FingerIndex == TouchItem.FingerIndex ) && (TouchItem.bMoved == false) )
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void AUDKPresentationCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && ( TouchItem.FingerIndex==FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D( MoveDelta.X, MoveDelta.Y) / ScreenSize;

if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AUDKPresentationCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AUDKPresentationCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void AUDKPresentationCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AUDKPresentationCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool AUDKPresentationCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if(FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch )
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AUDKPresentationCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AUDKPresentationCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AUDKPresentationCharacter::TouchUpdate);
}
return bResult;
}

EXAMPLES #8 and #9

// Fill out your copyright notice in the Description page of Project Settings.

#include "FPSCode.h"
#include "Gun.h"
#include "Animation/AnimInstance.h"
#include "FPSCodeCharacter.h"
#include "Kismet/KismetMathLibrary.h"

AGun::AGun()
{
Mesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Mesh"));

void AGun::BeginPlay()
{
Super::BeginPlay();

CurrentAmmo = MagSize;

//If Aiming FOV is not set, set it to 65


if (!AimingFOV)
{
AimingFOV = 65;
}
}

void AGun::Tick(float Seconds)


{
Super::Tick(Seconds);

//Applying negative recoil to compensate for recoil that was added during firing (occurs after stopping firing)
if (RecoilInterp)
{
if ((UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch > 270 && InitialRecoilPitch > 270) || (UGameplayStatics::GetPlayerController(this,
0)->GetControlRotation().Pitch < 270 && InitialRecoilPitch < 270))
{
if (UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch > InitialRecoilPitch)
{
float Pitch = UKismetMathLibrary::FInterpTo_Constant(UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch,
InitialRecoilPitch, Seconds, 12);
float Yaw = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Yaw;
float Roll = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Roll;

UGameplayStatics::GetPlayerController(this, 0)->SetControlRotation(FRotator{ Pitch, Yaw, Roll });


}
}
else
{
if (UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch < InitialRecoilPitch)
{
float Pitch = UKismetMathLibrary::FInterpTo_Constant(UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch,
UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch - 90, Seconds, 12);
float Yaw = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Yaw;
float Roll = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Roll;

UGameplayStatics::GetPlayerController(this, 0)->SetControlRotation(FRotator{ Pitch, Yaw, Roll });


}
else
{
float Pitch = UKismetMathLibrary::FInterpTo_Constant(UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch,
InitialRecoilPitch, Seconds, 12);
float Yaw = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Yaw;
float Roll = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Roll;

UGameplayStatics::GetPlayerController(this, 0)->SetControlRotation(FRotator{ Pitch, Yaw, Roll });


}
}
}

void AGun::Fire()
{

void AGun::OnEquip()
{
Super::OnEquip();

if (WeaponEquipAnimation)
{
Mesh->GetAnimInstance()->Montage_Play(WeaponEquipAnimation, 1.f);
}

if (PlayerEquipAnimation)
{
UAnimInstance* AnimInstance = Cast<AFPSCodeCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0))->GetMesh1P()->GetAnimInstance();

if (AnimInstance)
{
AnimInstance->Montage_Play(PlayerEquipAnimation, 1.f);
}
}

void AGun::Attack()
{
if (CurrentAmmo > 0)
{
if (bIsAuto)
{
if (bCanAttack)
{
bCanAttack = false;

CanStopShooting = true;

GetWorld()->GetTimerManager().SetTimer(BoolTimerHandle, this, &AGun::SetCanAttack, 1 / (RateOfFire / 60)); //Setting timer for being able
to attack again (to prevent spamming attack)

GetWorld()->GetTimerManager().SetTimer(AutoTimerHandle, this, &AGun::Fire, 1 / (RateOfFire / 60), true, 0.f); //Setting timer for
refiring for automatic weapons

InitialRecoilPitch = UGameplayStatics::GetPlayerController(this, 0)->GetControlRotation().Pitch; //Setting initial pitch before


recoil is added

if (!MuzzleSocket.IsNone()) //Drawing muzzle flash


{
FVector Location = Mesh->GetSocketLocation(MuzzleSocket);
FRotator Rotation = Mesh->GetSocketRotation(MuzzleSocket);

Flash = UGameplayStatics::SpawnEmitterAttached(MuzzleFlash, Mesh, MuzzleSocket, Location, Rotation,


EAttachLocation::KeepWorldPosition);
}
}
}
else
{
if (bCanAttack)
{
bCanAttack = false;

GetWorld()->GetTimerManager().SetTimer(BoolTimerHandle, this, &AGun::SetCanAttack, 1 / (RateOfFire / 60));

Fire();
}
}
}
}

void AGun::SetCanAttack()
{
bCanAttack = true;
}

void AGun::StopFire()
{
if (bIsAuto)
{
if (CanStopShooting)
{
CanStopShooting = false;

if (GetWorld()->GetTimerManager().IsTimerActive(AutoTimerHandle)) //Clearing timers


{
GetWorld()->GetTimerManager().ClearTimer(AutoTimerHandle);
}

if (Flash)
{
Flash->DestroyComponent();
}

RecoilInterp = true;

if (GetWorld()->GetTimerManager().IsTimerActive(RecoilHandle)) //If recoil is active, disable it


{
GetWorld()->GetTimerManager().ClearTimer(RecoilHandle);
}

GetWorld()->GetTimerManager().SetTimer(RecoilHandle, this, &AGun::DisableRecoilInterp, .4f); //Set timer to disable recoil after short amount of
time
}
}
}

void AGun::Reload()
{
if (!IsReloading)
{
if (CurrentAmmo < MagSize && TotalAmmo > 0)
{
IsReloading = true;

//Playing Reload animation on weapon


if (WeaponReloadAnimation)
{
Mesh->GetAnimInstance()->Montage_Play(WeaponReloadAnimation, 1.f);
}

//Playing Reload animation montage on player


if (PlayerReloadAnimation)
{
UAnimInstance* AnimInstance = Cast<AFPSCodeCharacter>(UGameplayStatics::GetPlayerCharacter(this, 0))->GetMesh1P()->GetAnimInstance();

if (AnimInstance)
{
bCanAttack = false;

float AnimTime = AnimInstance->Montage_Play(PlayerReloadAnimation, 1.f);

FTimerHandle AnimHandle;

GetWorld()->GetTimerManager().SetTimer(AnimHandle, this, &AGun::SetAmmoValues, AnimTime); //Set timer to apply reloaded


ammo values when animation is done
}
else
{
SetAmmoValues();
}
}
else
{
SetAmmoValues();
}
}
}
}

//Setting various ammo counts based on current ones


void AGun::SetAmmoValues()
{
bCanAttack = true;
IsReloading = false;

if ((TotalAmmo + CurrentAmmo) < MagSize)


{
CurrentAmmo = CurrentAmmo + TotalAmmo;

TotalAmmo = 0;
}
else
{
TotalAmmo = TotalAmmo - (MagSize - CurrentAmmo);

CurrentAmmo = MagSize;
}
}

void AGun::DisableRecoilInterp()
{
RecoilInterp = false;

EXAMPLE #10

(see EXAMPLE # 3)
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

/*=============================================================================
Character.cpp: ACharacter implementation
=============================================================================*/

#include "EnginePrivate.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/PrimitiveComponent.h"
#include "Net/UnrealNetwork.h"
#include "DisplayDebugHelpers.h"
#include "Animation/AnimInstance.h"
#include "Animation/AnimMontage.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/DamageType.h"

DEFINE_LOG_CATEGORY_STATIC(LogCharacter, Log, All);


DEFINE_LOG_CATEGORY_STATIC(LogAvatar, Log, All);
FName ACharacter::MeshComponentName(TEXT("CharacterMesh0"));
FName ACharacter::CharacterMovementComponentName(TEXT("CharMoveComp"));
FName ACharacter::CapsuleComponentName(TEXT("CollisionCylinder"));

ACharacter::ACharacter(const FObjectInitializer& ObjectInitializer)


: Super(ObjectInitializer)
{
// Structure to hold one-time initialization
struct FConstructorStatics
{
FName ID_Characters;
FText NAME_Characters;
FConstructorStatics()
: ID_Characters(TEXT("Characters"))
, NAME_Characters(NSLOCTEXT("SpriteCategory", "Characters", "Characters"))
{
}
};
static FConstructorStatics ConstructorStatics;

// Character rotation only changes in Yaw, to prevent the capsule from changing orientation.
// Ask the Controller for the full rotation if desired (ie for aiming).
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
bUseControllerRotationYaw = true;

CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>(ACharacter::CapsuleComponentName);
CapsuleComponent->InitCapsuleSize(34.0f, 88.0f);

static FName CollisionProfileName(TEXT("Pawn"));


CapsuleComponent->SetCollisionProfileName(CollisionProfileName);

CapsuleComponent->CanCharacterStepUpOn = ECB_No;
CapsuleComponent->bShouldUpdatePhysicsVolume = true;
CapsuleComponent->bCheckAsyncSceneOnMove = false;
CapsuleComponent->bCanEverAffectNavigation = false;
CapsuleComponent->bDynamicObstacle = true;
RootComponent = CapsuleComponent;

JumpKeyHoldTime = 0.0f;
JumpMaxHoldTime = 0.0f;

#if WITH_EDITORONLY_DATA
ArrowComponent = CreateEditorOnlyDefaultSubobject<UArrowComponent>(TEXT("Arrow"));
if (ArrowComponent)
{
ArrowComponent->ArrowColor = FColor(150, 200, 255);
ArrowComponent->bTreatAsASprite = true;
ArrowComponent->SpriteInfo.Category = ConstructorStatics.ID_Characters;
ArrowComponent->SpriteInfo.DisplayName = ConstructorStatics.NAME_Characters;
ArrowComponent->AttachParent = CapsuleComponent;
ArrowComponent->bIsScreenSizeScaled = true;
}
#endif // WITH_EDITORONLY_DATA

CharacterMovement = CreateDefaultSubobject<UCharacterMovementComponent>(ACharacter::CharacterMovementComponentName);
if (CharacterMovement)
{
CharacterMovement->UpdatedComponent = CapsuleComponent;
CrouchedEyeHeight = CharacterMovement->CrouchedHalfHeight * 0.80f;
}

Mesh = CreateOptionalDefaultSubobject<USkeletalMeshComponent>(ACharacter::MeshComponentName);
if (Mesh)
{
Mesh->AlwaysLoadOnClient = true;
Mesh->AlwaysLoadOnServer = true;
Mesh->bOwnerNoSee = false;
Mesh->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::AlwaysTickPose;
Mesh->bCastDynamicShadow = true;
Mesh->bAffectDynamicIndirectLighting = true;
Mesh->PrimaryComponentTick.TickGroup = TG_PrePhysics;
Mesh->bChartDistanceFactor = true;
Mesh->AttachParent = CapsuleComponent;
static FName MeshCollisionProfileName(TEXT("CharacterMesh"));
Mesh->SetCollisionProfileName(MeshCollisionProfileName);
Mesh->bGenerateOverlapEvents = false;
Mesh->bCanEverAffectNavigation = false;
}

BaseRotationOffset = FQuat::Identity;
}

void ACharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();

if (!IsPendingKill())
{
if (Mesh)
{
BaseTranslationOffset = Mesh->RelativeLocation;
BaseRotationOffset = Mesh->RelativeRotation.Quaternion();

// force animation tick after movement component updates


if (Mesh->PrimaryComponentTick.bCanEverTick && CharacterMovement)
{
Mesh->PrimaryComponentTick.AddPrerequisite(CharacterMovement, CharacterMovement->PrimaryComponentTick);
}
}

if (CharacterMovement && CapsuleComponent)


{
CharacterMovement->UpdateNavAgent(*CapsuleComponent);
}

if (Controller == NULL && GetNetMode() != NM_Client)


{
if (CharacterMovement && CharacterMovement->bRunPhysicsWithNoController)
{
CharacterMovement->SetDefaultMovementMode();
}
}
}
}

UPawnMovementComponent* ACharacter::GetMovementComponent() const


{
return CharacterMovement;
}

void ACharacter::SetupPlayerInputComponent(UInputComponent* InputComponent)


{
check(InputComponent);
}

void ACharacter::GetSimpleCollisionCylinder(float& CollisionRadius, float& CollisionHalfHeight) const


{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (IsTemplate())
{
UE_LOG(LogCharacter, Log, TEXT("WARNING ACharacter::GetSimpleCollisionCylinder : Called on default object '%s'. Will likely return zero size. Consider using GetDefaultHalfHeight()
instead."), *this->GetPathName());
}
#endif

if (RootComponent == CapsuleComponent && IsRootComponentCollisionRegistered())


{
// Note: we purposefully ignore the component transform here aside from scale, always treating it as vertically aligned.
// This improves performance and is also how we stated the CapsuleComponent would be used.
CapsuleComponent->GetScaledCapsuleSize(CollisionRadius, CollisionHalfHeight);
}
else
{
Super::GetSimpleCollisionCylinder(CollisionRadius, CollisionHalfHeight);
}
}

void ACharacter::UpdateNavigationRelevance()
{
if (CapsuleComponent)
{
CapsuleComponent->SetCanEverAffectNavigation(bCanAffectNavigationGeneration);
}
}

void ACharacter::ApplyWorldOffset(const FVector& InOffset, bool bWorldShift)


{
Super::ApplyWorldOffset(InOffset, bWorldShift);

// Update cached location values in movement component


if (CharacterMovement)
{
CharacterMovement->OldBaseLocation+= InOffset;
}
}

float ACharacter::GetDefaultHalfHeight() const


{
UCapsuleComponent* DefaultCapsule = GetClass()->GetDefaultObject<ACharacter>()->CapsuleComponent;
if (DefaultCapsule)
{
return DefaultCapsule->GetScaledCapsuleHalfHeight();
}
else
{
return Super::GetDefaultHalfHeight();
}
}

UActorComponent* ACharacter::FindComponentByClass(const TSubclassOf<UActorComponent> ComponentClass) const


{
// If the character has a Mesh, treat it as the first 'hit' when finding components
if (Mesh && Mesh->IsA(ComponentClass))
{
return Mesh;
}

return Super::FindComponentByClass(ComponentClass);
}
void ACharacter::OnWalkingOffLedge_Implementation(const FVector& PreviousFloorImpactNormal, const FVector& PreviousFloorContactNormal, const FVector& PreviousLocation, float TimeDelta)
{
}

void ACharacter::NotifyJumpApex()
{
// Call delegate callback
if (OnReachedJumpApex.IsBound())
{
OnReachedJumpApex.Broadcast();
}
}

void ACharacter::Landed(const FHitResult& Hit)


{
OnLanded(Hit);
}

bool ACharacter::CanJump() const


{
return CanJumpInternal();
}

bool ACharacter::CanJumpInternal_Implementation() const


{
const bool bCanHoldToJumpHigher = (GetJumpMaxHoldTime() > 0.0f) && IsJumpProvidingForce();

return !bIsCrouched && CharacterMovement && (CharacterMovement->IsMovingOnGround() || bCanHoldToJumpHigher) && CharacterMovement->IsJumpAllowed() && !CharacterMovement-
>bWantsToCrouch;
}

void ACharacter::OnJumped_Implementation()
{
}

bool ACharacter::IsJumpProvidingForce() const


{
return (bPressedJump && JumpKeyHoldTime > 0.0f && JumpKeyHoldTime < GetJumpMaxHoldTime());
}

// Deprecated
bool ACharacter::DoJump( bool bReplayingMoves )
{
return CanJump() && CharacterMovement->DoJump(bReplayingMoves);
}

void ACharacter::RecalculateBaseEyeHeight()
{
if (!bIsCrouched)
{
Super::RecalculateBaseEyeHeight();
}
else
{
BaseEyeHeight = CrouchedEyeHeight;
}
}

void ACharacter::OnRep_IsCrouched()
{
if (CharacterMovement)
{
if (bIsCrouched)
{
CharacterMovement->Crouch(true);
}
else
{
CharacterMovement->UnCrouch(true);
}
}
}

void ACharacter::SetReplicateMovement(bool bInReplicateMovement)


{
Super::SetReplicateMovement(bInReplicateMovement);

if (CharacterMovement != nullptr)
{
// Set prediction data time stamp to current time to stop extrapolating
// from time bReplicateMovement was turned off to when it was turned on again
FNetworkPredictionData_Server* NetworkPrediction = CharacterMovement->GetPredictionData_Server();

if (NetworkPrediction != nullptr)
{
NetworkPrediction->ServerTimeStamp = GetWorld()->GetTimeSeconds();
}
}
}

bool ACharacter::CanCrouch()
{
return !bIsCrouched && CharacterMovement && CharacterMovement->CanEverCrouch() && GetRootComponent() && !GetRootComponent()->IsSimulatingPhysics();
}

void ACharacter::Crouch(bool bClientSimulation)


{
if (CharacterMovement)
{
if (CanCrouch())
{
CharacterMovement->bWantsToCrouch = true;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
else if (!CharacterMovement->CanEverCrouch())
{
UE_LOG(LogCharacter, Log, TEXT("%s is trying to crouch, but crouching is disabled on this character! (check CharacterMovement NavAgentSettings)"), *GetName());
}
#endif
}
}

void ACharacter::UnCrouch(bool bClientSimulation)


{
if (CharacterMovement)
{
CharacterMovement->bWantsToCrouch = false;
}
}

void ACharacter::OnEndCrouch( float HeightAdjust, float ScaledHeightAdjust )


{
RecalculateBaseEyeHeight();

if (Mesh)
{
Mesh->RelativeLocation.Z = GetDefault<ACharacter>(GetClass())->Mesh->RelativeLocation.Z;
BaseTranslationOffset.Z = Mesh->RelativeLocation.Z;
}
else
{
BaseTranslationOffset.Z = GetDefault<ACharacter>(GetClass())->BaseTranslationOffset.Z;
}

K2_OnEndCrouch(HeightAdjust, ScaledHeightAdjust);
}

void ACharacter::OnStartCrouch( float HeightAdjust, float ScaledHeightAdjust )


{
RecalculateBaseEyeHeight();

if (Mesh)
{
Mesh->RelativeLocation.Z = GetDefault<ACharacter>(GetClass())->Mesh->RelativeLocation.Z + HeightAdjust;
BaseTranslationOffset.Z = Mesh->RelativeLocation.Z;
}
else
{
BaseTranslationOffset.Z = GetDefault<ACharacter>(GetClass())->BaseTranslationOffset.Z + HeightAdjust;
}

K2_OnStartCrouch(HeightAdjust, ScaledHeightAdjust);
}

void ACharacter::ApplyDamageMomentum(float DamageTaken, FDamageEvent const& DamageEvent, APawn* PawnInstigator, AActor* DamageCauser)
{
UDamageType const* const DmgTypeCDO = DamageEvent.DamageTypeClass->GetDefaultObject<UDamageType>();
float const ImpulseScale = DmgTypeCDO->DamageImpulse;

if ( (ImpulseScale > 3.f) && (CharacterMovement != NULL) )


{
FHitResult HitInfo;
FVector ImpulseDir;
DamageEvent.GetBestHitInfo(this, PawnInstigator, HitInfo, ImpulseDir);

FVector Impulse = ImpulseDir * ImpulseScale;


bool const bMassIndependentImpulse = !DmgTypeCDO->bScaleMomentumByMass;

// limit Z momentum added if already going up faster than jump (to avoid blowing character way up into the sky)
{
FVector MassScaledImpulse = Impulse;
if(!bMassIndependentImpulse && CharacterMovement->Mass > SMALL_NUMBER)
{
MassScaledImpulse = MassScaledImpulse / CharacterMovement->Mass;
}

if ( (CharacterMovement->Velocity.Z > GetDefault<UCharacterMovementComponent>(CharacterMovement->GetClass())->JumpZVelocity) && (MassScaledImpulse.Z > 0.f) )


{
Impulse.Z *= 0.5f;
}
}

CharacterMovement->AddImpulse(Impulse, bMassIndependentImpulse);
}
}

void ACharacter::ClearCrossLevelReferences()
{
if( BasedMovement.MovementBase != NULL && GetOutermost() != BasedMovement.MovementBase->GetOutermost() )
{
SetBase( NULL );
}
Super::ClearCrossLevelReferences();
}

namespace MovementBaseUtility
{
bool IsDynamicBase(const UPrimitiveComponent* MovementBase)
{
return (MovementBase && MovementBase->Mobility == EComponentMobility::Movable);
}

void AddTickDependency(FTickFunction& BasedObjectTick, UPrimitiveComponent* NewBase)


{
if (NewBase && MovementBaseUtility::UseRelativeLocation(NewBase))
{
if (NewBase->PrimaryComponentTick.bCanEverTick)
{
BasedObjectTick.AddPrerequisite(NewBase, NewBase->PrimaryComponentTick);
}

AActor* NewBaseOwner = NewBase->GetOwner();


if (NewBaseOwner)
{
if (NewBaseOwner->PrimaryActorTick.bCanEverTick)
{
BasedObjectTick.AddPrerequisite(NewBaseOwner, NewBaseOwner->PrimaryActorTick);
}

// @TODO: We need to find a more efficient way of finding all ticking components in an actor.
for (UActorComponent* Component : NewBaseOwner->GetComponents())
{
if (Component && Component->PrimaryComponentTick.bCanEverTick)
{
BasedObjectTick.AddPrerequisite(Component, Component->PrimaryComponentTick);
}
}
}
}
}

void RemoveTickDependency(FTickFunction& BasedObjectTick, UPrimitiveComponent* OldBase)


{
if (OldBase && MovementBaseUtility::UseRelativeLocation(OldBase))
{
BasedObjectTick.RemovePrerequisite(OldBase, OldBase->PrimaryComponentTick);
AActor* OldBaseOwner = OldBase->GetOwner();
if (OldBaseOwner)
{
BasedObjectTick.RemovePrerequisite(OldBaseOwner, OldBaseOwner->PrimaryActorTick);

// @TODO: We need to find a more efficient way of finding all ticking components in an actor.
for (UActorComponent* Component : OldBaseOwner->GetComponents())
{
if (Component && Component->PrimaryComponentTick.bCanEverTick)
{
BasedObjectTick.RemovePrerequisite(Component, Component->PrimaryComponentTick);
}
}
}
}
}

FVector GetMovementBaseVelocity(const UPrimitiveComponent* MovementBase, const FName BoneName)


{
FVector BaseVelocity = FVector::ZeroVector;
if (MovementBaseUtility::IsDynamicBase(MovementBase))
{
if (BoneName != NAME_None)
{
const FBodyInstance* BodyInstance = MovementBase->GetBodyInstance(BoneName);
if (BodyInstance)
{
BaseVelocity = BodyInstance->GetUnrealWorldVelocity();
return BaseVelocity;
}
}

BaseVelocity = MovementBase->GetComponentVelocity();
if (BaseVelocity.IsZero())
{
// Fall back to actor's Root component
const AActor* Owner = MovementBase->GetOwner();
if (Owner)
{
// Component might be moved manually (not by simulated physics or a movement component), see if the root component of the actor has a velocity.
BaseVelocity = MovementBase->GetOwner()->GetVelocity();
}
}

// Fall back to physics velocity.


if (BaseVelocity.IsZero() && MovementBase->GetBodyInstance())
{
BaseVelocity = MovementBase->GetBodyInstance()->GetUnrealWorldVelocity();
}
}

return BaseVelocity;
}

FVector GetMovementBaseTangentialVelocity(const UPrimitiveComponent* MovementBase, const FName BoneName, const FVector& WorldLocation)
{
if (MovementBaseUtility::IsDynamicBase(MovementBase))
{
if (const FBodyInstance* BodyInstance = MovementBase->GetBodyInstance(BoneName))
{
const FVector BaseAngVel = BodyInstance->GetUnrealWorldAngularVelocity();
if (!BaseAngVel.IsNearlyZero())
{
FVector BaseLocation;
FQuat BaseRotation;
if (MovementBaseUtility::GetMovementBaseTransform(MovementBase, BoneName, BaseLocation, BaseRotation))
{
const FVector RadialDistanceToBase = WorldLocation - BaseLocation;
const FVector TangentialVel = FVector::DegreesToRadians(BaseAngVel) ^ RadialDistanceToBase;
return TangentialVel;
}
}
}
}

return FVector::ZeroVector;
}

bool GetMovementBaseTransform(const UPrimitiveComponent* MovementBase, const FName BoneName, FVector& OutLocation, FQuat& OutQuat)
{
if (MovementBase)
{
if (BoneName != NAME_None)
{
const USkeletalMeshComponent* SkeletalBase = Cast<USkeletalMeshComponent>(MovementBase);
if (SkeletalBase)
{
const int32 BoneIndex = SkeletalBase->GetBoneIndex(BoneName);
if (BoneIndex != INDEX_NONE)
{
const FTransform BoneTransform = SkeletalBase->GetBoneTransform(BoneIndex);
OutLocation = BoneTransform.GetLocation();
OutQuat = BoneTransform.GetRotation();
return true;
}

UE_LOG(LogCharacter, Warning, TEXT("GetMovementBaseTransform(): Invalid bone '%s' for SkeletalMeshComponent base %s"), *BoneName.ToString(),
*GetPathNameSafe(MovementBase));
return false;
}
// TODO: warn if not a skeletal mesh but providing bone index.
}

// No bone supplied
OutLocation = MovementBase->GetComponentLocation();
OutQuat = MovementBase->GetComponentQuat();
return true;
}

// NULL MovementBase
OutLocation = FVector::ZeroVector;
OutQuat = FQuat::Identity;
return false;
}
}

/** Change the Pawn's base. */


void ACharacter::SetBase( UPrimitiveComponent* NewBaseComponent, const FName InBoneName, bool bNotifyPawn )
{
// If NewBaseComponent is null, ignore bone name.
const FName BoneName = (NewBaseComponent ? InBoneName : NAME_None);

// See what changed.


const bool bBaseChanged = (NewBaseComponent != BasedMovement.MovementBase);
const bool bBoneChanged = (BoneName != BasedMovement.BoneName);

if (bBaseChanged || bBoneChanged)
{
// Verify no recursion.
APawn* Loop = (NewBaseComponent ? Cast<APawn>(NewBaseComponent->GetOwner()) : NULL);
for( ; Loop!=NULL; Loop=Cast<APawn>(Loop->GetMovementBase()) )
{
if( Loop == this )
{
UE_LOG(LogCharacter, Warning, TEXT(" SetBase failed! Recursion detected. Pawn %s already based on %s."), *GetName(), *NewBaseComponent->GetName());
return;
}
}

// Set base.
UPrimitiveComponent* OldBase = BasedMovement.MovementBase;
BasedMovement.MovementBase = NewBaseComponent;
BasedMovement.BoneName = BoneName;

if (CharacterMovement)
{
if (bBaseChanged)
{
MovementBaseUtility::RemoveTickDependency(CharacterMovement->PrimaryComponentTick, OldBase);
MovementBaseUtility::AddTickDependency(CharacterMovement->PrimaryComponentTick, NewBaseComponent);
}

if (NewBaseComponent)
{
// Update OldBaseLocation/Rotation as those were referring to a different base
// ... but not when handling replication for proxies (since they are going to copy this data from the replicated values anyway)
if (!bInBaseReplication)
{
CharacterMovement->SaveBaseLocation();
}

// Enable pre-cloth tick if we are standing on a physics object, as we need to to use post-physics transforms
CharacterMovement->PreClothComponentTick.SetTickFunctionEnable(NewBaseComponent->IsSimulatingPhysics());
}
else
{
BasedMovement.BoneName = NAME_None; // None, regardless of whether user tried to set a bone name, since we have no base component.
BasedMovement.bRelativeRotation = false;
CharacterMovement->CurrentFloor.Clear();
CharacterMovement->PreClothComponentTick.SetTickFunctionEnable(false);
}

if (Role == ROLE_Authority || Role == ROLE_AutonomousProxy)


{
BasedMovement.bServerHasBaseComponent = (BasedMovement.MovementBase != NULL); // Also set on proxies for nicer debugging.
UE_LOG(LogCharacter, Verbose, TEXT("Setting base on %s for '%s' to '%s'"), Role == ROLE_Authority ? TEXT("Server") : TEXT("AutoProxy"), *GetName(),
*GetFullNameSafe(NewBaseComponent));
}
else
{
UE_LOG(LogCharacter, Verbose, TEXT("Setting base on Client for '%s' to '%s'"), *GetName(), *GetFullNameSafe(NewBaseComponent));
}

// Notify this actor of his new floor.


if ( bNotifyPawn )
{
BaseChange();
}
}
}

void ACharacter::SaveRelativeBasedMovement(const FVector& NewRelativeLocation, const FRotator& NewRotation, bool bRelativeRotation)


{
checkSlow(BasedMovement.HasRelativeLocation());
BasedMovement.Location = NewRelativeLocation;
BasedMovement.Rotation = NewRotation;
BasedMovement.bRelativeRotation = bRelativeRotation;
}

FVector ACharacter::GetNavAgentLocation() const


{
FVector AgentLocation = FNavigationSystem::InvalidLocation;

if (GetCharacterMovement() != nullptr)
{
AgentLocation = GetCharacterMovement()->GetActorFeetLocation();
}

if (FNavigationSystem::IsValidLocation(AgentLocation) == false && CapsuleComponent != nullptr)


{
AgentLocation = GetActorLocation() - FVector(0, 0, CapsuleComponent->GetScaledCapsuleHalfHeight());
}

return AgentLocation;
}

void ACharacter::TurnOff()
{
if (CharacterMovement != NULL)
{
CharacterMovement->StopMovementImmediately();
CharacterMovement->DisableMovement();
}

if (GetNetMode() != NM_DedicatedServer && Mesh != NULL)


{
Mesh->bPauseAnims = true;
if (Mesh->IsSimulatingPhysics())
{
Mesh->bBlendPhysics = true;
Mesh->KinematicBonesUpdateType = EKinematicBonesUpdateToPhysics::SkipAllBones;
}
}

Super::TurnOff();
}

void ACharacter::Restart()
{
Super::Restart();

bPressedJump = false;
JumpKeyHoldTime = 0.0f;
ClearJumpInput();
UnCrouch(true);

if (CharacterMovement)
{
CharacterMovement->SetDefaultMovementMode();
}
}

void ACharacter::PawnClientRestart()
{
if (CharacterMovement != NULL)
{
CharacterMovement->StopMovementImmediately();
CharacterMovement->ResetPredictionData_Client();
}

Super::PawnClientRestart();
}

void ACharacter::PossessedBy(AController* NewController)


{
Super::PossessedBy(NewController);

// If we are controlled remotely, set animation timing to be driven by client's network updates. So timing and events remain in sync.
if (Mesh && (GetRemoteRole() == ROLE_AutonomousProxy && GetNetConnection() != nullptr))
{
Mesh->bAutonomousTickPose = true;
}
}

void ACharacter::UnPossessed()
{
Super::UnPossessed();

if (CharacterMovement)
{
CharacterMovement->ResetPredictionData_Client();
CharacterMovement->ResetPredictionData_Server();
}

// We're no longer controlled remotely, resume regular ticking of animations.


if (Mesh)
{
Mesh->bAutonomousTickPose = false;
}
}

void ACharacter::TornOff()
{
Super::TornOff();

if (CharacterMovement)
{
CharacterMovement->ResetPredictionData_Client();
CharacterMovement->ResetPredictionData_Server();
}

// We're no longer controlled remotely, resume regular ticking of animations.


if (Mesh)
{
Mesh->bAutonomousTickPose = false;
}
}

void ACharacter::BaseChange()
{
if (CharacterMovement && CharacterMovement->MovementMode != MOVE_None)
{
AActor* ActualMovementBase = GetMovementBaseActor(this);
if ((ActualMovementBase != NULL) && !ActualMovementBase->CanBeBaseForCharacter(this))
{
CharacterMovement->JumpOff(ActualMovementBase);
}
}
}

void ACharacter::DisplayDebug(UCanvas* Canvas, const FDebugDisplayInfo& DebugDisplay, float& YL, float& YPos)
{
Super::DisplayDebug(Canvas, DebugDisplay, YL, YPos);

float Indent = 0.f;

UFont* RenderFont = GEngine->GetSmallFont();

static FName NAME_Physics = FName(TEXT("Physics"));


if (DebugDisplay.IsDisplayOn(NAME_Physics) )
{
FIndenter PhysicsIndent(Indent);

FString BaseString;
if ( CharacterMovement == NULL || BasedMovement.MovementBase == NULL )
{
BaseString = "Not Based";
}
else
{
BaseString = BasedMovement.MovementBase->IsWorldGeometry() ? "World Geometry" : BasedMovement.MovementBase->GetName();
BaseString = FString::Printf(TEXT("Based On %s"), *BaseString);
}

YL = Canvas->DrawText(RenderFont, FString::Printf(TEXT("RelativeLoc: %s Rot: %s %s"), *BasedMovement.Location.ToString(), *BasedMovement.Rotation.ToString(), *BaseString), Indent,


YPos);
YPos += YL;

if ( CharacterMovement != NULL )
{
CharacterMovement->DisplayDebug(Canvas, DebugDisplay, YL, YPos);
}
const bool Crouched = CharacterMovement && CharacterMovement->IsCrouching();
FString T = FString::Printf(TEXT("Crouched %i"), Crouched);
YL = Canvas->DrawText(RenderFont, T, Indent, YPos );
YPos += YL;
}
}

void ACharacter::LaunchCharacter(FVector LaunchVelocity, bool bXYOverride, bool bZOverride)


{
UE_LOG(LogCharacter, Verbose, TEXT("ACharacter::LaunchCharacter '%s' (%f,%f,%f)"), *GetName(), LaunchVelocity.X, LaunchVelocity.Y, LaunchVelocity.Z);

if (CharacterMovement)
{
FVector FinalVel = LaunchVelocity;
const FVector Velocity = GetVelocity();

if (!bXYOverride)
{
FinalVel.X += Velocity.X;
FinalVel.Y += Velocity.Y;
}
if (!bZOverride)
{
FinalVel.Z += Velocity.Z;
}

CharacterMovement->Launch(FinalVel);

OnLaunched(LaunchVelocity, bXYOverride, bZOverride);


}
}

void ACharacter::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PrevCustomMode)


{
K2_OnMovementModeChanged(PrevMovementMode, CharacterMovement->MovementMode, PrevCustomMode, CharacterMovement->CustomMovementMode);
MovementModeChangedDelegate.Broadcast(this, PrevMovementMode, PrevCustomMode);
}

/** Don't process landed notification if updating client position by replaying moves.
* Allow event to be called if Pawn was initially falling (before starting to replay moves),
* and this is going to cause him to land. . */
bool ACharacter::ShouldNotifyLanded(const FHitResult& Hit)
{
if (bClientUpdating && !bClientWasFalling)
{
return false;
}

// Just in case, only allow Landed() to be called once when replaying moves.
bClientWasFalling = false;
return true;
}

void ACharacter::Jump()
{
bPressedJump = true;
JumpKeyHoldTime = 0.0f;
}

void ACharacter::StopJumping()
{
bPressedJump = false;
JumpKeyHoldTime = 0.0f;
}

void ACharacter::CheckJumpInput(float DeltaTime)


{
const bool bWasJumping = bPressedJump && JumpKeyHoldTime > 0.0f;
if (bPressedJump)
{
// Increment our timer first so calls to IsJumpProvidingForce() will return true
JumpKeyHoldTime += DeltaTime;
const bool bDidJump = CanJump() && CharacterMovement && CharacterMovement->DoJump(bClientUpdating);
if (!bWasJumping && bDidJump)
{
OnJumped();
}
}
}

void ACharacter::ClearJumpInput()
{
// Don't disable bPressedJump right away if it's still held
if (bPressedJump && (JumpKeyHoldTime >= GetJumpMaxHoldTime()))
{
bPressedJump = false;
}
}

float ACharacter::GetJumpMaxHoldTime() const


{
return JumpMaxHoldTime;
}

//
// Static variables for networking.
//
static uint8 SavedMovementMode;

void ACharacter::PreNetReceive()
{
SavedMovementMode = ReplicatedMovementMode;
Super::PreNetReceive();
}

void ACharacter::PostNetReceive()
{
if (Role == ROLE_SimulatedProxy)
{
CharacterMovement->bNetworkUpdateReceived = true;
CharacterMovement->bNetworkMovementModeChanged = (CharacterMovement->bNetworkMovementModeChanged || (SavedMovementMode != ReplicatedMovementMode));
}

Super::PostNetReceive();
}

void ACharacter::OnRep_ReplicatedBasedMovement()
{
if (Role != ROLE_SimulatedProxy)
{
return;
}

// Skip base updates while playing root motion, it is handled inside of OnRep_RootMotion
if (IsPlayingNetworkedRootMotionMontage())
{
return;
}

TGuardValue<bool> bInBaseReplicationGuard(bInBaseReplication, true);

const bool bBaseChanged = (BasedMovement.MovementBase != ReplicatedBasedMovement.MovementBase || BasedMovement.BoneName != ReplicatedBasedMovement.BoneName);


if (bBaseChanged)
{
// Even though we will copy the replicated based movement info, we need to use SetBase() to set up tick dependencies and trigger notifications.
SetBase(ReplicatedBasedMovement.MovementBase, ReplicatedBasedMovement.BoneName);
}

// Make sure to use the values of relative location/rotation etc from the server.
BasedMovement = ReplicatedBasedMovement;

if (ReplicatedBasedMovement.HasRelativeLocation())
{
// Update transform relative to movement base
const FVector OldLocation = GetActorLocation();
const FQuat OldRotation = GetActorQuat();
MovementBaseUtility::GetMovementBaseTransform(ReplicatedBasedMovement.MovementBase, ReplicatedBasedMovement.BoneName, CharacterMovement->OldBaseLocation, CharacterMovement-
>OldBaseQuat);
const FVector NewLocation = CharacterMovement->OldBaseLocation + ReplicatedBasedMovement.Location;

if (ReplicatedBasedMovement.HasRelativeRotation())
{
// Relative location, relative rotation
FRotator NewRotation = (FRotationMatrix(ReplicatedBasedMovement.Rotation) * FQuatRotationMatrix(CharacterMovement->OldBaseQuat)).Rotator();

// TODO: need a better way to not assume we only use Yaw.


NewRotation.Pitch = 0.f;
NewRotation.Roll = 0.f;

SetActorLocationAndRotation(NewLocation, NewRotation);
}
else
{
// Relative location, absolute rotation
SetActorLocationAndRotation(NewLocation, ReplicatedBasedMovement.Rotation);
}

// When position or base changes, movement mode will need to be updated. This assumes rotation changes don't affect that.
CharacterMovement->bJustTeleported |= (bBaseChanged || GetActorLocation() != OldLocation);

INetworkPredictionInterface* PredictionInterface = Cast<INetworkPredictionInterface>(GetMovementComponent());


if (PredictionInterface)
{
PredictionInterface->SmoothCorrection(OldLocation, OldRotation);
}
}
}

void ACharacter::OnRep_ReplicatedMovement()
{
// Skip standard position correction if we are playing root motion, OnRep_RootMotion will handle it.
if (!IsPlayingNetworkedRootMotionMontage())
{
Super::OnRep_ReplicatedMovement();
}
}

/** Get FAnimMontageInstance playing RootMotion */


FAnimMontageInstance * ACharacter::GetRootMotionAnimMontageInstance() const
{
return (Mesh && Mesh->AnimScriptInstance) ? Mesh->AnimScriptInstance->GetRootMotionMontageInstance() : NULL;
}

void ACharacter::OnRep_RootMotion()
{
if( Role == ROLE_SimulatedProxy )
{
UE_LOG(LogRootMotion, Log, TEXT("ACharacter::OnRep_RootMotion"));

// Save received move in queue, we'll try to use it during Tick().


if( RepRootMotion.AnimMontage )
{
// Add new move
FSimulatedRootMotionReplicatedMove NewMove;
NewMove.RootMotion = RepRootMotion;
NewMove.Time = GetWorld()->GetTimeSeconds();
RootMotionRepMoves.Add(NewMove);
}
else
{
// Clear saved moves.
RootMotionRepMoves.Empty();
}
}
}

void ACharacter::SimulatedRootMotionPositionFixup(float DeltaSeconds)


{
const FAnimMontageInstance* ClientMontageInstance = GetRootMotionAnimMontageInstance();
if( ClientMontageInstance && CharacterMovement && Mesh )
{
// Find most recent buffered move that we can use.
const int32 MoveIndex = FindRootMotionRepMove(*ClientMontageInstance);
if( MoveIndex != INDEX_NONE )
{
const FVector OldLocation = GetActorLocation();
const FQuat OldRotation = GetActorQuat();
// Move Actor back to position of that buffered move. (server replicated position).
const FSimulatedRootMotionReplicatedMove& RootMotionRepMove = RootMotionRepMoves[MoveIndex];
if( RestoreReplicatedMove(RootMotionRepMove) )
{
const float ServerPosition = RootMotionRepMove.RootMotion.Position;
const float ClientPosition = ClientMontageInstance->GetPosition();
const float DeltaPosition = (ClientPosition - ServerPosition);
if( FMath::Abs(DeltaPosition) > KINDA_SMALL_NUMBER )
{
// Find Root Motion delta move to get back to where we were on the client.
const FTransform LocalRootMotionTransform = ClientMontageInstance->Montage->ExtractRootMotionFromTrackRange(ServerPosition, ClientPosition);

// Simulate Root Motion for delta move.


if( CharacterMovement )
{
const float MontagePlayRate = ClientMontageInstance->GetPlayRate();
// Guess time it takes for this delta track position, so we can get falling physics accurate.
if (!FMath::IsNearlyZero(MontagePlayRate))
{
const float DeltaTime = DeltaPosition / MontagePlayRate;

// Even with negative playrate deltatime should be positive.


check(DeltaTime > 0.f);
CharacterMovement->SimulateRootMotion(DeltaTime, LocalRootMotionTransform);

// After movement correction, smooth out error in position if any.


INetworkPredictionInterface* PredictionInterface = Cast<INetworkPredictionInterface>(GetMovementComponent());
if (PredictionInterface)
{
PredictionInterface->SmoothCorrection(OldLocation, OldRotation);
}
}
}
}
}

// Delete this move and any prior one, we don't need them anymore.
UE_LOG(LogRootMotion, Log, TEXT("\tClearing old moves (%d)"), MoveIndex+1);
RootMotionRepMoves.RemoveAt(0, MoveIndex+1);
}
}
}

int32 ACharacter::FindRootMotionRepMove(const FAnimMontageInstance& ClientMontageInstance) const


{
int32 FoundIndex = INDEX_NONE;

// Start with most recent move and go back in time to find a usable move.
for(int32 MoveIndex=RootMotionRepMoves.Num()-1; MoveIndex>=0; MoveIndex--)
{
if( CanUseRootMotionRepMove(RootMotionRepMoves[MoveIndex], ClientMontageInstance) )
{
FoundIndex = MoveIndex;
break;
}
}

UE_LOG(LogRootMotion, Log, TEXT("\tACharacter::FindRootMotionRepMove FoundIndex: %d, NumSavedMoves: %d"), FoundIndex, RootMotionRepMoves.Num());


return FoundIndex;
}

bool ACharacter::CanUseRootMotionRepMove(const FSimulatedRootMotionReplicatedMove& RootMotionRepMove, const FAnimMontageInstance& ClientMontageInstance) const


{
// Ignore outdated moves.
if( GetWorld()->TimeSince(RootMotionRepMove.Time) <= 0.5f )
{
// Make sure montage being played matched between client and server.
if( RootMotionRepMove.RootMotion.AnimMontage && (RootMotionRepMove.RootMotion.AnimMontage == ClientMontageInstance.Montage) )
{
UAnimMontage * AnimMontage = ClientMontageInstance.Montage;
const float ServerPosition = RootMotionRepMove.RootMotion.Position;
const float ClientPosition = ClientMontageInstance.GetPosition();
const float DeltaPosition = (ClientPosition - ServerPosition);
const int32 CurrentSectionIndex = AnimMontage->GetSectionIndexFromPosition(ClientPosition);
if( CurrentSectionIndex != INDEX_NONE )
{
const int32 NextSectionIndex = (CurrentSectionIndex < ClientMontageInstance.NextSections.Num()) ? ClientMontageInstance.NextSections[CurrentSectionIndex] : INDEX_NONE;

// We can only extract root motion if we are within the same section.
// It's not trivial to jump through sections in a deterministic manner, but that is luckily not frequent.
const bool bSameSections = (AnimMontage->GetSectionIndexFromPosition(ServerPosition) == CurrentSectionIndex);
// if we are looping and just wrapped over, skip. That's also not easy to handle and not frequent.
const bool bHasLooped = (NextSectionIndex == CurrentSectionIndex) && (FMath::Abs(DeltaPosition) > (AnimMontage->GetSectionLength(CurrentSectionIndex) / 2.f));
// Can only simulate forward in time, so we need to make sure server move is not ahead of the client.
const bool bServerAheadOfClient = ((DeltaPosition * ClientMontageInstance.GetPlayRate()) < 0.f);

UE_LOG(LogRootMotion, Log, TEXT("\t\tACharacter::CanUseRootMotionRepMove ServerPosition: %.3f, ClientPosition: %.3f, DeltaPosition: %.3f, bSameSections: %d, bHasLooped:
%d, bServerAheadOfClient: %d"),
ServerPosition, ClientPosition, DeltaPosition, bSameSections, bHasLooped, bServerAheadOfClient);

return bSameSections && !bHasLooped && !bServerAheadOfClient;


}
}
}
return false;
}

bool ACharacter::RestoreReplicatedMove(const FSimulatedRootMotionReplicatedMove& RootMotionRepMove)


{
UPrimitiveComponent* ServerBase = RootMotionRepMove.RootMotion.MovementBase;
const FName ServerBaseBoneName = RootMotionRepMove.RootMotion.MovementBaseBoneName;

// Relative Position
if( RootMotionRepMove.RootMotion.bRelativePosition )
{
bool bSuccess = false;
if( MovementBaseUtility::UseRelativeLocation(ServerBase) )
{
FVector BaseLocation;
FQuat BaseRotation;
MovementBaseUtility::GetMovementBaseTransform(ServerBase, ServerBaseBoneName, BaseLocation, BaseRotation);

const FVector ServerLocation = BaseLocation + RootMotionRepMove.RootMotion.Location;


FRotator ServerRotation;
if (RootMotionRepMove.RootMotion.bRelativeRotation)
{
// Relative rotation
ServerRotation = (FRotationMatrix(RootMotionRepMove.RootMotion.Rotation) * FQuatRotationTranslationMatrix(BaseRotation, FVector::ZeroVector)).Rotator();
}
else
{
// Absolute rotation
ServerRotation = RootMotionRepMove.RootMotion.Rotation;
}

UpdateSimulatedPosition(ServerLocation, ServerRotation);
bSuccess = true;
}
// If we received local space position, but can't resolve parent, then move can't be used. :(
if( !bSuccess )
{
return false;
}
}
// Absolute position
else
{
UpdateSimulatedPosition(RootMotionRepMove.RootMotion.Location, RootMotionRepMove.RootMotion.Rotation);
}

SetBase( ServerBase, ServerBaseBoneName );

return true;
}

void ACharacter::UpdateSimulatedPosition(const FVector& NewLocation, const FRotator& NewRotation)


{
// Always consider Location as changed if we were spawned this tick as in that case our replicated Location was set as part of spawning, before PreNetReceive()
if( (NewLocation != GetActorLocation()) || (CreationTime == GetWorld()->TimeSeconds) )
{
FVector FinalLocation = NewLocation;

// Only need to check for encroachment when teleported without any velocity.
// Normal movement pops the character out of geometry anyway, no use doing it before and after (with different rules).
bSimGravityDisabled = false;
if (CharacterMovement->Velocity.IsZero())
{
if (GetWorld()->EncroachingBlockingGeometry(this, NewLocation, NewRotation))
{
bSimGravityDisabled = true;
}
}

// Don't use TeleportTo(), that clears our base.


SetActorLocationAndRotation(FinalLocation, NewRotation, false);
CharacterMovement->bJustTeleported = true;
}
else if( NewRotation != GetActorRotation() )
{
GetRootComponent()->MoveComponent(FVector::ZeroVector, NewRotation, false);
}
}

void ACharacter::PostNetReceiveLocationAndRotation()
{
if( Role == ROLE_SimulatedProxy )
{
// Don't change transform if using relative position (it should be nearly the same anyway, or base may be slightly out of sync)
if (!ReplicatedBasedMovement.HasRelativeLocation())
{
const FVector OldLocation = GetActorLocation();
const FQuat OldRotation = GetActorQuat();
UpdateSimulatedPosition(ReplicatedMovement.Location, ReplicatedMovement.Rotation);

INetworkPredictionInterface* PredictionInterface = Cast<INetworkPredictionInterface>(GetMovementComponent());


if (PredictionInterface)
{
PredictionInterface->SmoothCorrection(OldLocation, OldRotation);
}
}
}
}

void ACharacter::PreReplication( IRepChangedPropertyTracker & ChangedPropertyTracker )


{
Super::PreReplication( ChangedPropertyTracker );

const FAnimMontageInstance* RootMotionMontageInstance = GetRootMotionAnimMontageInstance();

if ( RootMotionMontageInstance )
{
// Is position stored in local space?
RepRootMotion.bRelativePosition = BasedMovement.HasRelativeLocation();
RepRootMotion.bRelativeRotation = BasedMovement.HasRelativeRotation();
RepRootMotion.Location = RepRootMotion.bRelativePosition ? BasedMovement.Location : GetActorLocation();
RepRootMotion.Rotation = RepRootMotion.bRelativeRotation ? BasedMovement.Rotation : GetActorRotation();
RepRootMotion.MovementBase = BasedMovement.MovementBase;
RepRootMotion.MovementBaseBoneName = BasedMovement.BoneName;
RepRootMotion.AnimMontage = RootMotionMontageInstance->Montage;
RepRootMotion.Position = RootMotionMontageInstance->GetPosition();

DOREPLIFETIME_ACTIVE_OVERRIDE( ACharacter, RepRootMotion, true );


}
else
{
RepRootMotion.Clear();

DOREPLIFETIME_ACTIVE_OVERRIDE( ACharacter, RepRootMotion, false );


}

ReplicatedMovementMode = CharacterMovement->PackNetworkMovementMode();
ReplicatedBasedMovement = BasedMovement;

// Optimization: only update and replicate these values if they are actually going to be used.
if (BasedMovement.HasRelativeLocation())
{
// When velocity becomes zero, force replication so the position is updated to match the server (it may have moved due to simulation on the client).
ReplicatedBasedMovement.bServerHasVelocity = !CharacterMovement->Velocity.IsZero();

// Make sure absolute rotations are updated in case rotation occurred after the base info was saved.
if (!BasedMovement.HasRelativeRotation())
{
ReplicatedBasedMovement.Rotation = GetActorRotation();
}
}
}

void ACharacter::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const


{
Super::GetLifetimeReplicatedProps( OutLifetimeProps );

DOREPLIFETIME_CONDITION( ACharacter, RepRootMotion, COND_SimulatedOnly );


DOREPLIFETIME_CONDITION( ACharacter, ReplicatedBasedMovement, COND_SimulatedOnly );
DOREPLIFETIME_CONDITION( ACharacter, ReplicatedMovementMode, COND_SimulatedOnly );
DOREPLIFETIME_CONDITION( ACharacter, bIsCrouched, COND_SimulatedOnly );
}

bool ACharacter::IsPlayingRootMotion() const


{
if (Mesh && Mesh->AnimScriptInstance)
{
return (Mesh->AnimScriptInstance->RootMotionMode == ERootMotionMode::RootMotionFromEverything) ||
(Mesh->AnimScriptInstance->GetRootMotionMontageInstance() != NULL);
}
return false;
}

bool ACharacter::IsPlayingNetworkedRootMotionMontage() const


{
if (Mesh && Mesh->AnimScriptInstance)
{
return (Mesh->AnimScriptInstance->RootMotionMode == ERootMotionMode::RootMotionFromMontagesOnly) &&
(Mesh->AnimScriptInstance->GetRootMotionMontageInstance() != NULL);
}
return false;
}

float ACharacter::PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)


{
UAnimInstance * AnimInstance = (Mesh)? Mesh->GetAnimInstance() : NULL;
if( AnimMontage && AnimInstance )
{
float const Duration = AnimInstance->Montage_Play(AnimMontage, InPlayRate);

if (Duration > 0.f)


{
// Start at a given Section.
if( StartSectionName != NAME_None )
{
AnimInstance->Montage_JumpToSection(StartSectionName, AnimMontage);
}

return Duration;
}
}

return 0.f;
}

void ACharacter::StopAnimMontage(class UAnimMontage* AnimMontage)


{
UAnimInstance * AnimInstance = (Mesh)? Mesh->GetAnimInstance() : NULL;
UAnimMontage * MontageToStop = (AnimMontage)? AnimMontage : GetCurrentMontage();
bool bShouldStopMontage = AnimInstance && MontageToStop && !AnimInstance->Montage_GetIsStopped(MontageToStop);

if ( bShouldStopMontage )
{
AnimInstance->Montage_Stop(MontageToStop->BlendOutTime, MontageToStop);
}
}

class UAnimMontage * ACharacter::GetCurrentMontage()


{
UAnimInstance * AnimInstance = (Mesh)? Mesh->GetAnimInstance() : NULL;
if ( AnimInstance )
{
return AnimInstance->GetCurrentActiveMontage();
}

return NULL;
}

void ACharacter::ClientCheatWalk_Implementation()
{
SetActorEnableCollision(true);
if (CharacterMovement)
{
CharacterMovement->bCheatFlying = false;
CharacterMovement->SetMovementMode(MOVE_Falling);
}
}

void ACharacter::ClientCheatFly_Implementation()
{
SetActorEnableCollision(true);
if (CharacterMovement)
{
CharacterMovement->bCheatFlying = true;
CharacterMovement->SetMovementMode(MOVE_Flying);
}
}

void ACharacter::ClientCheatGhost_Implementation()
{
SetActorEnableCollision(false);
if (CharacterMovement)
{
CharacterMovement->bCheatFlying = true;
CharacterMovement->SetMovementMode(MOVE_Flying);
}
}
/** Returns Mesh subobject **/
USkeletalMeshComponent* ACharacter::GetMesh() const {
return Mesh;
}
#if WITH_EDITORONLY_DATA
/** Returns ArrowComponent subobject **/
UArrowComponent* ACharacter::GetArrowComponent() const {
return ArrowComponent;
}
#endif
/** Returns CharacterMovement subobject **/
UCharacterMovementComponent* ACharacter::GetCharacterMovement() const {
return CharacterMovement;
}
/** Returns CapsuleComponent subobject **/
UCapsuleComponent* ACharacter::GetCapsuleComponent() const {
return CapsuleComponent;
}

EXAMPLE #11

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "BET.h"
#include "BETCharacter.h"
#include "BETProjectile.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"
#include "BETProjectileWeapon.h"
#include "BETWeapon.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// ABETCharacter

ABETCharacter::ABETCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
// Create a gun mesh component
FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

void ABETCharacter::BeginPlay()
{
Super::BeginPlay();
if (WeaponClass)
{
FActorSpawnParameters SpawnParameters;
SpawnParameters.Instigator = this;
Weapon = GetWorld()->SpawnActor<ABETWeapon>(WeaponClass, SpawnParameters);
Weapon->AttachRootComponentToActor(this);
}
}
//////////////////////////////////////////////////////////////////////////
// Input

void ABETCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ABETCharacter::TouchStarted);


if( EnableTouchscreenMovement(InputComponent) == false )
{
InputComponent->BindAction("Fire", IE_Pressed, this, &ABETCharacter::OnFire);
}

InputComponent->BindAxis("MoveForward", this, &ABETCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &ABETCharacter::MoveRight);

// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &ABETCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &ABETCharacter::LookUpAtRate);
}

void ABETCharacter::OnFire()
{
if (Weapon){
Weapon->Fire();
}

// try and play the sound if specified


if (Weapon->FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, Weapon->FireSound, GetActorLocation());
}

// try and play a firing animation if specified


if (Weapon->FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(Weapon->FireAnimation, 1.f);
}
}
}

void ABETCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if( TouchItem.bIsPressed == true )
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void ABETCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if( ( FingerIndex == TouchItem.FingerIndex ) && (TouchItem.bMoved == false) )
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void ABETCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && ( TouchItem.FingerIndex==FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D( MoveDelta.X, MoveDelta.Y) / ScreenSize;

if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void ABETCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void ABETCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}

void ABETCharacter::TurnAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void ABETCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool ABETCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if(FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch )
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ABETCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &ABETCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &ABETCharacter::TouchUpdate);
}
return bResult;
}
EXAMPLE #12

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "Wizards.h"
#include "WizardsCharacter.h"
#include "WizardsProjectile.h"
#include "WizardsBlast.h"
#include "WizardsCone.h"
#include "WizardsSaveGame.h"
#include "UnrealNetwork.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"

//DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AWizardsCharacter

AWizardsCharacter::AWizardsCharacter()
{
bReplicates = true;
//Tick for mana regen
PrimaryActorTick.bCanEverTick = true;
//Set Health and Mana
Health = 100.0;
maxHealth = 100.0;
Mana = 100.0;
maxMana = 100.0;
//Spell Stuff for Testing
//SList.spellCost = 10.0;

ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Sparks.P_Sparks'"));
//SList.myParticle = ArbitraryParticleName.Object;
//SList.test = &ArbitraryParticleName;
currSpell = 0;
//For the record, this probably isn't the best way to get particles for the spells but it works
//A better method, implemented at a later and unknown date, would be to hold this array in its own class
//that is called once and never destroyed
//Projectiles
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName0(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Fire_Projectile.P_Fire_Projectile'"));
particleList.Add(ArbitraryParticleName0.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName1(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Lightning_Projectile.P_Lightning_Projectile'"));
particleList.Add(ArbitraryParticleName1.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName2(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Water_Projectile.P_Water_Projectile'"));
particleList.Add(ArbitraryParticleName2.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName3(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Ice_Projectile.P_Ice_Projectile'"));
particleList.Add(ArbitraryParticleName3.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName4(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Earth_Projectile.P_Earth_Projectile'"));
particleList.Add(ArbitraryParticleName4.Object);
//Now for all of the EXPLOSIONS
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName5(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Fire_Blast.P_Fire_Blast'"));
particleList.Add(ArbitraryParticleName5.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName6(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Lightning_Blast.P_Lightning_Blast'"));
particleList.Add(ArbitraryParticleName6.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName7(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Water_Blast.P_Water_Blast'"));
particleList.Add(ArbitraryParticleName7.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName8(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Ice_Blast.P_Ice_Blast'"));
particleList.Add(ArbitraryParticleName8.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName9(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Earth_Blast.P_Earth_Blast'"));
particleList.Add(ArbitraryParticleName9.Object);
//Next up is cones
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName10(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Fire_Cone.P_Fire_Cone'"));
particleList.Add(ArbitraryParticleName10.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName11(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Lightning_Cone.P_Lightning_Cone'"));
particleList.Add(ArbitraryParticleName11.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName12(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Water_Cone.P_Water_Cone'"));
particleList.Add(ArbitraryParticleName12.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName13(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Ice_Cone.P_Ice_Cone'"));
particleList.Add(ArbitraryParticleName13.Object);
ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName14(TEXT("ParticleSystem'/Game/FirstPerson/Particles/P_Earth_Cone.P_Earth_Cone'"));
particleList.Add(ArbitraryParticleName14.Object);

//spell test;
//SList.Add(test);
//SList[currSpell].spellCost = 10.0;
//SList[currSpell].theWizard = this;
//SList[currSpell].canBounce = true;
//ConstructorHelpers::FObjectFinder<UParticleSystem> ArbitraryParticleName8(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Sparks.P_Sparks'"));
//SList[currSpell].myParticle = ArbitraryParticleName8.Object;
//SList.test = &ArbitraryParticleName;
//SList.particleLocation = FName(TEXT("ParticleSystem'/Game/StarterContent/Particles/P_Sparks.P_Sparks'"));

// Set size for collision capsule


GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

// set our turn rates for input


BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;

// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;

// Default offset from the character location for projectiles to spawn


GunOffset = FVector(100.0f, 30.0f, 10.0f);

// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true); // only the owning player will see this mesh
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->RelativeLocation = FVector(0.f, 0.f, -150.f);
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;

// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

void AWizardsCharacter::newCharactersSpells()
{
UWizardsSaveGame* LoadGameInstance = NewObject<UWizardsSaveGame>();
// spellList = LoadGameInstance->spellList;
//if is empty
if (LoadGameInstance->LoadGameDataFromFile()) {

for (int i = 0; i < 5; i++) {


FtheSpell thisSpell;
mySpellBook.Add(thisSpell);
mySpellBook[i].spellEffect = LoadGameInstance->spellBook[i]->spellEffect;
mySpellBook[i].spellType = LoadGameInstance->spellBook[i]->spellType;
mySpellBook[i].spellCost = LoadGameInstance->spellBook[i]->spellCost;
mySpellBook[i].spellSpeed = LoadGameInstance->spellBook[i]->spellSpeed*9000.0 + 1000.0;//range from 1000 to 10000
mySpellBook[i].spellDamage = LoadGameInstance->spellBook[i]->spellDamage;//not in at the moment
mySpellBook[i].spellRange = LoadGameInstance->spellBook[i]->spellRange*4.0 + 1.0; //1 to 5 seconds
mySpellBook[i].spellSize = LoadGameInstance->spellBook[i]->spellSize*4.0 + 1.0;//range 1 to 5
mySpellBook[i].canBounce = LoadGameInstance->spellBook[i]->canBounce;//boolean, in
mySpellBook[i].hasGravity = LoadGameInstance->spellBook[i]->hasGravity;//totally in
mySpellBook[i].isHoming = LoadGameInstance->spellBook[i]->isHoming; //not currenttly implemented
mySpellBook[i].explodeOnCollision = LoadGameInstance->spellBook[i]->explodeOnCollision; //none of this shit down here is implemented
mySpellBook[i].explodeOnDeath = LoadGameInstance->spellBook[i]->explodeOnDeath;
mySpellBook[i].explosionHitDamage = LoadGameInstance->spellBook[i]->explosionHitDamage;
mySpellBook[i].explosionHitSize = LoadGameInstance->spellBook[i]->explosionHitSize*3.0 + 2.0;
mySpellBook[i].explosionDeathDamage = LoadGameInstance->spellBook[i]->explosionDeathDamage;
mySpellBook[i].explosionDeathSize = LoadGameInstance->spellBook[i]->explosionDeathSize*3.0 + 2.0;
//mySpellBook[i]->myParticle = particleList[mySpellBook[i]->spellEffect + mySpellBook[i]->spellType * 5];
//mySpellBook[i]->explParticle = particleList[mySpellBook[i]->spellEffect + 5];
UE_LOG(LogTemp, Warning, TEXT("Spell Gathering Succesful!"));
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("Abort Mission!"));
}
}

/////////////
// On Tick
void AWizardsCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (Mana <= maxMana) {
Mana += DeltaTime;
}
}

//////////////////////////////////////////////////////////////////////////
// Input

void AWizardsCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)


{
// set up gameplay key bindings
check(InputComponent);

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);


InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
InputComponent->BindAction("ChangeSpell1", IE_Pressed, this, &AWizardsCharacter::spellSwitch<0>);
InputComponent->BindAction("ChangeSpell2", IE_Pressed, this, &AWizardsCharacter::spellSwitch<1>);
InputComponent->BindAction("ChangeSpell3", IE_Pressed, this, &AWizardsCharacter::spellSwitch<2>);
InputComponent->BindAction("ChangeSpell4", IE_Pressed, this, &AWizardsCharacter::spellSwitch<3>);
InputComponent->BindAction("ChangeSpell5", IE_Pressed, this, &AWizardsCharacter::spellSwitch<4>);

//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AWizardsCharacter::TouchStarted);


if (EnableTouchscreenMovement(InputComponent) == false)
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AWizardsCharacter::OnFire);
InputComponent->BindAction("Fire", IE_Released, this, &AWizardsCharacter::OffFire);

InputComponent->BindAxis("MoveForward", this, &AWizardsCharacter::MoveForward);


InputComponent->BindAxis("MoveRight", this, &AWizardsCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AWizardsCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AWizardsCharacter::LookUpAtRate);
}

void AWizardsCharacter::OnFire()
{
if (!mySpellBook.IsValidIndex(0)) {
UE_LOG(LogTemp, Warning, TEXT("Spell Gathering Needed!"));
newCharactersSpells();
}
if (Mana > mySpellBook[currSpell].spellCost) {
Mana -= mySpellBook[currSpell].spellCost;
// try and fire a projectile

if (mySpellBook[currSpell].spellType == 0)
{
const FRotator SpawnRotation = GetControlRotation();
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World)
{
// spawn the projectile at the muzzle
/*UParticleSystem* projParticle = particleList[mySpellBook[currSpell].spellEffect + mySpellBook[currSpell].spellType * 5];
UParticleSystem* blastParticle = particleList[mySpellBook[currSpell].spellEffect + 5];
AWizardsProjectile* wizardsSpell = World->SpawnActor<AWizardsProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(&mySpellBook[currSpell], projParticle, blastParticle, this);*/
if (Role < ROLE_Authority)
{
ServerFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);//mySpellBook[currSpell]);
}
else {
ClientFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
}
}
else if (mySpellBook[currSpell].spellType == 1) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
// MuzzleOffset is
in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World)
{
// spawn the projectile at the muzzle
/*UParticleSystem* blastParticle = particleList[mySpellBook[currSpell].spellEffect + mySpellBook[currSpell].spellType * 5];
AWizardsBlast* wizardsSpell = World->SpawnActor<AWizardsBlast>(BlastClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(blastParticle, mySpellBook[currSpell].spellSize, mySpellBook[currSpell].spellDamage, this);
wizardsSpell->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks*/
if (Role < ROLE_Authority)
{
ServerFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
else {
ClientFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
}
}
else if (mySpellBook[currSpell].spellType == 2) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
// MuzzleOffset is
in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

UWorld* const World = GetWorld();


if (World)
{
// spawn the projectile at the muzzle
/*UParticleSystem* coneParticle = particleList[mySpellBook[currSpell].spellEffect + mySpellBook[currSpell].spellType * 5];
AWizardsCone* wizardsCone = World->SpawnActor<AWizardsCone>(ConeClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsCone->SpellCreation(coneParticle, mySpellBook[currSpell].spellSize, mySpellBook[currSpell].spellDamage, this);
wizardsCone->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
activeAttack = Cast<AActor>(wizardsCone);*/
if (Role < ROLE_Authority)
{
ServerFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
else {
ClientFireProjectile(mySpellBook[currSpell], SpawnRotation, SpawnLocation);
}
}
}
// God this sound is so annoying
/*if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}*/

// try and play a firing animation if specified


if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}

}
}

void AWizardsCharacter::OffFire() {
if (activeAttack != NULL) {
activeAttack->Destroy();
ServerDestroyCone(activeAttack);
}
}

void AWizardsCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == true)
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}

void AWizardsCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if (TouchItem.bIsPressed == false)
{
return;
}
if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
{
OnFire();
}
TouchItem.bIsPressed = false;
}

void AWizardsCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)


{
if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
if (ScaledDelta.X != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (ScaledDelta.Y != 0.0f)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y* BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}

void AWizardsCharacter::MoveForward(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}

void AWizardsCharacter::MoveRight(float Value)


{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}
void AWizardsCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AWizardsCharacter::LookUpAtRate(float Rate)


{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool AWizardsCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)


{
bool bResult = false;
if (FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch)
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AWizardsCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AWizardsCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AWizardsCharacter::TouchUpdate);
}
return bResult;
}

template<int newspell>
void AWizardsCharacter::spellSwitch()
{
currSpell = newspell;
}

float AWizardsCharacter::GetHealth() {
return Health;
}
float AWizardsCharacter::GetMana() {
return Mana;
}

void AWizardsCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const {


Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AWizardsCharacter, mySpellBook);
DOREPLIFETIME(AWizardsCharacter, Mana);
DOREPLIFETIME(AWizardsCharacter, currSpell);
}

void AWizardsCharacter::ServerFireProjectile_Implementation(FtheSpell castSpell, FRotator rotation, FVector location) {


//UWorld* const World = GetWorld();
UE_LOG(LogTemp, Warning, TEXT("Server Side"));
//OnFire();
ClientFireProjectile(castSpell, rotation, location);
/*if (theSpell->spellType == 0) {
const FRotator SpawnRotation = GetControlRotation();
const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);
AWizardsProjectile* wizardsSpell = World->SpawnActor<AWizardsProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(theSpell, this);

}
else if (theSpell->spellType == 1) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);
AWizardsBlast* wizardsSpell = World->SpawnActor<AWizardsBlast>(BlastClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsSpell->SpellCreation(theSpell->myParticle, theSpell->spellSize, theSpell->spellDamage, this);
wizardsSpell->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
UE_LOG(LogTemp, Warning, TEXT("Boom!"));
}
else if (theSpell->spellType == 2) {
const FRotator SpawnRotation = FRotator(0.0);//GetControlRotation();
const FVector SpawnLocation = FVector(0.0);//GetActorLocation() + SpawnRotation.RotateVector(GunOffset);
AWizardsCone* wizardsCone = World->SpawnActor<AWizardsCone>(ConeClass, SpawnLocation, SpawnRotation);// , myparams);
wizardsCone->SpellCreation(theSpell->myParticle, theSpell->spellSize, theSpell->spellDamage, this);
wizardsCone->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
activeAttack = Cast<AActor>(wizardsCone);
UE_LOG(LogTemp, Warning, TEXT("Svoosh!"));
}*/
}
bool AWizardsCharacter::ServerFireProjectile_Validate(FtheSpell castSpell, FRotator rotation, FVector location) {
return true;
}

void AWizardsCharacter::ClientFireProjectile_Implementation(FtheSpell castSpell, FRotator rotation, FVector location){


/*if (!mySpellBook.IsValidIndex(0)) {
UE_LOG(LogTemp, Warning, TEXT("Spell Gathering Needed!"));
newCharactersSpells();
}*/

//UE_LOG(LogTemp, Warning, TEXT("Role %d has currSpell %d and SpawnRotation "), Role, currSpell);

if (castSpell.spellType == 0)
{
UWorld* const World = GetWorld();
if (World)
{
// spawn the projectile at the muzzle
UParticleSystem* projParticle = particleList[castSpell.spellEffect + castSpell.spellType * 5];
UParticleSystem* blastParticle = particleList[castSpell.spellEffect + 5];
AWizardsProjectile* wizardsSpell = World->SpawnActor<AWizardsProjectile>(ProjectileClass, location, rotation);// , myparams);
wizardsSpell->SpellCreation(&castSpell, projParticle, blastParticle, this);
}
}
else if (castSpell.spellType == 1) {
UWorld* const World = GetWorld();
if (World)
{
// spawn the projectile at the muzzle
UParticleSystem* blastParticle = particleList[castSpell.spellEffect + castSpell.spellType * 5];
AWizardsBlast* wizardsSpell = World->SpawnActor<AWizardsBlast>(BlastClass, location, rotation);// , myparams);
wizardsSpell->SpellCreation(blastParticle, castSpell.spellSize, castSpell.spellDamage, this);
wizardsSpell->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
}
}
else if (castSpell.spellType == 2) {
UWorld* const World = GetWorld();
if (World)
{
// spawn the projectile at the muzzle
UParticleSystem* coneParticle = particleList[castSpell.spellEffect + castSpell.spellType * 5];
AWizardsCone* wizardsCone = World->SpawnActor<AWizardsCone>(ConeClass, location, rotation);// , myparams);
wizardsCone->SpellCreation(coneParticle, castSpell.spellSize, castSpell.spellDamage, this);
wizardsCone->AttachRootComponentTo(GetCapsuleComponent());//Probably useful for Blasts, Rays, and Conical attacks
activeAttack = Cast<AActor>(wizardsCone);
}
}
}
bool AWizardsCharacter::ClientFireProjectile_Validate(FtheSpell castSpell, FRotator rotation, FVector location){
return true;
}

void AWizardsCharacter::ServerDestroyCone_Implementation(AActor* theActor) {


ClientDestroyCone(theActor);

}
bool AWizardsCharacter::ServerDestroyCone_Validate(AActor* theActor) {
return true;
}

void AWizardsCharacter::ClientDestroyCone_Implementation(AActor* theActor) {


if (theActor != NULL) {
theActor->Destroy();
}
if (activeAttack != NULL) {
activeAttack->Destroy();
}
}
bool AWizardsCharacter::ClientDestroyCone_Validate(AActor* theActor) {
return true;
}

You might also like