Dennis_Chikin 3 663 Опубликовано 28 Августа 2014 (изменено) Мониторы, конечно же, у всех студийные и калиброванные, освещение - строго по ГОСТу, ну и, главное, глаза клонированные. Ролик у меня выглядит препоганейше. Да тут совсем не в этом дело. В том же Atmosfear Cromm Cruac корректировал параметры освещения в user.ltx для получения более сочной картинки, контрастных теней и годной имитации привыкания глаз к свету и темноте, единоразово изменяв их через скрипт. The. Изменено 28 Августа 2014 пользователем The ReapeR Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 663 Опубликовано 8 Ноября 2014 (изменено) Кстати, соляночный скриптик переписанный надо кому - упрощенный-ускоренный, + с управлением вероятности погоды не только переписыванием конфигов, но и "одним движением" ? https://dl.dropboxusercontent.com/u/27871782/level_weathers.script К вопросу о рефакторинге. Скриптик когда-то был соляночным. Сейчас, впрочем, он тоже соляночный, но с изменениями. Старый смысла разбирать не вижу, поскольку трэш, угар, и содомия, да еще и с багами типа "вечной ночи" (на самом деле была банальная опечатка, но от того не легче). Что сделано и как оно работает: во-первых, развернуто в плоские функции, ибо ООП там не нужно ни зачем вообще, во-вторых - динамический апдейт. это значит, что снесен весь мусор типа Показать class "WeatherManager"function WeatherManager:__init() init()endfunction WeatherManager:reset() endfunction WeatherManager:save( pk ) endfunction WeatherManager:load( pk ) if db.storage[0].pstor.weather then return end pk:r_stringZ() pk:r_u32()endfunction WeatherManager:update() -- called from bind_stalker.script update()endlocal weather_managerfunction get_weather_manager() if weather_manager then return weather_manager end weather_manager = WeatherManager() return weather_managerendclass "WeatherManager"function WeatherManager:__init() init()endfunction WeatherManager:reset() endfunction WeatherManager:save( pk ) endfunction WeatherManager:load( pk ) if db.storage[0].pstor.weather then return end pk:r_stringZ() pk:r_u32()endfunction WeatherManager:update() -- called from bind_stalker.script update()endlocal weather_managerfunction get_weather_manager() if weather_manager then return weather_manager end weather_manager = WeatherManager() return weather_managerend и соответствующая ему содомия в биндере актора. Хотите использовать без переделок - надо ее вернуть. Раз уж здесь требуют расписать построчно, распишу. Показать local string_sub, string_find, string_gmatch = string.sub, string.find, string.gmatchlocal math_floor = math.floorlocal math_random = math.randomlocal table_insert, table_sort = table.insert, table.sortfunction log( ... ) _util.log( "level_weathers", ... ) endfunction abort( ... ) _util.abort( "level_weathers", ... ) endlocal ini = ini_file( "weathers\\environment.ltx" )local lname = level.name()-- check if level name corresponds to one of the levels that should use special indoor level-- as configured in game_maps_single.ltxlocal indoors = {}for i, v in ipairs( { "l03u_agr_underground", "l04u_labx18", "peshera", "av_peshera", "warlab", "marsh", "l08u_brainlab", "l10u_bunker", "l12u_sarcofag", "l12u_control_monolith" } ) do indoors[v] = trueendlocal string_sub, string_find, string_gmatch = string.sub, string.find, string.gmatchlocal math_floor = math.floorlocal math_random = math.randomlocal table_insert, table_sort = table.insert, table.sortfunction log( ... ) _util.log( "level_weathers", ... ) endfunction abort( ... ) _util.abort( "level_weathers", ... ) endlocal ini = ini_file( "weathers\\environment.ltx" )local lname = level.name()-- check if level name corresponds to one of the levels that should use special indoor level-- as configured in game_maps_single.ltxlocal indoors = {}for i, v in ipairs( { "l03u_agr_underground", "l04u_labx18", "peshera", "av_peshera", "warlab", "marsh", "l08u_brainlab", "l10u_bunker", "l12u_sarcofag", "l12u_control_monolith" } ) do indoors[v] = trueend Библиотечные функции локализуются, дабы меньше томозило. С той же целью ini файл открывается один раз при подцеплении скрипта. Табличка уровней - такая же, как в оригинале - это уровни, где погода статическая. На ее основе считается переменная l_indoor и если она определена, то погода берется из game.ltx, а не вычисляется динамически. Показать local t_prob = { -- вероятности типов погоды ["beta"] = 20, ["dry"] = 10, ["foggy"] = 10, ["rainy"] = 20, ["grey"] = 20, ["stormy"] = 10, ["thunder"] = 10 }local trans_types = {} -- выходы из transitionlocal l_indoor = falseif indoors[lname] then l_indoor = true endlocal trans_t, trans_n, trans_id = {}, 0, 10000 -- таблица переходовlocal next_tm = 0local next_wlocal lv_weather = "default"local lv_suffixlocal t_prob = { -- вероятности типов погоды ["beta"] = 20, ["dry"] = 10, ["foggy"] = 10, ["rainy"] = 20, ["grey"] = 20, ["stormy"] = 10, ["thunder"] = 10 }local trans_types = {} -- выходы из transitionlocal l_indoor = falseif indoors[lname] then l_indoor = true endlocal trans_t, trans_n, trans_id = {}, 0, 10000 -- таблица переходовlocal next_tm = 0local next_wlocal lv_weather = "default"local lv_suffix табличка t_prob - собственно, позволяет поменять вероятность выбора той или иной погоды глобально, а не перелопачивать каждый раз все конфиги. Ну а дальше просто объявляется куча всяких разных нужных переменных. Eдем дальше, теперь - с конца: function init() Показать lv_suffix = ( ini:section_exist( "level_suffix_table" ) and ini:line_exist( "level_suffix_table", lname ) and ini:r_string( "level_suffix_table", lname ) ) local r, k, v, kk, vv, t if ini:section_exist( "weather_transition_times" ) then for i = 0, ini:line_count( "weather_transition_times" ) - 1 do r, k, v = ini:r_line( "weather_transition_times", i, "", "" ) t = {} trans_n = trans_n + 1; trans_t[trans_n] = { k + 0, v, t } if ini:section_exist( v ) then for ii = 0, ini:line_count( v ) - 1 do r, kk, vv = ini:r_line( v, ii, "", "" ) t[kk] = vv if t_prob[vv] then trans_types[kk] = vv end end else abort( "invalid section: [%s]", v ) end end else abort( "invalid section: [weather_transition_times]" ) end table_sort( trans_t, function( a, b ) return a[1] < b[1] end ) -- из конфигов читается как попало, поэтому сортируем по времени trans_n = trans_n + 1; trans_t[trans_n] = { 1440, trans_t[1][2], trans_t[1][3] } -- синтетическая секция для 24-х часов. -- Надо, чтобы был корректный переход со "стандартным" конфигом. -- _util.list_tbl( trans_t, "transitions" ) lv_suffix = ( ini:section_exist( "level_suffix_table" ) and ini:line_exist( "level_suffix_table", lname ) and ini:r_string( "level_suffix_table", lname ) ) local r, k, v, kk, vv, t if ini:section_exist( "weather_transition_times" ) then for i = 0, ini:line_count( "weather_transition_times" ) - 1 do r, k, v = ini:r_line( "weather_transition_times", i, "", "" ) t = {} trans_n = trans_n + 1; trans_t[trans_n] = { k + 0, v, t } if ini:section_exist( v ) then for ii = 0, ini:line_count( v ) - 1 do r, kk, vv = ini:r_line( v, ii, "", "" ) t[kk] = vv if t_prob[vv] then trans_types[kk] = vv end end else abort( "invalid section: [%s]", v ) end end else abort( "invalid section: [weather_transition_times]" ) end table_sort( trans_t, function( a, b ) return a[1] < b[1] end ) -- из конфигов читается как попало, поэтому сортируем по времени trans_n = trans_n + 1; trans_t[trans_n] = { 1440, trans_t[1][2], trans_t[1][3] } -- синтетическая секция для 24-х часов. -- Надо, чтобы был корректный переход со "стандартным" конфигом. -- _util.list_tbl( trans_t, "transitions" ) - чтение конфигов. Таблица суффиксов (уровней с динамической погодой) из environments.ltx (мы его открыли при подцеплении скрипта), таблица переходов и их вероятностей. Впрочем, вероятности теперь можно в конфигах не писать, по тому как есть табличка в скрипте, универсальная. Так что нам нужно только время, и на что переключаться можно. Показать local ini = ini_file( "game.ltx" ) local pp if ini:section_exist( lname ) then if ini:line_exist( lname, "postprocess" ) then pp = ini:r_string( lname, "postprocess" ) end if ini:line_exist( lname, "weathers" ) then lv_weather = ini:r_string( lname, "weathers" ) end end if pp then level.add_pp_effector( pp, 999, true ) else level.remove_pp_effector( 999 ) end -- log( "info", "init, default: %s, suffix: %s", lv_weather or "nil", lv_suffix or "nil" ) bind_stalker.task_add( "level_weathers.update", 5000, update ) return true local ini = ini_file( "game.ltx" ) local pp if ini:section_exist( lname ) then if ini:line_exist( lname, "postprocess" ) then pp = ini:r_string( lname, "postprocess" ) end if ini:line_exist( lname, "weathers" ) then lv_weather = ini:r_string( lname, "weathers" ) end end if pp then level.add_pp_effector( pp, 999, true ) else level.remove_pp_effector( 999 ) end -- log( "info", "init, default: %s, suffix: %s", lv_weather or "nil", lv_suffix or "nil" ) bind_stalker.task_add( "level_weathers.update", 5000, update ) return true end - из game.ltx читаем постпроцессы типа желтизны на янтаре, и запускаем, а также статическую погоду для подземных уровней. И, наконец, вешаем на динамический апдейт код самого апдейта. Не знаю, что там кто будет формировать - по мне так кроме самого скрипта вообще больше ничего не надо. Собственно, самая интересная часть здесь - апдейт. В логике того, что было, способен разобраться, наверное, только Создатель. Плюс там останки кучи каких-то древних модов, которые вообще ни за чем не нужны. Так что, все переделано в одну единственную функцию. function update() Показать local tm = level.get_time_hours() * 60 + level.get_time_minutes() if tm < next_tm then return end -- выбираем таблицу погод на следующий раз if ( trans_id >= trans_n ) or ( trans_t[trans_id][1] >= tm ) then -- нет данных или почему-то забежали вперед for i, v in ipairs( trans_t ) do -- вычисляем "с нуля" if tm < v[1] then trans_id = i break end end else trans_id = trans_id + 1 end -- log( "info", "update, id: %s (%s)", trans_id, trans_n ) local t = trans_t[trans_id] next_tm = t[1] -- _util.list_tbl( t[3], t[2] ) local tm = level.get_time_hours() * 60 + level.get_time_minutes() if tm < next_tm then return end -- выбираем таблицу погод на следующий раз if ( trans_id >= trans_n ) or ( trans_t[trans_id][1] >= tm ) then -- нет данных или почему-то забежали вперед for i, v in ipairs( trans_t ) do -- вычисляем "с нуля" if tm < v[1] then trans_id = i break end end else trans_id = trans_id + 1 end -- log( "info", "update, id: %s (%s)", trans_id, trans_n ) local t = trans_t[trans_id] next_tm = t[1] -- _util.list_tbl( t[3], t[2] ) - если время что-то делать не подошло, не делаем ничего. А вот если подошло, то выбираем таблицу погод для СЛЕДУЮЩЕГО раза. Даже если находимся под землей, или прямо сейчас происходит выброс. Зачем это надо - кроме упрощения кода можно прицепить, например, к новостям прогноз погоды. Не "фэйковый", а реальный. Ну и кроме того, плохая идея, когда при каждой загрузке сэйва погода - разная получается. Показать local pstor = db.storage[0].pstor if next_w then -- погоду знаем -- log( "info", "update, to: %s, next id: %s, at: %s", next_w, trans_id, next_tm / 60 ) pstor.weather = next_w else -- сначала выберем текущую next_w = pstor.weather if next_w then -- log( "info", "update, current: %s, next id: %s, at: %s", next_w, trans_id, next_tm / 60 ) else local tt, nn = {}, 0 for k, v in pairs( t[3] ) do nn = nn + 1; tt[nn] = k end next_w = tt[math_random( nn )] -- log( "info", "update, new: %s, next id: %s, at: %s", next_w, trans_id, next_tm / 60 ) pstor.weather = next_w end end local pstor = db.storage[0].pstor if next_w then -- погоду знаем -- log( "info", "update, to: %s, next id: %s, at: %s", next_w, trans_id, next_tm / 60 ) pstor.weather = next_w else -- сначала выберем текущую next_w = pstor.weather if next_w then -- log( "info", "update, current: %s, next id: %s, at: %s", next_w, trans_id, next_tm / 60 ) else local tt, nn = {}, 0 for k, v in pairs( t[3] ) do nn = nn + 1; tt[nn] = k end next_w = tt[math_random( nn )] -- log( "info", "update, new: %s, next id: %s, at: %s", next_w, trans_id, next_tm / 60 ) pstor.weather = next_w end end - а вот если "предсказанной" погоды нет - только тогда считаем на основе загруженной таблицы. И если не под землей, и не выброс - устанавливаем. А если под землей - ставим нужную, но для поверхности дальше все равно идет рассчет нового "прогноза": Показать local s = t[3][next_w] -- log( "info", "update, select: %s", string_sub( s or "nil", 1, 180 ) ) local tt, nn = {}, 0 if s then -- для следующего раза for k, v in string_gmatch( s, "([%w]+):?([%d]*),?" ) do -- log( "info", "update, allowed: [%s:%s]", k, v ) if t_prob[k] then v = t_prob[k]; tt[k] = v; nn = nn + v else v = trans_types[k] if v then v = t_prob[v]; tt[k] = v; nn = nn + v -- else log( "error", "invalid section: [%s], line: [%s]", t[2], k ) end end end end if t_prob[next_w] and not tt[next_w] then -- текущая тоже участвует в конкурсе s = t_prob[next_w]; tt[next_w] = s; nn = nn + s -- log( "info", "update, add current: [%s]", next_w ) end if nn ~= 0 then s = math_random( nn ) -- log( "info", "update, total: %s, rnd: %s", nn, s ) -- for k, v in pairs( tt ) do log( "info", "update, [%s:%s]", k, v ) end for k, v in pairs( tt ) do if v < s then s = s - v else next_w = k; break end end -- log( "info", "update, next: [%s]", next_w ) -- else log( "error", "invalid section: [%s], entry [%s] absent", t[2], next_w ) end local s = t[3][next_w] -- log( "info", "update, select: %s", string_sub( s or "nil", 1, 180 ) ) local tt, nn = {}, 0 if s then -- для следующего раза for k, v in string_gmatch( s, "([%w]+):?([%d]*),?" ) do -- log( "info", "update, allowed: [%s:%s]", k, v ) if t_prob[k] then v = t_prob[k]; tt[k] = v; nn = nn + v else v = trans_types[k] if v then v = t_prob[v]; tt[k] = v; nn = nn + v -- else log( "error", "invalid section: [%s], line: [%s]", t[2], k ) end end end end if t_prob[next_w] and not tt[next_w] then -- текущая тоже участвует в конкурсе s = t_prob[next_w]; tt[next_w] = s; nn = nn + s -- log( "info", "update, add current: [%s]", next_w ) end if nn ~= 0 then s = math_random( nn ) -- log( "info", "update, total: %s, rnd: %s", nn, s ) -- for k, v in pairs( tt ) do log( "info", "update, [%s:%s]", k, v ) end for k, v in pairs( tt ) do if v < s then s = s - v else next_w = k; break end end -- log( "info", "update, next: [%s]", next_w ) -- else log( "error", "invalid section: [%s], entry [%s] absent", t[2], next_w ) end - собственно, это и был прогноз погоды. Все. end В init() после строки table_sort( trans_t, function( a, b ) return a[1] < b[1] end ) -- из конфигов читается как попало, поэтому сортируем по времени Добавил: trans_n = trans_n + 1; trans_t[trans_n] = { 1440, trans_t[1][2], trans_t[1][3] } -- синтетическая секция для 24-х часов. -- Надо, чтобы был корректный переход со "стандартным" конфигом. Спасибо Карлан за то, что обратил внимание, и за тестирование с этим самым "стандартным" конфигом из Dynamic Weather v0.9.4 Изменено 1 Января 2015 пользователем Murarius 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 663 Опубликовано 25 Июня 2016 (изменено) В свое время здесь выкладывался упрощенный скриптик для динамической погоды, так вот он требует, во-первых, чтобы в конфигах не было ошибок, во-вторых - чтобы корректно отрабатывались вещи типа сна и выброса.Вот вариант с учетом известных проблем с конфигами (в комментариях все подробности)https://dl.dropboxusercontent.com/u/27871782/level_weathers_bad_cofigs.scriptВ идеале, конечно, лучше бы конфиги починить, а сон делать через исправленную set_curent_time() в _g.script: Показать local set_current_time_t, set_current_time_f function set_current_time_wait() if game.get_game_time() < set_current_time_t then return false end level.set_time_factor( set_current_time_f ) set_current_time_f = false return true end function set_current_time( hour, min, sec ) -- починено, работает. game_time_time = game.get_game_time() -- полное игровое время local new_time = game.CTime() -- нужно выставить new_time:setHMS( hour, min, sec ) local hh, mm, ss _, _, _, hh, mm, ss = game_time_time:get() -- текущее время local c_time = game.CTime() c_time:setHMS( hh, mm, ss ) if new_time == c_time then return -- ничего не делаем elseif new_time < c_time then -- следующие сутки new_time:setHMS( hour + 24, min, sec ) end new_time:setHMS( 0, 0, new_time:diffSec( c_time ) ) set_current_time_t = game_time_time + new_time if not set_current_time_f then set_current_time_f = level.get_time_factor() end level.set_time_factor( 10000 ) level.add_call( set_current_time_wait, dummy_action ) end local set_current_time_t, set_current_time_f function set_current_time_wait() if game.get_game_time() < set_current_time_t then return false end level.set_time_factor( set_current_time_f ) set_current_time_f = false return true end function set_current_time( hour, min, sec ) -- починено, работает. game_time_time = game.get_game_time() -- полное игровое время local new_time = game.CTime() -- нужно выставить new_time:setHMS( hour, min, sec ) local hh, mm, ss _, _, _, hh, mm, ss = game_time_time:get() -- текущее время local c_time = game.CTime() c_time:setHMS( hh, mm, ss ) if new_time == c_time then return -- ничего не делаем elseif new_time < c_time then -- следующие сутки new_time:setHMS( hour + 24, min, sec ) end new_time:setHMS( 0, 0, new_time:diffSec( c_time ) ) set_current_time_t = game_time_time + new_time if not set_current_time_f then set_current_time_f = level.get_time_factor() end level.set_time_factor( 10000 ) level.add_call( set_current_time_wait, dummy_action ) end Этот - именно для тех, кто любит чинить кривые, но зато Священные и Неприкосновенные конфиги скриптами. Основной принцип работы, что зачем, и как подключать - смотрите тремя постами выше. Здесь по сути добавлена именно обработка ошибок конфига. Изменено 25 Июня 2016 пользователем Dennis_Chikin 1 2 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 663 Опубликовано 14 Января 2017 Забавно, но, во-первых, "явных ошибок синтаксиса" не видит компилятор LUA, а во-вторых он с этими "ошибками" как-то проработал у меня целых 7 лет уже, и еще у 2-х человек минимум. Другое дело, там в посте вроде было расписано, в чем отличие от всем привычного, и WeatherManager в этом файле, действительно, больше нет. То есть, от bind_stalker требуется наличие функции task_add(), а в скрипте имеется функция init(), которую кто-то должен дернуть для запуска. Не важно, кто, по большому счету. Как удобнее будет использующему скрипт. То есть, описание требуется прочитать внимательно. Почему нельзя сделать так, чтоб все стало совсем по-другому, но при этом осталось точно так как было ? По тому что я не вижу в таком пожелании ни смысла, ни логики. Кто хочет - остается на старых скриптах, требующих странного, кому надо что-то маленькое, быстрое, с дополнительным функционалом и очевидной логикой работы - читает описание, и убирает из прочих мест останки от странного. Да, поскольку я не уверен в безошибочности конфигов там, к чему пытались приспособить - следует взять не этот вариант, а второй - в котором добавлена обработка разных странных ситуаций в конфигах и прочих "выбросах", "часах ужаса" и сне. 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 663 Опубликовано 1 Апреля 2017 (изменено) Фар плэйн - это расстояние, на котором рисуются элементы пейзажа. Фог дистанс - это расстояние заливки цветом тумана. https://www.dropbox.com/s/datlapi5x4b6xn6/rainy.png?dl=0 А вот денсити - это заляпывание картинки непосредственно перед носом. Собственно, на картинке денсити на нуле, дабы не тормозило, дистенс фога - то-ли 80, то-ли 100. Плейн - 280, поскольку мапперов у нас дофига, но вот локи на треугольники вменяемого размера разбить - "а зачем, если можно этот плэйн выставить в 100500, и лет через 20 непременно появятся видюхи, которые при этом не тормозят, и воют вентилятором может быть даже чуть тише стратегического бомбера на взлете ?" Изменено 1 Апреля 2017 пользователем Dennis_Chikin 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 663 Опубликовано 30 Мая 2018 Таких как ДМХ - на этом форуме действительно нет. Все вопросы - на сайт к авторам. А в принципе, просто узнаем текущую секцию через level.get_weather(), и меняем в этой и В СОСЕДНИХ по тому же конфигу. Если вопрос: "куда вписать level.get_weather() и как искать в конфигах" - курсов информатики на этом форуме, опять же, нет. Парадокс: пара-тройка учителей информатики техникумов были, но вот курсов - нет. 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение