using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
/// <summary>
/// GAME 1303 Tier 7 — Pick a box up, carry it in front of you, put it down or throw it.
/// Store Simulator's basic verb.
///
/// SETUP
/// 1. Add to the FirstPersonController player. Create an empty child of the CAMERA
/// called "CarryPoint" about 0.8 units forward and slightly down; drag it in.
/// 2. Carryable objects: Rigidbody + Collider, on a layer called "Carryable", with a
/// Carryable component (see the other script).
/// 3. Input: add a "Pickup" Button action (E, gamepad South/A). Throw uses "Attack"
/// (left mouse) — or set Can Throw off and it's just place-down.
/// 4. Reach is how far you can grab from. The crosshair-ray uses the camera's forward,
/// so students should keep the camera in the CameraPivot as set up by the controller.
/// 5. While carrying, OnCarryChanged fires with the object — use it to show the product
/// name and count on the HUD.
///
/// Carried objects become kinematic and are parented to the carry point, which is the
/// simple approach. It can clip through walls; that's a known trade and fine for a class
/// project. The physics-joint alternative is a good stretch goal for Sanad if he wants it.
/// </summary>
public class PickupCarry : MonoBehaviour
{
[Header("Reach")]
public Transform carryPoint;
public Camera viewCamera;
public float reach = 2.5f;
public LayerMask carryableLayers;
[Header("Throwing")]
public bool canThrow = true;
public float throwForce = 6f;
public float placeDistance = 1.2f;
[Header("Feedback")]
public string pickupSfx = "pickup";
public string dropSfx = "drop";
[Header("Events")]
public UnityEvent<Carryable> OnPickedUp;
public UnityEvent OnDropped;
public UnityEvent<Carryable> OnCarryChanged;
public Carryable Held { get; private set; }
public bool IsCarrying => Held != null;
private Rigidbody heldBody;
void Awake()
{
if (viewCamera == null) viewCamera = Camera.main;
}
void OnPickup(InputValue v)
{
if (!v.isPressed) return;
if (IsCarrying) Drop(false);
else TryPickUp();
}
void OnAttack(InputValue v)
{
if (!v.isPressed || !IsCarrying || !canThrow) return;
Drop(true);
}
void TryPickUp()
{
if (viewCamera == null) return;
Ray ray = new Ray(viewCamera.transform.position, viewCamera.transform.forward);
if (!Physics.Raycast(ray, out RaycastHit hit, reach, carryableLayers)) return;
var carryable = hit.collider.GetComponentInParent<Carryable>();
if (carryable == null || !carryable.canBePickedUp) return;
Held = carryable;
heldBody = carryable.GetComponent<Rigidbody>();
if (heldBody != null) { heldBody.isKinematic = true; heldBody.detectCollisions = false; }
carryable.transform.SetParent(carryPoint);
carryable.transform.localPosition = Vector3.zero;
carryable.transform.localRotation = Quaternion.identity;
carryable.OnPickedUp(this);
if (!string.IsNullOrEmpty(pickupSfx)) AudioManager.Instance?.PlaySFX(pickupSfx);
OnPickedUp?.Invoke(carryable);
OnCarryChanged?.Invoke(carryable);
}
public void Drop(bool thrown)
{
if (!IsCarrying) return;
var carryable = Held;
carryable.transform.SetParent(null);
if (heldBody != null)
{
heldBody.isKinematic = false;
heldBody.detectCollisions = true;
if (thrown && viewCamera != null)
heldBody.AddForce(viewCamera.transform.forward * throwForce, ForceMode.Impulse);
else if (viewCamera != null)
carryable.transform.position = viewCamera.transform.position + viewCamera.transform.forward * placeDistance;
}
carryable.OnDropped();
Held = null;
heldBody = null;
if (!string.IsNullOrEmpty(dropSfx)) AudioManager.Instance?.PlaySFX(dropSfx);
OnDropped?.Invoke();
OnCarryChanged?.Invoke(null);
}
/// <summary>ShelfSlot calls this when it takes the box out of your hands.</summary>
public void ClearHeld()
{
Held = null;
heldBody = null;
OnCarryChanged?.Invoke(null);
}
}