=300zx= 3 Опубликовано 28 Июля 2020 (изменено) Для адаптации AI Additions на OGSM ТЧ, насколько я понимаю, нужно еще операторам в новых модулях прописать следующие предусловия: if ogsm_hideout then action:add_precondition(world_property(ogsm_hideout.property_blowout, false)) action:add_precondition(world_property(ogsm_hideout.property_hideout_lost, false)) action:add_precondition(world_property(ogsm_hideout.property_inhide, false)) action:add_precondition(world_property(ogsm_hideout.property_psyzoned, false)) end if anomaly_evader then action:add_precondition (world_property(1099,false)) end Только вот вопрос, для всех ли операторов это нужно прописывать? Изменено 28 Июля 2020 пользователем =300zx= 1 Поделиться этим сообщением Ссылка на сообщение
=300zx= 3 Опубликовано 11 Августа 2020 04.08.2020 в 20:31, UriZzz сказал: @=300zx=, ну не знаю, я адаптировал аи адд и мод на выбросы, они друг другу не мешали. Во всяком случае у меня при тестах проблем выявлено не было Явных проблем может и не будет, но для каких-то action возможно стоит добавить эти предусловия. Например, чтобы непись во время выброса не шел лутать итд. В оригинальных модулях AI Additions есть закоментированные строчки для АМК-выброса: -- if blowout_scheme and blowout_scheme.evid_outside then -- action:add_precondition(world_property(blowout_scheme.evid_outside,false)) -- end В OGSE, например в файле rx_gl.script, есть такие строчки: local action = action_launch_grenade(npc,"act_launch_grenade",storage) ... action:add_precondition(world_property(ogsm_hideout.property_blowout, false)) action:add_precondition(world_property(ogsm_hideout.property_hideout_lost, false)) action:add_precondition(world_property(ogsm_hideout.property_psyzoned, false)) Но в OGSE используется предыдущая версия AI Additions и не такая схема обхода аномалий как в OGSM. 2 Поделиться этим сообщением Ссылка на сообщение
=300zx= 3 Опубликовано 16 Января 2021 @Bak Несколько предложений по поводу исправления оригинальных недоработок схем AI. 1) Неписи после потери цели из виду идут к тому месту, где последний раз ее видели, сбиваются в кучу и смотрят в стену/пол/потолок с криками: "Выходи, все равно найдем". Предложение: либо заставить нпс походить/побегать вокруг этого места, или включать что-то типа post-combat. 2) Реагирование неписей на несуществующую (или уже несуществующую) или далекую опасность по типу компании Петрухи, когда они разбегаются по сторонам и сидят целясь куда-то, хотя рядом опасностей нет. Часто такое происходит после сохранения-загрузки. Не знаю, пофиксено ли это в AI additions 2. 3) Неписи даже при получении урона не меняют позиции, не говоря уже при попадании рядом с ними. Предложение: был один мод на ЗП от Pavlov - Клондайк MOD, в нем была схема avoid headshot. С ней вражеские неписи отбегали в сторону, если им на голову навести прицел (курсор). Возможно на основе этой идеи можно улучшить схему боевки при перестрелке неписей с игроком. То есть, если непись видит игрока и игрок навел прицел на тушку непися, то нпс начинает маневрировать или уходить в укрытие. Хотя, по идее надо брать некоторый радуис вокруг прицела игрока. При перестрелке неписей с неписями, если правильно понимаю, просто проверка видят ли неписи друг друга и в зависимости от результатов - маневрировать, уходить в укрытие итд. В боевке ОГСЕ такие условия: Скрытый текст if (npc:see(be) and (be:see(npc) or raytrace_see)) or (is_armor(be) and get_armor_vis(be, npc)) then -- если видим врага и он видит нас -- стреляем, ищем укрытие ... elseif (npc:see(be) and not (be:see(npc) or raytrace_see)) or (is_armor(be) and not get_armor_vis(be, npc)) then -- если видим врага и он НЕ видит нас -- стреляем, маневрируя ... elseif not npc:see(be) and (be:see(npc) or raytrace_see) then -- если НЕ видим врага и он видит нас -- убегаем, ищем укрытие ... else -- если НЕ видим врага и он НЕ видит нас -- ищем врага ЗЫ Стоит ли ожидать доработки схем xr_camper, xr_danger итд из Prosectors Project в AI Additions? 1 Поделиться этим сообщением Ссылка на сообщение
=300zx= 3 Опубликовано 19 Января 2021 16.01.2021 в 23:08, gam сказал(а): @=300zx= Я не использовал версию выше 25.10.2009, но сбивание неписей в кучу там вроде правится. Возможно грешит адаптация - либо конфликт с другой схемой аи. Это баг c оригинала ТЧ.@Bak , насчет адаптаций, в OGSM v2.4.3 в move_mgr.script есть правка от RED75 на проверку доступности хоть одной точки на пути. Совместима ли она с правками move_mgr из AI Additions 2? Скрытый текст -- Movement manager -- Created by: Andrey Fidrya (Zmey), af@svitonline.com function printf() end local dist_walk = 10 -- < dist_run local dist_run = 2500 local walk_min_time = 3000 local run_min_time = 2000 local keep_state_min_time = 1500 -- переключившись в состояние (бег, ходьба, спринт), не переключаться в другое N ms local default_wait_time = 5000 local default_state_standing = "guard" local default_state_moving1 = "patrol" local default_state_moving2 = "rush" local default_state_moving3 = "sprint" arrival_before_rotation = 0 arrival_after_rotation = 1 local state_none = 0 local state_moving = 1 local state_standing = 2 local sync = {} ------------------------------------------------------------------------------------------------------------------------- function choose_look_point(patrol_look, path_look_info, search_for) local this_val -- значение флагов текущей точки local pts_found_total_weight = 0 -- количество найденных точек (с нужными флагами) local pt_chosen_idx = nil -- индекс выбранной точки local r local num_equal_pts = 0 for look_idx = 0, patrol_look:count() - 1 do this_val = path_look_info[look_idx].flags if this_val:equal(search_for) then num_equal_pts = num_equal_pts + 1 -- Нашли точку с нужными флагами, но поскольку в пути могут быть еще такие-же -- точки, возьмем текущую только с некоторой вероятностью: -- Шанс, с которым на точку посмотрит персонаж: local point_look_weight = path_look_info[look_idx]["p"] if point_look_weight then point_look_weight = tonumber(point_look_weight) else point_look_weight = 100 -- по умолчанию у всех точек вес = 100 end pts_found_total_weight = pts_found_total_weight + point_look_weight r = math.random(1, pts_found_total_weight) if r <= point_look_weight then pt_chosen_idx = look_idx end end end return pt_chosen_idx, num_equal_pts end ------------------------------------------------------------------------------------------------------------------------- class "move_mgr" function move_mgr:__init(npc) if npc == nil then abort("move_mgr:__init() - npc is nil, please update the script") end self.object = npc end function move_mgr:initialize(npc) if npc ~= nil then abort("Wrong arguments passed to move_mgr:initialize(), please update the script") end --printf("move_mgr:initialize()") self.object:set_callback(callback.patrol_path_in_point, self.waypoint_callback, self) end --' Удостоверяется, что пути и флажки на них проставлены корректно function move_mgr:validate_paths() if self.no_validation then return end local patrol_walk_count = self.patrol_walk:count() if patrol_walk_count == 1 then if self.path_walk_info[0].flags:get() == 0 then abort("object '%s': path_walk '%s' has 1 waypoint, but has no flags", self.object:name(), self.path_walk) end end end function move_mgr:extrapolate_callback(npc) self.can_use_get_current_point_index = true self.current_point_init_time = time_global() self.current_point_index = self.object:get_current_point_index() end function move_mgr:standing_on_terminal_waypoint() if self.patrol_walk ~= nil then for idx = 0, self.patrol_walk:count() - 1 do if utils.stalker_at_waypoint(self.object, self.patrol_walk, idx) and self.patrol_walk:terminal(idx) then return true, idx end end end return false end --' Может быть вызвано внешним скриптом после вызова reset() и до вызова finalize() --' Возвращает true, если персонаж прибыл в конечную точку пути function move_mgr:at_terminal_waypoint() return self.at_terminal_waypoint_flag end --' Из move_cb вернуть true, чтобы приостановить работу схемы. Чтобы продолжить движение, --' нужно вызвать метод set_movement_state, который включит перемещение по вейпоинтам с нужной --' скоростью. function move_mgr:reset(path_walk, path_walk_info, path_look, path_look_info, team, suggested_state, move_cb_info, no_validation, continue, use_default_sound) --printf("move_mgr:reset() [%s]", self.object:name()) --' сколько ждать в точке, где играем анимацию self.pt_wait_time = default_wait_time --' Запоминаем массив целиком на случай, если придется себя сбросить, повторно --' вызвав reset(): self.suggested_state = suggested_state --' После этого распарсиваем массив: if not suggested_state then self.default_state_standing = default_state_standing self.default_state_moving1 = default_state_moving1 self.default_state_moving2 = default_state_moving2 self.default_state_moving3 = default_state_moving3 else self.default_state_standing = if_then_else(suggested_state.standing, suggested_state.standing, default_state_standing) self.default_state_moving1 = if_then_else(suggested_state.moving1, suggested_state.moving1, default_state_moving1) self.default_state_moving2 = if_then_else(suggested_state.moving2, suggested_state.moving1, default_state_moving2) self.default_state_moving3 = if_then_else(suggested_state.moving3, suggested_state.moving1, default_state_moving3) end --' С момента включения схемы должна пройти как минимум секунда, прежде чем --' проверять состояние синхронизации с другими сталкерами (иначе после лоада --' они могут не успеть заспавниться). self.syn_signal_set_tm = time_global() + 1000 self.syn_signal = nil self.move_cb_info = move_cb_info --' Возможные изменения --' Изменилась команда if team ~= self.team then self.team = team if self.team then local s = sync[self.team] if not s then sync[self.team] = {} s = sync[self.team] end s[self.object:id()] = false -- not synchronized end end --' Изменились пути if self.path_walk ~= path_walk or self.path_look ~= path_look then self.no_validation = no_validation self.path_walk = path_walk self.patrol_walk = patrol(path_walk) if not self.patrol_walk then abort("object '%s': unable to find path_walk '%s' on the map", self.object:name(), path_walk) end if not path_walk_info then abort("object '%s': path_walk ('%s') field was supplied, but path_walk_info field is nil", self.object:name(), path_walk) end self.path_walk_info = path_walk_info if path_look then if not path_look_info then abort("object '%s': path_look ('%s') field was supplied, but path_look_info field is nil", self.object:name(), path_look) end self.patrol_look = patrol(path_look) if not self.patrol_look then abort("object '%s': unable to find path_look '%s' on the map", self.object:name(), path_look) end else self.patrol_look = nil end self.path_look = path_look self.path_look_info = path_look_info self:validate_paths() self.at_terminal_waypoint_flag = false self.cur_state_standing = self.default_state_standing self.cur_state_moving = self.default_state_moving1 self.retval_after_rotation = nil self.sound_after_anim_start = nil --' Пока этот флаг не станет true (он будет установлен в extrapolate_callback), нельзя использовать --' значение, которое возвращает get_current_point_index(). self.can_use_get_current_point_index = false self.current_point_index = nil self.walk_until = time_global() + walk_min_time self.run_until = time_global() + walk_min_time + run_min_time self.keep_state_until = time_global() self.last_index = nil self.last_look_index = nil self.use_default_sound = use_default_sound self.object:patrol_path_make_inactual() end self:setup_movement_by_patrol_path() end -- Red75 -- local safe_pp= { l01_escape="esc_specnaz_way3", l02_garbage="gar_psevdowounded_gar_way_spy" } -- Проверяем доступность хоть одной точки на пути. Иначе возвращаем другой путь function validate_accessibility(npc,ppname) local pp=patrol(ppname) local valid=false if pp then for i=0,pp:count()-1 do if npc:accessible(pp:level_vertex_id(i)) then valid=true break end end end if valid then return true,ppname else return false,safe_pp[level.name()] or "none" end end -- Red75 -- --' продолжить движение со следующей точки, а не с ближайшей. --' состояние move manager-a не сбрасывается. function move_mgr:continue() --printf("_bp: object '%s': continue moving", self.object:name()) self:setup_movement_by_patrol_path() end function move_mgr:setup_movement_by_patrol_path() -- Red75 -- local valid=validate_accessibility(self.object,self.path_walk) if valid then self.object:set_path_type(game_object.patrol_path) else self.object:set_path_type(game_object.level_path) end self.object:set_detail_path_type(move.line) if valid then if self.current_point_index then self.object:set_start_point(self.current_point_index) self.object:set_patrol_path(self.path_walk, patrol.next, patrol.continue, true) else self.object:set_patrol_path(self.path_walk, patrol.nearest, patrol.continue, true) end else -- путь недоступен. отправляем в ближайшую доступную точку pp=patrol(self.path_walk) if pp and pp:count()>0 then utils.send_to_nearest_accessible_vertex(self.object,pp:level_vertex_id(0)) end end -- Red75 -- self.state = state_moving local is_term, idx = self:standing_on_terminal_waypoint() if is_term then --printf("_bp: object '%s': TERMINAL WAYPOINT", self.object:name()) -- Стоим на терминальной вершине пути - сразу иммитировать прибытие self:waypoint_callback(self.object, nil, idx) else -- Реально идем в вершину self:update_movement_state() end local sect = self.object:section() if self.use_default_sound then self.default_sound = "state" xr_sound.set_sound(self.object, self.default_sound) else self.default_sound = nil end end function move_mgr:arrived_to_first_waypoint() return self.last_index ~= nil end --' Проверка синхронизации с остальными солдатами на пути. --' Возвращает true, если дальнейшее движение разрешено. function move_mgr:sync_ok() if self.team then local s = sync[self.team] local self_id = self.object:id() for k, v in pairs(s) do local obj = level.object_by_id(k) if obj and obj:alive() then if v ~= true then return false end else sync[self.team][k] = nil end end end return true end function move_mgr:update() --printf("move_mgr:update(self.state == %s)", utils.to_str(self.state)) --printf("move_mgr:update(self.object:anims == %d)", self.object:animation_count()) if self.syn_signal and time_global() >= self.syn_signal_set_tm then if self:sync_ok() then self:scheme_set_signal(self.syn_signal) self.syn_signal = nil end end if self.can_use_get_current_point_index and not self:arrived_to_first_waypoint() then local t = time_global() if t >= self.keep_state_until then self.keep_state_until = t + keep_state_min_time local cur_pt = self.current_point_index -- self.patrol_walk здесь по идее то же самое, что вернет patrol(self.object:patrol()), -- поэтому использую его для оптимизации. local dist = self.object:position():distance_to(self.patrol_walk:point(cur_pt)) --printf("_bp: move_mgr: distance to destination waypoint: %d", dist) if dist <= dist_walk or t < self.walk_until then self.cur_state_moving = self.default_state_moving1 elseif dist <= dist_run or t < self.run_until then self.cur_state_moving = self.default_state_moving2 else self.cur_state_moving = self.default_state_moving3 end self:update_movement_state() end return end end function move_mgr:finalize(npc) xr_sound.set_sound(self.object, nil) if self.team then sync[self.team][self.object:id()] = nil end -- чтобы избежать дальнейшего движения по пути при установке рестрикторов self.object:set_path_type(game_object.level_path) end --'----------------------------------------------------------------------------- --' IMPLEMENTATION --'----------------------------------------------------------------------------- function move_mgr:update_movement_state() --printf("%s UPDATE movement state", self.object:name()) state_mgr.set_state(self.object, self.cur_state_moving, nil, nil, nil) end function move_mgr:update_standing_state(look_pos, snd) --printf("_bp [%s]: update_standing_state: snd='%s', pt_wait_time = %s", self.object:name(), utils.to_str(snd), utils.to_str(self.pt_wait_time)) state_mgr.set_state(self.object, self.cur_state_standing, { obj = self, func = self.time_callback, turn_end_func = self.turn_end_callback }, self.pt_wait_time, { look_position = look_pos }, nil, snd ) end function move_mgr:time_callback() --printf("_bp [%s]: time_callback", self.object:name()) local sigtm = self.path_look_info[self.last_look_index]["sigtm"] if sigtm then self:scheme_set_signal(sigtm) end --' Если нет активной схемы - игнорировать. if db.storage[self.object:id()].active_scheme == nil then return end if self.last_index and self.patrol_walk:terminal(self.last_index) then if utils.stalker_at_waypoint(self.object, self.patrol_walk, self.last_index) then --' Если стоим на конечной точке пути и с нее никуда не сдвинулись, --' сразу иммитируем callback на прибытие, чтобы включить look. self:waypoint_callback(self.object, nil, self.last_index) return end --' Стоим на конечной точке пути, но неточно. Чтобы вернуться на ближайшую --' точку пути, сбрасываем схему. Обратите внимаине, что здесь нельзя --' просто вызвать update_movement_state, потому что мы УЖЕ были в --' конечной точке пути и дальше идти некуда, а reset_scheme сбросит --' настройки movement manager-а и выберет ближайшую точку, куда и пойдет. self:reset(self.path_walk, self.path_walk_info, self.path_look, self.path_look_info, self.team, self.suggested_state, self.move_cb_info, self.no_validation) else self:update_movement_state() -- идти дальше local syn = self.path_look_info[self.last_look_index]["syn"] if syn then abort("object '%s': path_walk '%s': syn flag used on non-terminal waypoint", self.object:name(), self.path_walk) end end end function move_mgr:scheme_set_signal(sig) local npc_id = self.object:id() local stor = db.storage[npc_id] --printf("_bp: object '%s': move_mgr: scheme_set_signal '%s', active scheme '%s'", self.object:name(), sig, utils.to_str(stor.active_scheme)) if stor ~= nil and stor[stor.active_scheme] ~= nil then local signals = stor[stor.active_scheme].signals if signals ~= nil then signals[sig] = true end end end function move_mgr:turn_end_callback() local syn = self.path_look_info[self.last_look_index]["syn"] if syn then self.syn_signal = self.path_look_info[self.last_look_index]["sig"] if not self.syn_signal then abort("object '%s': path_look '%s': syn flag uset without sig flag", self.object:name(), self.path_look) end -- Отметить, что мы сами уже прибыли в точку синхронизации: if self.team then sync[self.team][self.object:id()] = true end else local sig = self.path_look_info[self.last_look_index]["sig"] if sig then self:scheme_set_signal(sig) end end local anim_synced_snd = nil if self.sound_after_anim_start then -- Проиграть звук сразу после окончания поворота: anim_synced_snd = self.sound_after_anim_start self.sound_after_anim_start = nil end if self.retval_after_rotation then if not self.move_cb_info then abort("object '%s': path_look '%s': ret flag is set, but " .. "callback function wasn't registered in move_mgr:reset()", self.object:name(), self.path_look) end --' Отключаем таймер путем установки того же самого состояния, но без таймера, --' затем вызываем callback. --' Если callback вернул false, т.е. решил не вмешиваться в перемещение, --' то включаем опять таймер. --' Если callback вернул true - не восстанавливаем таймер т.к. это могли сделать в --' самом callback-е. --' 1) Отключаем таймер state_mgr.set_state(self.object, self.cur_state_standing, nil, nil, nil) --' 2) Вызываем callback if not self.move_cb_info then abort("object '%s': path_look '%s': ret flag is set, but " .. "callback function wasn't registered in move_mgr:reset()", self.object:name(), self.path_look) end if self.move_cb_info.func(self.move_cb_info.obj, this.arrival_after_rotation, self.retval_after_rotation, self.last_index) then --' Callback решил перехватить управление перемещением, не восстанавливаем таймер return end --' Callback не перехватил управление, нужно восстановить таймер: local look_pos = self.patrol_look:point(self.last_look_index) self:update_standing_state(look_pos, anim_synced_snd) end end function move_mgr:waypoint_callback(obj, action_type, index) --printf("move_mgr:waypoint_callback(): name=%s, index=%d", self.object:name(), index) if index == -1 or index == nil then --printf("ERROR: move_mgr: waypoint_callback: index is -1 or nil") return end self.last_index = index if self.patrol_walk:terminal(index) then self.at_terminal_waypoint_flag = true end --' <ЗВУК> local suggested_snd = self.path_walk_info[index]["s"] if suggested_snd then local snd_prob = self.path_walk_info[index]["sp"] if snd_prob then snd_prob = tonumber(snd_prob) else snd_prob = 100 end if snd_prob == 100 or snd_prob >= math.random(1, 100) then xr_sound.set_sound(self.object, suggested_snd) else xr_sound.set_sound(self.object, nil) end else xr_sound.set_sound(self.object, self.default_sound) end --' </ЗВУК> local suggested_state_moving = self.path_walk_info[index]["a"] if suggested_state_moving then self.cur_state_moving = suggested_state_moving else self.cur_state_moving = self.default_state_moving1 end local retv = self.path_walk_info[index]["ret"] if retv then local retv_num = tonumber(retv) if not self.move_cb_info then abort("object '%s': path_walk '%s': ret flag is set, but " .. "callback function wasn't registered in move_mgr:reset()", self.object:name(), self.path_walk) end if self.move_cb_info.func(self.move_cb_info.obj, this.arrival_before_rotation, retv_num, index) then return end end local sig = self.path_walk_info[index]["sig"] if sig then self:scheme_set_signal(sig) end local stop_probability = self.path_walk_info[index]["p"] if not self.patrol_look or (stop_probability and tonumber(stop_probability) < math.random(1, 100)) then self:update_movement_state() --' идти дальше return end -- Значение флагов точки, которую будем искать: local search_for = self.path_walk_info[index].flags if search_for:get() == 0 then self:update_movement_state() --' идти дальше return end local pt_chosen_idx, num_equal_pts = choose_look_point(self.patrol_look, self.path_look_info, search_for) --printf("_bp [%s]: pt_chosen_idx = %s", self.object:name(), utils.to_str(pt_chosen_idx)) if pt_chosen_idx then local suggested_anim_set = self.path_look_info[pt_chosen_idx]["a"] if suggested_anim_set then self.cur_state_standing = suggested_anim_set else self.cur_state_standing = self.default_state_standing end local suggested_wait_time = self.path_look_info[pt_chosen_idx]["t"] if suggested_wait_time then if suggested_wait_time == '*' then self.pt_wait_time = nil -- -1 else local tm = tonumber(suggested_wait_time) if tm ~= 0 and (tm < 1000 or tm > 30000) then abort("object '%s': path_look '%s': flag 't': incorrect time specified (* or number in interval [1000, 30000] is expected)", self.object:name(), self.path_look) end self.pt_wait_time = tm end else self.pt_wait_time = default_wait_time end local retv = self.path_look_info[pt_chosen_idx]["ret"] if retv then self.retval_after_rotation = tonumber(retv) else self.retval_after_rotation = nil end if not suggested_snd then -- в path_walk звук не задан, проверить, задан ли он на точке suggested_snd = self.path_look_info[pt_chosen_idx]["s"] if suggested_snd then local snd_prob = self.path_look_info[pt_chosen_idx]["sp"] if snd_prob then snd_prob = tonumber(snd_prob) else snd_prob = 100 end if snd_prob == 100 or snd_prob >= math.random(1, 100) then --xr_sound.set_sound(self.object, nil, true) self.sound_after_anim_start = suggested_snd else self.sound_after_anim_start = nil end end end local look_pos = self.patrol_look:point(pt_chosen_idx) self.last_look_index = pt_chosen_idx self:update_standing_state(look_pos, self.sound_after_anim_start) self.state = state_standing --' Сразу же стартовать update, не ждать execute. Тогда, если мы уже смотрим --' в нужную сторону - не будет паузы в несколько миллисекунд на поворот. self:update() else abort("object '%s': path_walk '%s', index %d: cannot find corresponding point(s) on path_look '%s'", self.object:name(), tostring(self.path_walk), tostring(index), tostring(self.path_look)) end end Поделиться этим сообщением Ссылка на сообщение