using System; using System.Collections.Generic; using System.Linq; using TMPro; using Unity.VisualScripting; using UnityEditor; using UnityEngine; using UnityEngine.UI; [ExecuteAlways] public class GameManager : MonoBehaviour { public Transform ConnectionParent; public GameObject ConnectionPrefab; public Transform NodeParent; public GameObject NodePrefab; public GameObject hoverObject; public TMP_Text energyText; public TMP_Text currentPlayerText; public TMP_Text timerText; public GameObject actionListItemPrefab; public Transform actionListParent; public Button finishTurnBtn; [SerializeField] public List players; public int currentPlayer = -1; public Node pressedNode = null; public Connection pressedCon = null; public KeyCode altActionKey = KeyCode.LeftShift; public float zoomSpeed = 20f; public float minFOV = 20f; public float maxFOV = 80f; public float roundTime = 90; public static GameManager Instance { get; private set; } public bool regenerateOnChange = false; [SerializeField] public List actions; [HideInInspector] public float minConnectionLength = 6; [HideInInspector] public float maxConnectionLength = 6; [HideInInspector] public int nodeCount = 100; [HideInInspector] public int hoverRadiusCon = 50; [HideInInspector] public int hoverRadiusNode = 50; [HideInInspector] public int selectedLevel = -1; [SerializeField] [HideInInspector] public List levels = new List(); public enum ActionType { NONE, MOVE_HALF_UNITS, MOVE_ALL_UNITS, ATTACK_NODE_WITH_HALF, ATTACK_NODE_WITH_ALL, ATTACK_CON_WITH_HALF, ATTACK_CON_WITH_ALL, CONSTRUCT_CON, DESTRUCT_CON, EXPLODE_CON }; public Dictionary ActionTypeDescription = new Dictionary { { ActionType.NONE, "You should really never see this O_o \n(-1 Turn)" }, { ActionType.MOVE_HALF_UNITS, "Read that title again... \n(1 Turn)" }, { ActionType.MOVE_ALL_UNITS, "Read that title again... \n(1 Turn)" }, { ActionType.ATTACK_NODE_WITH_HALF, "Read that title again... \n(1 Turn)" }, { ActionType.ATTACK_NODE_WITH_ALL, "Read that title again... \n(1 Turn)" }, { ActionType.ATTACK_CON_WITH_HALF, "Attacks the enemy node using half of your Units to break the pending connection \n(1 Turn)" }, { ActionType.ATTACK_CON_WITH_ALL, "Attacks the enemy node using all of your Units to break the pending connection \n(1 Turn)" }, { ActionType.CONSTRUCT_CON, "Begins construction of a connection \n(2 Turns)" }, { ActionType.DESTRUCT_CON, "Begins deconstruction of a connection \n(2 Turns)" }, { ActionType.EXPLODE_CON, "Begins blazingly fast deconstruction of a connection \n(1 Turn)" } }; public Dictionary ActionTypeText = new Dictionary { { ActionType.NONE, "" }, { ActionType.MOVE_HALF_UNITS, "Move troops (1/2)" }, { ActionType.MOVE_ALL_UNITS, "Move troops" }, { ActionType.ATTACK_NODE_WITH_HALF, "Attack (1/2)" }, { ActionType.ATTACK_NODE_WITH_ALL, "Attack" }, { ActionType.ATTACK_CON_WITH_HALF, "Interrupt Construction (1/2)" }, { ActionType.ATTACK_CON_WITH_ALL, "Interrupt Construction" }, { ActionType.CONSTRUCT_CON, "Construct" }, { ActionType.DESTRUCT_CON, "Destruct" }, { ActionType.EXPLODE_CON, "Explode (immediately)" } }; public Dictionary ActionTypeEnergyUsage = new Dictionary { { ActionType.NONE, 0}, { ActionType.MOVE_HALF_UNITS, 1}, { ActionType.MOVE_ALL_UNITS, 1}, { ActionType.ATTACK_NODE_WITH_HALF, 1}, { ActionType.ATTACK_NODE_WITH_ALL, 1}, { ActionType.ATTACK_CON_WITH_HALF, 1}, { ActionType.ATTACK_CON_WITH_ALL, 1}, { ActionType.CONSTRUCT_CON, 1}, { ActionType.DESTRUCT_CON, 1}, { ActionType.EXPLODE_CON, 2 } }; [Serializable] public struct Action { public ActionType intendedAction; public bool onConnection; public bool altAction; public int nodeFromId; public int nodeToId; public int amount; public int player; } [Serializable] public class LevelData { [Serializable] public class NodeData { public Vector3 position; public int owner; public int units; } [Serializable] public class ConnectionData { public int nodeAIndex; public int nodeBIndex; public bool allowed = true; } public float minConnectionLength = 0; public float maxConnectionLength = 0; public List nodes = new List(); public List connections = new List(); } float timer = 0; void Awake() { if (Instance != null && Instance != this) { if (Application.isPlaying) Destroy(gameObject); return; } Instance = this; if (Application.isPlaying) DontDestroyOnLoad(gameObject); GetConnections().ForEach(obj => obj.DelConnect()); GetConnections().ForEach(obj => obj.SetConnect()); } private void Start() { LoadLevelData(selectedLevel); } private void Update() { hoverObject.SetActive(false); Player player = players.Find(p => p.id == currentPlayer); int totalEnergyCost = 0; actions.ForEach(a => totalEnergyCost += ActionTypeEnergyUsage[a.intendedAction]); if (player != null && Application.isPlaying) { timer -= Time.deltaTime; if (timer < 0) { ExecuteTurn(); return; } timerText.text = "[" + (int)timer / 60 + ":" + ((int)timer % 60 < 10 ? "0" : "") + $"{((int)timer % 60)}" + "]"; energyText.text = string.Concat(Enumerable.Repeat("□", Math.Max(0, player.energy - totalEnergyCost))) + string.Concat(Enumerable.Repeat("-", Math.Min(player.energy, totalEnergyCost))); currentPlayerText.text = "[ Player " + currentPlayer + " ]"; } if (Input.GetMouseButtonDown(0)) { pressedNode = GetNodes().Find(n => n.hovered && n.Owner == currentPlayer); } if (Input.GetMouseButton(0)) { if(pressedNode != null) { Node node = GetNodes().Find(n => n.hovered && n != pressedNode); if (node) { Connection con = GetConnections().Find(c => (c.nodeA == pressedNode && c.nodeB == node) || (c.nodeB == pressedNode && c.nodeA == node)) ?? null; ActionType possibleAction = CalcActionBetweenNodes(currentPlayer, pressedNode.id, node.id, Input.GetKey(altActionKey)); hoverObject.GetComponentsInChildren()[0].text = ActionTypeText[possibleAction]; hoverObject.GetComponentsInChildren()[1].text = ActionTypeDescription[possibleAction]; hoverObject.GetComponentsInChildren()[2].text = string.Concat(Enumerable.Repeat("□", ActionTypeEnergyUsage[possibleAction])); hoverObject.SetActive(true); if (con != null && con.allowed) con.hovered = true; } } } if (Input.GetMouseButtonUp(0)) { if (pressedNode != null) { Node toNode = GetNodes().Find(n => n.hovered && pressedNode != n); if (toNode) { Connection con = GetConnections().Find(c => (c.nodeA == pressedNode && c.nodeB == toNode) || (c.nodeB == pressedNode && c.nodeA == toNode)) ?? null; if (con != null && con.allowed) PushNewAction(pressedNode.id, toNode.id); } } } if (!hoverObject.activeSelf) { Connection hoveredCon = GetConnections().Find(c => c.hovered); if (hoveredCon != null) { ActionType possibleAction = CalcActionBetweenNodes(currentPlayer, hoveredCon.nodeA.id, hoveredCon.nodeB.id, Input.GetKey(altActionKey), true); if (possibleAction != ActionType.NONE) { hoverObject.GetComponentsInChildren()[0].text = ActionTypeText[possibleAction]; hoverObject.GetComponentsInChildren()[1].text = ActionTypeDescription[possibleAction]; hoverObject.GetComponentsInChildren()[2].text = string.Concat(Enumerable.Repeat("□", ActionTypeEnergyUsage[possibleAction])); hoverObject.SetActive(true); } } } } public ActionType CalcActionBetweenNodes(int player, int nodeFromId, int nodeToId, bool altAction = false, bool onConnection = false) { Node nodeTo = GetNodes().Find(n => n.id == nodeToId); Node nodeFrom = GetNodes().Find(n => n.id == nodeFromId); Connection con = GetConnections().Find(c => (c.nodeA == nodeTo && c.nodeB == nodeFrom) || (c.nodeA == nodeFrom && c.nodeB == nodeTo)); if (con == null || con.allowed == false) return ActionType.NONE; // Clicked on connection if (onConnection) { if (con.state == Connection.BuildState.BUILT && nodeFrom.Owner == player && nodeTo.Owner == player) { return altAction ? ActionType.EXPLODE_CON : ActionType.DESTRUCT_CON; } else if (con.state == Connection.BuildState.EMPTY && (nodeFrom.Owner == player || nodeTo.Owner == player)) { return ActionType.CONSTRUCT_CON; } } // Dragged owned nodeFrom to nodeTo else if (nodeFrom.Owner == player) { // To Owned if (nodeTo.Owner == player) { switch (con.state) { case Connection.BuildState.EMPTY: return ActionType.CONSTRUCT_CON; case Connection.BuildState.CONSTRUCTING: case Connection.BuildState.DECONSTRUCTING: return ActionType.NONE; case Connection.BuildState.BUILT: return altAction ? ActionType.MOVE_HALF_UNITS : ActionType.MOVE_ALL_UNITS; } } // To Hostile else if (nodeTo.Owner >= 0) { switch (con.state) { case Connection.BuildState.EMPTY: return ActionType.CONSTRUCT_CON; case Connection.BuildState.CONSTRUCTING: return altAction ? ActionType.ATTACK_CON_WITH_HALF : ActionType.ATTACK_CON_WITH_ALL; case Connection.BuildState.DECONSTRUCTING: return ActionType.NONE; case Connection.BuildState.BUILT: return altAction ? ActionType.ATTACK_NODE_WITH_HALF : ActionType.ATTACK_NODE_WITH_ALL; } } // To Unclaimed else { switch (con.state) { case Connection.BuildState.EMPTY: return ActionType.CONSTRUCT_CON; case Connection.BuildState.CONSTRUCTING: case Connection.BuildState.DECONSTRUCTING: return ActionType.NONE; case Connection.BuildState.BUILT: return altAction ? ActionType.MOVE_HALF_UNITS : ActionType.MOVE_ALL_UNITS; } } } return ActionType.NONE; } public List GetNodes() => NodeParent.GetComponentsInChildren().ToList(); public List GetConnections() => ConnectionParent != null ? ConnectionParent.GetComponentsInChildren()?.ToList() ?? new() : new(); public void UpdateConstructions() { foreach(Connection con in GetConnections()) { if (con.constructingPlayerId == currentPlayer) { if (con.state == Connection.BuildState.CONSTRUCTING) { con.state = Connection.BuildState.BUILT; con.SetConnect(); } else if (con.state == Connection.BuildState.DECONSTRUCTING) { con.state = Connection.BuildState.EMPTY; con.DelConnect(); } con.constructingPlayerId = -1; } } } public void ExecuteTurn() { Player player = players.Find(p => p.id == currentPlayer); if (player == null) { Debug.LogWarning("Player " + currentPlayer + " not found!"); return; } int totalEnergyCost = 0; actions.ForEach(a => totalEnergyCost += ActionTypeEnergyUsage[a.intendedAction]); if (totalEnergyCost > player.energy) { Debug.Log("Insuficcient Energy for Turn! (needs " + totalEnergyCost + " has " + player.energy + ")"); return; } actions.ForEach(a => ExecuteAction(a)); for (int i = 0; i < actionListParent.childCount; i++) { Destroy(actionListParent.GetChild(i).gameObject); } actions.Clear(); // Select next player currentPlayer++; Player nextPlayer = players.Find(p => p.id == currentPlayer); // Executed turn of last Player => Round ended if(nextPlayer == null) currentPlayer = 0; UpdateConstructions(); nextPlayer = players.Find(p => p.id == currentPlayer); // Refill energy nextPlayer.energy = 3; nextPlayer.GetOwnedNodes().ForEach(node => { nextPlayer.energy += node.Nachos; if (node.Nachos > 0) { node.Units += 5 * node.Nachos; } }); timer = roundTime; } public void PushAction(Action action) { var ui = Instantiate(actionListItemPrefab, actionListParent); ui.GetComponentInChildren().text = ActionTypeText[action.intendedAction] + " (" + action.nodeFromId + ">" + action.nodeToId + ")"; ui.GetComponentInChildren