// Fill out your copyright notice in the Description page of Project Settings. #include "PickupItem.h" #include "Components/SphereComponent.h" #include "Components/StaticMeshComponent.h" #include "InventoryComponent.h" #include "ItemDataAsset.h" #include "Net/UnrealNetwork.h" #define LOCTEXT_NAMESPACE "PickupItem" APickupItem::APickupItem() { PrimaryActorTick.bCanEverTick = false; // Acteur du monde : le serveur fait autorite. Un pickup ramasse doit // disparaitre chez les quatre joueurs, pas seulement chez celui qui l'a pris. bReplicates = true; // Un pickup ne bouge plus une fois pose. Couper la replication de mouvement // evite d'envoyer une transform a chaque joueur qui entre dans sa zone de // pertinence, alors que celle du spawn suffit. SetReplicateMovement(false); Mesh = CreateDefaultSubobject(TEXT("Mesh")); SetRootComponent(Mesh); // Purement decoratif : c'est la sphere qui porte la collision. Mesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); // Le trace d'interaction passe par le canal Visibility. On ne bloque QUE // ce canal : le pickup reste traversable par le joueur et les projectiles. InteractionSphere = CreateDefaultSubobject(TEXT("InteractionSphere")); InteractionSphere->SetupAttachment(Mesh); InteractionSphere->InitSphereRadius(50.f); InteractionSphere->SetCollisionEnabled(ECollisionEnabled::QueryOnly); InteractionSphere->SetCollisionResponseToAllChannels(ECR_Ignore); InteractionSphere->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); } void APickupItem::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(APickupItem, ItemData); DOREPLIFETIME(APickupItem, Quantity); DOREPLIFETIME(APickupItem, RemainingUses); } void APickupItem::InitPickup(UItemDataAsset* InItemData, int32 InQuantity, int32 InRemainingUses) { ItemData = InItemData; Quantity = FMath::Max(1, InQuantity); RemainingUses = InRemainingUses; } void APickupItem::ApplyItemVisuals() { if (bUseMeshFromItemData && ItemData && ItemData->WorldMesh) { ItemData->ApplyWorldVisualsTo(Mesh); } } void APickupItem::OnRep_ItemData() { ApplyItemVisuals(); } void APickupItem::OnConstruction(const FTransform& Transform) { Super::OnConstruction(Transform); // Confort d'edition : le mesh suit l'objet choisi, tu vois immediatement // dans le viewport ce que tu es en train de poser. Vaut aussi a l'execution // pour les objets jetes, puisque OnConstruction s'execute a FinishSpawning. // // Chez un client, ItemData est encore nul a cet instant : c'est OnRep_ItemData // qui reposera le visuel quand la propriete arrivera. ApplyItemVisuals(); } FText APickupItem::GetInteractionPrompt_Implementation(AActor* Interactor) const { // Interactor ne sert pas ici : ramasser dit la meme chose a tout le monde. if (!ItemData) { return LOCTEXT("PickupUnconfigured", "Unconfigured item"); } if (Quantity > 1) { return FText::Format(LOCTEXT("PickupPromptStack", "Pick up {0} x{1}"), ItemData->DisplayName, FText::AsNumber(Quantity)); } return FText::Format(LOCTEXT("PickupPrompt", "Pick up {0}"), ItemData->DisplayName); } bool APickupItem::CanInteract_Implementation(AActor* Interactor) const { // Un pickup sans donnee est une erreur de configuration : on le rend // inerte plutot que de laisser le joueur ramasser du vide. return ItemData != nullptr; } void APickupItem::Interact_Implementation(AActor* Interactor) { // Filet de securite : UInteractionComponent ne nous appelle deja que cote // autorite, mais un Blueprint ou un futur appelant pourrait l'oublier, et // un ramassage joue en local donnerait un objet qui reapparait a la // prochaine replication -- le pire des symptomes, parce qu'il est // intermittent. if (!HasAuthority()) { return; } UInventoryComponent* Inventory = Interactor ? Interactor->FindComponentByClass() : nullptr; if (!Inventory) { UE_LOG(LogTemp, Warning, TEXT("APickupItem : %s n'a pas d'UInventoryComponent."), *GetNameSafe(Interactor)); return; } const int32 Leftover = Inventory->AddItem(ItemData, Quantity, RemainingUses); // Inventaire plein : on ne detruit surtout pas le tas, on retire seulement // ce qui est rentre. Le joueur peut revenir chercher le reste. if (Leftover >= Quantity) { return; } // Les anciens AddOnScreenDebugMessage ont disparu : en reseau ils // s'affichaient sur l'ecran de l'HOTE, puisque c'est lui qui execute ce // code, y compris quand c'est un client qui ramasse. L'UI d'inventaire // existe desormais et dit la meme chose au bon joueur. if (Leftover > 0) { Quantity = Leftover; return; } Destroy(); } void APickupItem::OnBeginFocus_Implementation() { if (bHighlightOnFocus) { Mesh->SetRenderCustomDepth(true); } } void APickupItem::OnEndFocus_Implementation() { if (bHighlightOnFocus) { Mesh->SetRenderCustomDepth(false); } } #undef LOCTEXT_NAMESPACE