Files
MRCS/Assets/_MrCs/Scripts/Manager/GameManager.cs
2025-12-01 10:41:15 +08:00

827 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
using Common;
using DamageNumbersPro;
using DragonLi.Core;
using Mirror;
using UnityEngine;
using UnityEngine.PlayerLoop;
using XPlugin.Data.JsonLiteDB;
using static XPlugin.Data.JsonLiteDB.JsonLiteDB;
using LitJson;
using Pathfinding;
using DarkTonic.MasterAudio;
using NaughtyAttributes;
using Unity.Mathematics;
using UnityEngine.UIElements;
using EventDispatcher = DragonLi.Core.EventDispatcher;
using Random = UnityEngine.Random;
/// <summary>
/// 游戏状态
/// </summary>
public enum GameState
{
None = 0,
Playing = 1,
/// <summary>
/// 胜利
/// </summary>
Victory = 2,
/// <summary>
/// 失败
/// </summary>
Failur = 3,
Settle,
Waiting= 5,
}
public class GameManager : NetworkBehaviour
{
public static GameManager Ins { get; private set; }
#region
public GameObject redDoorPre;
public GameObject blueDoorPre;
public GameObject AirdropPlanePre;//空投飞机
public GameObject[] airdropItems;//空头道具
public GameObject startGameItemPre;
public GameObject damageNumberPre;
public GameObject redDamageNumberPre;
// 怪物ui预制体
public GameObject EnemyUIPre;
public GameObject roundPlaceDoorPre;
#endregion
#region
public GameObject redDoor;
public GameObject blueDoor;
#endregion
// json数据库
private JsonLiteDB DB;
// 怪物信息集合
public Dictionary<EnemyType, Dictionary<int, EnemyInfo>> EnemyInfos = new Dictionary<EnemyType, Dictionary<int, EnemyInfo>>();
// 枪械信息集合
public Dictionary<GunType, Dictionary<int, GunInfo>> GunInfos = new Dictionary<GunType, Dictionary<int, GunInfo>>();
/// <summary>
/// 所有敌人UI
/// </summary>
public Dictionary<int, PlayerUI> EnemyUIList = new Dictionary<int, PlayerUI>();
public Dictionary<string, SettleInfo> SettleInfos = new Dictionary<string, SettleInfo>();
public List<Player> allPlayers = new List<Player>();
// 游玩结束时间
[NonSerialized]
[SyncVar]
public long vistEnd = 0;
[NonSerialized]
[SyncVar]
public long vistRoundEnd = 0;
// 总游玩时长
private int vistAllTime = (int)(60*10f);
[NonSerialized]
[SyncVar]
private int roundAllTime;
[SyncVar]
public string settleData = "";
[SyncVar]
[NonSerialized]
public int CurRoundIndex;
[SyncVar]
public GameState gameState = GameState.None;
[NonSerialized]
[SyncVar]
public int BlueScore = 0;
[NonSerialized]
[SyncVar]
public int RedScore = 0;
[NonSerialized]
[SyncVar]
public int RedRoundScore = 0;
[NonSerialized]
[SyncVar]
public int BlueRoundScore = 0;
void Awake()
{
Ins = this;
}
void Start()
{
if (isClient)
{
UpdateConf();
}
if (isServer)
{
CreateDoor(GameLocal.Ins.redDoorPos, GameLocal.Ins.blueDoorPos,GameLocal.Ins.startGameItemPos);
roundAllTime = vistAllTime / 3;
}
}
/// <summary>
/// 创建门
/// </summary>
[Server]
public void CreateDoor(Transform redPos, Transform bluePos,Transform startGamePos)
{
SetGameState(GameState.Waiting);
if(!isServer)
return;
//创建红色门
redDoor = Instantiate(redDoorPre,redPos.position,Quaternion.identity);
NetworkServer.Spawn(redDoor);
//创建蓝色门
blueDoor = Instantiate(blueDoorPre,bluePos.position,Quaternion.identity);
NetworkServer.Spawn(blueDoor);
//创建开始球
// GameObject gameStartObj = Instantiate(startGameItemPre,startGamePos.position,Quaternion.identity);
// gameStartObj.transform.eulerAngles = new Vector3(0f,180f,0f);
// NetworkServer.Spawn(gameStartObj);
//测试枪械
//GameLocal.Ins.self.GivePistol();
}
[ClientRpc]
public void RpcShowDamageNumber(float damage, Vector3 pos, bool isCritical)
{
GameObject prefab = isCritical ? redDamageNumberPre : damageNumberPre;
if (prefab == null) return;
GameObject obj = Instantiate(prefab, pos, Quaternion.identity);
var dmg = obj.GetComponent<DamageNumberNetwork>();
if (dmg != null)
{
dmg.worldPos = pos;
dmg.damageValue = isCritical? damage*2 : damage;
dmg.OnStartClient();
}
Destroy(obj, 10f);
}
[Server]
public void GameStart()
{
Debug.Log("开始游戏");
vistEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + vistAllTime;
vistRoundEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + roundAllTime;
_airdropTime = 25;
_curAirdropTime = _airdropTime;
ChangeBgmRpc(1);
StartAirDrop();
// 延迟1s后给所有人发放武器
CoroutineTaskManager.Instance.WaitSecondTodo(() =>
{
foreach (var player in allPlayers)
{
if (player != null)
{
player.HideInvincible();
ShowTeamUI(player.connectionToClient, (int)player.teamType);
}
}
RpcShowSettle();
}, 1f);
StartRoundTime();
}
[TargetRpc]
public void ShowTeamUI(NetworkConnectionToClient conn,int teamId)
{
EventDispatcher.TriggerEvent("ChangeTeam", teamId);
}
[Server]
public void StartRoundTime()
{
CurRoundIndex++;
CoroutineTaskManager.Instance.WaitSecondTodo(() =>
{
if(gameState==GameState.Waiting)
return;
StopRound();
}, roundAllTime);
}
[Server]
public void RegisterPlayer(Player p)
{
if (!allPlayers.Contains(p))
allPlayers.Add(p);
}
// ★ 玩家离开游戏时调用在Player.OnStopServer里调用
[Server]
public void UnRegisterPlayer(Player p)
{
if (allPlayers.Contains(p))
allPlayers.Remove(p);
}
// ★ 游戏开始时调用 → 自动分配队伍
[Server]
public void AutoAssignTeams()
{
int redCount = 0;
int blueCount = 0;
// 统计已选队伍玩家
foreach (var p in allPlayers)
{
if (p.teamType == TeamType.Red) redCount++;
else if (p.teamType == TeamType.Blue) blueCount++;
}
int totalPlayers = allPlayers.Count;
// -----------------------------
// ★ 第一步:强制平衡队伍(如果人数>=2 且某队==0
// -----------------------------
if (totalPlayers >= 2)
{
if (redCount == 0 && blueCount > 0)
{
// 需要从蓝队分一半人到红队
int need = Mathf.Max(1, blueCount / 2);
foreach (var p in allPlayers)
{
if (need <= 0) break;
if (p.teamType == TeamType.Blue)
{
p.teamType = TeamType.Red;
need--;
redCount++;
blueCount--;
}
}
}
else if (blueCount == 0 && redCount > 0)
{
// 需要从红队分一半人到蓝队
int need = Mathf.Max(1, redCount / 2);
foreach (var p in allPlayers)
{
if (need <= 0) break;
if (p.teamType == TeamType.Red)
{
p.teamType = TeamType.Blue;
need--;
blueCount++;
redCount--;
}
}
}
}
// -----------------------------
// ★ 第二步:给未分配队伍(NONE)的玩家自动分配
// -----------------------------
foreach (var p in allPlayers)
{
if (p.teamType != TeamType.None)
continue;
if (redCount < blueCount)
{
p.teamType = TeamType.Red;
redCount++;
}
else if (blueCount < redCount)
{
p.teamType = TeamType.Blue;
blueCount++;
}
else
{
// 平局 → 随机
if (Random.value > 0.5f)
{
p.teamType = TeamType.Red;
redCount++;
}
else
{
p.teamType = TeamType.Blue;
blueCount++;
}
}
// 给队伍分配武器
GivePistol(p.index);
}
}
[Server]
public void CheckGameStart()
{
int curTeamIndex = 0;
bool isStartGame=false;
foreach (var player in allPlayers)
{
if (player.teamType != TeamType.None)
curTeamIndex++;
}
isStartGame=curTeamIndex >= GameLocal.Ins.addPlayerIndex;
if (isStartGame)
{
if(gameState== GameState.Playing)
return;
AutoAssignTeams();
gameState = GameState.Playing;
BlueRoundScore = 0;
RedRoundScore = 0;
NetworkServer.Destroy(redDoor);
NetworkServer.Destroy(blueDoor);
RpcShowHUD();
MonoSingleton<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
{
GameStart();
}, 3f, this);
}
}
[ClientRpc]
public void RpcShowSettle()
{
GameLocal.Ins.Settle.SetActive(true);
}
[ClientRpc]
public void GivePistol(int index)
{
Debug.Log("创建武器中...");
foreach (var item in NetworkServer.connections.Values)
{
Player player = item.identity.GetComponent<Player>();
if (player != null&&player.index == index)
{
player.GivePistol();
player.GiveArmor();
}
}
}
[Server]
public void SetGameState(GameState state)
{
gameState = state;
}
[Server]
public void GameOver()
{
gameState = GameState.Settle;
// bool isBlue = GameLocal.Ins.self.teamType == TeamType.Blue;
// bool isWin;
// if (isBlue)
// {
// isWin = BlueScore > RedScore;
// }
// RpcShowWin(isBlue);
// if (blueScore >= redScore)
// {
// RpcShowWin(true);
// }
// else
// {
// RpcShowWin(false);
// }
}
[ClientRpc]
public void RpcShow()
{
SettlePanel.Show();
}
[ClientRpc]
public void ChangeBgmRpc(int i)
{
GameLocal.Ins.BGMState.StateChange(i);
}
[Server]
public void CreateAirdropPlane(Vector3 startPos, Vector3 endPos)
{
GameObject airdropPlane = Instantiate(AirdropPlanePre,startPos,quaternion.identity);
NetworkServer.Spawn(airdropPlane);
AirdropPlane airdropPlaneScript = airdropPlane.GetComponent<AirdropPlane>();
airdropPlaneScript.OnSpawn(endPos);
}
[Server]
public void CreateAirDropItem(int id ,Vector3 startPos)
{
GameObject airdropItem = Instantiate(airdropItems[id],startPos,quaternion.identity);
NetworkServer.Spawn(airdropItem);
}
/// <summary>
/// 痛击场上所有敌方单位
/// </summary>
[Command(requiresAuthority = false)]
public void DamageAllEnemy()
{
}
/// <summary>
/// 更新配置表
/// </summary>
public void UpdateConf()
{
string text = Resources.Load<TextAsset>("Data").text;
if (text != null)
{
ParseGameJson(text);
}
}
public void ParseGameJson(string text)
{
DB = new JsonLiteDB();
DB.Load(text);
TableReader infoReader = DB["GunsInfo"].GetReader();
while (infoReader.Read())
{
GunInfo info = new GunInfo(infoReader);
GunInfos.TryGetValue((GunType)info.Type, out Dictionary<int, GunInfo> infoList);
if (infoList == null)
{
Dictionary<int, GunInfo> list = new Dictionary<int, GunInfo>();
list.Add(info.Lvl, info);
GunInfos.Add((GunType)info.Type, list);
}
else
{
infoList.Add(info.Lvl, info);
}
}
Debug.Log("游戏数值更新 -> complete");
}
[Server]
public void CreateEnemyUI(Enemy enemy)
{
GameObject enemyUI = Instantiate(EnemyUIPre);
NetworkServer.Spawn(enemyUI);
PlayerUI enemyUIScript = enemyUI.GetComponent<PlayerUI>();
//enemyUIScript.Init(enemy);
EnemyUIList.Add(enemy.id, enemyUIScript);
}
[Server]
public void DeleteEnemyUI(int id)
{
GameObject enemyUI = EnemyUIList[id].gameObject;
EnemyUIList.Remove(id);
NetworkServer.Destroy(enemyUI);
}
[ClientRpc]
public void RpcShowHUD()
{
HUDPanel.Show();
}
[Server]
public void AddData(int playerIndex)
{
SettleInfos.Add(playerIndex.ToString(), new SettleInfo());
}
#region
public string GetLessTimeStr()
{
string res = "";
if (gameState == GameState.Playing)
{
res = FormatTime((int)(vistEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds));
}
return res;
}
public string GetRoundLessTimeStr()
{
string res = "";
if (gameState == GameState.Playing)
{
res = FormatTime((int)(vistRoundEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds));
}
return res;
}
public int GetLessTimeSeconds()
{
if (isServer)
{
return (int)(vistEnd - NetworkTime.time);
}
else
{
return (int)(vistEnd - NetworkClient.connection.remoteTimeStamp);
}
}
// 时分秒
public string FormatTime(int totalSeconds)
{
//确保时间不为负
totalSeconds = Mathf.Max(0, totalSeconds);
int hours = totalSeconds / 3600;
// string hh = hours < 10 ? "0" + hours : hours.ToString();
int minutes = (totalSeconds - hours * 3600) / 60;
string mm = minutes < 10f ? "0" + minutes : minutes.ToString();
int seconds = totalSeconds - hours * 3600 - minutes * 60;
string ss = seconds < 10 ? "0" + seconds : seconds.ToString();
return string.Format("{0}:{1}", mm, ss);
}
#endregion
[ClientRpc]
public void PlaySound2DRPC(string sound)
{
MasterAudio.PlaySound(sound);
}
[SyncVar]
private int syncRemainingTime;
private float _airdropTime;//空投时间
private float _curAirdropTime;//当前空头时间
private bool _isStartAirdrop;//是否开始倒计时空投
public void StartAirDrop()
{
_isStartAirdrop = true;
}
public void StopAirDrop()
{
_isStartAirdrop = false;
}
void Update()
{
if (isServer&& gameState==GameState.Playing&&_isStartAirdrop)
{
//服务器计算剩余时间并同步
//syncRemainingTime = GetLessTimeSeconds();
_curAirdropTime+=Time.deltaTime;
if (_curAirdropTime >= _airdropTime)
{
int airdropPosId = Random.Range(0, 4);
int endAirdropPosId =0;
if (airdropPosId == 0)
endAirdropPosId = 1;
if (airdropPosId == 1)
endAirdropPosId = 0;
if (airdropPosId == 2)
endAirdropPosId = 3;
if (airdropPosId == 3)
endAirdropPosId = 2;
CreateAirdropPlane(GameLocal.Ins.startPlanePos[airdropPosId].position,GameLocal.Ins.startPlanePos[endAirdropPosId].position);
_curAirdropTime = 0;
StopAirDrop();
}
}
if (isServer)
{
if (Input.GetKeyDown(KeyCode.Space))
{
StopRound();
}
}
}
[Server]
public void StartRound()
{
gameState = GameState.Playing;
ChangeBgmRpc(CurRoundIndex+1);
vistRoundEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + roundAllTime;
foreach (var item in allPlayers)
{
item.HideInvincible();
}
StartRoundTime();
}
[Server]
public void StopRound()
{
gameState = GameState.Waiting;
Debug.LogError("本回合结束");
ChangeBgmRpc(0);
if (CurRoundIndex >= 3)
gameState = GameState.Settle;
if (BlueRoundScore > RedRoundScore)
{
BlueScore += 1;
}
if (BlueRoundScore < RedRoundScore)
{
RedScore += 1;
}
if (BlueRoundScore == RedRoundScore&& BlueRoundScore!=0&& RedRoundScore!=0)
{
RedScore += 1;
BlueScore += 1;
}
foreach (var item in allPlayers)
{
item.ClearGun();
}
MonoSingleton<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
{
foreach (var item in allPlayers)
{
RpcPlayerRoundEnd(item.connectionToClient);
}
}, 1f, this);
CreateRoundPlaceDoorPre();
MonoSingleton<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
{
if(gameState==GameState.Settle)
return;
BlueRoundScore = 0;
RedRoundScore = 0;
foreach (var player in allPlayers)
{
RpcPlayerWaitRoundEnd(player.connectionToClient);
}
}, 10f, this);
MonoSingleton<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
{
if(gameState==GameState.Settle)
return;
StartRound();
}, 13f, this);
}
[TargetRpc]
public void RpcPlayerRoundEnd(NetworkConnection target)
{
EventDispatcher.TriggerEvent("NewRoundStart",
CurRoundIndex==3? BlueScore:BlueRoundScore,
CurRoundIndex==3? RedScore:RedRoundScore,
CurRoundIndex);
}
[TargetRpc]
public void RpcPlayerWaitRoundEnd(NetworkConnection target)
{
GameLocal.Ins.RoundUI.SetActive(false);
EventDispatcher.TriggerEvent("NewWaveStart");
}
public Player GetPlayerByIndex(int index)
{
return allPlayers.FirstOrDefault(p => p.index == index);
}
#region
[Server]
public void CreateRoundPlaceDoorPre()
{
//创建吸收场地黑洞
var roundPlaceDoor = Instantiate(roundPlaceDoorPre,GameLocal.Ins.roundPlaceDoorPos.position,Quaternion.identity);
roundPlaceDoor.transform.eulerAngles = GameLocal.Ins.roundPlaceDoorPos.eulerAngles;
NetworkServer.Spawn(roundPlaceDoor);
foreach (var player in allPlayers)
{
PlaceFly(player.connectionToClient, roundPlaceDoor.transform.position);
}
MonoSingleton<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
{
if (roundPlaceDoor != null)
NetworkServer.Destroy(roundPlaceDoor);
if(gameState==GameState.Settle)
return;
// RPC通知客户端执行地图升起动画
foreach (var player in allPlayers)
{
RpcMapRise(player.connectionToClient);
}
}, 8f, this);
}
[TargetRpc]
public void PlaceFly(NetworkConnection target, Vector3 blackHolePos)
{
GameObject curPlace = GameLocal.Ins.CurPlace(CurRoundIndex);
foreach (Transform child in curPlace.GetComponentsInChildren<Transform>())
{
if (child == curPlace.transform) continue; // 跳过根节点
Collider childCollider = child.GetComponent<Collider>();
if (childCollider != null)
childCollider.enabled = false;
GameLocal.Ins.AddObject(child, blackHolePos);
}
}
[TargetRpc]
public void RpcMapRise(NetworkConnection target)
{
StartCoroutine(MapRiseCoroutine());
}
private IEnumerator MapRiseCoroutine()
{
GameLocal.Ins.ShowPlace(CurRoundIndex+1);
GameObject newMap = GameLocal.Ins.CurPlace(CurRoundIndex+1); // 新地图预先放在场景里
Vector3 startPos = newMap.transform.position;
Vector3 endPos = new Vector3(startPos.x, 0f, startPos.z);
float duration = 2f;
float t = 0f;
while (t < duration)
{
t += Time.deltaTime;
float progress = t / duration;
// 使用平滑插值(可改成 easeOut
float smooth = Mathf.SmoothStep(0, 1, progress);
newMap.transform.position = Vector3.Lerp(startPos, endPos, smooth);
yield return null;
}
newMap.transform.position = endPos;
}
#endregion
#region
/// <summary>
/// 红色方加分
/// </summary>
/// <param name="score"></param>
public void AddRedScore(int score)
{
RedRoundScore+=score;
}
/// <summary>
/// 蓝色方加分
/// </summary>
/// <param name="score"></param>
public void AddBlueScore(int score)
{
BlueRoundScore+=score;
}
#endregion
}