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

Рефакторинг: _g.script

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

@Zander_driver, Артос не выкладывал свой рефакторинг скриптов для общего пользования (он только в симбионе, ну и местами его почерк в ЛА проглядывается), он выкладывал готовые модули с понятными инструкциями, потому их все и используют, а описывать весь его рефакторинг комменатриями я думаю занятие весьма долгое (и опять же его не каждый поймет, не говоря уже об использовании), я вот разобрался в его скриптах, и многое почерпнул, за что ему прямо могу сказать большое спасибо.

 

Ну вот скажи, зачем перелопачивать кучи if-then-end? Там выигрыш опять же применимо для сталкера довольно не велик, вот тебе пример, возьмем простую функцию, которая режет взгляд:

function compare_part(a, 
  local l = math.min(string.len(a),string.len()
  if string.sub(a, 1, l) == string.sub(b, 1, l) then
    return true
  end
  return false
end

Задаемся целью убрать ненавистный тут нам всем if-then-end, я переписал вот так (конечно переписать можно под унифицированный вид, как многие любят, но пока остановимся на адекватном варианте, а именно том, который требует наша задача):

function compare_part(a, 
  local l = math.min(#a,#
  return rawequal(string.sub(a, 1, l),string.sub(b, 1, l)) 
end

Мой вариант показывает явный положительный результат (выигрыш ~ 10 сотых секунды) на 80 тысячах итераций (плюс начинает появляться на >50т итераций). Собственно вывод? Смотрится красивее, скрипт будет весить на несколько килобайт меньше при полной его переработке, а для игры "плюс" и не велик вроде как, т.к. опять же, ну кто тебе будет 80т раз две строки сравнивать? Поэтому такие переделки либо для каких-нибудь любителей красивого кода, либо для таких как Денис, которые гонятся за прибылью в наносекунды. Понятно, что хорошо будет, если каждый единичный if-then перепишем на тернарный оператор, а каждые if-then-elseif на ассоциативные массивы, заменив попутно все корявые конструкции на стандартные луа-функции, но ощутимый и существенный плюс то какой, я так и не понимаю. За скоростью гнаться надо когда у тебя "синхронизация" идет минут 5 или больше.

 

 

А, да, речь шла о Дезертире и Полтергейсте.

Добавлено Dennis_Chikin,

Я, кстати, у обих интересного нашел. И упер к себе.

Хотя лучше бы, конечно, если бы оно статьями было, а не выковыривать.

Ссылка на комментарий

@Struck

Гонятся за скоростью может и нет большого смысла.

Как тут говорили, - при загрузке смысла нет, да и во многом другом.

 

Но если можно написать быстро (действительно быстро), то почему этого не сделать?

Ну например если из показанного тобой кода максимально убрать вызовы функций (как затратных средств),

то можно практически вдвое сократить время выполнения начальной функции 

function compare_part(a, 
  local x = #a < #b
  a, b = x and a or b, not x and a or b -- теперь 'a' - всегда будет минимальной строкой, 'b' - максимальной
  return b:sub(1, #a) == a
end

Вроде и не нужна особо эта функция, а на душе приятно от двойной скорости.

Изменено пользователем Nazgool
Добавлено Dennis_Chikin,

Нагло злоупотреблю, и вот прям здесь замечу:

На душе приятно даже не столько от "двойной скорости", сколько от того, что оно "охватывается одним взглядом". Скорость то здесь можно и еще вытянуть, но уже не нужно.

Ссылка на комментарий

Ну, во-первых, как бы 1 наносекунда - я за ней и не гонюсь.
Если глянуть по ссылке, про "историю пессимизации" - там вообще-то 10-15 секунд загрузки на вычищенных конфигах, и аж минуты на невычищенных. Плюс "хромота" непосредственно в игре.
Плюс 10 секунд (до 2-х минут на ОПКМ) той же загрузки на "торговле", + 4 на тайниках, + 4 на ящиках, плюс 10-30 загрузки и лаги непосредственно в игре по 2 сек на звуках.

Ага, вот это у нас называется нынче "гоняться за наносекундами", и "делать ничего не надо".
Запуск игры по 4 минуты и перезагрузка по полторы-две после каждой смерти... На не самом древнем железе.

Ну нифига ж себе - "неважно"...
Не говоря о десятках и сотнях мегабайт памяти при разборах этих конфигов.
Да, я понимаю, можно сказать и про "сначала конфиги зачисти". Ага, во всех солянках, всех дмх, всех опах и что там еще наплодили, я вот должен бегать и конфиги чистить. Офигеть...

Это как бы во-первых.


Второе - а вот действительно, объясните мне, ЗАЧЕМ нужен скрипт о 800 килобайт, если 60 там - выше крыши. Ну, нет у вас 800-килобайтных скриптов, а у других - есть. И, что теперь ?
Причем, заметим, пока я разбираюсь с этими 800кб простынями, я должен наслаждаться вот этой вот самой загрузкой по 4 минуты по 20 раз за вечер ?
И что такого сакрального в  800кб простыни if ... then ..., что ее и тронуть-то нельзя ?

Наконец, разгоревщийся сыр-бор про по сути "А кто тебе РАЗРЕШИЛ пихать в _g.script твой богомерзкий sys_ini ?"
Типа, если имеешь наглость что-то там делать - ОБЯЗАН делать под КОШЕРНЫЙ 1.0004 и ни как иначе (вот кстати со всеми его ini_file() в цикле, да еще аж по 2 раза), и если что написал под другое - снеси всю свою богомерзкую ересь обратно, и делай "как у всех", через 20 скриптов.
 
Знаете, вот при такой постановке вопроса - ну не читайте вы мои посты, и будет вам счастье. Делов-то... Ну или идите в райотдел, и пишите заявление по "Оскорблению чуйств верующих" - нынче а аж специальную статью придумали, специально для всех оскорбившихся.

Модераторское:
Еще n страниц флуда ушло в более соответствующее место. Очень жаль, что надо было 6 МЕСЯЦЕВ дожидаться, пока темы перенесут из закрытого раздела в общий, дабы имевшие возможность нафлудится вдосталь, предпочли реализовать именно на публике. Еще более жаль, что имея возможность сказать много полезного, предпочли заняться выяснением "Кто разрешил ?"

Изменено пользователем Dennis_Chikin
  • Нравится 1
Ссылка на комментарий

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

 

Собственно, те куски, которые были выше - им в общем-то в _g.script вообще не место. Ну вот плохая это идея - оформлять ОДНУ строку отдельной функцией (особенно, в скриптовом языке), формировать там таблицу, возвращать в виде результата, а потом уныло разбирать возвращенную таблицу.

И вообще, даже если что-то очень сильно хочется иметь в глобальном нэймспейсе - есть в конце-концов метод _G.* =

 

Ну да ладно. Перейдем, наконец, уже к той части, которая была в оригинале и в очень классических модах.

 

 

 

schemes	= {}	-- соответствие схем модулям
stypes	= {}	-- типы схем

-- Загружает схему из файла на диске и активирует в мотиваторе.

function load_scheme( filename, scheme, stype )	-- имя файла без расширения
	schemes[scheme] = filename
	stypes[scheme] = stype
end


if not time_global then
	time_global = function() return device():time_global() end
end


function wait_game( time_to_wait )	-- игровые секунды
	verify_if_thread_is_running()
	if time_to_wait then
		get_game_time = game.get_game_time
		game_time_time = get_game_time()
		local time_to_stop = game.CTime()	-- нужно выставить
		time_to_stop:setHMS( 0, 0, time_to_wait )
		time_to_stop = game_time_time + time_to_stop
		while time_to_stop >= get_game_time() do coroutine.yield() end
	else coroutine.yield()
	end
end


function wait( time_to_wait )	-- реальные секунды
	verify_if_thread_is_running()
	if time_to_wait then
		local time_to_stop = time_global() + time_to_wait
		while time_to_stop >= time_global() do coroutine.yield() end
	else coroutine.yield()
	end
end

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

 

Второе - избавление от некоей монстровидной строки, путем определения функции с коротким именем. Сама функция - получение времени от начала загрузки игры в миллисекундах. Ну, это все знают. Единственно что, сразу возникает вопрос: а нельзя ли оное упрощение как-то еще больше упростить ? Можно, и выиграть при этом в скорости. Но про это будет позже.

 

А вот две следующие - уже вызывают вопросы. Вот на самом деле не знаю, используются ли они где-либо, и определено ли вообще это самое verify_if_thread_is_running() и coroutine.yield(), ибо невозможно объять необъятное силами одного человека, но очевидно, что используемое в оригинале game.time() здесь не подходит, ибо рассчитано на один игровой месяц.

По-этому, сразу переписал. Переписал, кстати, в весьма не самом оптимальном виде, поскольку не уверен в моменте использования этой функции.

 

Вторая - то же самое ожидание, но уже в реальном времени и от загрузки. Поскольку реальный месяц без перезагрузки вряд-ли кто будет ждать - вполне допустимо, и вопрос сводится опять же к тому, определено ли coroutine.yield()

 

Две следующих - реально используются, как минимум монстрами (включая, "почему-то", дохлых), БТРами и, видимо, кем-то еще. Вот лень прямо сейчас поиск делать.

 

Привожу сразу оригинал, и попытку это как-то изменить:

function action(obj,...)
    local arg = {...}
    local act = entity_action()
    local i = 1
    while true do
        if (arg[i] ~= nil) then
            act:set_action(arg[i])
        else
            break
        end
        i = i + 1
    end
    if (obj ~= nil) then
        obj:command(act,false)
    end
    return  entity_action(act)
end

function action_first(obj,...)
    local arg = {...}
    local act = entity_action()
    local i = 1
    while true do
        if (arg[i] ~= nil) then
            act:set_action(arg[i])
        else
            break
        end
        i = i + 1
    end
    if (obj ~= nil) then
        obj:command(act,true)
    end
    return  entity_action(act)
end

странным мне здесь представляется все.

Во-первых, если в аргументах предусмотрена возможность получить nil - это автоматически приводит к краху таблицы, к которой пытаются обращаться по индексу.

При том что аргументы в общем-то должны выглядеть как move( ... ), cond - то есть, объект акции и условие.

Во-вторых, я уже поднимал этот вопрос в параллельной теме:

1. получаем новый a = entity_action()

2. выполняем для него a:set_action( arg )

3. делаем что-то еще

4. а теперь - опять получаем entity_action( a ) - то есть, с аргументом, и тут же его возвращаем вызвавшему. Зачем ? Я бы понял, если бы это надо было зачем-то сохранить оригинал, на случай, когда с копией в дальнейшем пытаются что-то сделать. Но в том и дело, что оригинал - не сохраняется. Его уборщик убирает. Теоретически.

Тут бы надо кому-то в исходники глянуть.

 

Но вот вроде бы в таком вот виде:

function action( obj, ... )	-- здесь и далее - странная фигня с entity_action( entity_action() )
	local a = entity_action()
	for i, v in ipairs( { ... } ) do a:set_action( v ) end
	if obj then obj:command( a, false ) end
	-- return a	-- entity_action( a )	-- может быть просто act ? посмотрим...
	return entity_action( a )
end


function action_first( obj, ... )
	local a = entity_action()
	for i, v in ipairs( { ... } ) do a:set_action( v ) end
	if obj then obj:command( a, true ) end
	-- return a	-- entity_action( a )	-- аналогично предыдущему
	return entity_action( a )
end

- у меня пока все работает без проблем.

Ну да всего 2-й день гоняю. Надо, конечно, присмотреться ко всем этим крысам и прочим тушканчикам.

 

Чем отличаются вообще action() и action_first() ? В первой - obj:command( act, false ), во второй - true.

 

 

Upd: тут дополняют, что вот это вот false/true - это очередь/немедленное исполнение.

Что касается entity_action( act ) - лучше все же оставить просто по принципу "как бы чего не вышло". Ибо исходники воскурению весьма таки сопротивляются.

И, да, переменную переименовать.

Изменено пользователем Dennis_Chikin
Ссылка на комментарий
Но в том и дело, что оригинал - не сохраняется. Его уборщик убирает. Теоретически. Тут бы надо кому-то в исходники глянуть.

Я так понимаю, копия нужна для защиты оригинала от изменения.

После вызова obj:command( act, false) действие добавляется в очередь. А вот какие странности будут, если попытаться изменить его после добавление в очередь

local new_act = action_first(obj, что-то_там)
new_act:set_action(что-то_ещё)

- можно только догадываться.

 

И кстати переменную act лучше переименовать, т.к. есть одноимённый экспортированный класс.

Изменено пользователем Полтергейст
Ссылка на комментарий

На счет act и переименования - там, кстати, дальше будет функция совершенно феерическая. Просто я до нее не дошел еще.

 

В общем, логично, про защиту от странного. Теоретически - не нужна, на практике - вот черт ее знает.

Ссылка на комментарий

Едем дальше.

 

function debug_get_level_object( obj_name )

function debug_get_actor()

 

По понятной причине - не работают. Если кому не понятно - в стандартном движке нет wait().

Можно оставить как есть, можно - удалить. Если вруг, в чем я сильно сомневаюсь, случится вылет - найти, и удалить там.

 

function distance_between( obj1, obj2 )

function distance_between_safe( obj1, obj2 )

Собственно, все очевидно, кроме того, что это исключительно для game_object, и ни как иначе. Кроме того, непонятно, зачем делать функцию, которая дает повисание при отсутсвии объекта.

 

Опять же, про однострочные функции уже говорилось - это не тот движок, и не тот язык, где можно позволять себе подобную роскошь. Читаемости вашему коду оно не добавляет.

function has_alife_info( info_id )	-- Проверка на инфопоршны, даже если игрока не существует
	local sim = alife()
	if sim then return sim:has_info( 0, info_id ) end
	return false
end
- в общем, функция много где использующаяся, но смысл ее использовать - сомнителен. По тому что и alife() и actor в абсолютном большинстве случаев - есть, а там, где их нет - там и не нужно. Где нужно - там - actor_proxy более уместен, или более другой вариант, чем info. Да, я помню про smart_terrain и респавнеры, но я не уверен в пользе их работы в ходе загрузки. Там и так есть, чем заняться. То есть, нужна ревизия.

 

Ну а дальше у нас будет несколько классов, весьма, я бы сказал, феерических, и опять же - с неработающим кодом.

Изменено пользователем Dennis_Chikin
Ссылка на комментарий

@Dennis_Chikin,

function has_alife_info(info_id)
	local sim = alife()
	return info_id ~= "" and sim and sim:has_info(0, info_id)
end
Хотя смысла лишний раз проверять наличие симулятора по большому счету и нет. Изменено пользователем RayTwitty
Ссылка на комментарий

 

 

это не тот движок, и не тот язык, где можно позволять себе подобную роскошь

А можно ссылку на бенчмарки, исследования по этому поводу?

Freedom

Ссылка на комментарий

info_id ~= "" - если подобное вызвали - у нас где-то проблемы. Проверка на sim ЗДЕСЬ - по тому что если вызвали, а alife() еще нет - мы об этом все равно ни как сообщить не сможем - и висяк будет весьма загадочный.

 

"А можно ссылку на бенчмарки, исследования по этому поводу?" - функции в сталкере не инлайнятся.

Ссылка на комментарий

Проверка на sim ЗДЕСЬ - по тому что если вызвали, а alife() еще нет

Это когда такие случаи были? До загрузки игры? Не имею возможности сейчас посмотреть места вызовов конктруктора и деструктора симулятора, но скорее всего, он доступен даже когда актор в оффлайне.

function has_alife_info(info_id)
	return info_id ~= "" and alife():has_info(0, info_id)
end
 

info_id ~= "" - если подобное вызвали - у нас где-то проблемы

Неявно может быть и пустая строка. Такое вполне вероятно, по крайней мере намного вероятнее, чем вызов из главного меню до загрузки игры :D Изменено пользователем RayTwitty
Ссылка на комментарий

проверка алайфа все же может и нужна. так как различные деятели порой во всякие меню пихают не только смену флагов (и висяк опять-же, но это личное кому-то в прикол там где надо по сто раз одно и тоже писать), а info_id ~= "" лишнее так как невозможно написать has_alife_info(""), ну никак в моей голове не укладывается что напишут в массиве (ну не руками же) такое работая с этой функцией, если и делать что-то такое, то проверку существования инфо в массиве по переданному аргументу и выводить явно в лог что не так, ну или аборт тут кому как нравится, или вообще поддержку и создание инфо только об этом надо сообщать, а то незадачливый модмейкер слишком сильно обрадуется. и опять-же эта затычка вообще никак не локализирует проблему, а только все усугубит.
так или иначе у меня вообще вот так:
 

function has_alife_info(info_id)
return sim and sim:has_info(0, info_id)
end
function has_info(info_id)
return actor and actor:has_info(info_id) or has_alife_info(info_id)
end

"Читаемости вашему коду оно не добавляет."

 

вот посмотри, откопал какую-то старую версию торговли, вот там несколько функций построено таким образом, так как в противном случае код увеличивался невероятно:

tbl_sell = flag and (IsTable(vv.traders) and vv.traders) or (is_friend and (IsTable(vv.sell_stalkers_friend) and vv.sell_stalkers_friend) or (IsTable(vv.sell_stalkers) and vv.sell_stalkers))

в целом я наверное и сейчас так бы сделал, иное трудно придумать, разве что ini_file ;), зато т.о. вся недвижковая торговля вышла в 350 строк. и читаемость как никрути выше чем у кучи if-then-elseif

Изменено пользователем Карлан
Добавлено Kirgudu,

Прошу не забывать про существование заглавных букв.

Ссылка на комментарий

Ты не понял. function return_true( v ) if v == true then return true else return false end
В лучших традициях индусского кода.

Теперь, где-то, в другом скрипте, вызываем if return_true( another_var ) == true then
Что делает вызываемое ? Правильно, ищем, где оно, и смотрим.

Нет, функция НЕ бессмысленна. По тому что где-то в xr_logic.чего-то() или еше в каких диалогах требуют строго true или строго false. То есть, сей индусский код имеет право быть.
В принципе. Для вот этого вот, где таки требуют.
Но не ВЕЗДЕ.


"function has_info(info_id)
return actor and actor:has_info(info_id) or has_alife_info(info_id)
end"

Вот опять же плохо по той же причине: в 99% случаев у тебя есть актор, и 146% гарантии, что без него до сюда не дойдет. Вот и можно писать сразу actor:has_info(info_id), не через 10 скриптов и 20 проверок.

Если не уверен - alife():has_info(0, info_id) сразу. Тоже наглядно, и жрет всяко меньше, чем + 2 вызова функции плюс пачка эзотерических проверок внутри каждой.



"по крайней мере намного вероятнее, чем вызов из главного меню до загрузки игры" - у меня куча скриптов, где отключен вывод в лог об успешном контроле целостности. По тому как smart_terrain и иже с ним хотят их не только до алайфа, но даже до консоли.
Опять же, половина level.* кучу времени выдает nil.

Изменено пользователем Dennis_Chikin
Ссылка на комментарий

Так как непонятно, к кому обращается @Dennis_Chikin (неплохо было бы использовать цитирование), но вроде как ко мне, отвечу:

По тому что где-то в xr_logic.чего-то() или еше в каких диалогах требуют строго true или строго false.

info_id ~= "" and alife():has_info(0, info_id)
по твоему это не булевое?))

 

Вот опять же плохо по той же причине: в 99% случаев у тебя есть актор, и 146% гарантии, что без него до сюда не дойдет. Вот и можно писать сразу actor:has_info(info_id), не через 10 скриптов и 20 проверок.

Если как ты говоришь, актор почти всегда есть и ты его не проверяешь, то зачем нужна проверка алайфа? В каких случаях есть алайф, но нет актора?))

 

а info_id ~= "" лишнее так как невозможно написать has_alife_info("")

Почему ты так уверен? А если строка генерируется не вручную, а каким-нибудь генератором? Мало ли какие сбои. Если в lua правильно сделано сравнение строк, то никаких внутренних затрат на сравнения не потребуется, функция почти сразу выйдет с результатом. Изменено пользователем RayTwitty
Ссылка на комментарий

"Почему ты так уверен?"

 

о том тебе и говорю, что там где есть подозрение, там и проверяй, генератор строк я думаю у тебя в паре мест (н-р диалоги, квесты), когда эта проверка в сотне. какой-то реальный смысл в этой проверке не вижу так как функция все равно вернет false даже при "" и вообще чему попало непонятному, она именно булевая и другое она вернуть не может (если конечно ты не из любителей гонятся за наносекундами которые начнут говорить что str ~= "" быстрее итерации по массиву, но в таком случае эту проверку стоит переписать хотя бы на (type(str) == 'string' and str ~= ''), а в функции например выдачи инфо эта проверка вообще понесет вред.

Ссылка на комментарий

Так как непонятно, к кому обращается Dennis_Chikin

Э-эээ... Распутываю. Текст, естественно, отвечает на предыдущий пост. Кроме того места, где цитата из более раннего.

 

В основной части текста идет речь про то, для чего может быть нужна однострочная функция в глобальном пространстве, в которой к тому же еще и проверяется наличие alife().

То есть, представим момент загрузки какого-нибудь смарта или респавнера (а они грузятся НУ ОЧЕНЬ своеобразно), в котором зачем-то добавили xr_logic.pic_section_непомнючего, которая проверяет это самое инфо.

Вот в этот момент действительно есть вероятность (и я такой код ВИДЕЛ), что все встанет колом, если нашу однострочную функцию "упростить". Но при этом мы и не можем адекватно обработать и случай с несуществующим инфо на входе, так что пущай падает/вылетает/рушится и т.д. ССЗБ тот, кто написал такой странный код.

 

В остальных 99% случаев - да, есть и актор, и инфо, и вот эту однострочную проверку лучше выполнять "на месте", явным образом.

Также есть места, где нам заведомо известно, что и alife() есть, и несуществующих инфо нет, и эти места выполняются точно и при отсутствии актора. Здесь, опять же, явным образом вписать alife():has_info(0, info_id) - то есть, конструкция сама за себя говорит и о том, что делает, и об условиях, в которых работает.

 

Впрочем, это все на любителя, конечно. Кому и 100500 строк if ... end; if ... end (даже не elseif) милее.

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

 

 

Посты с личными разборками, которые здесь были, ушли по назначению. Не зависимо от того, что в них было кроме разборок.

Изменено пользователем Dennis_Chikin
Ссылка на комментарий

Мой вариант (одна функция, работающая и в online, и в offline).

function has_alife_info(info_id)
       if not (sim and info_id and info_id:len() > 0) then
             -- ругаемся в лог
       end
       if actor then -- online
             return actor:has_info(info_id)
       end
       -- offline
       return sim:has_info(0, info_id)
end

Для избавления от лишних проверок сделать немного по-другому - написать 2 разные функции для online и offline проверке, и в биндере игрока написать что-то такое

_G.has_alife_info = has_info_online -- пишем в net_spawn
_G.has_alife_info = has_info_offline -- в net_destroy
Изменено пользователем Полтергейст
  • Нравится 1
Ссылка на комментарий

Не помню, писал, или нет, но:

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

По-моему, все очевидно. И вот именно через нее надо делать сон и прочее. Для вящей пущести можно в аргументы сам таймфактор добавить, и сильно большими значениями не увлекаться.

 

P.S. Кстати, а вот при старте игры чтение таймфактора из конфига и принудительную установку - следовало бы добавить. На случай очумелых ручек в странных местах.

Изменено пользователем Dennis_Chikin
  • Полезно 1
Ссылка на комментарий

Моя практика говорит другое, я делал две реализации тайм-фактора, и выводы такие, что ляжешь спать часов на десять, а проснешься на рабочем столе (особенно если у тебя еще какие-нибудь "универсальные" схемы стоят). Виной всему менеджер логики. Так что лично я буду делать сон как в ЗП (технически, визуально он сделан абсолютно бездарно).

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

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

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

AMK-Team.ru

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