| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- using UnityEngine;
- using UnityEngine.AI;
- using System.Collections.Generic;
- using System;
- [RequireComponent(typeof(NavMeshAgent))]
- public class Staff : MonoBehaviour
- {
- [Header("Staff Info")]
- [SerializeField] private string staffName;
- [SerializeField] private StaffType staffType = StaffType.Receptionist;
- [SerializeField] private float efficiency = 0.7f; // 0 to 1
- [SerializeField] private float workSpeed = 1f;
- [Header("Work Settings")]
- [SerializeField] private Transform workStation;
- [SerializeField] private float workRadius = 5f;
- [SerializeField] private LayerMask workLayerMask = -1;
- [Header("Movement")]
- [SerializeField] private float walkSpeed = 3f;
- [SerializeField] private float runSpeed = 5f;
- [Header("Visual")]
- [SerializeField] private GameObject workIndicator;
- [SerializeField] private SpriteRenderer statusIcon;
- private NavMeshAgent agent;
- private StaffState currentState = StaffState.Idle;
- private List<StaffTask> taskQueue = new List<StaffTask>();
- private StaffTask currentTask;
- private Vector3 homePosition;
- private float workTimer = 0f;
- private float shiftStartTime;
- private float shiftDuration = 8f * 60f; // 8 hours in game minutes
- // Events
- public static event Action<Staff> OnStaffHired;
- public static event Action<Staff> OnStaffCompleteTask;
- public static event Action<Staff, StaffTask> OnTaskAssigned;
- public enum StaffState
- {
- Idle,
- MovingToTask,
- Working,
- OnBreak,
- OffDuty
- }
- public enum StaffType
- {
- Receptionist,
- Housekeeper,
- Chef,
- Janitor,
- Bellhop,
- Valet,
- Security,
- Manager
- }
- #region Properties
- public string StaffName
- {
- get => string.IsNullOrEmpty(staffName) ? GenerateRandomName() : staffName;
- set => staffName = value;
- }
- public StaffState CurrentState => currentState;
- public StaffType Type => staffType;
- public float Efficiency => efficiency;
- public Transform WorkStation => workStation;
- public int TaskCount => taskQueue.Count;
- public bool IsAvailable => currentState == StaffState.Idle && taskQueue.Count == 0;
- #endregion
- private void Awake()
- {
- agent = GetComponent<NavMeshAgent>();
- agent.speed = walkSpeed;
- SetupWorkIndicator();
- homePosition = transform.position;
- shiftStartTime = Time.time;
- }
- private void Start()
- {
- SetState(StaffState.Idle);
- OnStaffHired?.Invoke(this);
- // Find or create work station based on staff type
- SetupWorkStation();
- }
- private void Update()
- {
- UpdateWorkShift();
- UpdateTaskExecution();
- UpdateMovement();
- UpdateVisuals();
- }
- #region State Management
- private void SetState(StaffState newState)
- {
- currentState = newState;
- Debug.Log($"Staff {StaffName} ({staffType}) state changed to: {newState}");
- switch (newState)
- {
- case StaffState.Idle:
- agent.speed = walkSpeed;
- if (workStation != null)
- MoveToPosition(workStation.position);
- break;
- case StaffState.MovingToTask:
- agent.speed = currentTask?.isUrgent == true ? runSpeed : walkSpeed;
- break;
- case StaffState.Working:
- agent.speed = 0f;
- StartWork();
- break;
- case StaffState.OnBreak:
- MoveToPosition(homePosition);
- break;
- }
- }
- private void UpdateWorkShift()
- {
- float timeWorked = Time.time - shiftStartTime;
- if (timeWorked > shiftDuration)
- {
- if (currentState != StaffState.OffDuty)
- {
- SetState(StaffState.OffDuty);
- ClearAllTasks();
- }
- }
- else if (timeWorked % (2f * 60f) < Time.deltaTime) // Break every 2 game hours
- {
- if (currentState == StaffState.Idle && UnityEngine.Random.value < 0.3f)
- {
- TakeBreak();
- }
- }
- }
- private void TakeBreak()
- {
- SetState(StaffState.OnBreak);
- // Return to work after break
- Invoke(nameof(ReturnFromBreak), 30f); // 30 second break
- }
- private void ReturnFromBreak()
- {
- if (currentState == StaffState.OnBreak)
- {
- SetState(StaffState.Idle);
- }
- }
- #endregion
- #region Task Management
- public void AssignTask(StaffTask task)
- {
- if (task == null) return;
- taskQueue.Add(task);
- OnTaskAssigned?.Invoke(this, task);
- Debug.Log($"Task assigned to {StaffName}: {task.taskType}");
- // If idle, start working immediately
- if (currentState == StaffState.Idle)
- {
- ProcessNextTask();
- }
- }
- private void ProcessNextTask()
- {
- if (taskQueue.Count == 0)
- {
- SetState(StaffState.Idle);
- return;
- }
- // Get highest priority task
- taskQueue.Sort((a, b) => b.priority.CompareTo(a.priority));
- currentTask = taskQueue[0];
- taskQueue.RemoveAt(0);
- // Move to task location
- if (currentTask.location != Vector3.zero)
- {
- SetState(StaffState.MovingToTask);
- MoveToPosition(currentTask.location);
- }
- else
- {
- SetState(StaffState.Working);
- }
- }
- private void UpdateTaskExecution()
- {
- if (currentTask == null) return;
- switch (currentState)
- {
- case StaffState.MovingToTask:
- if (HasReachedDestination())
- {
- SetState(StaffState.Working);
- }
- break;
- case StaffState.Working:
- workTimer += Time.deltaTime * efficiency * workSpeed;
- if (workTimer >= currentTask.duration)
- {
- CompleteCurrentTask();
- }
- break;
- }
- }
- private void StartWork()
- {
- workTimer = 0f;
- if (workIndicator != null)
- workIndicator.SetActive(true);
- Debug.Log($"{StaffName} started working on: {currentTask.taskType}");
- }
- private void CompleteCurrentTask()
- {
- if (currentTask == null) return;
- Debug.Log($"{StaffName} completed task: {currentTask.taskType}");
- // Execute task effects
- ExecuteTaskEffects(currentTask);
- OnStaffCompleteTask?.Invoke(this);
- if (workIndicator != null)
- workIndicator.SetActive(false);
- currentTask = null;
- workTimer = 0f;
- // Process next task
- ProcessNextTask();
- }
- private void ExecuteTaskEffects(StaffTask task)
- {
- switch (task.taskType)
- {
- case TaskType.CheckInGuest:
- // Handle guest check-in
- if (task.targetGuest != null)
- {
- task.targetGuest.GetComponent<Guest>()?.AssignRoom(HotelManager.Instance?.GetAvailableRoom(task.targetGuest.GetComponent<Guest>()));
- }
- break;
- case TaskType.CleanRoom:
- // Mark room as clean
- if (task.targetRoom != null)
- {
- Debug.Log($"Room cleaned by {StaffName}");
- }
- break;
- case TaskType.DeliverService:
- // Deliver room service
- Debug.Log($"Service delivered by {StaffName}");
- break;
- }
- }
- public void ClearAllTasks()
- {
- taskQueue.Clear();
- currentTask = null;
- workTimer = 0f;
- if (workIndicator != null)
- workIndicator.SetActive(false);
- }
- public bool CanHandleTask(TaskType taskType)
- {
- switch (staffType)
- {
- case StaffType.Receptionist:
- return taskType == TaskType.CheckInGuest || taskType == TaskType.AnswerPhone;
- case StaffType.Housekeeper:
- return taskType == TaskType.CleanRoom || taskType == TaskType.LaundryService;
- case StaffType.Chef:
- return taskType == TaskType.PrepareFood || taskType == TaskType.DeliverService;
- case StaffType.Janitor:
- return taskType == TaskType.CleanArea || taskType == TaskType.Maintenance;
- case StaffType.Bellhop:
- return taskType == TaskType.CarryLuggage || taskType == TaskType.DeliverService;
- case StaffType.Valet:
- return taskType == TaskType.ParkCar || taskType == TaskType.CarryLuggage;
- default:
- return false;
- }
- }
- #endregion
- #region Movement
- private bool MoveToPosition(Vector3 position)
- {
- if (!agent.enabled || !agent.isOnNavMesh) return false;
- try
- {
- agent.SetDestination(position);
- return true;
- }
- catch (System.Exception e)
- {
- Debug.LogWarning($"Failed to set destination for {StaffName}: {e.Message}");
- return false;
- }
- }
- private bool HasReachedDestination()
- {
- return agent.enabled && agent.isOnNavMesh && !agent.pathPending && agent.remainingDistance < 0.5f;
- }
- private void UpdateMovement()
- {
- // Update animation or visual effects based on movement
- if (agent.enabled && agent.velocity.magnitude > 0.1f)
- {
- // Staff is moving
- }
- else
- {
- // Staff is stationary
- }
- }
- #endregion
- #region Work Station Setup
- private void SetupWorkStation()
- {
- if (workStation == null)
- {
- workStation = FindWorkStation();
- }
- if (workStation == null)
- {
- CreateDefaultWorkStation();
- }
- }
- private Transform FindWorkStation()
- {
- GameObject[] stations = null;
- try
- {
- switch (staffType)
- {
- case StaffType.Receptionist:
- stations = GameObject.FindGameObjectsWithTag("Reception");
- break;
- case StaffType.Chef:
- stations = GameObject.FindGameObjectsWithTag("Kitchen");
- break;
- case StaffType.Housekeeper:
- case StaffType.Janitor:
- // These staff types move around, no fixed station
- return transform; // Use current position
- }
- }
- catch (UnityException)
- {
- Debug.LogWarning($"Tag for {staffType} not defined. Staff will create default work station.");
- return null;
- }
- if (stations != null && stations.Length > 0)
- {
- return stations[0].transform;
- }
- return null;
- }
- private void CreateDefaultWorkStation()
- {
- GameObject station = new GameObject($"{StaffName} Work Station");
- station.transform.position = transform.position;
- workStation = station.transform;
- // Add visual indicator
- GameObject indicator = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
- indicator.transform.SetParent(station.transform);
- indicator.transform.localPosition = Vector3.zero;
- indicator.transform.localScale = new Vector3(0.5f, 0.1f, 0.5f);
- indicator.GetComponent<Renderer>().material.color = GetStaffTypeColor();
- indicator.name = $"{staffType} Station";
- }
- private Color GetStaffTypeColor()
- {
- switch (staffType)
- {
- case StaffType.Receptionist: return Color.blue;
- case StaffType.Housekeeper: return Color.green;
- case StaffType.Chef: return Color.red;
- case StaffType.Janitor: return Color.yellow;
- case StaffType.Bellhop: return Color.cyan;
- case StaffType.Valet: return Color.magenta;
- default: return Color.gray;
- }
- }
- #endregion
- #region Visual Setup
- private void SetupWorkIndicator()
- {
- if (workIndicator == null)
- {
- workIndicator = new GameObject("Work Indicator");
- workIndicator.transform.SetParent(transform);
- workIndicator.transform.localPosition = Vector3.up * 2f;
- // Create a simple spinning cube as work indicator
- GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
- cube.transform.SetParent(workIndicator.transform);
- cube.transform.localScale = Vector3.one * 0.3f;
- cube.GetComponent<Renderer>().material.color = Color.yellow;
- // Add spinning animation
- cube.AddComponent<SpinIndicator>();
- workIndicator.SetActive(false);
- }
- }
- private void UpdateVisuals()
- {
- // Update staff appearance based on state
- Renderer renderer = GetComponent<Renderer>();
- if (renderer != null)
- {
- Color baseColor = GetStaffTypeColor();
- switch (currentState)
- {
- case StaffState.Working:
- renderer.material.color = Color.Lerp(baseColor, Color.white, 0.3f);
- break;
- case StaffState.OnBreak:
- renderer.material.color = Color.Lerp(baseColor, Color.gray, 0.5f);
- break;
- case StaffState.OffDuty:
- renderer.material.color = Color.Lerp(baseColor, Color.black, 0.5f);
- break;
- default:
- renderer.material.color = baseColor;
- break;
- }
- }
- }
- #endregion
- #region Utility Methods
- private string GenerateRandomName()
- {
- string[] firstNames = { "Alice", "Bob", "Carol", "Dave", "Emma", "Frank", "Grace", "Henry", "Iris", "Jack" };
- string[] lastNames = { "Anderson", "Brown", "Clark", "Davis", "Evans", "Foster", "Green", "Harris", "Johnson", "King" };
- return $"{firstNames[UnityEngine.Random.Range(0, firstNames.Length)]} {lastNames[UnityEngine.Random.Range(0, lastNames.Length)]}";
- }
- private void OnMouseDown()
- {
- // Show staff info when clicked
- Debug.Log($"Staff: {StaffName} ({staffType})\nState: {currentState}\nTasks: {taskQueue.Count}\nEfficiency: {efficiency:P0}");
- }
- private void OnDrawGizmosSelected()
- {
- // Draw work radius
- Gizmos.color = Color.blue;
- Gizmos.DrawWireSphere(transform.position, workRadius);
- // Draw line to work station
- if (workStation != null)
- {
- Gizmos.color = Color.green;
- Gizmos.DrawLine(transform.position, workStation.position);
- }
- }
- #endregion
- }
- // Supporting classes
- [System.Serializable]
- public class StaffTask
- {
- public TaskType taskType;
- public Vector3 location;
- public float duration = 10f; // seconds
- public int priority = 1; // 1-10, higher is more urgent
- public bool isUrgent = false;
- public GameObject targetGuest;
- public Room targetRoom;
- public string description;
- }
- public enum TaskType
- {
- CheckInGuest,
- CleanRoom,
- DeliverService,
- PrepareFood,
- AnswerPhone,
- CarryLuggage,
- ParkCar,
- CleanArea,
- Maintenance,
- LaundryService
- }
- // Helper component for work indicator animation
- public class SpinIndicator : MonoBehaviour
- {
- [SerializeField] private float spinSpeed = 90f;
- private void Update()
- {
- transform.Rotate(Vector3.up, spinSpeed * Time.deltaTime);
- }
- }
|