chunk 1: core gameplay scripts scenes runtime assets

This commit is contained in:
2026-04-06 11:02:34 +03:00
parent fa0388bc79
commit 0d11a097d8
703 changed files with 2292651 additions and 0 deletions

View File

@@ -0,0 +1,453 @@
using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections.Generic;
/// <summary>
/// TeamControlManager - Manages player control switching between team members.
/// Inspired by FRAG Pro Shooter's instant character switching system.
/// </summary>
public class TeamControlManager : MonoBehaviour
{
public static TeamControlManager Instance { get; private set; }
[Header("Current Control")]
[SerializeField] private GameObject currentControlledCharacter;
[SerializeField] private int currentCharacterIndex = 0;
[Header("Team References")]
[SerializeField] private List<GameObject> playerTeamCharacters = new List<GameObject>();
[Header("Camera Settings")]
[SerializeField] private bool autoFollowCamera = true;
// Events
public event System.Action<GameObject, GameObject> OnControlSwitched; // (old, new)
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
/// <summary>
/// Register a character to the player team
/// </summary>
public void RegisterPlayerTeamCharacter(GameObject character, bool isPlayerControlled = false)
{
if (!playerTeamCharacters.Contains(character))
{
playerTeamCharacters.Add(character);
DevLog.Log($"[TeamControlManager] Registered {character.name} to player team");
if (isPlayerControlled)
{
SetControlledCharacter(character);
}
}
}
/// <summary>
/// Remove a character from the team (e.g., on death)
/// </summary>
public void UnregisterCharacter(GameObject character)
{
if (playerTeamCharacters.Contains(character))
{
playerTeamCharacters.Remove(character);
DevLog.Log($"[TeamControlManager] Unregistered {character.name}");
// If this was the controlled character, switch to another
if (currentControlledCharacter == character)
{
SwitchToNextAvailableCharacter();
}
}
}
/// <summary>
/// Set which character is player-controlled using the unified ControlType system
/// </summary>
public void SetControlledCharacter(GameObject newCharacter)
{
if (newCharacter == null || newCharacter == currentControlledCharacter) return;
GameObject oldCharacter = currentControlledCharacter;
// Get TeamMember components
TeamMember oldMember = oldCharacter?.GetComponent<TeamMember>();
TeamMember newMember = newCharacter.GetComponent<TeamMember>();
// Switch control using ControlType system (handles all component config!)
if (oldMember != null)
{
// Old character becomes PlayerAI (still on player team, but AI controlled)
oldMember.SetControlType(TeamMember.ControlType.PlayerAI);
DevLog.Log($"[TeamControlManager] {oldCharacter.name} -> PlayerAI");
}
if (newMember != null)
{
// New character becomes HumanControlled
newMember.SetControlType(TeamMember.ControlType.HumanControlled);
DevLog.Log($"[TeamControlManager] {newCharacter.name} -> HumanControlled");
// CRITICAL: Bind input actions for the new controlled character
BindPlayerInputActions(newCharacter);
}
else
{
// Fallback for characters without TeamMember (shouldn't happen)
DevLog.LogWarning($"[TeamControlManager] {newCharacter.name} has no TeamMember component!");
EnablePlayerControl(newCharacter);
}
currentControlledCharacter = newCharacter;
currentCharacterIndex = playerTeamCharacters.IndexOf(newCharacter);
// Update camera using CameraManager (preferred) or legacy UpdateCameraTarget
if (CameraManager.Instance != null)
{
CameraManager.Instance.SetTarget(newMember);
}
else if (autoFollowCamera)
{
UpdateCameraTarget(newCharacter);
}
DevLog.Log($"[TeamControlManager] Switched control to {newCharacter.name}");
OnControlSwitched?.Invoke(oldCharacter, newCharacter);
}
/// <summary>
/// Switch to the next character in the team
/// </summary>
public void SwitchToNextCharacter()
{
if (playerTeamCharacters.Count <= 1) return;
int nextIndex = (currentCharacterIndex + 1) % playerTeamCharacters.Count;
// Find next alive character
for (int i = 0; i < playerTeamCharacters.Count; i++)
{
int checkIndex = (currentCharacterIndex + 1 + i) % playerTeamCharacters.Count;
GameObject candidate = playerTeamCharacters[checkIndex];
if (candidate != null && candidate.activeInHierarchy && IsCharacterAlive(candidate))
{
SetControlledCharacter(candidate);
return;
}
}
DevLog.LogWarning("[TeamControlManager] No other alive characters to switch to!");
}
/// <summary>
/// Switch to a specific character by index
/// </summary>
public void SwitchToCharacter(int index)
{
if (index >= 0 && index < playerTeamCharacters.Count)
{
GameObject character = playerTeamCharacters[index];
if (character != null && character.activeInHierarchy && IsCharacterAlive(character))
{
SetControlledCharacter(character);
}
}
}
/// <summary>
/// Switch to next available character when current dies
/// </summary>
private void SwitchToNextAvailableCharacter()
{
foreach (var character in playerTeamCharacters)
{
if (character != null && character.activeInHierarchy && IsCharacterAlive(character))
{
SetControlledCharacter(character);
return;
}
}
Debug.LogError("[TeamControlManager] No available characters to control!");
currentControlledCharacter = null;
}
private bool IsCharacterAlive(GameObject character)
{
// Check if character has health and is alive
HealthNew health = character.GetComponent<HealthNew>();
if (health != null)
{
return !health.isDead;
}
return true; // Assume alive if no health component
}
private void EnablePlayerControl(GameObject character)
{
// Update TeamMember
TeamMember teamMember = character.GetComponent<TeamMember>();
if (teamMember != null)
{
teamMember.IsPlayerControlled = true;
}
// Enable PlayerScript
PlayerScript playerScript = character.GetComponent<PlayerScript>();
if (playerScript != null)
{
playerScript.enabled = true;
playerScript.attack = true;
}
// Enable PlayerInput
PlayerInput playerInput = character.GetComponent<PlayerInput>();
if (playerInput != null)
{
playerInput.enabled = true;
try
{
playerInput.SwitchCurrentActionMap("Player Controls");
}
catch (System.Exception) { }
}
// Update tag
character.tag = "Player";
DevLog.Log($"[TeamControlManager] Enabled player control on {character.name}");
}
private void DisablePlayerControl(GameObject character)
{
// Update TeamMember
TeamMember teamMember = character.GetComponent<TeamMember>();
if (teamMember != null)
{
teamMember.IsPlayerControlled = false;
}
// Disable PlayerScript (attacks, not movement animation)
PlayerScript playerScript = character.GetComponent<PlayerScript>();
if (playerScript != null)
{
playerScript.attack = false;
// Keep enabled for animation but disable input
}
// Disable PlayerInput
PlayerInput playerInput = character.GetComponent<PlayerInput>();
if (playerInput != null)
{
playerInput.enabled = false;
}
DevLog.Log($"[TeamControlManager] Disabled player control on {character.name}");
}
private void EnableTeamAI(GameObject character)
{
TeamAIController aiController = character.GetComponent<TeamAIController>();
if (aiController != null)
{
aiController.enabled = true;
}
// Also enable CashSystemAI if present
CashSystemAI cashAI = character.GetComponent<CashSystemAI>();
if (cashAI != null)
{
cashAI.enabled = true;
}
DevLog.Log($"[TeamControlManager] Enabled AI on {character.name}");
}
private void DisableTeamAI(GameObject character)
{
TeamAIController aiController = character.GetComponent<TeamAIController>();
if (aiController != null)
{
aiController.enabled = false;
}
// Also disable CashSystemAI if present (player makes own decisions)
CashSystemAI cashAI = character.GetComponent<CashSystemAI>();
if (cashAI != null)
{
cashAI.enabled = false;
}
DevLog.Log($"[TeamControlManager] Disabled AI on {character.name}");
}
private void UpdateCameraTarget(GameObject character)
{
// Correct separation: Follow = root (orbit center), LookAt = head (aim point)
Transform followTarget = character.transform;
Transform lookAtTarget = FindCharacterHead(character);
// Update Cinemachine FreeLook with correct separation
Cinemachine.CinemachineFreeLook[] freeLookCameras = FindObjectsOfType<Cinemachine.CinemachineFreeLook>();
foreach (var cam in freeLookCameras)
{
if (cam.gameObject.activeInHierarchy)
{
cam.Follow = followTarget;
cam.LookAt = lookAtTarget;
}
}
// Update Cinemachine Virtual Cameras
Cinemachine.CinemachineVirtualCamera[] virtualCameras = FindObjectsOfType<Cinemachine.CinemachineVirtualCamera>();
foreach (var cam in virtualCameras)
{
if (cam.gameObject.activeInHierarchy)
{
cam.Follow = followTarget;
cam.LookAt = lookAtTarget;
}
}
DevLog.Log($"[TeamControlManager] Updated camera to follow {character.name} (Follow=root, LookAt=head)");
}
private Transform FindCharacterHead(GameObject character)
{
string[] headNames = { "Head", "head", "Bip001 Head", "mixamorig:Head", "Bip01 Head" };
foreach (string headName in headNames)
{
Transform head = FindChildRecursive(character.transform, headName);
if (head != null) return head;
}
// Fallback to character root
return character.transform;
}
private Transform FindChildRecursive(Transform parent, string name)
{
foreach (Transform child in parent)
{
if (child.name == name) return child;
Transform found = FindChildRecursive(child, name);
if (found != null) return found;
}
return null;
}
/// <summary>
/// Get the currently controlled character
/// </summary>
public GameObject GetCurrentControlledCharacter() => currentControlledCharacter;
/// <summary>
/// Get all player team characters
/// </summary>
public List<GameObject> GetPlayerTeamCharacters() => playerTeamCharacters;
/// <summary>
/// Clear all registered characters
/// </summary>
public void ClearTeam()
{
playerTeamCharacters.Clear();
currentControlledCharacter = null;
currentCharacterIndex = 0;
}
/// <summary>
/// Bind input action callbacks to PlayerScript methods.
/// This mirrors what Practice mode does in SelectionOptions.SetUpPlayerInputAI.
/// </summary>
private void BindPlayerInputActions(GameObject character)
{
PlayerInput playerInput = character.GetComponent<PlayerInput>();
PlayerScript playerScript = character.GetComponent<PlayerScript>();
if (playerInput == null || playerScript == null)
{
DevLog.LogWarning($"[TeamControlManager] Cannot bind input - PlayerInput: {playerInput != null}, PlayerScript: {playerScript != null}");
return;
}
// Ensure PlayerInput is enabled
playerInput.enabled = true;
try { playerInput.SwitchCurrentActionMap("Player Controls"); } catch { }
var actionMap = playerInput.currentActionMap;
if (actionMap == null)
{
Debug.LogError("[TeamControlManager] No current action map found!");
return;
}
// CRITICAL: Bind the Move action!
TryBindAction(actionMap, "Move", playerScript.OnMove);
// Bind attack actions
TryBindAction(actionMap, "Jab", playerScript.OnJab);
TryBindAction(actionMap, "Right", playerScript.OnRight);
TryBindAction(actionMap, "LeftHook", playerScript.OnLeftHook);
TryBindAction(actionMap, "RightHook", playerScript.OnRightHook);
TryBindAction(actionMap, "LeftElbow", playerScript.OnLeftElbow);
TryBindAction(actionMap, "RightElbow", playerScript.OnRightElbow);
TryBindAction(actionMap, "RightBody", playerScript.OnRightBody);
TryBindAction(actionMap, "PowerPunchLeft", playerScript.OnPowerPunchLeft);
TryBindAction(actionMap, "SupermanPunch", playerScript.OnSupermanPunch);
// Kick actions
TryBindAction(actionMap, "LegKickRight", playerScript.OnLegKickRight);
TryBindAction(actionMap, "BodyKickRight", playerScript.OnBodyKickRight);
TryBindAction(actionMap, "KneeRight", playerScript.OnKneeRight);
TryBindAction(actionMap, "BackSideKick", playerScript.OnBackSideKick);
TryBindAction(actionMap, "FrontKickRight", playerScript.OnFrontKickRight);
TryBindAction(actionMap, "LegPowerKickRight", playerScript.OnLegPowerKickRight);
// Dodge actions
TryBindAction(actionMap, "JumpBack", playerScript.OnJumpBack);
TryBindAction(actionMap, "JumpLeft", playerScript.OnJumpLeft);
TryBindAction(actionMap, "JumpRight", playerScript.OnJumpRight);
// Block actions
TryBindAction(actionMap, "BlockStepBack", playerScript.OnBlockStepBack);
TryBindAction(actionMap, "BlockBodyLeft", playerScript.OnBlockBodyLeft);
TryBindAction(actionMap, "BlockLeg", playerScript.OnBlockLeg);
// Special attacks
TryBindAction(actionMap, "Chokeslam", playerScript.OnChokeslam);
TryBindAction(actionMap, "Suplex", playerScript.OnSuplex);
TryBindAction(actionMap, "GiantSwing", playerScript.OnGiantSwing);
TryBindAction(actionMap, "DiamondCrusher", playerScript.OnDiamondCrusher);
TryBindAction(actionMap, "SumoSlap", playerScript.OnSumoSlap);
TryBindAction(actionMap, "JavelinTackle", playerScript.OnJavelinTackle);
TryBindAction(actionMap, "RKO", playerScript.OnRKO);
TryBindAction(actionMap, "Spear", playerScript.OnSpear);
TryBindAction(actionMap, "RockBottom", playerScript.OnRockBottom);
TryBindAction(actionMap, "F5", playerScript.OnF5);
DevLog.Log($"[TeamControlManager] Input actions bound for {character.name}");
}
/// <summary>
/// Helper to safely bind an action
/// </summary>
private void TryBindAction(InputActionMap actionMap, string actionName, System.Action<InputAction.CallbackContext> callback)
{
InputAction action = actionMap.FindAction(actionName);
if (action != null)
{
action.performed += callback;
if (actionName == "Move") action.canceled += callback;
}
}
}