diff --git a/Content/_Game/Assets/Textures/Icons/ButtonIcons/ArrowIcon.uasset b/Content/_Game/Assets/Textures/Icons/ButtonIcons/ArrowIcon.uasset new file mode 100644 index 00000000..1cd259f7 --- /dev/null +++ b/Content/_Game/Assets/Textures/Icons/ButtonIcons/ArrowIcon.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf4fbf091aa1d8b2a553817ba34a6dd88114916f29854a40ab637ffd83582cf9 +size 9285 diff --git a/Content/_Game/HUD/InventoryBar.uasset b/Content/_Game/HUD/InventoryBar.uasset index c59e45fc..b0b49d18 100644 --- a/Content/_Game/HUD/InventoryBar.uasset +++ b/Content/_Game/HUD/InventoryBar.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0e4a52a7c6b612f3fa955294a61b53e6791a99ab4c684eff77980a2d069e54f -size 319225 +oid sha256:252ae0ea02fcfdca2593648c5aa94b90550503521cb029314f68251af796c047 +size 424976 diff --git a/Content/_Game/HUD/WeaponSlot.uasset b/Content/_Game/HUD/WeaponSlot.uasset index 8983f81a..d285e5a5 100644 --- a/Content/_Game/HUD/WeaponSlot.uasset +++ b/Content/_Game/HUD/WeaponSlot.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d84266c7fe496b5c8e40238d9495911b1d2ae41e734e54d4dbafa0fad164ff11 -size 295355 +oid sha256:60c2fa535f722ee965241d1a08bc8d0f4d9671275b3b6c7804921eaae9a797cb +size 340852 diff --git a/Source/Shooter/Item.cpp b/Source/Shooter/Item.cpp index 03277b2a..219acdaa 100644 --- a/Source/Shooter/Item.cpp +++ b/Source/Shooter/Item.cpp @@ -519,6 +519,8 @@ void AItem::FinishInterping() // Subtract 1 from the Item Count of the interp location struct Character->IncrementInterpLocItemCount(InterpLocIndex, -1); Character->GetPickupItem(this); + + Character->UnhighlightInventorySlot(); } // Set scale back to normal SetActorScale3D(FVector(1.f)); diff --git a/Source/Shooter/ShooterCharacter.cpp b/Source/Shooter/ShooterCharacter.cpp index 4452db81..c6ef5983 100644 --- a/Source/Shooter/ShooterCharacter.cpp +++ b/Source/Shooter/ShooterCharacter.cpp @@ -74,7 +74,9 @@ AShooterCharacter::AShooterCharacter() : bShouldPlayPickupSound(true), bShouldPlayEquipSound(true), PickupSoundResetTime(0.2f), - EquipSoundResetTime(0.2f) + EquipSoundResetTime(0.2f), + // Icon animation property + HighlightedSlot(-1) { // Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; @@ -416,7 +418,7 @@ void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 New { if (CurrentItemIndex == NewItemIndex || NewItemIndex >= Inventory.Num() - || CombatState != ECombatState::ECS_Unoccupied) + || (CombatState != ECombatState::ECS_Unoccupied && CombatState != ECombatState::ECS_Equipping)) return; auto OldEquippedWeapon = EquippedWeapon; @@ -437,6 +439,41 @@ void AShooterCharacter::ExchangeInventoryItems(int32 CurrentItemIndex, int32 New NewWeapon->PlayEquipSound(true); } +int32 AShooterCharacter::GetEmptyInventorySlot() +{ + for (int32 i = 0; i < Inventory.Num(); ++i) + { + if (Inventory[i] == nullptr) + return i; + } + + if (Inventory.Num() < INVENTORY_CAPACITY) + { + return Inventory.Num(); + } + + return -1; // Inventory full +} + +void AShooterCharacter::HighlightInventorySlot() +{ + const int32 EmptySlot{ GetEmptyInventorySlot() }; + if (EmptySlot != -1) + { + HighlightIconDelegate.Broadcast(EmptySlot, true); + HighlightedSlot = EmptySlot; + } +} + +void AShooterCharacter::UnhighlightInventorySlot() +{ + if (HighlightedSlot != -1) + { + HighlightIconDelegate.Broadcast(HighlightedSlot, false); + HighlightedSlot = -1; + } +} + int32 AShooterCharacter::GetInterpLocationIndex() { int32 LowestIndex = 1; @@ -766,14 +803,19 @@ void AShooterCharacter::TraceForItems() { // If an item was being looked at from last frame, we should hide its widget if (OldTraceHitItem != TraceHitItem) + { + UnhighlightInventorySlot(); SetItemPickupWidgetVisibility(OldTraceHitItem, false); - - // Show the new item's widget - OldTraceHitItem = TraceHitItem; + } if (TraceHitItem && TraceHitItem->GetItemState() == EItemState::EIS_EquipInterping) TraceHitItem = nullptr; + const auto TraceHitWeapon = Cast(TraceHitItem); + if (TraceHitWeapon && HighlightedSlot == -1) + HighlightInventorySlot(); + + // Show the new item's widget SetItemPickupWidgetVisibility(TraceHitItem, true); if (Inventory.Num() >= INVENTORY_CAPACITY) @@ -785,15 +827,21 @@ void AShooterCharacter::TraceForItems() { TraceHitItem->SetCharacterInventoryFull(false); } + + OldTraceHitItem = TraceHitItem; } else if (OldTraceHitItem) // Hide widget for the item from last frame { + UnhighlightInventorySlot(); + SetItemPickupWidgetVisibility(OldTraceHitItem, false); OldTraceHitItem = nullptr; } } else if (TraceHitItem) // Hide widget for the item from last frame { + UnhighlightInventorySlot(); + SetItemPickupWidgetVisibility(TraceHitItem, false); TraceHitItem = nullptr; } @@ -822,7 +870,7 @@ AWeapon* AShooterCharacter::SpawnDefaultWeapon() return nullptr; } -void AShooterCharacter::EquipWeapon(AWeapon* WeaponToEquip) +void AShooterCharacter::EquipWeapon(AWeapon* WeaponToEquip, bool bSwapping) { // Get the hand socket const USkeletalMeshSocket* HandSocket = GetMesh()->GetSocketByName(FName("RightHandSocket")); @@ -837,7 +885,7 @@ void AShooterCharacter::EquipWeapon(AWeapon* WeaponToEquip) // -1 == no EquippedWeapon yet. No need to reverse the icon animation EquipItemDelegate.Broadcast(-1, WeaponToEquip->GetSlotIndex()); } - else + else if (!bSwapping) { EquipItemDelegate.Broadcast(EquippedWeapon->GetSlotIndex(), WeaponToEquip->GetSlotIndex()); } @@ -886,7 +934,7 @@ void AShooterCharacter::SwapWeapon(AWeapon* WeaponToSwap) } DropWeapon(); - EquipWeapon(WeaponToSwap); + EquipWeapon(WeaponToSwap, true); } void AShooterCharacter::InitializeAmmoMap() diff --git a/Source/Shooter/ShooterCharacter.h b/Source/Shooter/ShooterCharacter.h index bede7c80..e04c1dd8 100644 --- a/Source/Shooter/ShooterCharacter.h +++ b/Source/Shooter/ShooterCharacter.h @@ -45,6 +45,8 @@ struct FInterpLocation DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FEquipItemDelegate, int32, CurrentSlotIndex, int32, NewSlotIndex); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FHighlightIconDelegate, int32, SlotIndex, bool, bStartAnimation); + UCLASS() class SHOOTER_API AShooterCharacter : public ACharacter @@ -140,7 +142,7 @@ protected: AWeapon* SpawnDefaultWeapon(); /* Takes a weapon and attaches it to the mesh */ - void EquipWeapon(AWeapon* WeaponToEquip); + void EquipWeapon(AWeapon* WeaponToEquip, bool bSwapping = false); /** Detach weapon and let it fall to the ground */ void DropWeapon(); @@ -210,6 +212,10 @@ protected: void ExchangeInventoryItems(int32 CurrentItemIndex, int32 NewItemIndex); + int32 GetEmptyInventorySlot(); + + void HighlightInventorySlot(); + private: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = true)) USpringArmComponent* CameraBoom; @@ -477,6 +483,14 @@ private: UPROPERTY(BlueprintAssignable, Category = Delegates, meta = (AllowPrivateAccess = true)) FEquipItemDelegate EquipItemDelegate; + /** Delegate for sending slot information for playing the icon animation */ + UPROPERTY(BlueprintAssignable, Category = Delegates, meta = (AllowPrivateAccess = true)) + FHighlightIconDelegate HighlightIconDelegate; + + /** The index for the currently highlighted slot */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = true)) + int32 HighlightedSlot; + public: /* Returns CameraBoom SubObject */ FORCEINLINE USpringArmComponent* GetCameraBoom() const { return CameraBoom; } @@ -515,4 +529,6 @@ public: void StartPickupSoundTimer(); void StartEquipSoundTimer(); + + void UnhighlightInventorySlot(); };