Перейти к контенту
Dennis_Chikin

Прозекторская

Рекомендуемые сообщения

"Вскрытие показало, что больной умер от вскрытия."

Тема для "крупной формы", то есть, на уровне скриптов целиком или больших частей оных скриптов. "Что у него внутри, зачем оно там, и что с этим можно сделать ?"

Изменено пользователем Dennis_Chikin

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах
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

 
А какие другие варианты ?

След от кругов на воде - это тоже след (с)

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах

Это в следующей части.

Там будут не просто грабли, там - целый склад сельхозинвентаря разнообразного.

 

А вот в том куске, что разобрал - либо 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

 

ну или типа того.

Хотя на самом деле вообще не так, ибо дальше опять же сельхозсклад.

 

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах

Следующая проверка:

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

Как подобные правки работают - в каждом конкретном случае надо разбираться отдельно.

 

Вопрос о максимальной вместимости смартов - это отдельная тема, пока лишь скажу, что лучше бы задавать в самих смартах такую, чтобы не превышала максимальное количество имеющихся работ для ВООБЩЕ любого возможного случая, и для невозможного - тоже. Иначе - вылеты, и советы "знатотов" "снизить настройки графики и добавить памяти".

 

Вот теперь, вроде, все.

 

Изменено пользователем Dennis_Chikin

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах

"В книге речь пойдет в осовном о хоббитах..." (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

 

 

 

Как-то так.

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах

@Dennis_Chikin, могу точно сказать, что по smart terrain есть ещё кучка движковых граблей.

Чтобы NPC шёл в смарт, надо чтобы все эти проверки вообще вызывались движком. А это происходит не всегда. ЕМНИП, для NPC должен быть выставлен флаг Interactive, без которого всё это работать не будет. Как минимум, в оффлайне.

Так же, надо чтобы can_choose_alife_tasks было выставлено в true. По умолчанию это так, и в оригинале оно нигде не меняется. В модах может и по-другому быть.

Например, я таким образом отключал поиск смартов для "эксклюзивных" NPC, чтобы задавать им id смарта в явном виде.

 

И да, алгоритм расчёта предпочтительности смарта на основе его заполненности - это полная шляпа. Должно учитываться как минимум расстояние (ближе - лучше). И не хватает подсчёта "населения" отдельно по каждой группировке.

 

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах

Цитата

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, А вот ещё интересная инфа насчёт работы скрипта: если от какого-то непися убежать, чтобы он перешёл в оффлайн, а затем встретить его снова, то непись начнет торговать ВСЕМИ своими вещами, включая гитару, губную гармошку и КПК. И всё это можно будет у него купить. Но на старте игры у неписей этих вещей в продаже нет! Эксперимент проделан много раз с одинаковым результатом.

Поделиться этим сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединиться к обсуждению

Вы можете ответить сейчас, а зарегистрироваться позже. Если у вас уже есть аккаунт, войдите, чтобы ответить от своего имени.

Гость
Ответить в этой теме...

×   Вы вставили отформатированный текст.   Удалить форматирование

  Допустимо не более 75 смайлов.

×   Ваша ссылка была автоматически заменена на медиа-контент.   Отображать как ссылку

×   Ваши публикации восстановлены.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.

  • Недавно просматривали   0 пользователей

    Ни один зарегистрированный пользователь не просматривает эту страницу.

AMK-Team.ru

×
×
  • Создать...