Dennis_Chikin 3 658 Опубликовано 30 Января 2015 Автор Поделиться Опубликовано 30 Января 2015 (изменено) "Вскрытие показало, что больной умер от вскрытия."Тема для "крупной формы", то есть, на уровне скриптов целиком или больших частей оных скриптов. "Что у него внутри, зачем оно там, и что с этим можно сделать ?" Изменено 30 Января 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Okichi 922 Опубликовано 25 Января 2019 Поделиться Опубликовано 25 Января 2019 26 минут назад, Dennis_Chikin сказал: может пойти, а может не пойти. А разве здесь не вполне взаимоисключающая конструкция? Если есть поршень +agroprom_military_case_have - то не пойти в назначенный смарт непись вроде как только по двум причинам может: 1) емкость того смарта уже полностью занята - и все, кто там на данный момент есть - это эксклюзивы ( и тогда это вылет, видимо) 2) или если как в NLC ( тут спасибо dsh за данные когда-то пояснения ) - любой смарт не принимает моба с локации, которая не находится в той же группе, что и локация, на которой расположен этот смарт, потому что в smart_terrain.script есть вот такая проверка. Скрытый текст if level_groups[smart_level_group]~=level_groups[npc_level_group] then return false end А какие другие варианты ? След от кругов на воде - это тоже след (с) Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 25 Января 2019 Автор Поделиться Опубликовано 25 Января 2019 Это в следующей части. Там будут не просто грабли, там - целый склад сельхозинвентаря разнообразного. А вот в том куске, что разобрал - либо if name == "none" then return disagreed, либо elseif name == self:name() then return agreed_exclusive Что найдем в цикле первым. Как бы по-хорошему - none надо хранить и проверять отдельно, а уж если не выполнилось, то уже потом выбирать условия по local t = obj.smart_terrain_conditions[self:name()] if t and xr_logic.pick_section_from_condlist( db.actor_proxy, obj, t ) then return agreed_exclusive end return disagreed ну или типа того. Хотя на самом деле вообще не так, ибо дальше опять же сельхозсклад. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Это популярное сообщение. Dennis_Chikin 3 658 Опубликовано 27 Января 2019 Автор Это популярное сообщение. Поделиться Опубликовано 27 Января 2019 А сейчас - даже не сельхозинвентарь, а, не побоюсь этого слова, посевная и уборочная техника для крупнотоннажного, так сказать, производства. Что, впрочем, не мешает исполнять на ней те же действия с теми же результатами, что и для граблей обыкновенных. local a = self:smart_terrain_accepts_obj( obj, b ) где второй аргумент у нас, напоминаю - можно ли непися запускать в смарт. Уже замечательно для случая с disagreed. Впрочем, если даже неписю предписано идти в конкретный смарт - счастья это особо не прибавляет, ибо: function se_smart_terrain:smart_terrain_accepts_obj( obj, obj_agreement ) if obj_agreement == disagreed then return false end if obj_agreement == agreed_exclusive or self.gulag:get_non_exclusive_population() < self.gulag.capacity_non_exclusive then local community, is_stalker = self:get_obj_community( obj ) local smart_level_group = self:get_level_name() local npc_level_group = alife():level_name(game_graph():vertex(obj.m_game_vertex_id):level_id()) if level_groups[smart_level_group] ~= level_groups[npc_level_group] then return false end if self.accepted_communities and not self.accepted_communities[community] then return false end if obj_agreement ~= agreed_exclusive and not self:check_preset( community, obj:rank(), self.gparams.preset_name ) then return false end if not xr_gulag.checkNpc( community, is_stalker, self.gparams.type, obj:rank(), obj ) then return false end return self.gulag:is_there_any_suitable_job( self:fill_npc_info( obj ), obj_agreement == agreed_exclusive ) else return false end end Первая строка - прекрасна сама по себе. Следующая проверка - если неписю подходит любой смарт - проверяем ограничение на численность неписей, для которых подходит любой смарт. Если непись явно назначен именно в этот смарт, ЛИБО получил, наконец, разрешение идти в него - ограничение на численность неписей, которым подходит любой смарт, не проверяется. С точки зрения банальной эрудиции следует предположить, что все остальные проверки - действуют. Не помню, был ли в оригинальном ТЧ квест на принести уникальный пистолет от командира блокпоста, или он остался от билдов, но именно по этой причине выполнить его крайне проблематично как минимум уже в АМК. Ибо командир блокпоста может быть где угодно, но только не на блокпосту. Ибо банально не проходит все остальные проверки. А вот это - к вопросу о бандитах/наемниках/армейцев/монстрах в баре. local smart_level_group = self:get_level_name() local npc_level_group = alife():level_name(game_graph():vertex(obj.m_game_vertex_id):level_id()) if level_groups[smart_level_group] ~= level_groups[npc_level_group] then return false end В начале скриптика присутствует такая табличка: local level_groups = { l01_escape = "group1", l02_garbage = "group1", l03_agroprom = "group1", l03u_agr_underground = "group1", l04_darkvalley = "group1", l04u_labx18 = "group1", l05_bar = "group1", l06_rostok = "group1", l07_military = "group1", l08_yantar = "group1", l08u_brainlab = "group1", l10_radar = "group1", l10u_bunker = "group1", l11_pripyat = "group1", l12_stancia = "group3", l12u_sarcofag = "group3", l12u_control_monolith = "group3", l12_stancia_2 = "group3" } В модах у нее появился комментарий "чтобы не ходили через бар", впрочем, являющийся, как правило, единственным изменением. Всякое разное же продолжило шастать через бар, материализуясь, как правило, либо у Арни, либо в бункере у Воронина, с результатом слегка предсказуемым. Как это работает, и что с этим делать? В предшествующих текстах, которые все равно никто не прочитал, и читать не будет, уже говорилось, что с момента появления непися/монстра, и далее - периодически, происходит перебор всех, то есть, абсолютно всех смартов на предмет согласия его принять. Какой согласился, и выдал максимальный приоритет - под тот и пойдет. То есть, появился, к примеру, в ТД, а приняли, к примеру, на Янтаре - пойдет на Янтарь. Передвигаются они по вершинкам, из одной в другую, и если такая, которая по пути, окажется, к примеру, в бункере Воронина - монстр/непись появится в бункере Воронина, если мы в этот момент в радиусе онлайна. А если прикрутить еще "оффлайн-боевку" или "офлайн-шмон" - эта "офлайн-боевка" того Воронина (или еще какого Арни) грохнет и без входа того тушканчика в онлайн. Аналогично, если путь будет через базу Свободы - снорки с кровососами будут сыпаться на головы свободовцам. Вот чтобы подобного казуса не произошло - либо надо убрать подобные пути, либо не пускать живность с ТД, кордонов и прочих болот ни на Янтарь, ни на Склады ни в прочие места, куда они пойдут через бар. То есть, Кордон-x18 должны иметь один ключ, а вот Росток и Янтарь - уже другой. Примерно так: local level_groups = { l01_escape = "1", l02_garbage = "1", l03_agroprom = "1", l03u_agr_underground = "1", l04_darkvalley = "1", l04u_labx18 = "1", l05_bar = "1", l06_rostok = "2", l07_military = "2", l08_yantar = "2", l08u_brainlab = "3", l10_radar = "2", l10u_bunker = "2", l11_pripyat = "2", l12_stancia = "2", l12u_sarcofag = "2", l12u_control_monolith = "2", l12_stancia_2 = "2" } Росток-Янтарь оставляем в одной группе с радаром и прочими припятями, потому что тот уголок, где у них остается путь, нам не мешает. Ну и не забыть всякие перещеры с лощинами добавить. Если мы не хотим, чтобы с конкретной локации кто-то уходил, и не пускать в не чужих - ставим отдельный, уникальный ключ. Кстати, на счет "убрать пути" (или их не прописать, или запретить монстрам/неписям ходить по вершинкам с определенным флагом, но разрешить захватываться в смарт на локации, куда пути нет - будет как раз знаменитый вылет по плотям в x16 5 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 27 Января 2019 Автор Поделиться Опубликовано 27 Января 2019 (изменено) Следующая проверка: if self.accepted_communities and not self.accepted_communities[community] then return false end здесь, вроде, все очевидно - берется из custom data смарта, строчка communities = чего-то-там, через запятые, если не совпало ни с одним - не пускаем, если строчки нет вообще - проверяем дальше. Понятно, что никакие назначения не конкретного непися или конкретного монстра в конкретный смарт ничем не помогут. if obj_agreement ~= agreed_exclusive and not self:check_preset( community, obj:rank(), self.gparams.preset_name ) then return false end А вот здесь назначение уже учитывается, и непись с принудительно назначенным смартом проверку пройдет. Если кто-то скрипт не "доработал" так, чтобы не проходили. В чем смысл самой проверки? function se_smart_terrain:check_preset( npc_community, npc_rank, preset_name ) local preset = smart_terrain_params.get_preset( preset_name ) if preset == false then return true else local t = preset[npc_community] if t and ( npc_rank >= t[1] and npc_rank <= t[2] ) then return true else return false end end end preset_name - это у нас опять же custom data, строка вида preset = l01_escape или типа того. smart_terrain_params.sctipt ищет это в misc\\smart_terrain_presets.ltx, и вставляет в табличку, по которой опять же проверяются допустимое коммунити и ранги для всей локации. И если мы пишем, чтобы капитана с уникальным пистолетом и рангом мастер на Кордон не пускать - его на Кордоне и не будет. Далее, если и эту проверку благополучно прошли, то if not xr_gulag.checkNpc( community, is_stalker, self.gparams.type, obj:rank(), obj ) then return false end - через 100500 скриптов перебирается список level_gulags = {} в xr_gulag.script, и в каждом из скриптов списка дергаются checkStalker()/checkMonster() на предмет опять же проверки коммунити, ранга, имени и т.д. в надежде, что они вернут нам true. Ну и, наконец, return self.gulag:is_there_any_suitable_job( self:fill_npc_info( obj ), obj_agreement == agreed_exclusive ) - ищутся свободные работы, чтобы запихнуть на них претендента. Впрочем, редкая птица долетит до середины Днепра, и если тот, кого мы хотим видеть в каком-то смарте, туда почему-то не идет - обвешиваем логами предыдущие проверки, и смотрим, как он их проходит (или не проходит). Не забывая, при этом, что чтение данных, повторюсь, случается при появлении монстра/непися в игре, а вот как, когда и куда мы их записали - это уже знает только тот, кто их записывает. Да, кстати, философский вопрос о первичности курицы и яйца - в данном случае отнюдь не филосовский, и условия на, скажем, спавн непися, и на разрешение ему идти в тот или иной смарт - могут срабатывать в разное время. Если же кто думает, что список граблей, а также прочих борон, сеялок и молотилок исчерпан - он думает неправильно. То есть, если нашелся единственный смарт, который только и подходит нашему монстру или неписю - в какой-то момент монстронепись пойдет в него. Если же таких несколько, то: function se_smart_terrain:suitable( obj ) local v = 0 if self:obj_accepts_smart_terrain(obj) == agreed_exclusive then v = 100000 end for id, strn in pairs( smart_terrains[self:get_level_name()] ) do if strn:is_gulag_available() then v = v + strn.gulag.capacity - strn.gulag:get_population() end end return v end Мы видим некое очень сильное колдунство: перебор всех смартов на локации, в которые претендента в принципе могут пустить, с вычислением некоего приоритета в зависимости от их заполненности. Даже думать не хочу, каким может получиться результат, но именно сюда очень любят лазить разные шаловливые ручки, и править так, чтобы все живое пыталось лезть туда, где максимальная емкость, но в смарты с емкостью на 1-2 объекта шли только тогда, когда совсем уж податься некуда. Еще очень популярное исправление - проверка на то, можно ли объекту ходить в онлайн. Суть здесь вот в чем: есть у нас, допустим, в олспавне на генераторах некий кровосос, которому в онлайн можно только после некоего события. Если этот кровосос окажется, допустим, в смарте в кровососовке на Складах, а мы взяли задание на острел кровососов - мы его не сможем выполнить, пока не произойдет искомое событие на генераторах. Логичное решение - не пускать таких в смарты вообще, пока им не разрешили онлайн. Но, это, опять же, кто как правит, и как проверяет это самое разрешение на онлайн. см., например, вариант амк: function se_smart_terrain:obj_accepts_smart_terrain( obj ) if obj.smart_terrain_conditions then local any_exclusive = false local s for name, condlist in pairs(obj.smart_terrain_conditions) do s = xr_logic.pick_section_from_condlist( db.actor_proxy, obj, condlist ) if s ~= nil then if name == "none" then return disagreed elseif name == self:name() then return agreed_exclusive end else if name == self:name() then return disagreed end end end -- Если объекту запрещено переходить в online и эксклюзивные смарты недоступны, то не пускаем его никуда. if obj:can_switch_online() == false then return disagreed end end return agreed end Как подобные правки работают - в каждом конкретном случае надо разбираться отдельно. Вопрос о максимальной вместимости смартов - это отдельная тема, пока лишь скажу, что лучше бы задавать в самих смартах такую, чтобы не превышала максимальное количество имеющихся работ для ВООБЩЕ любого возможного случая, и для невозможного - тоже. Иначе - вылеты, и советы "знатотов" "снизить настройки графики и добавить памяти". Вот теперь, вроде, все. Изменено 27 Января 2019 пользователем Dennis_Chikin 3 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 10 Февраля 2019 Автор Поделиться Опубликовано 10 Февраля 2019 "В книге речь пойдет в осовном о хоббитах..." (C) JRRT Точнее, о такой штуке, как "импульс", многообразно представленной в разных конфигах. Подозреваю, что нижеприводящиеся эмпирические данные зависят от чего-попало еще из тех же конфигов, но в движок за подтверждением или опровержением лазить лень, так что имеем то, что имеем. Итак, импульс - это то, что по идее должно швырять нечто, подвергшееся какому то воздействию, но на практике - швыряется отнюдь не все, и не всем. А если швыряет, то значение имеет именно связка из оного импульса и типа хита + то, на что действует. Для аномалий упомянутая связка выглядит как hit_type =, hit_impulse_scale = + min_start_power =, max_start_power = + расстояние от центра, для оружия - hit_impulse =, hit_type = + k_impulse = патрона + расстояние В скриптах - local h = hit() h.impulse = h.type = Практический интерес представляет воздействие на монстров/неписей живых, на трупы, на актора живого и на тушку актора дохлого. Как уже сказал, разница есть, зачастую - принципиальная. И еще важный момент: живые и дохлые монстры/неписи - это абсолютно разные вещи, и если в результате воздействия на что-то живое получился труп, то труп о произошедшем уже "ничего не знает". Хотя вот труп актора - знает даже слишком хорошо. Результаты, соответственно, иногда получаются странные, и для того, чтобы получить то, что мы хотим, это самое воздействие надо организовывать раздельно и для живого, и для образовавшегося трупика, а в случае с актором - не просто раздельное, но противоположное. Практически на все, то есть, абсолютно на все, воздействует хит типа explosion. Так что если мы хотим, чтобы у трамплинов работала "сбыча мечт" - меняем им удар на взрыв. Но, на трупы, особенно, на труп актора, взрыв действует на много сильнее, и то, что слегка подбрасывает актора живого, мертвую тушку запускает в космос. Также, на тушку актора неплохо действует hit.shock На все без исключения трупы, как и на неживые объекты, действует hit.fire_wound - наверное, все заметили, что вертолет замечательно отправляет актора полетать даже без посадки в оный. На практике достаточно примерно hit_impulse = 40 в характеристиках оружия, чтобы обеспечить этот самый полет. Почему оно срабатывает не всегда ? Как уже говорилось, труп - ничего не знает и не помнит ни про какой хит. Потому что во время смерти этого трупа просто не существовала, и замена ранее бодро бегавшего непися на этот самый труп происходит тогда, когда до него дойдет апдейт, каковой зависит от расстояния до актора, и может случиться чере 20ms, а может - аж через 2 секунды. Отсюда, если хотим, чтобы свежий покойник как-то сдвинулся с того места, где образовался, хит следует повторить уже искусственно, после того, как удалось получить npc:get_physics_shell() - что свидетельствует о наконец-то свершившейся замене. Ну и внезапно, на живого актора очень хорошо действует hit.wound Что же нам нужно, чтобы МЕЧТА наконец сбылась? Во-первых, ставим трамплинам hit_type = explosion Во-вторых, добавляем актору отслеживание хита. Поскольку коллбэка для этого в оригинале ТЧ нет - отслеживаем изменение здоровья. Ну и далее все очевидно: Скрытый текст local hit_lvid local hit_anom_id, hit_anom_name function on_hit( victim, amount ) if actor:level_vertex_id() ~= hit_lvid then hit_lvid = actor:level_vertex_id() local id, p, r, dist = amk_anoms.get_nearest_anomaly_for_pos( actor:position() ) if id then local a = level.object_by_id( id ) if a and dist < -0.05 and a:clsid() == clsid.zone_mbald_s and string.find( a:section(), "bald" ) then hit_anom_id, hit_anom_name = id, a:name() else hit_anom_id, hit_anom_name = false, false end else hit_anom_id, hit_anom_name = false, false end end if hit_anom_id then local a = level.object_by_id( hit_anom_id ) if a and a:name() == hit_anom_name then throw_dir = actor:position() throw_dir.y = throw_dir.y + 1 throw_dir = throw_dir:sub( a:position() ) throw_dir = throw_dir:add( throw_dir ) throw_anom_id, throw_anom_name = hit_anom_id, hit_anom_name throw_count = 0 throw_pos, throw_down = false, 0 level.add_call( throw_actor, dummy ) end end end Если в момент получения хита актор был в радиусе трамплина - вычисляем предполагаемую траекторию, и отправляем актора в управляемый полет. Сам полет: Скрытый текст local throw_anom_id, throw_anom_name, throw_dir, throw_count = false, false, false, 0 local throw_pos, throw_down = false, 0 function throw_actor() if actor.health < 0.02 then throw_count = 0 return true end if throw_count >= 20 then if throw_pos then local pos = actor:position() if pos.y - throw_pos.y >= 0.1 then throw_pos = actor:position() return false elseif ( pos.y - throw_pos.y < 0.05 ) and ( throw_down == 0 ) then throw_down = throw_down + 1 local dir = vector():set( throw_dir ) local h = hit() h.draftsman = actor h.type = hit.wound h.direction = dir h.power = 0 h.impulse = 100 throw_pos = actor:position() throw_count = throw_count + 1 if throw_count < 21 then return false end end end throw_pos = actor:position() throw_count = throw_count + 1 if throw_count >= 60 or throw_down ~= 0 then throw_count = 0 return true end return false end local a = level.object_by_id( throw_anom_id ) if a and a:name() == throw_anom_name then throw_count = throw_count + 1 local h = hit() h.draftsman = a h.type = hit.wound h.direction = vector():set( throw_dir ) h.power = 0 h.impulse = 100000 / throw_count actor:hit( h ) return false end throw_count = 0 return true end Собственно, разные значения для разных фаз полета и в зависимости от состояния тушки. Ограничение if actor.health < 0.02 then - для прекращения действа, если актор и без того близок к состоянию трупа, на который, как мы помним, импульс действует иначе. А вот это - для ограничения на выход трупа актора в стратосферу, куда он может быть закинут по тысяче причин даже без нашего участия: Скрытый текст function stop_flight() if flight_pos then local pos = actor:position() if pos.y - flight_pos_start.y >= 0.6 and pos.y - flight_pos.y >= 0.01 then local dir = vector():set( 0, -1, 0 ) flight_force = flight_force + 100 actor:set_const_force( dir, flight_force, 65535 * 65535 ) flight_pos = pos return false end if flight_force ~= 0 then actor:set_const_force( vector():set( 0, -1, 0 ), 9.81 * 80, 65535 * 65535 ) flight_force = 0 end flight_pos = pos return false end flight_pos = actor:position() or vector():set( 0, 0, 0 ) flight_pos_start = flight_pos level.add_call( stop_flight, dummy ) end Как-то так. 1 2 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 10 Февраля 2019 Автор Поделиться Опубликовано 10 Февраля 2019 Тьфу, трамплинам - не wound, а explosion. Поправил. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Полтергейст 37 Опубликовано 20 Мая 2019 Поделиться Опубликовано 20 Мая 2019 @Dennis_Chikin, могу точно сказать, что по smart terrain есть ещё кучка движковых граблей. Чтобы NPC шёл в смарт, надо чтобы все эти проверки вообще вызывались движком. А это происходит не всегда. ЕМНИП, для NPC должен быть выставлен флаг Interactive, без которого всё это работать не будет. Как минимум, в оффлайне. Так же, надо чтобы can_choose_alife_tasks было выставлено в true. По умолчанию это так, и в оригинале оно нигде не меняется. В модах может и по-другому быть. Например, я таким образом отключал поиск смартов для "эксклюзивных" NPC, чтобы задавать им id смарта в явном виде. И да, алгоритм расчёта предпочтительности смарта на основе его заполненности - это полная шляпа. Должно учитываться как минимум расстояние (ближе - лучше). И не хватает подсчёта "населения" отдельно по каждой группировке. Ссылка на комментарий
DMT 2 Опубликовано 14 Декабря 2019 Поделиться Опубликовано 14 Декабря 2019 Цитата https://www.dropbox.com/s/wvub0j4ix30gecy/trade_manager.script?dl=0 В общем, вот. На ту же тему - "очистим от тормозов". При загрузке в частности, ну и вообще. Поскольку чистая соль сейчас мало кого интересует, а к ОПам патчефиксы пекутся как блины - по-людски, с перевешиванием всего на xr_meet, делать не стал. Зато можно прямо файл закидывать, как есть. Смысл - тот же, что и с _g.script, но при загрузке в присутствии неписей, ну или при подходе к лагерям неписей. Плюс обновление ассортимента раз в сутки, либо сюжетное. Зато сюжетное - максимум в течение 2-х минут. Как-то так. upd: h24 - игровые часы между этими самыми обновлениями. upd2: Если нет файла _util.script - который поддерживает вывод логов, то замените строку function log( ... ) _util.log( "trade_manager", ... ) end на function log() end Обновление ассортимента раз в сутки работает некорректно. Если поспать рядом с торговцем больше суток и проверить, то ассортимент будет тот же самый. Но если подождать несколько (около 5) игровых минут, то ассортимент обновится прямо на ваших глазах прямо в окне торговли. Я тестировал много раз, и каждый раз наблюдается вышеописанная ситуация. @Dennis_Chikin, А вот ещё интересная инфа насчёт работы скрипта: если от какого-то непися убежать, чтобы он перешёл в оффлайн, а затем встретить его снова, то непись начнет торговать ВСЕМИ своими вещами, включая гитару, губную гармошку и КПК. И всё это можно будет у него купить. Но на старте игры у неписей этих вещей в продаже нет! Эксперимент проделан много раз с одинаковым результатом. Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти