Скрипт делает следующее:
ИИ группа выдвигается из одной точки в другую.
При обнаружении противника группа меняет маршрут и обходит противника на безопасном расстоянии по более короткому пути. При этом учитываются все известные противники, стоящие на пути группы.
Если противник стоит слишком близко к точке завершения движения, группа выбирает наиболее близкую точку на безопасном расстоянии и завершает патрульные действия в ней.
Скачать шаблон:
Или:
1 - создать файл в папке с миссией и назвать его sqripts_and_variables.sqf
В этот файл скопировать:
// Иконка: (Можно отключить) group_2 addGroupIcon ["n_recon"]; setGroupIconsVisible [true,true]; group_2 setGroupIconParams [[0,0.5,0,1], "Recon group", 1, true]; // AUTOCOMBAT: {_x disableAI "AUTOCOMBAT"} forEach (units group_2); // Переменные: PC_RECON_MOVING_DIST = 200; PC_ENEMY_SIDE = "EAST"; // Скрипты и функции: PC_fn_forget_all_targets = { params ["_leader"]; _targets = _leader targets [true, 500, [], 0]; { group _leader forgetTarget _x; } forEach _targets; }; PC_fn_recon_path_do_not_intersect_enemy_zones = { params ["_start_pos", "_end_pos", "_radius", "_enemies"]; _do_not_intersect = true; if (count _enemies < 1) exitWith { _do_not_intersect; }; { _enemy = _x; _nearest_position = [_start_pos, _end_pos, position _enemy, true] call BIS_fnc_nearestPoint; if (_nearest_position distance2D _enemy < _radius) then { if ((_start_pos distance2d _nearest_position) + (_end_pos distance2d _nearest_position) > (_start_pos distance2d _end_pos)+1) then { switch (true) do { case (_start_pos distance2d _nearest_position < _radius): { _do_not_intersect = false; }; case (_end_pos distance2d _nearest_position < _radius): { _do_not_intersect = false; }; }; } else { _do_not_intersect = false; }; }; } forEach _enemies; _do_not_intersect; }; PC_direct_fn_allies_recon = { params ["_group","_action_position"]; private _leader = leader _group; private _name = format ["%1_PC_WP_NAME_recon_%2", _group, (count waypoints _group)]; private _wp1 = group _leader addWaypoint [_action_position, 0, (count waypoints _leader) + 1]; _wp1 setWaypointName _name; _wp1 setWaypointType "SCRIPTED"; _group setFormation "FILE"; waitUntil {formation _group in ["FILE"]}; _script = [_leader, _action_position] spawn PC_direct_fn_allies_are_reconing; private _wp1 = group _leader addWaypoint [_action_position, 0, (count waypoints _leader) + 1]; _wp1 setWaypointName _name; _wp1 setWaypointType "MOVE"; //_wp1 setWaypointBehaviour "STEALTH"; //_wp1 setWaypointForceBehaviour true; _wp1 setWaypointCombatMode "GREEN"; _wp1 setWaypointFormation "DIAMOND"; //_wp1 setWaypointSpeed "NORMAL"; _wp1 setWaypointStatements ["true", "group this setVariable ['PC_AI_isReconing', false, true]; "]; }; PC_direct_fn_allies_are_reconing = { params ["_recon","_action_position"]; _group = leader _recon; _merged_enemies_on_the_path = []; /// Подготовка отделения: { //_x setUnitPos "MIDDLE"; //_x setBehaviour "STEALTH"; _x setBehaviour "AWARE"; _x setCombatMode "GREEN"; _x setUnitTrait ["audibleCoef", (_x getUnitTrait "audibleCoef")/2]; _x setUnitTrait ["camouflageCoef", (_x getUnitTrait "camouflageCoef")/2]; _x disableAI "AUTOCOMBAT"; } forEach units group _recon; group _recon setVariable ["PC_AI_isReconing", true, true]; group _recon setVariable ["PC_AI_recon_at_his_last_temperary_point", false, true]; group _recon setVariable ["PC_AI_recon_path", [], true]; [_recon] call PC_fn_forget_all_targets; _goal_waypoint = []; { if ( (waypointPosition _x distance2d _action_position < 5) && ((waypointName _x) find "_recon_" != -1) ) exitWith { _goal_waypoint = _x; }; } forEach waypoints (group _recon); /// Цикл разведки: while {group _recon getVariable "PC_AI_isReconing"} do /// Основной цикл разведки { /// Фиксация известных группе врагов: _all_known_targets_before = _recon targets [true]; _all_enemy_leaders = _all_known_targets_before apply { (leader group _x) }; _all_enemy_leaders = _all_enemy_leaders arrayIntersect _all_enemy_leaders; /// Получение временных мар точек из переменной PC_AI_recon_path: if (count (group _recon getVariable ["PC_AI_recon_path", []]) > 0 ) then { /// Есть обходной путь, ставим временные мар точки: _goal_waypoint_index = _goal_waypoint select 1; _first_temporary_waypoint_index = 0; for "_i" from 0 to (count (group _recon getVariable ["PC_AI_recon_path", []]))-1 do { _name = format ["%1_PC_WP_NAME_temporary_%2", _group, _i]; _wp1 = group _recon addWaypoint [(group _recon getVariable ["PC_AI_recon_path", []]) select _i, 0, _goal_waypoint_index + 1 + _i]; _wp1 setWaypointName _name; _wp1 setWaypointType "MOVE"; /// Получаем индекс первой временной точки: if (_i == 0) then { _first_temporary_waypoint_index = _goal_waypoint_index + 1; group _recon setCurrentWaypoint [group _recon, _first_temporary_waypoint_index]; }; /// Если это последняя временная точка: if (_i == (count (group _recon getVariable ["PC_AI_recon_path", []]))-1) then { _wp1 setWaypointStatements ["true", "(group this) setVariable ['PC_AI_recon_at_his_last_temperary_point', true, true]"]; /// Если эта последняя временная точка должна стать завершением разведки: if !([_action_position, _action_position, PC_RECON_MOVING_DIST, _merged_enemies_on_the_path] call PC_fn_recon_path_do_not_intersect_enemy_zones) then { _wp1 setWaypointStatements ["true", "[(group this), (currentWaypoint (group this))+1] setWaypointPosition [position this, 0];"]; }; /// После создания всех временных маршрутных точек переводим группу на первую временную точку: group _recon setCurrentWaypoint [group _recon, _first_temporary_waypoint_index]; }; /* _marker = createMarker ["enemy_marker_" + (str time) + "_" + (str _i), (group _recon getVariable ["PC_AI_recon_path", []]) select _i]; _marker setMarkerType "Contact_pencilCircle2"; _marker setMarkerColor "ColorBlue"; _marker setMarkerText format ["%1", _i + 1];*/ }; }; /// Цикл ожидания для повтора вычисления пути или выхода из разведки: waitUntil { sleep 1; _recon = leader _group; /// Проверка, каких врагов группа видит: _all_targets = (nearestObjects [_recon, ["Land","Air"], 300]) select { (alive _x) && (side group _x isEqualTo PC_ENEMY_SIDE) && ([position _recon, getDir _recon, 180, position _x] call BIS_fnc_inAngleSector) && (([objNull, "IFIRE"] checkVisibility [eyePos _recon, eyepos _x] > 0.0001)) }; if (count _all_targets > 0) then { { _target = _x; _recon reveal [_target, 2]; leader group _recon reveal [_target, 2]; } forEach _all_targets; }; _all_known_targets = _recon targets [true]; _all_enemy_leaders = []; if (count _all_known_targets > 0) then { _all_enemy_leaders = _all_known_targets apply { (leader group _x) }; _all_enemy_leaders = _all_enemy_leaders arrayIntersect _all_enemy_leaders; }; /// Условия выхода из ожидания: !(group _recon getVariable "PC_AI_isReconing") /// Группа отозвана или закончила разведку || (if (count _all_known_targets_before > 0) then {({if !(_x in _all_known_targets_before) exitWith {true}; false} forEach (_recon targets [true]))} else {if (count (_recon targets [true]) > 0) then {true} else {false}}) /// Появился новый враг || (group _recon getVariable ["PC_AI_recon_at_his_last_temperary_point", false]); /// Разведчики дошли до последней временной точки }; /// Удаляем все маркеры старых маршрутов: { if ((_x find "enemy_marker_" != -1) || (_x find "goal_marker_" != -1)) then { deleteMarker _x; }; } forEach allMapMarkers; /// Если это обход противника, в зоне которого конечное назначение: if ((group _recon getVariable ["PC_AI_recon_at_his_last_temperary_point", false]) && (_recon distance2d (waypointPosition _goal_waypoint) < 10)) then { group _recon setVariable ["PC_AI_isReconing", false, true]; }; /// Удаление временных маршрутных точек: _group_waypoints = waypoints group _recon; reverse _group_waypoints; { if (waypointName _x find "_temporary" != -1) then { deleteWaypoint _x; }; } forEach _group_waypoints; group _recon setVariable ["PC_AI_recon_at_his_last_temperary_point", false, true]; group _recon setVariable ["PC_AI_recon_path", [], true]; /// Если разведку отменили или разведка дошла до своей конечной цели: if !(group _recon getVariable "PC_AI_isReconing") exitWith { /// Выход из разведки: }; /// Начинается проверка: /// Определение врагов на глаз плюс всех известных до этого целей: _all_targets = (nearestObjects [_recon, ["Land","Air"], 300]) select { (alive _x) && (side group _x isEqualTo PC_ENEMY_SIDE) && ([position _recon, getDir _recon, 180, position _x] call BIS_fnc_inAngleSector) && (([objNull, "IFIRE"] checkVisibility [eyePos _recon, eyepos _x] > 0.0001)) }; if (count _all_targets > 0) then { { _target = _x; _recon reveal [_target, 2]; leader group _recon reveal [_target, 2]; } forEach _all_targets; }; _all_known_targets = _recon targets [true]; /// Если известны враги, находим путь для их обхода: if (count _all_known_targets > 0) then { _all_enemy_leaders = _all_known_targets apply { (leader group _x) }; _all_enemy_leaders = _all_enemy_leaders arrayIntersect _all_enemy_leaders; _all_enemy_leaders_and_distance = _all_enemy_leaders apply { [_x distance (leader group _recon), _x] }; _all_enemy_leaders_and_distance sort true; _closest_enemy = _all_enemy_leaders_and_distance select 0 select 1; _recon_dir_to_end = _recon getDir _action_position; _recon_dir_to_closest_enemy = _recon getDir _closest_enemy; /// Проверяем, есть ли кто-то из этих врагов на пути группы: if ([position _recon, _action_position, PC_RECON_MOVING_DIST, _all_enemy_leaders] call PC_fn_recon_path_do_not_intersect_enemy_zones) exitWith { /// Нет пересечения с опасными зонами. Повторение цикла: }; /// Создаем массив с точками вокруг всех известных врагов. _all_positions_around_enemies_and_distance_to_recon = []; { _enemy_pos = (_x select 1); for "_i" from 0 to 7 do { _pos = _enemy_pos getPos [PC_RECON_MOVING_DIST + (PC_RECON_MOVING_DIST/10), _recon_dir_to_end + (45*_i)]; _all_positions_around_enemies_and_distance_to_recon = _all_positions_around_enemies_and_distance_to_recon + [[_recon distance2d _pos, _pos]]; }; } forEach _all_enemy_leaders_and_distance; /// Убираем из массива те точки, которые попадают в радиус других врагов: _merged_positions = []; { _pos = (_x select 1); if ({if (_pos distance2d _x < PC_RECON_MOVING_DIST) exitWith {true}; false} forEach _all_enemy_leaders) then { _merged_positions = _merged_positions + [_x]; }; } forEach _all_positions_around_enemies_and_distance_to_recon; _all_positions_around_enemies_and_distance_to_recon = _all_positions_around_enemies_and_distance_to_recon - _merged_positions; /// Вычисляем тех врагов, через которых проходит путь: _enemy_leaders_on_the_path = []; { if !([position _recon, _action_position, PC_RECON_MOVING_DIST, [_x]] call PC_fn_recon_path_do_not_intersect_enemy_zones) then { _enemy_leaders_on_the_path = _enemy_leaders_on_the_path + [_x]; }; } forEach _all_enemy_leaders; /// Из них ближайший: _enemy_leaders_on_the_path_and_distance = _enemy_leaders_on_the_path apply {[_x distance2d _recon, _x]}; _enemy_leaders_on_the_path_and_distance sort true; _closest_enemy_on_the_path = _enemy_leaders_on_the_path_and_distance select 0 select 1; /// Проверяем, кто из врагов приклеен зоной к первому врагу на пути: _merged_enemies_on_the_path = [_closest_enemy_on_the_path]; { _leader = (_x select 1); if ({if (_leader distance2d _x < (PC_RECON_MOVING_DIST*2)) exitWith {true}; false} forEach _merged_enemies_on_the_path) then { _merged_enemies_on_the_path = _merged_enemies_on_the_path + [_leader]; }; sleep 0.1; } forEach _all_enemy_leaders_and_distance; _merged_enemies_on_the_path = _merged_enemies_on_the_path arrayIntersect _merged_enemies_on_the_path; /// Удаляем те точки, которые не от этих врагов: _positions_not_merged = []; { _pos = (_x select 1); if ({if ((_pos distance2d _x) < (5 + PC_RECON_MOVING_DIST + (PC_RECON_MOVING_DIST/10))) exitWith {false}; true} forEach _merged_enemies_on_the_path) then { _positions_not_merged = _positions_not_merged + [_x]; }; } forEach _all_positions_around_enemies_and_distance_to_recon; _all_positions_around_enemies_and_distance_to_recon = _all_positions_around_enemies_and_distance_to_recon - _positions_not_merged; ///Тест: создаем маркеры: { _marker = createMarker ["enemy_marker_" + (str time) + (str _forEachIndex), (_x select 1)]; _marker setMarkerType "mil_dot_noShadow"; _marker setMarkerColor "ColorBlack"; //_marker setMarkerAlpha 1; } forEach _all_positions_around_enemies_and_distance_to_recon; sleep 0.001; /// Пересечение с опасными зонами. Определяем те точки, до которых группа дойдет не пересекая опасные сектора: _closest_point = []; _farest_point = []; if ({if (_recon distance2d _x < PC_RECON_MOVING_DIST + (PC_RECON_MOVING_DIST/10)) exitWith {true}; false} forEach _all_enemy_leaders) then { /// Группа оказалась в опасном секторе. Отходит к ближайшей позиции за радиус опасного сектора: _all_positions_around_enemies_and_distance_to_recon sort true; _closest_point = _all_positions_around_enemies_and_distance_to_recon select 0 select 1; /* _marker = createMarker ["enemy_marker_" + (str time), _closest_point]; _marker setMarkerType "mil_dot_noShadow"; _marker setMarkerColor "ColorRed";*/ } else { /// Группа вне опасного сектора. Ищет наиболее дальнюю позицию от разведчика с учетом не пересечения опасных зон: _all_positions_around_enemies_and_distance_to_recon sort false; { _pos = (_x select 1); if ([position _recon, _pos, PC_RECON_MOVING_DIST, _all_enemy_leaders] call PC_fn_recon_path_do_not_intersect_enemy_zones) exitWith { /// эта точка дальше всего от группы и не пересекает опасную зону: _farest_point = _pos; /* _marker = createMarker ["enemy_marker_" + (str time), _farest_point]; _marker setMarkerType "mil_dot_noShadow"; _marker setMarkerColor "ColorBlue";*/ }; } forEach _all_positions_around_enemies_and_distance_to_recon; }; /// Проверка, не пройдет ли группа от ближайшей или наиболее дальней точки сразу к цели: switch (true) do { case (count _closest_point > 0): { if ([_closest_point, _action_position, PC_RECON_MOVING_DIST, _all_enemy_leaders] call PC_fn_recon_path_do_not_intersect_enemy_zones) exitWith { /// Нет пересечения с опасными зонами: group _recon setVariable ["PC_AI_recon_path", [_closest_point], true]; }; }; case (count _farest_point > 0): { if ([_farest_point, _action_position, PC_RECON_MOVING_DIST, _all_enemy_leaders] call PC_fn_recon_path_do_not_intersect_enemy_zones) exitWith { /// Нет пересечения с опасными зонами: group _recon setVariable ["PC_AI_recon_path", [_farest_point], true]; }; }; }; if (count (group _recon getVariable ["PC_AI_recon_path", []]) > 0) exitWith { /// Есть путь. Возвращение назад к началу цикла: }; /// Группа не может пройти через ближайшую или наиболее дальнюю точку сразу до цели. Определение двух ближайших точек по обе стороны от группы: _all_positions_around_enemies_and_distance_to_recon sort true; _closest_position = _all_positions_around_enemies_and_distance_to_recon select 0 select 1; //---------- TEST /* _marker = createMarker ["enemy_marker_" + (str time), _closest_position]; _marker setMarkerType "mil_dot_noShadow"; _marker setMarkerColor "ColorGreen"; */ //---------- _fake_object = createVehicle ["Land_InvisibleBarrier_F", position _recon, [], 1, "CAN_COLLIDE"]; _fake_object setDir (_recon getDir _action_position); _left_side_positions = []; _right_side_positions = []; _all_positions_around_enemies_and_distance_to_recon = _all_positions_around_enemies_and_distance_to_recon - [_closest_position]; /// Здесь, если зона врага пересекается всего в одной точке, выход с путем из одной точки: if (count _all_positions_around_enemies_and_distance_to_recon < 1) exitWith { group _recon setVariable ["PC_AI_recon_path", [_closest_position], true]; }; /// Если путь состоит из больше, чем одной точки: { _pos = (_x select 1); if (_fake_object getRelDir _pos < 180) then { _right_side_positions = _right_side_positions + [_pos]; } else { _left_side_positions = _left_side_positions + [_pos]; }; } forEach _all_positions_around_enemies_and_distance_to_recon; sleep 0.001; deleteVehicle _fake_object; /// Ищем позицию на второй стороне зоны: _position_on_other_side = []; _all_positions_around_enemies_and_distance_to_recon_and_to_line = _all_positions_around_enemies_and_distance_to_recon apply {[(([position _recon, _action_position, (_x select 1), true] call BIS_fnc_nearestPoint) distance2d (_x select 1))] + _x}; _all_positions_around_enemies_and_distance_to_recon_and_to_line sort true; _all_positions_around_enemies_and_distance_to_recon_and_to_line resize 4; _all_positions_around_enemies_and_distance_to_recon_and_to_line = _all_positions_around_enemies_and_distance_to_recon_and_to_line apply {[(_x select 1),(_x select 2)]}; _all_positions_around_enemies_and_distance_to_recon_and_to_line sort false; _position_on_other_side = _all_positions_around_enemies_and_distance_to_recon_and_to_line select 0 select 1; //---------- TEST /* _marker = createMarker ["enemy_marker_" + (str time), _position_on_other_side]; _marker setMarkerType "mil_dot_noShadow"; _marker setMarkerColor "ColorYellow"; */ //---------- /// Вычисление пути с двух сторон: _left_side_positions = _left_side_positions + [_position_on_other_side]; _right_side_positions = _right_side_positions + [_position_on_other_side]; _left_side_positions = _left_side_positions arrayIntersect _left_side_positions; _right_side_positions = _right_side_positions arrayIntersect _right_side_positions; _left_side_positions_and_distances = _left_side_positions apply {[(_x distance2D _closest_position), _x]}; _left_side_positions_and_distances sort true; _first_left_side_position = _left_side_positions_and_distances select 0 select 1; _right_side_positions_and_distances = _right_side_positions apply {[(_x distance2D _closest_position), _x]}; _right_side_positions_and_distances sort true; _first_right_side_position = _right_side_positions_and_distances select 0 select 1; _left_path = [_closest_position, _first_left_side_position]; _right_path = [_closest_position, _first_right_side_position]; _left_path_dist = 0; _right_path_dist = 0; { if (_forEachIndex == ((count _right_side_positions)-1)) exitWith {}; private _last_pos = _right_path select ((count _right_path)-1); private _pos_array = _right_side_positions select {!(_x in _right_path)}; _pos_array = _pos_array apply {[(_x distance2D _last_pos), _x]}; _pos_array sort true; private _new_pos = _pos_array select 0 select 1; _right_path = _right_path + [_new_pos]; _right_path_dist = _right_path_dist + (_last_pos distance2d _new_pos); } forEach _right_side_positions; { if (_forEachIndex == ((count _left_side_positions)-1)) exitWith {}; private _last_pos = _left_path select ((count _left_path)-1); private _pos_array = _left_side_positions select {!(_x in _left_path)}; _pos_array = _pos_array apply {[(_x distance2D _last_pos), _x]}; _pos_array sort true; private _new_pos = _pos_array select 0 select 1; _left_path = _left_path + [_new_pos]; _left_path_dist = _left_path_dist + (_last_pos distance2d _new_pos); } forEach _left_side_positions; /// Определяем, с какой стороны обходить: _path = []; if (_left_path_dist > _right_path_dist) then { _path = _right_path; } else { _path = _left_path; }; /// Если конечная точка не находится в зоне врага, отрезаем от пути те точки, которые можно сократить: _shorten_path = _path; /// Наиболее далекая точка: { _pos = _x; if ([_action_position, _pos, PC_RECON_MOVING_DIST, _all_enemy_leaders] call PC_fn_recon_path_do_not_intersect_enemy_zones) exitWith { _index = _shorten_path find _pos; _shorten_path resize (_index + 1); }; } forEach _path; reverse _shorten_path; /// Наиболее близкая точка: { _pos = _x; if ([position _recon, _pos, PC_RECON_MOVING_DIST, _all_enemy_leaders] call PC_fn_recon_path_do_not_intersect_enemy_zones) exitWith { _index = _shorten_path find _pos; _shorten_path resize (_index + 1); }; } forEach _shorten_path; reverse _shorten_path; /// Проверка, не находится ли конечное место разведки в зоне известных врагов, с которыми пересекается путь: if !([_action_position, _action_position, PC_RECON_MOVING_DIST, _merged_enemies_on_the_path] call PC_fn_recon_path_do_not_intersect_enemy_zones) then { _shorten_path_to_cut = _shorten_path; _shorten_path_to_cut_1 = _shorten_path; _shorten_path_to_cut_and_distances = _shorten_path_to_cut apply {[(_x distance2d _action_position), _x]}; _shorten_path_to_cut_and_distances sort true; _position_with_shortes_distance = _shorten_path_to_cut_and_distances select 0 select 1; { if (_x isEqualTo _position_with_shortes_distance) exitWith { _shorten_path resize (_forEachIndex + 1); }; } forEach _shorten_path_to_cut_1; }; /// TEST { _marker = createMarker ["enemy_marker_" + (str time) + (str _forEachIndex), _x]; _marker setMarkerType "Contact_pencilCircle2"; _marker setMarkerColor "ColorWhite"; _marker setMarkerText str (_forEachIndex +1); //_marker setMarkerAlpha 1; } forEach _shorten_path; sleep 0.001; /// Есть путь. Возврашение к началу цикла: group _recon setVariable ["PC_AI_recon_path", _shorten_path, true]; } else { /// Враги не известны, продолжаем движение }; }; /// Выход из маршрутной точки: _group setVariable ["PC_AI_recon_at_his_last_temperary_point", false, true]; _group setVariable ["PC_AI_recon_path", [], true]; { _x setUnitPos "AUTO"; //_x setBehaviour "STEALTH"; _x setBehaviour "AWARE"; //_x setCombatMode "GREEN"; _x setUnitTrait ["audibleCoef", (_x getUnitTrait "audibleCoef")*2]; _x setUnitTrait ["camouflageCoef", (_x getUnitTrait "camouflageCoef")*2]; } forEach units group _recon; };
2 - вызвать этот скрипт из init.sqf:
[] call compile preprocessfilelinenumbers "sqripts_and_variables.sqf";
3 - вызвать запуск патруля из игры любым способом:
// group_2 - имя переменной развед-группы // getMarkerPos "m_1" - точка завершения действий патруля [group_2,getMarkerPos "m_1"] spawn PC_direct_fn_allies_recon;