火力规则:组件匹配修改

This commit is contained in:
MHW
2026-03-30 16:43:24 +08:00
parent d8719c30c6
commit 1dba7584cd
2 changed files with 182 additions and 3 deletions

View File

@@ -63,7 +63,12 @@
- `blueMissileRangeDefault`:蓝方导弹范围缺失时采用的默认值。
- `minBlueMissileCountForLinkage`:蓝方导弹数量达到该值才触发联动增强。
### 2.5 targetId 自动绑定参数(新增)
### 2.5 全组件数量匹配参数(新增)
- `enableComponentQuantityMatch`:是否启用全组件数量覆盖(非导弹组件)。
- `componentDeviceNameMappingCsv`:组件名映射表(逗号分隔),格式 `蓝组件deviceName->红组件deviceName`;为空则默认 `deviceName` 直匹配。
- `skipMissileComponentsByNameContains`:跳过覆盖关键词:蓝方组件 `deviceName` 包含该关键词则不覆盖;默认 `导弹`(用于保留导弹联动偏移)。
### 2.6 targetId 自动绑定参数(新增)
- `enableTargetAutoBind`:是否自动给红方武器写入 `targetId`
- `minTargetBindRatio`:最低绑定比例(例如 `0.7` 表示至少 70% 红方武器有目标)。
- `allowReserveWithoutTarget`
@@ -79,7 +84,7 @@
- 导弹发射车优先绑定蓝方导弹能力目标
- 当优先池不足时自动回退到地面池/全目标池,保证大部分武器有目标。
### 2.6 阵位规则参数(新增)
### 2.7 阵位规则参数(新增)
- `enablePositionRules`:阵位规则总开关。
- 阵位输入来源:`blueTask.warZoneLocation``blueTask.defZoneLocation`(各 4 个经纬点)。
- `fireUnitSpacingMeters`:防区/作战区点位间距(米),例如 `100` 代表约每 100 米一个火力单元。
@@ -113,7 +118,7 @@
}
```
### 2.7 航迹规则参数(新增)
### 2.8 航迹规则参数(新增)
- `enableTrajectoryRules`:航迹规则总开关。
- `strategyMode``auto/shortest/flank/interfere`
- `auto`:智能选择策略。
@@ -141,6 +146,7 @@
- `装备组件匹配``组件参数匹配`已作为 `legacy` 占位不承担当前业务决策
- 主决策在 `红方武器自适应装配规则`调用 `configureRedWeaponsByBlue(...)`映射配置添加武器
- 导弹增强在 `导弹联动增强规则`调用 `applyMissileLinkage(...)`受开关和阈值控制
- 全组件数量匹配在 `全组件数量匹配规则`按红方 `targetId` 绑定蓝方装备覆盖非导弹组件 `componentParams[0].number`找不到组件/targetId 允许跳过
- 任务命名在 `任务自动匹配规则`调用 `assignTaskNameByRedWeapons(...)`按红方最终武器自动生成任务名和 `dataType`
- 炮类约束命中炮类条件时炮类武器只保留 `炮弹` 组件单位 `范围米`
- `targetId` 绑定在装配后自动执行尽量为红方武器绑定蓝方 `equipmentId`允许少量空值冗余

View File

@@ -149,6 +149,14 @@ function Map buildBusinessConfig() {
cfg.put("blueMissileRangeDefault", 220); // 蓝方导弹范围默认值
cfg.put("minBlueMissileCountForLinkage", 1); // 联动触发门槛
// ---------- 全组件数量匹配(可改) ----------
// 逻辑:按红方武器 targetId 找到对应蓝方装备equipmentId然后覆盖所有“非导弹组件”的数量
cfg.put("enableComponentQuantityMatch", Boolean.TRUE);
// 格式示例:蓝穿甲弹->红穿甲弹,蓝火控雷达->红火控雷达;为空则默认 deviceName 一致匹配
cfg.put("componentDeviceNameMappingCsv", "");
// 跳过覆盖:蓝方组件 deviceName 包含该关键词则不覆盖(默认用来保留导弹联动偏移)
cfg.put("skipMissileComponentsByNameContains", "导弹");
// ---------- 任务自动命名模板(可改) ----------
// 任务分类优先级:导弹突击 > 防空压制 > 反装甲打击 > 炮火压制 > 通用打击
cfg.put("taskName_missile_strike", "导弹突击打击任务");
@@ -218,6 +226,18 @@ then
applyMissileLinkage($fact, cfg);
end
//-------------------------------------------------------------------------------
// 全组件数量匹配:按 redWeapon.targetId 对应蓝方装备,覆盖“非导弹组件”的数量
rule "全组件数量匹配规则"
agenda-group "打击任务"
salience 53
when
$fact : FactTask(blueTask.side == "蓝方", redTask.side == "红方")
then
Map cfg = buildBusinessConfig();
applyAllComponentQuantities($fact, cfg);
end
//-------------------------------------------------------------------------------
rule "任务自动匹配规则"
agenda-group "打击任务"
@@ -581,6 +601,159 @@ function void bindTargetIdsForRedWeapons(List redWeapons, List blueWeapons, Map
}
}
//-------------------------------------------------------------------------------
// component 映射解析 + 全组件数量覆盖
function Map parseDeviceNameMapping(String csv) {
Map result = new java.util.HashMap();
if (csv == null) {
return result;
}
String text = csv.trim();
if (text.equals("")) {
return result;
}
String[] parts = text.split(",");
for (int i = 0; i < parts.length; i++) {
String one = parts[i];
if (one == null) {
continue;
}
String item = one.trim();
if (item.equals("")) {
continue;
}
int idx = item.indexOf("->");
if (idx <= 0 || idx >= item.length() - 2) {
continue;
}
String left = item.substring(0, idx).trim();
String right = item.substring(idx + 2).trim();
if (left.equals("") || right.equals("")) {
continue;
}
result.put(left, right);
}
return result;
}
function void applyAllComponentQuantities(FactTask fact, Map cfg) {
if (fact == null || fact.getBlueTask() == null || fact.getRedTask() == null) {
return;
}
if (!readBooleanCfg(cfg, "enableComponentQuantityMatch", true)) {
return;
}
Task blueTask = fact.getBlueTask();
Task redTask = fact.getRedTask();
List blueWeapons = blueTask.getTaskWeapons();
List redWeapons = redTask.getTaskWeapons();
if (blueWeapons == null || blueWeapons.isEmpty() || redWeapons == null || redWeapons.isEmpty()) {
return;
}
String mappingCsv = cfg == null ? null : (String) cfg.get("componentDeviceNameMappingCsv");
Map deviceNameMapping = parseDeviceNameMapping(mappingCsv);
String skipKeyword = cfg == null ? null : (String) cfg.get("skipMissileComponentsByNameContains");
if (skipKeyword != null) {
skipKeyword = skipKeyword.trim();
if (skipKeyword.equals("")) {
skipKeyword = null;
}
}
// 遍历红方每个武器:用 targetId 找蓝方装备equipmentId
for (Object objR : redWeapons) {
Weapon redWeapon = (Weapon) objR;
if (redWeapon == null) {
continue;
}
String targetId = redWeapon.getTargetId();
if (isBlank(targetId)) {
// 允许不匹配targetId 为空直接跳过
continue;
}
Weapon blueWeapon = null;
for (Object objB : blueWeapons) {
Weapon w = (Weapon) objB;
if (w == null) {
continue;
}
String eqId = w.getEquipmentId();
if (eqId != null && eqId.equals(targetId)) {
blueWeapon = w;
break;
}
}
if (blueWeapon == null) {
continue;
}
List blueComps = blueWeapon.getComponents();
List redComps = redWeapon.getComponents();
if (blueComps == null || blueComps.isEmpty() || redComps == null || redComps.isEmpty()) {
continue;
}
// 用蓝方组件驱动覆盖红方组件数量
for (Object objBC : blueComps) {
SubComponents blueComp = (SubComponents) objBC;
if (blueComp == null) {
continue;
}
String blueCompName = blueComp.getDeviceName();
if (isBlank(blueCompName)) {
continue;
}
// 跳过导弹组件覆盖,避免覆盖导弹联动偏移逻辑
if (skipKeyword != null && blueCompName.contains(skipKeyword)) {
continue;
}
String redCompName = (String) deviceNameMapping.get(blueCompName);
if (redCompName == null || redCompName.trim().equals("")) {
redCompName = blueCompName;
}
SubComponents redComp = null;
for (Object objRC : redComps) {
SubComponents rc = (SubComponents) objRC;
if (rc == null) {
continue;
}
if (redCompName.equals(rc.getDeviceName())) {
redComp = rc;
break;
}
}
if (redComp == null) {
// 允许不匹配:红方下没有该组件则跳过
continue;
}
List blueParams = blueComp.getComponentParams();
List redParams = redComp.getComponentParams();
if (blueParams == null || blueParams.isEmpty() || redParams == null || redParams.isEmpty()) {
continue;
}
ComponentParam blueFirst = (ComponentParam) blueParams.get(0);
ComponentParam redFirst = (ComponentParam) redParams.get(0);
if (blueFirst == null || redFirst == null) {
continue;
}
if (blueFirst.getNumber() == null) {
continue;
}
// 只覆盖数量componentParams[0].number
redFirst.setNumber(blueFirst.getNumber());
}
}
}
function void prepareDeploymentPools(FactTask fact, Map cfg, Map runtime) {
if (!readBooleanCfg(cfg, "enablePositionRules", true)) {
return;