using System; using System.Collections; using System.Collections.Generic; using UnityEngine; /// /// 编辑评估类 按照提供规则评估扣分情况 并直接计入操作纪录类 /// public class EditorAppraiser { #region //行军编队评估 static Dictionary> rightPersonDic = new Dictionary>() { { FormationGroupingEnum.前进保障队, new List() { "连长", "指导员", "副连长", "装甲技师", "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "液压修理工", "车辆综合电子系统修理工", "汽车修理工" } }, { FormationGroupingEnum.指挥组, new List() { "连长", "指导员", "副连长", "装甲技师", "汽车技师" } }, { FormationGroupingEnum.前出保障组, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } }, { FormationGroupingEnum.抢救抢修队, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } }, //{ FormationGroupingEnum.主体编队, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } }, { FormationGroupingEnum.殿后保障组, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "车辆抢救工", "汽车修理工" } }, }; static Dictionary> rightCarDic = new Dictionary>() { { FormationGroupingEnum.前进保障队, new List() {"NY033H型轮式轻型防化分队指挥车", "ZJQ84Q式中型坦克抢救车", "VME104履带装甲抢修车","VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ311型装甲拆装工程车", "CXZ511型装甲保养工程车", "CXC012汽车修理工程车","VME301-5型轮式装备抢救车", "CXC022汽车保养车" } }, { FormationGroupingEnum.指挥组, new List() { "NY033H型轮式轻型防化分队指挥车" } }, { FormationGroupingEnum.前出保障组, new List() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车","VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "徐工QY25K型汽车起重机", "VME301-5型轮式装备抢救车", "CXC022汽车保养车" } }, { FormationGroupingEnum.抢救抢修队, new List() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车", "VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "VMG101履带装甲补给车", "VME301-5型轮式装备抢救车", "CXC022汽车保养车", "CXC012汽车修理工程车" } }, //{ FormationGroupingEnum.主体编队, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } }, { FormationGroupingEnum.殿后保障组, new List() { "ZJQ84Q式中型坦克抢救车", "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "VME301-5型轮式装备抢救车"} }, }; /// /// 评估编队得分 记录扣分项 /// public void AppraiseTeam(Dictionary> 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($"行军编队部分评估结束!"); } /// /// 训练模式操作过程中检查车辆类型是否为当前车队的标准配置人员 /// /// 车辆类型 /// 编组 /// public static bool CheckCar(string carType, FormationGroupingEnum groupingEnum) { if (rightCarDic.ContainsKey(groupingEnum)) return rightCarDic[groupingEnum].Contains(carType); else return true; } /// /// 训练模式操作过程中检查人员类型是为当前车队的标准配置人员 /// /// /// /// public static bool CheckPerson(string personName, FormationGroupingEnum groupingEnum) { if (rightPersonDic.ContainsKey(groupingEnum)) return rightPersonDic[groupingEnum].Contains(personName); else return true; } //评价指挥组 与其他分组评价方法略有差异 不做整合 private void AppraisLeaderGroup(List item) { float deductsScore = 0; //指挥车数量 int leaderCarCount = 0; //电源车 和电子检修车 int PowerAndConnectCarCount = 0; //其它不应该出现的车型 List otherCars = new List(); //车队中具备的人员 List personType = new List(); 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 targetPerson = new List() { "连长", "指导员", "副连长", "装甲技师", "汽车技师" }; //车队应该配备的人员 List anyPerson = GetAny(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 item, string groupName) { List carTypes = GetCarTypes(item);//已有车队类型 List dueCars = new List() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车", "VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "徐工QY25K型汽车起重机", "VME301-5型轮式装备抢救车", "CXC022汽车保养车" };//应当具备的车型 List notDeductCars = new List() { "VME301机械维修车", "VME105履带装甲抢修车", "CXZ311型装甲拆装工程车", "CXZ321型装甲拆装工程车", "徐工QY16K型汽车起重机", "高机动电源挂车" };//多出但不扣分的车型 GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 12); Dictionary personDic;//车队人员类型+数量字典 List personTypes;//车队人员类型 GetPersonTypes(item, out personDic, out personTypes); List targetPerson = new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" }; List notDeductPerson = new List() { "钳工", "制配工", "充电工", "车工", "钣焊工", "光电修理工" };//多出不扣分的工种 Dictionary aloneCheckPersons = new Dictionary();//需要单独检查不得低于某数量的工种 每工种少一人扣一分 aloneCheckPersons.Add("抢救工", 6); aloneCheckPersons.Add("装甲底盘修理工", 5); aloneCheckPersons.Add("汽车修理工", 4); GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons); } //评价殿后保障组 private float AppraisFollowGroup(List item, string groupName) { float deductsScore = 0; List carTypes = GetCarTypes(item);//已有车队类型 List dueCars = new List() { "ZJQ84Q式中型坦克抢救车", "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "VME301-5型轮式装备抢救车" };//应当具备的车型 List notDeductCars = new List() { "96式坦克抢救车", "VME105履带装甲抢修车", "CXZ311型装甲拆装工程车", "CXZ321型装甲拆装工程车" };//多出但不扣分的车型 GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 6); Dictionary personDic;//车队人员类型+数量字典 List personTypes;//车队人员类型 GetPersonTypes(item, out personDic, out personTypes); List targetPerson = new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "车辆抢救工", "汽车修理工" };//标准人员工种 List notDeductPerson = new List() { "钳工", "制配工", "充电工", "车工", "钣焊工", "液压修理工" };//多出不扣分的工种 Dictionary aloneCheckPersons = new Dictionary();//需要单独检查不得低于某数量的工种 每工种少一人扣一分 aloneCheckPersons.Add("抢救工", 2); aloneCheckPersons.Add("装甲底盘修理工", 1); aloneCheckPersons.Add("汽车修理工", 2); GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons); return deductsScore; } //评价前进保障队 不连贯训练 private void AppraisFrontTeam(List item, string groupName) { List carTypes = GetCarTypes(item);//已有车队类型 List dueCars = new List() { "NY033H型轮式轻型防化分队指挥车", "ZJQ84Q式中型坦克抢救车", "VME104履带装甲抢修车", "VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ311型装甲拆装工程车", "CXZ511型装甲保养工程车", "CXC012汽车修理工程车","VME301-5型轮式装备抢救车", "CXC022汽车保养车" };//应当具备的车型 List notDeductCars = new List() { "96式坦克抢救车", "VME105履带装甲抢修车", "CXZ321型装甲拆装工程车", "徐工QY16K型汽车起重机", "徐工QY25K型汽车起重机", "VME301机械维修车", "VMD301型电源车" };//多出但不扣分的车型 GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 12); Dictionary personDic;//车队人员类型+数量字典 List personTypes;//车队人员类型 GetPersonTypes(item, out personDic, out personTypes); List targetPerson = new List() { "连长", "指导员", "副连长", "装甲技师", "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "液压修理工", "车辆综合电子系统修理工", "汽车修理工" }; List notDeductPerson = new List() { "钳工", "制配工", "充电工", "钣焊工" };//多出不扣分的工种 Dictionary aloneCheckPersons = new Dictionary();//需要单独检查不得低于某数量的工种 每工种少一人扣一分 aloneCheckPersons.Add("抢救工", 6); aloneCheckPersons.Add("装甲底盘修理工", 7); aloneCheckPersons.Add("汽车修理工", 4); aloneCheckPersons.Add("电气修理工", 3); GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons); } //计算编组中人员扣分情况 /// /// 缺少必要工种扣1分 增加非必要的多余工种扣1分 某工种少于一定数量 每少一人扣0.5分 /// /// 编组名称 /// 最终扣分 /// 人员类型+数量字典 /// 人员类型集合 /// 标准人员 /// 超出不扣分人员 /// 需检查数量的某工种+最少数量 /// private float GetPersonScore(string groupName, /*ref float deductsScore,*/ Dictionary personDic, List personTypes, List targetPerson, List notDeductPerson, Dictionary checkPersonCountDic) { float deductsScore = 0; List anyPerson = GetAny(targetPerson, personTypes);//缺少的标准工种 List unnecessaryPerson = GetAny(personTypes, targetPerson);//多出的工种 List deductPerson = GetAny(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; } /// /// 梳理 车队中人员工种信息 /// /// /// 返回车载人员字典类 /// 返回车载人员类型集合 private void GetPersonTypes(List item, out Dictionary personDic, out List personTypes) { personDic = new Dictionary(); personTypes = new List(); 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; } } } /// /// 计算某组中某一类型的工种 是否满足人员要求,并计算应扣除的分数 /// /// 组名 用于生成扣分记录 /// 人员字典 /// 待检查的人员类型 /// 最少人数 /// 少一人扣除的分数 /// 改变当前分数 private void CheckPersonCount(string groupName, Dictionary 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()); } /// /// 遍历集合A 取出未在集合B中出现过的元素 /// /// 类型 /// 待遍历查找的集合 /// 待排除元素的集合 /// 不存在的值 private List GetAny(List listA, List listB) { List anyT = new List(); for (int i = 0; i < listA.Count; i++) { if (!listB.Contains(listA[i])) anyT.Add(listA[i]); } return anyT; } /// /// 将一系列字符串以、号分割组成一个长字符串 /// /// /// private string GetStrFromList(List strList) { string longStr = ""; for (int i = 0; i < strList.Count; i++) { longStr += $"{strList[i]}、"; } return longStr; } #endregion #region//修理厂开展评估 /// /// 评估修理区布置间距和车型 /// /// 存储了厂区+车辆集合的字典 key 厂区名称 value 该厂区中的车辆 /// 铣消场距离水源的最近距离 /// public void AppraiseRepairArea(Dictionary> 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}"); } /// /// 训练模式中检查当前厂区与其他厂区位置是否合适 /// /// 完成规划的厂区 /// 距离异常提示 public static string CheckAreaDistance(List 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 $"{allPlanedAreas[i].AreaName}与{allPlanedAreas[j].AreaName}边界间距为{(distance).ToString("0.0")}M,小于200M"; } } else //其他厂区间距限制在100M { if (distance < 100) { return $"{allPlanedAreas[i].AreaName}与{allPlanedAreas[j].AreaName}边界间距为{(distance).ToString("0.0")}M,小于100M"; } } } } return ""; } /// /// 训练模式中检查 洗销厂和水源的距离 /// /// /// public static string CheckAreaToWaterDistance(Transform xixiaoPos) { float distance= GetXiXiaoToWaterDistance(new List() { xixiaoPos }); if (distance > 100) return $"{AreaNameEnum.洗消场.ToString()}距离水源距离为{distance.ToString("0.0")}M,超出100M"; return ""; } //评估洗消场和水源距离扣分情况 private void AppraiseWaterToXiXiao(List 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 xixiao) { WaterManager[] distanceManagers = GameObject.FindObjectsOfType();//查找所有的水源 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().Radius; } return xixiaoToRiverDis; } //评估各厂区间的间距 和转运时间 private void AppraiseAreaDistanceAndTime(Dictionary> areaCars, ref float refScore) { float score=0; Dictionary> areasDistanceDic = new Dictionary>();//记录每个厂区与其他厂区之间的距离 Kye=当前厂区名称 ,value.Key = 与当前厂区计算距离的目标厂区名称 , value.value = 当前厂区与目标厂区之间的距离 List dicKeys = new List(); List> dicValues = new List>(); 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()); } if (!areasDistanceDic.ContainsKey(dicKeys[j])) { areasDistanceDic.Add(dicKeys[j], new Dictionary()); } 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 cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "NY033H型轮式轻型防化分队指挥车" };//应当具备的车型 List notDeductCars = new List() { "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 cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "徐工QY25K型汽车起重机", "96A坦克抢救车", "VME301-5型轮式装备抢救车", "CXZ211型装甲检测工程车", "VMZ306机电检测维修车" };//应当具备的车型 List notDeductCars = new List() { "ZJQ84Q式中型坦克抢救车", "CXL211型雷达检测车", "CXL221型雷达检测车", "CXL231型雷达检测车" };//多出不扣分的车型 List notDeductLessCars = new List() { "徐工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 cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "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 cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "油料运输车" };//应当具备的车型 GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况 return deductsScore; } //评估待修装备停放场 private float AppraiseDaiXiu(string areaName, List cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "ZJQ84Q式中型坦克抢救车"};//应当具备的车型 List notDeductCars = new List() { "徐工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 cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "VMG101履带装甲补给车" };//应当具备的车型 GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况 return deductsScore; } //评估修竣装备停放场 private float AppraiseXiuJun(string areaName, List cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "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 cars) { float deductsScore = 0; List carTypes = GetCarTypes(cars); List dueCars = new List() { "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 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 GetCarTypes(List carCtrls) where T : Component { List names = new List(); 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; } /// ///计算两个厂区之间的间距 /// private float GetAreasDistance(List area1, List 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().Radius; //以厂区1边缘计算距离 需扣除厂区1半径 if (area2.Count == 1 && "Flag" == LayerMask.LayerToName(area2[0].gameObject.layer))//厂区2内部也无车辆 { nearestDistance -= area2[0].GetComponent().Radius;//以厂区2边缘计算距离 需扣除厂区2半径 } } if (nearestDistance < 0)//两个厂区有交集时 避免出现负数 nearestDistance = 0; return nearestDistance; } /// /// 评估修理时在各厂区间的转运时间 暂时使用直线距离计算 后续若出现厂区中阻挡目标的情况时 使用寻路系统获取距离 /// /// private int AppraiseRepairTime(Dictionary> areasDistanceDic) { if (areasDistanceDic.Count < 10) { Debug.Log("存在空厂区,无法判定转运时间"); StudentOperateRecorder.Instance.InjectOperateMsgLocalized("存在未安置装备的厂区,无法判定距离和转运时间!"); return 0; } else { StudentOperateRecorder.Instance.InjectOperateMsgLocalized("按照15km/h速度计算转运时间:");//15KM每小时 等于4.16米每秒 List repairOrder = new List() { 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; } } /// /// 获取在两个厂区间的转运时间 /// /// /// /// 耗时 private float AppraiseTwoAreaRepairTime(Dictionary> 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 /// /// 获取编队中车型部分扣分分值 并梳理当前车队中的人员种类列表 种类+数量 排除允许增加车型后的多余车型默认扣1分 超出最大数量扣0.5分 /// /// 编组集合 /// 编组名称 /// 标准车型 /// 多出但不扣分的车型 /// 编组当前数量 /// 编组最大允许数量 不计算最大数量时赋0 /// 当前分数字段 传入引用 /// 排除允许增加车型后的多余车型扣分 默认1 /// 缺少但不扣分的车型 默认1 private void GetCarTypesScore(List carTypes, string groupName, List dueCars, float lessDueScore, List notDeductCars, int groupCount, int maxCount, /*ref float deductsScore,*/ float unnecessOneScore = 1, List lessNotDeductCars = null) { float deductsScore = 0; List anyCars = GetAny(dueCars, carTypes);//梳理缺少的车型 if (lessNotDeductCars != null) anyCars = GetAny(anyCars, lessNotDeductCars); List unnecessaryCars = GetAny(carTypes, dueCars);//梳理多出的车型 List deductCars; if (notDeductCars != null) deductCars = GetAny(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 rightSteps) { List checkOver = new List();//检查通过的步骤 认为规划正确可以完成 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 { //两科目共用本因素 连贯训练时累加扣分 型号组成, 装备数量, 人员工种, 人员数量, //修理所展开时 厂区内部的车辆间距 车辆间距, 通路距离, 厂区间距, }