I’m not sure what is happening in my code.
I’m trying to code a FPS and I have a class for my character and one for my weapon.
In the begening, my character spawns a default inventory with some weapons.
Each time a weapon is added in the inventory, one slot is assigned, primary or secondary.
Everythings dépends on that after so it’s very important.
On the server, everythings works fine and , the slot is assigned correctly.
But on the clients, the weapons are added corectly in the inventory butthe slot isn’t assigned and I can’t figure out why.
Anyone can help ?
Here is my code.
Character CPP:
// Fill out your copyright notice in the Description page of Project Settings.
#include "BaseCharacter.h"
#include "Items/Ammo/Ammo.h"
#include "TacticalOps.h"
#include "Game/BaseGameMode.h "
#include "Components/CapsuleComponent.h"
FOnBaseCharacterEquipWeapon ABaseCharacter::NotifyEquipWeapon;
FOnBaseCharacterUnEquipWeapon ABaseCharacter::NotifyUnEquipWeapon;
// Vault
#define VAULT_SWEEP_DIST 20.f
#define VAULT_SWEEP_SPHERE_RADIUS 20.f
#define MIN_VAULT_HEIGHT 70.f
#define MAX_VAULT_HEIGHT 130.f
// Sets default values
ABaseCharacter::ABaseCharacter(const FObjectInitializer & ObjectInitializer) :Super(ObjectInitializer)
{
//Mesh1P = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("PawnMesh1P"));
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("PawnMesh1P"));
Mesh1P->SetupAttachment(GetCapsuleComponent());
Mesh1P->bOnlyOwnerSee = true;
Mesh1P->bOwnerNoSee = false;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->bReceivesDecals = false;
Mesh1P->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
Mesh1P->PrimaryComponentTick.TickGroup = TG_PrePhysics;
Mesh1P->SetCollisionObjectType(ECC_Pawn);
Mesh1P->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Mesh1P->SetCollisionResponseToAllChannels(ECR_Ignore);
GetMesh()->bOnlyOwnerSee = false;
GetMesh()->bOwnerNoSee = true;
GetMesh()->bReceivesDecals = false;
GetMesh()->SetCollisionObjectType(ECC_Pawn);
GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
//GetMesh()->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Block);
//GetMesh()->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
GetMesh()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
//GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
//GetCapsuleComponent()->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
//GetCapsuleComponent()->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Ignore);
PrimaryActorTick.bCanEverTick = true;
}
void ABaseCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
if (Role == ROLE_Authority)
{
Health = HealthMax;
SpawnDefaultInventory();
}
// play respawn effects
if (GetNetMode() != NM_DedicatedServer)
{
/*if (RespawnFX)
{
UGameplayStatics::SpawnEmitterAtLocation(this, RespawnFX, GetActorLocation(), GetActorRotation());
}
if (RespawnSound)
{
UGameplayStatics::PlaySoundAtLocation(this, RespawnSound, GetActorLocation());
}*/
}
}
// Called when the game starts or when spawned
void ABaseCharacter::BeginPlay()
{
Super::BeginPlay();
}
void ABaseCharacter::Destroyed()
{
Super::Destroyed();
DestroyInventory();
}
void ABaseCharacter::Tick(float DeltaTime)
{
Super::Tick( DeltaTime);
}
///////////////////////////////////////////////////////
//inventory
void ABaseCharacter::AddPickable(APickable* Picked)
{
if (Picked && Role == ROLE_Authority)
{
if (ABaseWeapon* weapon = Cast <ABaseWeapon>(Picked))
{
AddWeapon(weapon);
}
else {
Picked->OnEnterInventory(this);
Inventory.AddUnique(Picked);
CurrentInventoryWeight = CurrentInventoryWeight + Picked->Weight;
Picked->IsPickable = false;
}
}
}
void ABaseCharacter::AddWeapon(ABaseWeapon* weapon)
{
if (weapon)
{
if (weapon->WeapKind == EWeaponKind::SideGun)
{
if (SideGun == NULL)
{
weapon->OnEnterInventory(this);
Inventory.AddUnique(weapon);
SideGun = weapon;
weapon->CurrentSlot = EWeaponSlot::SideGun;
if (CurrentWeapon == NULL)
{
EquipSideGun();
}
}
else
{
RemoveWeapon(SideGun);
ThrowPickable(SideGun);
SideGun = NULL;
weapon->OnEnterInventory(this);
Inventory.AddUnique(weapon);
SideGun = weapon;
weapon->CurrentSlot = EWeaponSlot::SideGun;
EquipSideGun();
}
}
else
if (weapon->WeapKind == EWeaponKind::Shotgun || weapon->WeapKind == EWeaponKind::Rifle || weapon->WeapKind == EWeaponKind::SMG)
{
if (PrimaryWeapon == NULL)
{
weapon->OnEnterInventory(this);
Inventory.AddUnique(weapon);
PrimaryWeapon = weapon;
weapon->CurrentSlot = EWeaponSlot::Primary;
weapon->IsPickable = false;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("primaryadd"));
if (CurrentWeapon != NULL)
{
}
else EquipPrimary();
return;
}
if (PrimaryWeapon != NULL && SecondaryWeapon == NULL)
{
weapon->OnEnterInventory(this);
Inventory.AddUnique(weapon);
SecondaryWeapon = weapon;
weapon->CurrentSlot = EWeaponSlot::Secondary;
weapon->IsPickable = false;
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("secondaryyadd"));
return;
}
if (PrimaryWeapon != NULL && SecondaryWeapon != NULL)
{
if (CurrentWeapon == PrimaryWeapon)
{
RemoveWeapon(PrimaryWeapon);
ThrowPickable(PrimaryWeapon);
weapon->OnEnterInventory(this);
Inventory.AddUnique(weapon);
PrimaryWeapon = weapon;
weapon->CurrentSlot = EWeaponSlot::Primary;
weapon->IsPickable = false;
return;
}
if (CurrentWeapon == SecondaryWeapon)
{
RemoveWeapon(SecondaryWeapon);
ThrowPickable(SecondaryWeapon);
weapon->OnEnterInventory(this);
Inventory.AddUnique(weapon);
SecondaryWeapon = weapon;
weapon->CurrentSlot = EWeaponSlot::Secondary;
weapon->IsPickable = false;
return;
}
else
RemoveWeapon(PrimaryWeapon);
ThrowPickable(PrimaryWeapon);
weapon->OnEnterInventory(this);
Inventory.AddUnique(weapon);
PrimaryWeapon = weapon;
weapon->CurrentSlot = EWeaponSlot::Primary;
}
}
weapon->IsPickable = false;
}
}
bool ABaseCharacter::CanAddInventory(APickable* PickableToAdd)
{
if (PickableToAdd)
{
float Pickableweight = PickableToAdd->Weight;
if (ABaseWeapon* weapon = Cast <ABaseWeapon>(PickableToAdd))
{
return true;
}
else
{
if (Pickableweight + CurrentInventoryWeight < MaxInventoryWeight)
return true;
}
}
return false;
}
void ABaseCharacter::EquipWeapon(EWeaponSlot weaponslot)
{
if (Role == ROLE_Authority)
{
switch (weaponslot)
{
case (EWeaponSlot::Primary):
if (PrimaryWeapon != NULL)
{
CurrentWeapon = PrimaryWeapon;
CurrentWeapon->OnEquip(CurrentWeapon);
}
break;
case (EWeaponSlot::Secondary):
if (SecondaryWeapon != NULL)
{
CurrentWeapon = SecondaryWeapon;
CurrentWeapon->OnEquip(CurrentWeapon);
}
break;
case (EWeaponSlot::SideGun):
if (SideGun != NULL)
{
CurrentWeapon = SideGun;
CurrentWeapon->OnEquip(CurrentWeapon);
}
break;
}
}
else
{
ServerEquipWeapon(weaponslot);
}
}
bool ABaseCharacter::ServerEquipWeapon_Validate(EWeaponSlot weaponslot)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("true , serverequipweaponvalidate"));
return true;
}
void ABaseCharacter::ServerEquipWeapon_Implementation( EWeaponSlot weaponslot)
{
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("serverequipweapon with autority"));
EquipWeapon( weaponslot);
}
void ABaseCharacter::DestroyInventory()
{
if (Role < ROLE_Authority)
{
return;
}
// remove all weapons from inventory and destroy them
for (int32 i = Inventory.Num() - 1; i >= 0; i--)
{
APickable* Weapon = Inventory*;
if (Weapon)
{
//RemovePickable(Weapon);
Weapon->Destroy();
}
}
}
void ABaseCharacter::RemoveWeapon(class ABaseWeapon* weapon)
{
if (weapon && Role == ROLE_Authority)
{
weapon->OnLeaveInventory();
Inventory.RemoveSingle(weapon);
}
}
//on jette sur le sol l'objet dont on ne veut plus
void ABaseCharacter::ThrowPickable(APickable* PickableThrow)
{
PickableThrow->IsPickable = true;
PickableThrow->OnLeaveInventory();
//A compléter
}
void ABaseCharacter::SpawnDefaultInventory()
{
if (Role < ROLE_Authority)
{
return;
}
int32 NumWeaponClasses = DefaultInventoryClasses.Num();
for (int32 i = 0; i < NumWeaponClasses; i++)
{
if (DefaultInventoryClasses*)
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ABaseWeapon* NewWeapon = GetWorld()->SpawnActor<ABaseWeapon>(DefaultInventoryClasses*, SpawnInfo);
NewWeapon->OnEnterInventory(this);
AddWeapon(NewWeapon);
}
}
}
void ABaseCharacter::EquipPrimary()
{
if (CurrentWeapon != PrimaryWeapon && PrimaryWeapon != NULL)
{//si current weapon n'est pas nul alors il faut déséquiper currrentweapon
if (CurrentWeapon!=NULL)
{
//EWeaponSlot SlotToDesequip=CurrentWeapon->CurrentSlot;
UnequipWeapon(CurrentWeapon);
EquipWeapon(EWeaponSlot::Primary);
}
else
{
EquipWeapon(EWeaponSlot::Primary);
}
}
}
void ABaseCharacter::EquipSecondary()
{
if (CurrentWeapon != SecondaryWeapon && SecondaryWeapon != NULL)
{//si current weapon n'est pas nul alors il faut déséquiper currrentweapon
if (CurrentWeapon != NULL)
{
EWeaponSlot SlotToDesequip = CurrentWeapon->CurrentSlot;
UnequipWeapon(CurrentWeapon);
EquipWeapon(EWeaponSlot::Secondary);
}
else
{
EquipWeapon(EWeaponSlot::Secondary);
}
}
}
void ABaseCharacter::EquipSideGun()
{
}
void ABaseCharacter::EquipThrowable()
{
}
void ABaseCharacter::OnNextWeapon()
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("next weapon"));
if (CurrentWeapon != NULL)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("pasnul"));
switch (CurrentWeapon->CurrentSlot)
{
case (EWeaponSlot::Primary):
if (SecondaryWeapon != NULL)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("next primary"));
UnequipWeapon(PrimaryWeapon);
EquipSecondary();
return;
}
if (SideGun != NULL)
{
UnequipWeapon(PrimaryWeapon);
EquipSideGun();
return;
}
if (Throwable!=NULL)
{
UnequipWeapon(PrimaryWeapon);
EquipThrowable();
}
break;
case(EWeaponSlot::Secondary):
if (SideGun != NULL)
{
UnequipWeapon(SecondaryWeapon);
EquipSideGun();
return;
}
if (Throwable != NULL)
{
UnequipWeapon(SecondaryWeapon);
EquipThrowable();
}
if (PrimaryWeapon != NULL)
{
UnequipWeapon(SecondaryWeapon);
EquipPrimary();
return;
}
break;
}
}
else
//if (PrimaryWeapon = NULL)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("pas de promary ,nul"));
EquipPrimary();
}
}
void ABaseCharacter::OnPreviousWeapon()
{
if (CurrentWeapon != NULL)
{
switch (CurrentWeapon->CurrentSlot)
{
case (EWeaponSlot::Primary):
if (Throwable != NULL)
{
EquipThrowable();
}
if (SideGun != NULL)
{
EquipSideGun();
return;
}
if (SecondaryWeapon != NULL)
{
EquipSecondary();
return;
}
break;
case(EWeaponSlot::Secondary):
if (PrimaryWeapon != NULL)
{
UnequipWeapon(SecondaryWeapon);
EquipPrimary();
return;
}
if (Throwable != NULL)
{
UnequipWeapon(SecondaryWeapon);
EquipThrowable();
}
if (SideGun != NULL)
{
UnequipWeapon(SecondaryWeapon);
EquipSideGun();
return;
}
break;
}
}
}
void ABaseCharacter::UnequipWeapon(ABaseWeapon* WeaponToUnequip)
{
if (CurrentWeapon != NULL)
{
CurrentWeapon->OnUnEquip();
CurrentWeapon = NULL;
}
}
/////////////////////////////////////////////////////////
//Replication
void ABaseCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(ABaseCharacter, Inventory,COND_OwnerOnly);
//DOREPLIFETIME(ABaseCharacter, DefaultInventoryClasses);
DOREPLIFETIME(ABaseCharacter, CurrentWeapon);
DOREPLIFETIME(ABaseCharacter,PrimaryWeapon );
DOREPLIFETIME(ABaseCharacter, SecondaryWeapon);
DOREPLIFETIME(ABaseCharacter, SideGun);
DOREPLIFETIME(ABaseCharacter, Health);
}
void ABaseCharacter::OnRep_CurrentWeapon(class ABaseWeapon* LastWeapon)
{
if (LastWeapon != NULL)
{
LastWeapon->SetOwningPawn(this);
}
}
//////////////////////////////////////////////////////
//Inputs
// Called to bind functionality to input
void ABaseCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
check(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &ABaseCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveForward", this, &ABaseCharacter::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &ABaseCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &ABaseCharacter::LookUpAtRate);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ABaseCharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ABaseCharacter::StopJumping);
//PlayerInputComponent->BindAction("Crouch", IE_Pressed, this, &ABaseCharacter::Crouch);
//PlayerInputComponent->BindAction("Crouch", IE_Released, this, &ABaseCharacter::StopCrouching);
PlayerInputComponent->BindAction("FireMode", IE_Pressed, this, &ABaseCharacter::ChangeFireMode);
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &ABaseCharacter::Fire);
PlayerInputComponent->BindAction("Reload", IE_Pressed, this, &ABaseCharacter::Reload);
PlayerInputComponent->BindAction("NextWeapon", IE_Pressed, this, &ABaseCharacter::OnNextWeapon);
PlayerInputComponent->BindAction("PreviousWeapon", IE_Pressed, this, &ABaseCharacter::OnPreviousWeapon);
PlayerInputComponent->BindAction("EquipPrimary", IE_Pressed, this, &ABaseCharacter::EquipPrimary);
PlayerInputComponent->BindAction("EquipSecondary", IE_Pressed, this, &ABaseCharacter::EquipSecondary);
}
void ABaseCharacter::MoveForward(float Val)
{
if (Controller && Val != 0.f)
{
// Limit pitch when walking or falling
const bool bLimitRotation = (GetCharacterMovement()->IsMovingOnGround() || GetCharacterMovement()->IsFalling());
const FRotator Rotation = bLimitRotation ? GetActorRotation() : Controller->GetControlRotation();
const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Val);
}
}
void ABaseCharacter::MoveRight(float Val)
{
if (Val != 0.f)
{
const FQuat Rotation = GetActorQuat();
const FVector Direction = FQuatRotationMatrix(Rotation).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Val);
}
}
void ABaseCharacter::TurnAtRate(float Val)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Val * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void ABaseCharacter::LookUpAtRate(float Val)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Val * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void ABaseCharacter::Jump()
{
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(this);
const float CapsuleHalfHeight = GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
const float CapsuleRadius = GetCapsuleComponent()->GetScaledCapsuleRadius();
const FVector ForwardVector = GetActorForwardVector();
const FVector SweepStart = GetActorLocation() + FVector(0.f, 0.f, CapsuleHalfHeight) + ForwardVector * (CapsuleRadius + VAULT_SWEEP_DIST);
const FVector SweepEnd = SweepStart - FVector(0.f, 0.f, CapsuleHalfHeight * 2.f);
FHitResult Hit;
if (GetWorld()->SweepSingleByChannel(Hit, SweepStart, SweepEnd, FQuat::Identity, ECC_Pawn, FCollisionShape::MakeSphere(VAULT_SWEEP_SPHERE_RADIUS), QueryParams))
{
const float ObstacleHeight = Hit.ImpactPoint.Z - SweepEnd.Z;
if (ObstacleHeight > MIN_VAULT_HEIGHT && ObstacleHeight < MAX_VAULT_HEIGHT)
{
Vault();
return;
}
}
Super::Jump();
}
void ABaseCharacter::Vault()
{
//PlayAnimMontage(VaultMontage);
}
bool ABaseCharacter::IsFirstPerson() const
{
return IsAlive() && Controller && Controller->IsLocalPlayerController();
}
bool ABaseCharacter::IsAlive() const
{
return Health > 0;
}
USkeletalMeshComponent* ABaseCharacter::GetPawnMesh() const
{
return IsFirstPerson() ? Mesh1P : GetMesh();
}
USkeletalMeshComponent* ABaseCharacter::GetSpecifcPawnMesh(bool WantFirstPerson) const
{
return WantFirstPerson == true ? Mesh1P : GetMesh();
}
FName ABaseCharacter::GetWeaponAttachPoint() const
{
return WeaponAttachPoint;
}
/////////////////////////////////////////////////////
//Damage
float ABaseCharacter::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, class AActor* DamageCauser)
{
if (Health <= 0.f)
{
return 0.f;
}
// Modify based on game rules.
ABaseGameMode* const Game = GetWorld()->GetAuthGameMode<ABaseGameMode>();
Damage = Game ? Game->ModifyDamage(Damage, this, DamageEvent, EventInstigator, DamageCauser) : 0.f;
const float ActualDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
if (ActualDamage > 0.f)
{
Health -= ActualDamage;
if (Health <= 0)
{
Die(ActualDamage, DamageEvent, EventInstigator, DamageCauser);
}
/*else
{
PlayHit(ActualDamage, DamageEvent, EventInstigator ? EventInstigator->GetPawn() : NULL, DamageCauser);
}
MakeNoise(1.0f, EventInstigator ? EventInstigator->GetPawn() : this);*/
}
return ActualDamage;
}
bool ABaseCharacter::Die(float KillingDamage, FDamageEvent const& DamageEvent, AController* Killer, AActor* DamageCauser)
{
/*if (!CanDie(KillingDamage, DamageEvent, Killer, DamageCauser))
{
return false;
}*/
Health = FMath::Min(0.0f, Health);
// if this is an environmental death then refer to the previous killer so that they receive credit (knocked into lava pits, etc)
/*UDamageType const* const DamageType = DamageEvent.DamageTypeClass ? DamageEvent.DamageTypeClass->GetDefaultObject<UDamageType>() : GetDefault<UDamageType>();
Killer = GetDamageInstigator(Killer, *DamageType);
AController* const KilledPlayer = (Controller != NULL) ? Controller : Cast<AController>(GetOwner());
GetWorld()->GetAuthGameMode<AShooterGameMode>()->Killed(Killer, KilledPlayer, this, DamageType);
NetUpdateFrequency = GetDefault<AShooterCharacter>()->NetUpdateFrequency;
GetCharacterMovement()->ForceReplicationUpdate();
OnDeath(KillingDamage, DamageEvent, Killer ? Killer->GetPawn() : NULL, DamageCauser);*/
return true;
}
//////////////////////////////////////////////////////////////////////////
// Animations
float ABaseCharacter::PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
{
USkeletalMeshComponent* UseMesh = GetPawnMesh();
if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance)
{
return UseMesh->AnimScriptInstance->Montage_Play(AnimMontage, InPlayRate);
}
return 0.0f;
}
void ABaseCharacter::StopAnimMontage(class UAnimMontage* AnimMontage)
{
USkeletalMeshComponent* UseMesh = GetPawnMesh();
if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance &&
UseMesh->AnimScriptInstance->Montage_IsPlaying(AnimMontage))
{
UseMesh->AnimScriptInstance->Montage_Stop(AnimMontage->BlendOut.GetBlendTime(), AnimMontage);
}
}
void ABaseCharacter::StopAllAnimMontages()
{
USkeletalMeshComponent* UseMesh = GetPawnMesh();
if (UseMesh && UseMesh->AnimScriptInstance)
{
UseMesh->AnimScriptInstance->Montage_Stop(0.0f);
}
}
//////////////////////////////////////////////////////////////////////
//Fire and weapons management
void ABaseCharacter::Fire()
{
if (CanFire())
{
if (Role == ROLE_Authority)
{
TriggerPulled = true;
CurrentWeapon->WeaponFire(true);
}
else ServerFire();
}
}
void ABaseCharacter::OnStopFire()
{
TriggerPulled = false;
CurrentWeapon->WeaponFire(false);
}
bool ABaseCharacter::CanFire()
{
if (CurrentWeapon != NULL && !IsReloading && CurrentWeapon->GetIsEquiped())
{
return true;
}
else return false;
}
bool ABaseCharacter::ServerFire_Validate()
{
return true;
}
void ABaseCharacter::ServerFire_Implementation()
{
Fire();
}
void ABaseCharacter::ChangeFireMode()
{
if (CurrentWeapon != NULL)
{
CurrentWeapon->SetCurrentFireMode();
}
}
int8 ABaseCharacter::GetAmmo(EAmmoKind Ammo)
{
int8 AmmoToReturn = 0;
for (int32 i = Inventory.Num() - 1; i >= 0; i--)
{
APickable* Ammotocheck = Inventory*;
if (AAmmo* ItemTocheck = Cast<AAmmo>(Ammotocheck))
{
if (ItemTocheck != NULL && ItemTocheck->AmmoType == Ammo)
{
AmmoToReturn = ItemTocheck->AmmoCount;
if (CurrentWeapon->HasFastExtandedMag)
{
ItemTocheck->GetAmmo(CurrentWeapon->MaxAmmoWithExtandedMag);
}
else
{
ItemTocheck->GetAmmo(CurrentWeapon->MaxAmmo);
}
}
}
}
return AmmoToReturn;
}
void ABaseCharacter::Reload()
{
if (Role == ROLE_Authority)
{
if (CurrentWeapon != NULL)
{
IsReloading = true;
CurrentWeapon->WeaponReload();
IsReloading = false;
}
}
else ServerReload();
}
bool ABaseCharacter::ServerReload_Validate()
{
return true;
}
void ABaseCharacter::ServerReload_Implementation()
{
Reload();
}
:
Pickable.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Net/UnrealNetwork.h"
#include "Pickable.generated.h"
class UAnimMontage;
class ABaseCharacter;
class USoundCue;
class UAudioComponent;
class UAnimMontage;
class UParticleSystemComponent;
class UCameraShake;
class UForceFeedbackEffect;
USTRUCT()
struct FPickableAnim
{
GENERATED_USTRUCT_BODY()
/** animation played on pawn (1st person view) */
UPROPERTY(EditDefaultsOnly, Category = Animation)
UAnimMontage* Pawn1P;
/** animation played on pawn (3rd person view) */
UPROPERTY(EditDefaultsOnly, Category = Animation)
UAnimMontage* Pawn3P;
};
UENUM(BlueprintType)
enum class EWeaponSlot : uint8
{
None,
Primary,
Secondary,
SideGun,
Throwable,
Other,
InHand,
};
UCLASS(Abstract, Blueprintable)
class APickable : public AActor
{
GENERATED_UCLASS_BODY()
public:
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
private:
/** weapon mesh: 1st person view */
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
USkeletalMeshComponent* Mesh1P;
/** weapon mesh: 3rd person view */
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
USkeletalMeshComponent* Mesh3P;
public:
//nom de l'objet
UPROPERTY(BluePrintReadWrite, EditAnywhere, Category = Items)
FName ItemName;
// Thumbnail for this item used in buymenu and hud
/*UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Items)
UTexture2D * ItemThumbnail;*/
//Socket pour accrocher l'item en vue FPS
UPROPERTY(BluePrintReadWrite, VisibleDefaultsOnly, Category = Mesh)
FTransform FpsSocket;
//Socket pour accrocher l'item en TPS
UPROPERTY(BluePrintReadWrite, VisibleDefaultsOnly, Category = Mesh)
FTransform TpsSocket;
public:
//Socket pour accrocher l'item dans le dos
UPROPERTY(BluePrintReadWrite, VisibleDefaultsOnly, Category = Mesh)
FTransform PrimarySocket;
//Socket pour accrocher l'item dans le dos
UPROPERTY(BluePrintReadWrite, VisibleDefaultsOnly, Category = Mesh)
FTransform SecondarySocket;
UPROPERTY(EditDefaultsOnly, Category = Items)
FPickableAnim EquipAnim;
UPROPERTY(BluePrintReadWrite, VisibleDefaultsOnly, Category = Pickable)
bool IsPickable;
float Weight;
UPROPERTY(BluePrintReadWrite, VisibleDefaultsOnly, Category = Pickable)
EWeaponSlot CurrentSlot;
protected:
/** pawn owner */
UPROPERTY(Transient, ReplicatedUsing = OnRep_MyPawn)
class ABaseCharacter* MyPawn;
//////////////////////////////////////////////////////////////////////////
// Weapon usage helpers
public:
/** play weapon sounds */
UAudioComponent* PlayWeaponSound(USoundCue* Sound);
/** play weapon animations */
float PlayWeaponAnimation(const FPickableAnim& Animation);
/** stop playing weapon animations */
void StopWeaponAnimation(const FPickableAnim& Animation);
//////////////////////////////////////////////////////////////////////////
// Replication & effects
UFUNCTION()
void OnRep_MyPawn();
//////////////////////////////////////////////////////////////////////////
// Inventory
void OnEquip(const APickable* LastWeapon);
void OnEquipFinished();
void OnDesequip(const APickable* LastWeapon);
/** Handle for efficient management of OnEquipFinished timer */
FTimerHandle TimerHandle_OnEquipFinished;
/** [server] weapon was added to pawn's inventory */
virtual void OnEnterInventory(ABaseCharacter* NewOwner);
/** [server] weapon was removed from pawn's inventory */
virtual void OnLeaveInventory();
/** set the weapon's owning pawn */
void SetOwningPawn(ABaseCharacter* AShooterCharacter);
/** check if mesh is already attached */
bool IsAttachedToPawn() const;
UFUNCTION(BluePrintCallable)
void AttachMeshToPawn(EWeaponSlot Pickableslot);
void DetachMeshFromPawn();
/** weapon is holstered by owner pawn */
virtual void OnUnEquip();
protected:
/** is equip animation playing? */
uint32 bPendingEquip : 1;
/** is weapon currently equipped? */
uint32 bIsEquipped : 1;
//is weapon attach hands
bool bIsinHands;
public :
bool GetIsEquiped() const { return bIsEquipped; };
/** equip sound */
UPROPERTY(EditDefaultsOnly, Category = Sound)
USoundCue* EquipSound;
/** unequip sound */
UPROPERTY(EditDefaultsOnly, Category = Sound)
USoundCue* UnEquipSound;
/** last time when this weapon was switched to */
float EquipStartedTime;
/** how much time weapon needs to be equipped */
float EquipDuration;
public:
UPROPERTY(EditDefaultsOnly, Category = Animation)
FPickableAnim PawnEquipAnim;
private:
UPROPERTY(ReplicatedUsing = OnRep_ReplicatedLocation)
FVector ReplicatedLocation;
UFUNCTION()
void OnRep_ReplicatedLocation();
};
[/CODE]
Here is Pickable.cpp the code for the baseclass of my weapons
// Fill out your copyright notice in the Description page of Project Settings.
#include “Pickable.h”
#include “TacticalOps.h”
#include “Player/BaseCharacter.h”
// Sets default values
APickable::APickable(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
Mesh1P = ObjectInitializer.CreateDefaultSubobject(this, TEXT(“WeaponMesh1P”));
Mesh1P->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
Mesh1P->bReceivesDecals = false;
Mesh1P->CastShadow = false;
Mesh1P->SetCollisionObjectType(ECC_WorldDynamic);
Mesh1P->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Mesh1P->SetCollisionResponseToAllChannels(ECR_Ignore);
RootComponent = Mesh1P;
Mesh3P = ObjectInitializer.CreateDefaultSubobject(this, TEXT(“WeaponMesh3P”));
Mesh3P->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
Mesh3P->bReceivesDecals = false;
Mesh3P->CastShadow = true;
Mesh3P->SetCollisionObjectType(ECC_WorldDynamic);
Mesh3P->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Mesh3P->SetCollisionResponseToAllChannels(ECR_Ignore);
//Mesh3P->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Block);
Mesh3P->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
//Mesh3P->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);
Mesh3P->SetupAttachment(Mesh1P);
IsPickable = true;
bPendingEquip = false;
bIsEquipped = false;
bIsinHands = false;
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.TickGroup = TG_PrePhysics;
SetRemoteRoleForBackwardsCompat(ROLE_SimulatedProxy);
bReplicates = true;
bAlwaysRelevant = true;
bNetUseOwnerRelevancy = false;
}
// Called when the game starts or when spawned
void APickable::BeginPlay()
{
Super::BeginPlay();
}
void APickable::Tick(float DeltaTime)
{
// Tick will only execute when dropped
if (Instigator)
{
SetActorTickEnabled(false);
return;
}
if (GetNetMode() == NM_Client)
{
// Update location on clients
if ((Mesh3P->GetComponentLocation() - ReplicatedLocation).SizeSquared() > 400.f)
{
Mesh3P->SetAllPhysicsPosition(ReplicatedLocation);//(Mesh->GetComponentLocation() + ReplicatedLocation) * 0.5f);
}
}
else
{
// Set replicated location so clients will receive it
ReplicatedLocation = Mesh3P->GetComponentLocation();
}
}
//////////////////////////////////////////////////////////////////////////
// Weapon usage helpers
UAudioComponent* APickable::PlayWeaponSound(USoundCue* Sound)
{
UAudioComponent* AC = NULL;
if (Sound && MyPawn)
{
AC = UGameplayStatics::SpawnSoundAttached(Sound, MyPawn->GetRootComponent());
}
return AC;
}
float APickable::PlayWeaponAnimation(const FPickableAnim& Animation)
{
float Duration = 0.0f;
if (MyPawn)
{
UAnimMontage* UseAnim = MyPawn->IsFirstPerson() ? Animation.Pawn1P : Animation.Pawn3P;
if (UseAnim)
{
Duration = MyPawn->PlayAnimMontage(UseAnim);
}
}
return Duration;
}
void APickable::StopWeaponAnimation(const FPickableAnim& Animation)
{
if (MyPawn)
{
UAnimMontage* UseAnim = MyPawn->IsFirstPerson() ? Animation.Pawn1P : Animation.Pawn3P;
if (UseAnim)
{
MyPawn->StopAnimMontage(UseAnim);
}
}
}
///////////////////////////////////////////////////
//Inventory
void APickable::OnEquip(const APickable* LastWeapon)
{
bPendingEquip = true;
AttachMeshToPawn(EWeaponSlot::InHand);
//DetermineWeaponState();
// Only play animation if last weapon is valid
if (LastWeapon)
{
float Duration = PlayWeaponAnimation(EquipAnim);
if (Duration <= 0.0f)
{
// failsafe
Duration = 0.5f;
}
EquipStartedTime = GetWorld()->GetTimeSeconds();
EquipDuration = Duration;
GetWorldTimerManager().SetTimer(TimerHandle_OnEquipFinished, this, &APickable::OnEquipFinished, Duration, false);
PlayWeaponSound(EquipSound);
}
else
{
OnEquipFinished();
}
if (MyPawn && MyPawn->IsLocallyControlled())
{
PlayWeaponSound(EquipSound);
}
ABaseCharacter::NotifyEquipWeapon.Broadcast(MyPawn, this);
}
void APickable::OnEquipFinished()
{
//AttachMeshToPawn();
bIsEquipped = true;
bPendingEquip = false;
}
void APickable::OnDesequip(const APickable* LastWeapon)
{
AttachMeshToPawn(CurrentSlot);
bIsEquipped = false;
PlayWeaponSound(UnEquipSound);
}
void APickable::OnEnterInventory(ABaseCharacter* NewOwner)
{
SetOwningPawn(NewOwner);
}
void APickable::OnLeaveInventory()
{
if (IsAttachedToPawn())
{
OnUnEquip();
}
if (Role == ROLE_Authority)
{
SetOwningPawn(NULL);
}
}
void APickable::SetOwningPawn(ABaseCharacter* NewOwner)
{
if (MyPawn != NewOwner)
{
Instigator = NewOwner;
MyPawn = NewOwner;
// net owner for RPC calls
SetOwner(NewOwner);
}
}
bool APickable::IsAttachedToPawn() const
{
return bIsEquipped || bPendingEquip;
}
void APickable::AttachMeshToPawn(EWeaponSlot Pickableslot)
{
switch (Pickableslot)
{
case(EWeaponSlot::InHand):
if (MyPawn)
{
FName AttachPoint = MyPawn->GetWeaponAttachPoint();
if (MyPawn->IsLocallyControlled() == true)
{
USkeletalMeshComponent* PawnMesh1p = MyPawn->GetSpecifcPawnMesh(true);
USkeletalMeshComponent* PawnMesh3p = MyPawn->GetSpecifcPawnMesh(false);
Mesh1P->SetHiddenInGame(false);
Mesh3P->SetHiddenInGame(false);
Mesh1P->AttachToComponent(PawnMesh1p, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);
Mesh3P->AttachToComponent(PawnMesh3p, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);
FVector NewLocation;
FRotator NewRotation; // = TpsSocket.GetRotation;
SetActorLocationAndRotation(NewLocation, NewRotation, false, 0, ETeleportType::None);
PlayWeaponSound(EquipSound);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT(“in hands”));
}
}
break;
case (EWeaponSlot::Primary):
if (MyPawn)
{
FName AttachPoint = MyPawn->PrimaryAttachPoint;
USkeletalMeshComponent* PawnMesh3p = MyPawn->GetSpecifcPawnMesh(false);
Mesh1P->SetHiddenInGame(true);
Mesh3P->AttachToComponent(PawnMesh3p, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);
PlayWeaponSound(EquipSound);
FVector NewLocation= TpsSocket.GetLocation();
FRotator NewRotation;// = TpsSocket.Rotation();
SetActorLocationAndRotation(NewLocation, NewRotation, false, 0, ETeleportType::None);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT(“in primary”));
}
break;
case (EWeaponSlot::Secondary):
if (MyPawn)
{
FName AttachPoint = MyPawn->SecondaryAttachPoint;
USkeletalMeshComponent* PawnMesh3p = MyPawn->GetSpecifcPawnMesh(false);
Mesh1P->SetHiddenInGame(true);
Mesh3P->AttachToComponent(PawnMesh3p, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);
PlayWeaponSound(EquipSound);
FVector NewLocation;
FRotator NewRotation; // = TpsSocket.GetRotation;
SetActorLocationAndRotation(NewLocation, NewRotation, false, 0, ETeleportType::None);
}
break;
case (EWeaponSlot::SideGun):
if (MyPawn)
{
FName AttachPoint = MyPawn->SideGunAttachPoint;
USkeletalMeshComponent* PawnMesh3p = MyPawn->GetSpecifcPawnMesh(false);
Mesh1P->SetHiddenInGame(true);
Mesh3P->AttachToComponent(PawnMesh3p, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);
PlayWeaponSound(EquipSound);
}
break;
}
}
void APickable::DetachMeshFromPawn()
{
Mesh1P->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
Mesh1P->SetHiddenInGame(true);
Mesh3P->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
Mesh3P->SetHiddenInGame(true);
PlayWeaponSound(UnEquipSound);
}
void APickable::OnUnEquip()
{
DetachMeshFromPawn();
bIsEquipped = false;
AttachMeshToPawn(CurrentSlot);
PlayWeaponSound(UnEquipSound);
}
//////////////////////////////////////////////////////////////////////////
// Replication & effects
void APickable::OnRep_MyPawn()
{
if (MyPawn)
{
OnEnterInventory(MyPawn);
}
else
{
OnLeaveInventory();
}
}
void APickable::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(APickable, MyPawn);
DOREPLIFETIME(APickable, ReplicatedLocation);
}
void APickable::OnRep_ReplicatedLocation()
{
if (!Instigator)
{
// Move mesh to replicated ragdoll location
Mesh3P->SetAllPhysicsPosition(ReplicatedLocation);
}
}