unityzgy/Assets/Scripts/ThisProjectTool/EditorAppraiser.cs
ayuan9957 64d16aa5ec fix: MyToggleButton本地化+厂区间距提示本地化+测距按钮翻译
- MyToggleButton: 使用LocalizationManager.Get翻译切换按钮文本
- EditorAppraiser: CheckAreaDistance/CheckAreaToWaterDistance使用Format
- 新增翻译: 开始测距/结束测距/Ctrl提示/边界间距提示/水源距离提示
2026-07-16 15:24:53 +08:00

1026 lines
48 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 UnityEngine;
/// <summary>
/// 编辑评估类 按照提供规则评估扣分情况 并直接计入操作纪录类
/// </summary>
public class EditorAppraiser
{
#region //行军编队评估
static Dictionary<FormationGroupingEnum, List<string>> rightPersonDic = new Dictionary<FormationGroupingEnum, List<string>>()
{
{ FormationGroupingEnum., new List<string>() { "连长", "指导员", "副连长", "装甲技师", "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "液压修理工", "车辆综合电子系统修理工", "汽车修理工" } },
{ FormationGroupingEnum., new List<string>() { "连长", "指导员", "副连长", "装甲技师", "汽车技师" } },
{ FormationGroupingEnum., new List<string>() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
{ FormationGroupingEnum., new List<string>() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
//{ FormationGroupingEnum.主体编队, new List<string>() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
{ FormationGroupingEnum.殿, new List<string>() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "车辆抢救工", "汽车修理工" } },
};
static Dictionary<FormationGroupingEnum, List<string>> rightCarDic = new Dictionary<FormationGroupingEnum, List<string>>()
{
{ FormationGroupingEnum., new List<string>() {"NY033H型轮式轻型防化分队指挥车", "ZJQ84Q式中型坦克抢救车", "VME104履带装甲抢修车","VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ311型装甲拆装工程车", "CXZ511型装甲保养工程车", "CXC012汽车修理工程车","VME301-5型轮式装备抢救车", "CXC022汽车保养车" } },
{ FormationGroupingEnum., new List<string>() { "NY033H型轮式轻型防化分队指挥车" } },
{ FormationGroupingEnum., new List<string>() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车","VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "徐工QY25K型汽车起重机", "VME301-5型轮式装备抢救车", "CXC022汽车保养车" } },
{ FormationGroupingEnum., new List<string>() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车", "VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "VMG101履带装甲补给车", "VME301-5型轮式装备抢救车", "CXC022汽车保养车", "CXC012汽车修理工程车" } },
//{ FormationGroupingEnum.主体编队, new List<string>() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
{ FormationGroupingEnum.殿, new List<string>() { "ZJQ84Q式中型坦克抢救车", "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "VME301-5型轮式装备抢救车"} },
};
/// <summary>
/// 评估编队得分 记录扣分项
/// </summary>
public void AppraiseTeam(Dictionary<FormationGroupingEnum, List<CarIconInstanceCtrl>> onMapCarsGroups)
{
foreach (var item in onMapCarsGroups)
{
switch (item.Key)
{
case FormationGroupingEnum.://不连贯训练时的默认分组
AppraisFrontTeam(item.Value, item.Key.ToString());
break;
case FormationGroupingEnum.:
AppraisLeaderGroup(item.Value);
break;
case FormationGroupingEnum.:
AppraisFrontGroup(item.Value, item.Key.ToString());
break;
case FormationGroupingEnum.://暂时无评估规则
break;
case FormationGroupingEnum.://暂时无评估规则
break;
case FormationGroupingEnum.殿:
AppraisFollowGroup(item.Value, item.Key.ToString());
break;
default:
break;
}
}
//StudentOperateRecorder.Instance.InjectOperateMsg($"行军编队部分评估结束!");
}
/// <summary>
/// 训练模式操作过程中检查车辆类型是否为当前车队的标准配置人员
/// </summary>
/// <param name="carType"> 车辆类型</param>
/// <param name="groupingEnum">编组</param>
/// <returns></returns>
public static bool CheckCar(string carType, FormationGroupingEnum groupingEnum)
{
if (rightCarDic.ContainsKey(groupingEnum))
return rightCarDic[groupingEnum].Contains(carType);
else
return true;
}
/// <summary>
/// 训练模式操作过程中检查人员类型是为当前车队的标准配置人员
/// </summary>
/// <param name="personName"></param>
/// <param name="groupingEnum"></param>
/// <returns></returns>
public static bool CheckPerson(string personName, FormationGroupingEnum groupingEnum)
{
if (rightPersonDic.ContainsKey(groupingEnum))
return rightPersonDic[groupingEnum].Contains(personName);
else
return true;
}
//评价指挥组 与其他分组评价方法略有差异 不做整合
private void AppraisLeaderGroup(List<CarIconInstanceCtrl> item)
{
float deductsScore = 0;
//指挥车数量
int leaderCarCount = 0;
//电源车 和电子检修车
int PowerAndConnectCarCount = 0;
//其它不应该出现的车型
List<string> otherCars = new List<string>();
//车队中具备的人员
List<string> personType = new List<string>();
for (int i = 0; i < item.Count; i++)
{
string carName = item[i].text_IconName.text;
foreach (var personItem in item[i].personNames)//统计指挥组中的人员类型
{
if (!personType.Contains(personItem.Key))
personType.Add(personItem.Key);
}
if (carName.Contains("NY033H"))//模糊检查 避免个别名称不太符合的情况和新增装备的情况
{
leaderCarCount++;
}
else if (carName.Contains("电源") || carName.Contains("电子检测维修"))
PowerAndConnectCarCount++;
else
{
if (!otherCars.Contains(carName))
otherCars.Add(carName);
}
}
//增加多余车型 每种车型扣1分
if (otherCars.Count != 0)
{
deductsScore += otherCars.Count * 1;
string otherCarsStr = GetStrFromList(otherCars);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}增加{1}等{2}种车型,扣{3}分", "指挥组", otherCarsStr, otherCars.Count, otherCars.Count);
}
int groupCount = item.Count;
if (leaderCarCount == 0)
{
deductsScore += 10;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}缺少标准车型:{1},扣{2}分", "指挥组", "NY033H型轮式轻型防化分队指挥车", 10);
}
if (leaderCarCount == 1)
{
deductsScore += 3;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("指挥组仅有一台分队指挥车扣3分");
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum..ToString());
deductsScore = 0;
//装备数量超过四台 每超一台扣0.5分
if (groupCount > 4) //数量超出
{
deductsScore += ((groupCount - 4) * 0.5f);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}装备数量为{1}台,超出{2}台,扣{3}分", "指挥组", groupCount, 4, otherCars.Count * 0.5f);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum..ToString());
deductsScore = 0;
List<string> targetPerson = new List<string>() { "连长", "指导员", "副连长", "装甲技师", "汽车技师" }; //车队应该配备的人员
List<string> anyPerson = GetAny<string>(targetPerson, personType);//不存在的标准人员
if (anyPerson.Count > 0)
{
deductsScore += anyPerson.Count;
string anyPersonsStr = GetStrFromList(anyPerson);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}人员工种缺少{1},扣{2}分", "指挥组", anyPersonsStr, anyPerson.Count);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum..ToString());
}
//评价前出保障组
private void AppraisFrontGroup(List<CarIconInstanceCtrl> item, string groupName)
{
List<string> carTypes = GetCarTypes(item);//已有车队类型
List<string> dueCars = new List<string>() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车",
"VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "徐工QY25K型汽车起重机", "VME301-5型轮式装备抢救车", "CXC022汽车保养车" };//应当具备的车型
List<string> notDeductCars = new List<string>() { "VME301机械维修车", "VME105履带装甲抢修车", "CXZ311型装甲拆装工程车", "CXZ321型装甲拆装工程车", "徐工QY16K型汽车起重机", "高机动电源挂车" };//多出但不扣分的车型
GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 12);
Dictionary<string, int> personDic;//车队人员类型+数量字典
List<string> personTypes;//车队人员类型
GetPersonTypes(item, out personDic, out personTypes);
List<string> targetPerson = new List<string>() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" };
List<string> notDeductPerson = new List<string>() { "钳工", "制配工", "充电工", "车工", "钣焊工", "光电修理工" };//多出不扣分的工种
Dictionary<string, int> aloneCheckPersons = new Dictionary<string, int>();//需要单独检查不得低于某数量的工种 每工种少一人扣一分
aloneCheckPersons.Add("抢救工", 6);
aloneCheckPersons.Add("装甲底盘修理工", 5);
aloneCheckPersons.Add("汽车修理工", 4);
GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons);
}
//评价殿后保障组
private float AppraisFollowGroup(List<CarIconInstanceCtrl> item, string groupName)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(item);//已有车队类型
List<string> dueCars = new List<string>() { "ZJQ84Q式中型坦克抢救车", "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "VME301-5型轮式装备抢救车" };//应当具备的车型
List<string> notDeductCars = new List<string>() { "96式坦克抢救车", "VME105履带装甲抢修车", "CXZ311型装甲拆装工程车", "CXZ321型装甲拆装工程车" };//多出但不扣分的车型
GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 6);
Dictionary<string, int> personDic;//车队人员类型+数量字典
List<string> personTypes;//车队人员类型
GetPersonTypes(item, out personDic, out personTypes);
List<string> targetPerson = new List<string>() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "车辆抢救工", "汽车修理工" };//标准人员工种
List<string> notDeductPerson = new List<string>() { "钳工", "制配工", "充电工", "车工", "钣焊工", "液压修理工" };//多出不扣分的工种
Dictionary<string, int> aloneCheckPersons = new Dictionary<string, int>();//需要单独检查不得低于某数量的工种 每工种少一人扣一分
aloneCheckPersons.Add("抢救工", 2);
aloneCheckPersons.Add("装甲底盘修理工", 1);
aloneCheckPersons.Add("汽车修理工", 2);
GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons);
return deductsScore;
}
//评价前进保障队 不连贯训练
private void AppraisFrontTeam(List<CarIconInstanceCtrl> item, string groupName)
{
List<string> carTypes = GetCarTypes(item);//已有车队类型
List<string> dueCars = new List<string>() { "NY033H型轮式轻型防化分队指挥车", "ZJQ84Q式中型坦克抢救车", "VME104履带装甲抢修车",
"VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ311型装甲拆装工程车", "CXZ511型装甲保养工程车", "CXC012汽车修理工程车","VME301-5型轮式装备抢救车", "CXC022汽车保养车" };//应当具备的车型
List<string> notDeductCars = new List<string>() { "96式坦克抢救车", "VME105履带装甲抢修车", "CXZ321型装甲拆装工程车", "徐工QY16K型汽车起重机", "徐工QY25K型汽车起重机", "VME301机械维修车", "VMD301型电源车" };//多出但不扣分的车型
GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 12);
Dictionary<string, int> personDic;//车队人员类型+数量字典
List<string> personTypes;//车队人员类型
GetPersonTypes(item, out personDic, out personTypes);
List<string> targetPerson = new List<string>() { "连长", "指导员", "副连长", "装甲技师", "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "液压修理工", "车辆综合电子系统修理工", "汽车修理工" };
List<string> notDeductPerson = new List<string>() { "钳工", "制配工", "充电工", "钣焊工" };//多出不扣分的工种
Dictionary<string, int> aloneCheckPersons = new Dictionary<string, int>();//需要单独检查不得低于某数量的工种 每工种少一人扣一分
aloneCheckPersons.Add("抢救工", 6);
aloneCheckPersons.Add("装甲底盘修理工", 7);
aloneCheckPersons.Add("汽车修理工", 4);
aloneCheckPersons.Add("电气修理工", 3);
GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons);
}
//计算编组中人员扣分情况
/// <summary>
/// 缺少必要工种扣1分 增加非必要的多余工种扣1分 某工种少于一定数量 每少一人扣0.5分
/// </summary>
/// <param name="groupName">编组名称</param>
/// <param name="deductsScore">最终扣分</param>
/// <param name="personDic">人员类型+数量字典</param>
/// <param name="personTypes">人员类型集合</param>
/// <param name="targetPerson">标准人员</param>
/// <param name="notDeductPerson">超出不扣分人员</param>
/// <param name="checkPersonCountDic">需检查数量的某工种+最少数量</param>
/// <returns></returns>
private float GetPersonScore(string groupName, /*ref float deductsScore,*/ Dictionary<string, int> personDic, List<string> personTypes, List<string> targetPerson, List<string> notDeductPerson, Dictionary<string, int> checkPersonCountDic)
{
float deductsScore = 0;
List<string> anyPerson = GetAny<string>(targetPerson, personTypes);//缺少的标准工种
List<string> unnecessaryPerson = GetAny<string>(personTypes, targetPerson);//多出的工种
List<string> deductPerson = GetAny<string>(unnecessaryPerson, notDeductPerson);//多出待扣分的工种
if (anyPerson.Count > 0)
{
deductsScore += anyPerson.Count;
string anyPersonsStr = GetStrFromList(anyPerson);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}人员工种缺少{1}\u201C等\u201D{2}种,扣{3}分", groupName, anyPersonsStr, anyPerson.Count, anyPerson.Count);
}
if (deductPerson.Count > 0)
{
deductsScore += (deductPerson.Count * 1f);
string deductsPersonStr = GetStrFromList(deductPerson);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}人员工种增加:{1}\u201C等\u201D{2}种,扣{3}分", groupName, deductsPersonStr, deductPerson.Count, deductPerson.Count * 1f);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum..ToString());
foreach (var item in checkPersonCountDic)
{
CheckPersonCount(groupName, personDic, item.Key, item.Value, 0.5f);
}
return deductsScore;
}
/// <summary>
/// 梳理 车队中人员工种信息
/// </summary>
/// <param name="item"></param>
/// <param name="personDic">返回车载人员字典类</param>
/// <param name="personTypes">返回车载人员类型集合</param>
private void GetPersonTypes(List<CarIconInstanceCtrl> item, out Dictionary<string, int> personDic, out List<string> personTypes)
{
personDic = new Dictionary<string, int>();
personTypes = new List<string>();
for (int i = 0; i < item.Count; i++)
{
foreach (var personItem in item[i].personNames)//统计所有车中的人员类型
{
if (!personTypes.Contains(personItem.Key))
personTypes.Add(personItem.Key);
if (!personDic.ContainsKey(personItem.Key))
personDic[personItem.Key] = 0;
personDic[personItem.Key] += 1;
}
}
}
/// <summary>
/// 计算某组中某一类型的工种 是否满足人员要求,并计算应扣除的分数
/// </summary>
/// <param name="groupName">组名 用于生成扣分记录</param>
/// <param name="personDic">人员字典</param>
/// <param name="personType">待检查的人员类型</param>
/// <param name="LeastCount">最少人数</param>
/// <param name="onePersonDeductScore">少一人扣除的分数</param>
/// <param name="score">改变当前分数</param>
private void CheckPersonCount(string groupName, Dictionary<string, int> personDic, string personType, int LeastCount, float onePersonDeductScore)
{
float score = 0;
int personCount = 0;
if (personDic.ContainsKey(personType))
personCount = personDic[personType];
if (personCount < LeastCount)
{
score += ((LeastCount - personCount) * onePersonDeductScore);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}{1}人数为{2},少于{3}人,扣{4}分", groupName, personType, personCount, LeastCount, (LeastCount - personCount) * onePersonDeductScore);
}
StudentOperateRecorder.Instance.InjectDecScore(score, EvaluationFactorsEnum..ToString());
}
/// <summary>
/// 遍历集合A 取出未在集合B中出现过的元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="listA">待遍历查找的集合</param>
/// <param name="listB">待排除元素的集合</param>
/// <returns>不存在的值</returns>
private List<T> GetAny<T>(List<T> listA, List<T> listB)
{
List<T> anyT = new List<T>();
for (int i = 0; i < listA.Count; i++)
{
if (!listB.Contains(listA[i]))
anyT.Add(listA[i]);
}
return anyT;
}
/// <summary>
/// 将一系列字符串以、号分割组成一个长字符串
/// </summary>
/// <param name="strList"></param>
/// <returns></returns>
private string GetStrFromList(List<string> strList)
{
string longStr = "";
for (int i = 0; i < strList.Count; i++)
{
longStr += $"{strList[i]}、";
}
return longStr;
}
#endregion
#region//修理厂开展评估
/// <summary>
/// 评估修理区布置间距和车型
/// </summary>
/// <param name="areaCars">存储了厂区+车辆集合的字典 key 厂区名称 value 该厂区中的车辆</param>
/// <param xixiaoToRiverDis="areaCars">铣消场距离水源的最近距离</param>
/// <returns></returns>
public void AppraiseRepairArea(Dictionary<string, List<Transform>> areaCars)
{
float score = 0;//修理厂展开部分扣除总分数
AppraiseAreaDistanceAndTime(areaCars, ref score);//评估厂区间距 和转运时间
AppraiseWaterToXiXiao(areaCars["洗消场"]);//评估洗消场距离河边的距离
foreach (var item in areaCars)//遍历各厂区 分别评估厂区内部扣分
{
switch (item.Key)
{
case "指控区":
score += AppraiseCC(item.Key, item.Value);
break;
case "油料场":
score += AppraiseYouLiao(item.Key, item.Value);
break;
case "器材存放场":
score += AppraiseQiCai(item.Key, item.Value);
break;
case "弹药场"://距离已经评价完成 无需评价其他内容
break;
case "修竣装备停放场":
score += AppraiseXiuJun(item.Key, item.Value);
break;
case "修理场"://无相关评价规则
break;
case "待修装备停放场":
score += AppraiseDaiXiu(item.Key, item.Value);
break;
case "洗消场":
score += AppraiseXiXiao(item.Key, item.Value);
break;
case "待后送装备停放场":
score += AppraiseDaiHouSong(item.Key, item.Value);
break;
case "接收场":
score += AppraiseJieShou(item.Key, item.Value);
break;
default:
Debug.Log($"{item.Key}无评估规则");
break;
}
}
//StudentOperateRecorder.Instance.InjectOperateMsg($"修理厂开设布置扣分{score}");
}
/// <summary>
/// 训练模式中检查当前厂区与其他厂区位置是否合适
/// </summary>
/// <param name="allPlanedAreas">完成规划的厂区</param>
/// <returns>距离异常提示</returns>
public static string CheckAreaDistance(List<AreaFlageCtrl> allPlanedAreas)
{
if (allPlanedAreas.Count < 2) return "";//单个厂区时无需检测厂区间距距离是否合适
for (int i = 0; i < allPlanedAreas.Count - 1; i++)//从开始到结尾依次取出一个厂区 和剩余厂区分别计算距离 计算统一规则的距离得分
{
for (int j = i + 1; j < allPlanedAreas.Count; j++)
{
float distance = Vector3.Distance(allPlanedAreas[i].transform.position, allPlanedAreas[j].transform.position) - allPlanedAreas[i].Radius - allPlanedAreas[j].Radius; // 计算厂区间距 标志距离-厂区半径
if (allPlanedAreas[i].AreaName == "弹药场" || allPlanedAreas[j].AreaName == "弹药场") //弹药场与其他厂区的间距限制在200M
{
if (distance < 200)
{
return LocalizationManager.Format("{0}与{1}边界间距为{2}M,小于200M", allPlanedAreas[i].AreaName, allPlanedAreas[j].AreaName, distance.ToString("0.0"));
}
}
else //其他厂区间距限制在100M
{
if (distance < 100)
{
return LocalizationManager.Format("{0}与{1}边界间距为{2}M,小于100M", allPlanedAreas[i].AreaName, allPlanedAreas[j].AreaName, distance.ToString("0.0"));
}
}
}
}
return "";
}
/// <summary>
/// 训练模式中检查 洗销厂和水源的距离
/// </summary>
/// <param name="xixiaoPos"></param>
/// <returns></returns>
public static string CheckAreaToWaterDistance(Transform xixiaoPos)
{
float distance= GetXiXiaoToWaterDistance(new List<Transform>() { xixiaoPos });
if (distance > 100)
return LocalizationManager.Format("{0}距离水源距离为{1}M,超出100M", AreaNameEnum..ToString(), distance.ToString("0.0"));
return "";
}
//评估洗消场和水源距离扣分情况
private void AppraiseWaterToXiXiao(List<Transform> xixiao)
{
float xixiaoToRiverDis = GetXiXiaoToWaterDistance(xixiao);
if (xixiaoToRiverDis == -1)
{
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("当前场景无水源,不计算洗消场与水源间距扣分项");
return;
}
if (xixiaoToRiverDis > 100)
{
float decScore = 0.05f + ((int)(xixiaoToRiverDis - 100)) / 10 * 0.05f;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("洗消场与水源间距为{0}M,大于100M,每10米扣0.05f,计扣{1}分", xixiaoToRiverDis.ToString("0.0"), decScore);
StudentOperateRecorder.Instance.InjectDecScore(decScore, null);//水源不做为权重调整因素
}
}
private static float GetXiXiaoToWaterDistance(List<Transform> xixiao)
{
WaterManager[] distanceManagers = GameObject.FindObjectsOfType<WaterManager>();//查找所有的水源
if (distanceManagers == null || distanceManagers.Length == 0)//不存在水源
{
return -1;
}
float xixiaoToRiverDis = 0;
WaterManager nearWater = ArrayHelper.Min(distanceManagers, (p) => p.GetDistanceFromWater(xixiao[0].position));
Transform trans = ArrayHelper.Min(xixiao, (p) => nearWater.GetNearestPointDistance(p.position));//找洗消场中距离水源最近的Trans
//Debug.Log(nearWater.name);
xixiaoToRiverDis = nearWater.GetNearestPointDistance(trans.position);//距离水源的距离
if (xixiao.Count == 1 && "Flag" == LayerMask.LayerToName(xixiao[0].gameObject.layer))//如果洗消场中不存在车辆 距离需要扣除洗消场默认半径
{
xixiaoToRiverDis -= xixiao[0].GetComponent<AreaFlageCtrl>().Radius;
}
return xixiaoToRiverDis;
}
//评估各厂区间的间距 和转运时间
private void AppraiseAreaDistanceAndTime(Dictionary<string, List<Transform>> areaCars, ref float refScore)
{
float score=0;
Dictionary<string, Dictionary<string, float>> areasDistanceDic = new Dictionary<string, Dictionary<string, float>>();//记录每个厂区与其他厂区之间的距离 Kye=当前厂区名称 value.Key = 与当前厂区计算距离的目标厂区名称 value.value = 当前厂区与目标厂区之间的距离
List<string> dicKeys = new List<string>();
List<List<Transform>> dicValues = new List<List<Transform>>();
foreach (var item in areaCars)//按照顺序取出字典形成有序的集合 便于后续计算厂区间距
{
dicKeys.Add(item.Key);
dicValues.Add(item.Value);
}
for (int i = 0; i < dicKeys.Count - 1; i++)//从开始到结尾依次取出一个厂区 和剩余厂区分别计算距离 计算统一规则的距离得分
{
for (int j = i + 1; j < dicKeys.Count; j++)
{
float distance = GetAreasDistance(dicValues[i], dicValues[j]); //每计算出一个距离 分别为两个厂区建立各自的距离字典
if (dicKeys[i] == "弹药场" || dicKeys[j] == "弹药场") //弹药场与其他厂区的间距限制在200M
{
if (distance < 200)
{
score += 1;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}与{1}间距为{2}M,小于200M扣1分", dicKeys[i], dicKeys[j], distance.ToString("0.0"));
}
}
else //其他厂区间距限制在100M
{
if (distance < 100)
{
score += 1;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}与{1}间距为{2}M,小于100M扣1分", dicKeys[i], dicKeys[j], distance.ToString("0.0"));
}
}
if (!areasDistanceDic.ContainsKey(dicKeys[i])) //存入字典便于提取单个厂区的间距情况
{
areasDistanceDic.Add(dicKeys[i], new Dictionary<string, float>());
}
if (!areasDistanceDic.ContainsKey(dicKeys[j]))
{
areasDistanceDic.Add(dicKeys[j], new Dictionary<string, float>());
}
areasDistanceDic[dicKeys[i]].Add(dicKeys[j], distance);
areasDistanceDic[dicKeys[j]].Add(dicKeys[i], distance);
}
}
if (areasDistanceDic.ContainsKey("指控区"))
{
if (areasDistanceDic["指控区"].ContainsKey("修理场"))
{
float dis1 = areasDistanceDic["指控区"]["修理场"];
if (dis1 < 300)
{
float decScore = 0.5f + ((int)(300 - dis1)) / 50 * 0.5f;
score += decScore;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("指控区与修理场间距为{0}M,小于300M,每50米扣0.5f,计扣{1}分", dis1.ToString("0.0"), decScore);
}
else if (dis1 > 600)
{
float decScore = 0.5f + ((int)(dis1 - 600)) / 50 * 0.5f;
score += decScore;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("指控区与修理场间距为{0}M,大于600M,每50米扣0.5f,计扣{1}分", dis1.ToString("0.0"), decScore);
}
}
else
{
Debug.Log("指控区与修理厂无距离数据");
}
}
else
{
Debug.Log("无指控区距离数据");
}
if (areasDistanceDic.ContainsKey("器材存放场"))
{
if (areasDistanceDic["器材存放场"].ContainsKey("修理场"))
{
float dis2 = areasDistanceDic["器材存放场"]["修理场"];
if (dis2 > 50)
{
float decScore = 0.05f + ((int)(dis2 - 50)) / 10 * 0.05f;
score += decScore;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("器材存放场与修理场间距为{0}M,大于50M,每10米扣0.05f,计扣{1}分", dis2.ToString("0.0"), decScore);
}
}
else {
Debug.Log("器材存放场与修理厂无距离数据");
}
}
else {
Debug.Log("无器材存放场距离数据");
}
refScore += score;
StudentOperateRecorder.Instance.InjectDecScore(score, EvaluationFactorsEnum..ToString());
int time = AppraiseRepairTime(areasDistanceDic);//计算转运用时
StudentOperateRecorder.Instance.InjectTransportTime(time);
}
//评估指控区域
private float AppraiseCC(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "NY033H型轮式轻型防化分队指挥车" };//应当具备的车型
List<string> notDeductCars = new List<string>() { "VMD301型电源车", "VMZ308型通指装备电子检测维修车" };//多出不扣分的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, notDeductCars, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
return deductsScore;
}
//评估接收场
private float AppraiseJieShou(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "徐工QY25K型汽车起重机", "96A坦克抢救车", "VME301-5型轮式装备抢救车", "CXZ211型装甲检测工程车", "VMZ306机电检测维修车" };//应当具备的车型
List<string> notDeductCars = new List<string>() { "ZJQ84Q式中型坦克抢救车", "CXL211型雷达检测车", "CXL221型雷达检测车", "CXL231型雷达检测车" };//多出不扣分的车型
List<string> notDeductLessCars = new List<string>() { "徐工QY25K型汽车起重机" };//缺少不扣分的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, notDeductCars, 0, 0, 0.5f, notDeductLessCars);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
return deductsScore;
}
//评估铣消场
private float AppraiseXiXiao(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "CXF111-1型防化修理车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
return deductsScore;
}
//评估油料场
private float AppraiseYouLiao(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "油料运输车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
return deductsScore;
}
//评估待修装备停放场
private float AppraiseDaiXiu(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "ZJQ84Q式中型坦克抢救车"};//应当具备的车型
List<string> notDeductCars = new List<string>() { "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "96式坦克抢救车", "VME301-5型轮式装备抢救车" };//多出不扣分的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, notDeductCars, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
//GetMainLoadDisScore();//评估距离主通路距离
return deductsScore;
}
//评估器材存放场
private float AppraiseQiCai(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "VMG101履带装甲补给车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
return deductsScore;
}
//评估修竣装备停放场
private float AppraiseXiuJun(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "VMZ301电子检测维修车", "CXZ511型装甲保养工程车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
//GetMainLoadDisScore();//评估距离主通路距离
return deductsScore;
}
//评估待后送装备停放场
private float AppraiseDaiHouSong(string areaName, List<Transform> cars)
{
float deductsScore = 0;
List<string> carTypes = GetCarTypes(cars);
List<string> dueCars = new List<string>() { "ZJQ84Q式中型坦克抢救车", "VME301-5型轮式装备抢救车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
//GetMainLoadDisScore();//评估距离主通路距离
return deductsScore;
}
//评估距离主通路距离 待实现
private void GetMainLoadDisScore()
{
}
//评估区域内部的距离扣分情况
private void GetInAreaCarDistanceScore(string areaName, List<Transform> cars, ref float refScore)
{
float score = 0;
for (int i = 0; i < cars.Count - 1; i++)
{
for (int j = i; j < cars.Count; j++)//双循环依次计算车队中每两个之间的距离
{
float carsDistance = Vector3.Distance(cars[i].transform.position, cars[j].transform.position);
if (carsDistance < 50)//小于50m的 扣0.5f
{
score += 0.5f;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}中{1}与{2}间距为{3}M,小于50M扣0.5分", areaName, cars[i].transform.name, cars[j].transform.name, carsDistance.ToString("0.0"));
}
}
}
refScore = score;
StudentOperateRecorder.Instance.InjectDecScore(score, EvaluationFactorsEnum..ToString());
}
//根据车辆/图标名称获取所选车辆类型
private List<string> GetCarTypes<T>(List<T> carCtrls) where T : Component
{
List<string> names = new List<string>();
for (int i = 0; i < carCtrls.Count; i++)
{
string type = carCtrls[i].transform.name.Split('(')[0];//取括号前的部分
if (!names.Contains(type))
names.Add(type);
}
return names;
}
/// <summary>
///计算两个厂区之间的间距
/// </summary>
private float GetAreasDistance(List<Transform> area1, List<Transform> area2)
{
//if (area1.Count == 0 || area2.Count == 0) return -1;//负一代表无效数据 出现未安置车辆的厂区时为-1
float nearestDistance = float.MaxValue;
for (int i = 0; i < area1.Count; i++)
{
for (int j = 0; j < area2.Count; j++)//遍历两个数组 依次计算两个数组中的所有元素的间距 记录最小间距
{
float currenDis = Vector3.Distance(area1[i].transform.position, area2[j].transform.position);
if (currenDis < nearestDistance)
nearestDistance = currenDis;
}
}
if (area1.Count == 1 && "Flag" == LayerMask.LayerToName(area1[0].gameObject.layer)) //厂区1 内部无车辆
{
nearestDistance -= area1[0].GetComponent<AreaFlageCtrl>().Radius; //以厂区1边缘计算距离 需扣除厂区1半径
if (area2.Count == 1 && "Flag" == LayerMask.LayerToName(area2[0].gameObject.layer))//厂区2内部也无车辆
{
nearestDistance -= area2[0].GetComponent<AreaFlageCtrl>().Radius;//以厂区2边缘计算距离 需扣除厂区2半径
}
}
if (nearestDistance < 0)//两个厂区有交集时 避免出现负数
nearestDistance = 0;
return nearestDistance;
}
/// <summary>
/// 评估修理时在各厂区间的转运时间 暂时使用直线距离计算 后续若出现厂区中阻挡目标的情况时 使用寻路系统获取距离
/// </summary>
/// <returns></returns>
private int AppraiseRepairTime(Dictionary<string, Dictionary<string, float>> areasDistanceDic)
{
if (areasDistanceDic.Count < 10)
{
Debug.Log("存在空厂区,无法判定转运时间");
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("存在未安置装备的厂区,无法判定距离和转运时间!");
return 0;
}
else
{
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("按照15km/h速度计算转运时间");//15KM每小时 等于4.16米每秒
List<string> repairOrder = new List<string>() { AreaNameEnum..ToString(), AreaNameEnum..ToString(), AreaNameEnum..ToString(), AreaNameEnum..ToString(), AreaNameEnum..ToString(), AreaNameEnum..ToString(), AreaNameEnum..ToString(), AreaNameEnum..ToString(), AreaNameEnum..ToString() };
float totalTime = 0;
for (int i = 0; i < repairOrder.Count - 1; i++)//依次计算 两场地之间的转运时间
{
totalTime += AppraiseTwoAreaRepairTime(areasDistanceDic, repairOrder[i], repairOrder[i + 1]);
}
System.TimeSpan ts = System.TimeSpan.FromSeconds(totalTime);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("修理厂转运总用时:{0}时{1}分{2}秒", ts.Hours, ts.Minutes, ts.Seconds);
return (int)totalTime;
}
}
/// <summary>
/// 获取在两个厂区间的转运时间
/// </summary>
/// <param name="area1"></param>
/// <param name="area2"></param>
/// <returns>耗时</returns>
private float AppraiseTwoAreaRepairTime(Dictionary<string, Dictionary<string, float>> areasDistanceDic, string area1, string area2)
{
float dis = areasDistanceDic[area1][area2];
float time = dis / 4.16f;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}距离{1}:{2}米,预计转运用时:{3}秒", area1, area2, dis.ToString("0"), time.ToString("0.0"));
return time;
}
#endregion
//部分科目共用的评估方法
#region
/// <summary>
/// 获取编队中车型部分扣分分值 并梳理当前车队中的人员种类列表 种类+数量 排除允许增加车型后的多余车型默认扣1分 超出最大数量扣0.5分
/// </summary>
/// <param name="carTypes">编组集合</param>
/// <param name="groupName">编组名称</param>
/// <param name="dueCars">标准车型</param>
/// <param name="notDeductCars">多出但不扣分的车型</param>
/// <param name="groupCount">编组当前数量</param>
/// <param name="maxCount">编组最大允许数量 不计算最大数量时赋0</param>
/// <param name="deductsScore">当前分数字段 传入引用</param>
/// <param name="unnecessOneScore">排除允许增加车型后的多余车型扣分 默认1</param>
/// <param name="unnecessOneScore">缺少但不扣分的车型 默认1</param>
private void GetCarTypesScore(List<string> carTypes, string groupName, List<string> dueCars, float lessDueScore, List<string> notDeductCars, int groupCount, int maxCount, /*ref float deductsScore,*/ float unnecessOneScore = 1, List<string> lessNotDeductCars = null)
{
float deductsScore = 0;
List<string> anyCars = GetAny<string>(dueCars, carTypes);//梳理缺少的车型
if (lessNotDeductCars != null)
anyCars = GetAny<string>(anyCars, lessNotDeductCars);
List<string> unnecessaryCars = GetAny<string>(carTypes, dueCars);//梳理多出的车型
List<string> deductCars;
if (notDeductCars != null)
deductCars = GetAny<string>(unnecessaryCars, notDeductCars);//梳理多出车型中需要扣分的车型
else//多出的车型都需要扣分
deductCars = unnecessaryCars;
if (anyCars.Count > 0)
{
deductsScore += (anyCars.Count * lessDueScore);
string anyCarsStr = GetStrFromList(anyCars);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}缺少标准车型:{1},扣{2}分", groupName, anyCarsStr, anyCars.Count * lessDueScore);
}
if (deductCars.Count > 0)
{
deductsScore += (deductCars.Count * 1f);
string deductsCarsStr = GetStrFromList(deductCars);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}增加车型:{1}\u201C等\u201D{2}种,扣{3}分", groupName, deductsCarsStr, deductCars.Count, deductCars.Count * unnecessOneScore);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum..ToString());
deductsScore = 0;//记录后清空开始计算装备数量扣分情况
if (maxCount != 0 && groupCount > maxCount)//部分组别无需评估最大数量
{
deductsScore += ((groupCount - maxCount) * 0.5f);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}装备数量为{1}台,超出{2}台,扣{3}分", groupName, groupCount, maxCount, (groupCount - maxCount) * 0.5f);
}
StudentOperateRecorder.Instance.InjectDecScore( deductsScore, EvaluationFactorsEnum..ToString());
}
#endregion
#region//装备修理科目评估
//判断维修逻辑是否正确 判断依据是前置步骤是否已经完成 完成规划返回空 未完成时返回错误描述
internal string AppraiseSteps(List<StepData> rightSteps)
{
List<StepData> checkOver = new List<StepData>();//检查通过的步骤 认为规划正确可以完成
for (int i = 0; i < rightSteps.Count; i++)//一次检查规划的步骤
{
if (rightSteps[i].preIDs != null && rightSteps[i].preIDs.Length > 0)//存在前置步骤 需要判断前置步骤是否完成
{
for (int j = 0; j < rightSteps[i].preIDs.Length; j++)
{
StepData stepData = CollectionHelper.Find(checkOver, p => p.id == rightSteps[i].preIDs[j]);//在已完成的步骤中寻找是否存在前置步骤
if (stepData == default(StepData))//不存在前置步骤,代表前置步骤还未完成
{
string errorMsg;//规划步骤错误描述
stepData = CollectionHelper.Find(rightSteps, p => p.id == rightSteps[i].preIDs[j]);//在传入的所有步骤中查找是否存在前置步骤
if (stepData == default(StepData))//所有的步骤中都不存在前置步骤 证明教员端发送的步骤库有误 此时只记录 但认为该步骤通过
{
errorMsg = $"规划的维修步骤不完善缺少id为{rightSteps[i].preIDs[j]}的操作步骤,导致{rightSteps[i].describe} 操作无法正确评估";
}
else
{
errorMsg = $"{rightSteps[i].describe} 之前需要先完成 {stepData.describe}";
return errorMsg;
}
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0} 之前需要先完成 {1}", rightSteps[i].describe, stepData.describe);//记录评估结果
}
}
}
checkOver.Add(rightSteps[i]);//通过检查的添加
}
return string.Empty;
}
#endregion
}
//评估因素枚举 便于后续教师端调整权重时统一名称
public enum EvaluationFactorsEnum
{
//两科目共用本因素 连贯训练时累加扣分
,
,
,
,
//修理所展开时 厂区内部的车辆间距
,
,
,
}