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

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

@Serge!, чем они таинственные? w_open - IWriter; r_open - reader. Но о них лучше забыть, разве что на чтение, но для этого полно других штатных методов. Касаемо записи, файл создать можно, но записать вроде более 300 байт нельзя, если память не изменяет. Давным-давно с этим разбирался.

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

Всем доброго времени суток, появился такой вопрос: возможно ли скриптово реализовать тепловизор? Ну вот к примеру обычный пнв("белая ночь") , а скрипт таким классам как нпс и монстр присваивает эффект другого цвета, ну или что-то типа того?

 

Мать: ASRock X470 Master SLI. Процессор: AMD Ryzen 9 3900X 12-Core(4200 MHz).
Память: Patriot Memory 3200 C16 Series. DDR4-3200(1600МГц), 16Гбх2(32Гб).
Видео: GeForce GTX 1060 6GB. Блок питания: CoolerMaster 750 Вт. Корпус: Zalman i3 Edge.

Химера конечно сильный хищник, а все держится дома. Чего же ты пришел к ней домой и пытаешься её убить? © Болотный Доктор

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

2 groks, про кровососа:

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

 

--/#+# 'засос кровососа боксёра' --------------------------------------------------
if self.object:clsid() == clsid.bloodsucker_s and self.object.health > 0.25 then
	local oTarget = self.object:get_enemy()
	if oTarget and IsStalker(oTarget) and self.object:see(oTarget) then
		--/ если у кровососа есть враг/цель и кровосос его видит то ...
		local iDist = self.object:position():distance_to(oTarget:position())
		if iDist <= 2 then --/ дистанция для анимации 'засоса'
			local iTime = time_global()
			--/ таймер для анимации и хита ...
			if (self.timer_anim or 0) < iTime then
				self.timer_anim = iTime + 500 --/ таймер анимации ~ 0.5 сек
				if self.object:animation_count() > 0 then
					self.object:clear_animations()
				end
				self.object:add_animation("wounded_2_out") --/ варианты: "vampire_0" | "idle"
				--/ озвучка 'засоса'
				local oSnd = sound_object("material\\dead-body\\collide\\hithard06hl")
				oSnd:play_no_feedback(oTarget, sound_object.s2d, 0, vector(), 5.0)
			end
			--/ хит жертве (притягиваем)
			if (self.timer_hit or 0) < iTime and (self.timer_anim and self.timer_anim + 200 >= iTime) then
				self.timer_hit = iTime + 10 --/ таймер хита ~ 0.1 сек
				local vDir = self.object:direction() --/ вектор кровососа
				local vDirHit = vector_rotate_y(vDir,179.0)
				local h = hit()
				h.draftsman = self.object
				h.direction = vDirHit --/ направление хита
				h:bone("bip01_spine") --/ для учета 'брони'
				h.power = 0.02
				level.add_cam_effector("camera_effects\\fusker.anm",959,false,"")
				h.impulse = 50/iDist --/ чем ближе - тем сильнее 'засос'
				h.type = hit.wound --hit.strike
				oTarget:hit(h) --/ наносим хит жертве
				--/ разворот жертвы на кровососа. TODO: требует доработки/замены!
				if oTarget:id() == db.actor:id() then --/ жертва == актор? (а нужно?)
					oTarget:set_actor_direction(vDirHit:getH())
end	end	end	end	end

 

 

Во-вторых, в данном случае оно меня сильно разочаровало. Ну да ладно.

if (self.timer_anim or 0) < iTime then -- то есть, если меньше, то начинает притягивать и озвучивать.

self.timer_anim = iTime + 500 -- а к self.timer_anim добавляет пол секунды. Мало это, или много - тут на усмотрение. Но через пол-секунды повторит.

 

if (self.timer_hit or 0) < iTime and (self.timer_anim and self.timer_anim + 200 >= iTime) then

если 10 миллисекунд хит ни кому не наносился, и 200 ms (чуть меньше половины времени от начала засасывания НЕ прошло, то наносит.

self.timer_hit = iTime + 10 --/ таймер хита ~ 0.1 сек

и еще 10ms добавляет. То есть, аки пионэр, снова будет готов.

Мдя, видал я разные варианты кровососов извращенцев, но этот - вообще какой-то озабоченный.

Ну а дальше он собственно и занимается извращениями всякими нехорошими.

 

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

 

В общем вот здесь:

if self.object:clsid() == clsid.bloodsucker_s and self.object.health > 0.25 then

не хватает чего-то вроде and ( self.tmer_attack or 0 ) < time_global() then

 

а после  self.timer_hit = iTime + 10 --/ таймер хита ~ 0.1 сек

еще self.timer_attack = iTime + сколько надо. -- я бы что-то типа 2000 или даже 5000 поставил.

Тогда один раз анимку запустит, и один раз хит нанесет.

 

Впрочем, все равно уродство редкостное, поскольку вообще не здесь надо править, а здесь все проще закрасить, чем отскребать.

 

 

upd:

if (self.timer_hit or 0) < iTime and (self.timer_anim and self.timer_anim + 200 >= iTime) then

 

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

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

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

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

Но оно работает. И совершенно без сбоев. Действительно как из пулемёта ... эээ ... присасывается.

Теперь хоть понял, что за паузу отвечает.

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

@Dennis_Chikin, Спасибо.

@Карлан, Предложи вариант.

Может проще будет не вертеть ГГ и просто прилепить анимацию опьянения например?

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

Ну вот как-то так уж, что-ли. Не проверял, ибо где я в такую погоду найду вам кровососа, да еще и извращенца ?

 

 

	local tg = time_global()  -- должно быть сразу после function что-то_там( что_попало )
	local mob = self.object	-- и дальше заменяем все self.object на mob до конца функции

	--/#+# 'засос кровососа боксёра' --------------------------------------------------
	if mob:clsid() == clsid.bloodsucker_s and mob.health > 0.25 then
		if self.attack_time and tg < self.attack_time	-- атака пошла
			local enemy = mob:get_enemy()
			if enemy and enemy:id() == 0 and mob:see( enemy )	-- видит актора
			  and enemy:position():distance_to_sqr( mob() ) < 4.3 then	-- близко
				if self.timer_hit then
					if self.timer_hit < tg then	-- наносим хит
						self.timer_hit = tg + 50	-- повторим потом
						local vDir = mob:direction() --/ вектор кровососа
						local vDirHit = vector_rotate_y( vDir, 179.0 )
						local h = hit()
						h.draftsman = mob
						h.direction = vDirHit --/ направление хита
						h:bone( "bip01_spine" ) --/ для учета 'брони'
						h.power = 0.02
						level.add_cam_effector( "camera_effects\\fusker.anm", 959, false, "" )
						h.impulse = 50 / enemy:position():distance_to( mob() ) --/ чем ближе - тем сильнее пинаем
						h.type = hit.wound
						enemy:hit(h) --/ наносим хит жертве
						--/ разворот жертвы на кровососа. TODO: требует доработки/замены!
						enemy:set_actor_direction( vDirHit:getH() )
					end
				else	-- запуск анимации
					if mob:animation_count() ~= 0 then mob:clear_animations() end	-- анимации остановили
					mob:add_animation( "wounded_2_out" ) --/ варианты: "vampire_0" | "idle"
					--/ озвучка 'засоса'
					local snd= sound_object( "material\\dead-body\\collide\\hithard06hl" )
					if snd then snd:play_no_feedback( enemy, sound_object.s2d, 0, vector(), 5.0 ) end
					self.timer_hit = tg + 200	-- отыграет анимку, и нанесет хит
				end
			else self.attack_time = false	-- атака сорвалась
			end
		elseif ( self.check_attack or 0 ) < tg then	-- проверяем не постоянно
 			local enemy = mob:get_enemy()
			if enemy and enemy:id() == 0 and mob:see( enemy )	-- видит актора
			  and enemy:position():distance_to_sqr( mob() ) < 4.3 then	-- близко
				self.attack_time = tg + 500	-- атака пошла, пол секунды на все
				self.check_attack = tg + 1000	-- ну и повтор через секунду
				self.timer_hit = false
			else self.check_attack = tg + 200	-- хотя бы 200ms
	end	end	end

 

upd: 22:15 - и еще поправил. Ибо нефиг прямо на форуме код писать.

 

И, блин, нет ничего хуже, чем постоянно что-то править под древние кривые скрипты, когда у тебя давно вменяемые написаны. 8(

 

2 Zander_driver: на риторический вопрос я могу дать только риторический ответ. Надо ?

По тому что про кровососа был пост на форуме, и этого самого кровососа пытаются впихнуть именно в древний кривой скрипит.

 

Upd2: тьфу, блин. if self.attack_time and tg < self.attack_time -- атака пошла

вот так надо. И дальше тоже на attack_time поменять, для красивости.

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

Раз началась чистка форума, то хочу завершить нашу подвисшую дискуссию об использовании текстовых файлов в модах на движке ТЧ. Это актуально потому, что все мои посты, кроме самого первого с вопросом, уже убраны, а остались только 2 сумбурных по содержанию ответа от Struck, в которых, к тому же, второй противоречит первому. Это говорит лишь о том, что товарищ не достаточно глубоко разобрался в этом вопросе, но, тем не менее, легко берётся учить других. А вот полезной информацией по этому вопросу, как оказалось, не владеет.
Учитывая вышеизложенное, немного предыстории, которую можно и пропустить, т.к. в этом сообщении ни она главная цель.
 

При игре в Сталкера меня всегда утомляла порой бессмысленная и бестолковая беготня ГГ по локациям. Поэтому возникла потребность как-то оптимизировать этот процесс путём быстрого перемещения ГГ по тем точкам, в которых он уже побывал ранее. Этот, казалось бы, простой аспект (только по собственным точкам)на самом деле является очень важным, т.к. перемещение только по пройденному тобой пути, а не по предварительно кем-то оцифрованному, гарантирует выполнение сюжетной линии Авторов. Для этого нужен был «перемещатель».


У меня, при разработке скрипта по перемещению ГГ в пределах локации, возникла задача сохранения координат этих точек перемещения. При этом на этот процесс были сформулированы следующие ограничения:
1. количество сохранённых точек перемещения должно быть сколь угодно большим;
2. минимальное, а лучше полное отсутствие, вмешательство скрипта в штатную архитектуру;
3. сохранность информации о координатах перехода в независимости от любых форс-мажоров (внезапные вылеты, перезагрузка с более ранней сохранки и т.п.);
4. работа с любыми модами, независимо от их собственных примочек и наворотов;
5. стабильность и устойчивость в работе;
6. быстрое и простое подключение к любой модификации.

Недолгие раздумья привели к выводу, что это задача решается только с использованием внешних файлов, что и было быстро реализовано для модов на движке ЗП. (для ЧН тоже, скорее всего, подойдёт, но в моды ЧН я практически не играю и, поэтому данный аспект не проверял).
А вот с ТЧ вышел облом, т.к. этот движок отказался работать с внешними файлами. Только сохранки, картинки и логи. Меня это совершенно не устраивало. Последующий длительный поиск информации в Интернете и по сталкерским сайта показал, что подавляющее число людей, так или иначе связанных с разработками по Сталкеру, почти консолидировано считали – такое не возможно, потому, что не возможно никогда. Однако всё же нашёлся один разработчик, который, видимо, придерживается и моего любимого принципа – «Если это нельзя, но очень хочется, то значит это можно. Просто надо найти какой-то другой путь решения». Этим человеком был Artos, который, судя по описанию, ещё в 2010 году решил эту проблему путём возвращения в пространство имён Lua для Сталкера класса io и исправления глобального пространства для класса FS. Там и ещё много другого важного и интересного сделано, но здесь речь только о работе с файлами. Я пока не стал её реализовывать (даже не смотрел ничего кроме описания, хотя скачал), т.к., в моём мелком случае, она несколько «тяжеловата». Однако взял на заметку для возможного будущего использования. Странно, что профи мододелы, так важно и авторитетно говорящие всякие красивые слова на форумах того же сайта, где эта разработка описана и опубликована, про это не подозревают или молчат(?), а я, дилетант в этих вопросах по сути дела – знаю и говорю.
Однако вернёмся к нашим проблемам.
Основных, да и единственных, предложений в ответах оппонентов было два. Т.к. эти посты уже тоже вычищены, то напомню: 1 - использовать статические таблицы точек перехода (такие действительно применяются во многих поделках) и 2 - использовать нет-пакеты. Я начал возражать, мне, не очень аргументировано, продолжали доказывать свою правоту. Всея эта «плодотворная дискуссия» завершилась моим «баном» и последующей чисткой. Сильный аргумент ничего не скажешь.
Приведу свои доводы против обоих предложений.
Статические таблицы. Чтобы их заполнить самому, надо долго побегать/полетать по всем локациям, собрать координаты в не понятно каких точках (в игру я же ещё не играл, а если уже играл, то зачем мне это?) и проделать большую работу по заполнению таблиц. Можно воспользоваться таблицами других авторов (если они найдутся), поковыряться в скриптах, а потом бегать, как собачка на поводке по меткам кем-то для тебя приготовленными. Фу… Это было явно не для меня и отпало сразу.
Нет-пакеты. Их можно было бы и использовать, но они не соответствовали моим ограничениям по следующим соображениям:
1. Нет-пакеты привязаны к объектам. К какому объекту мог бы их привязать я? Выбор не большой – а) ГГ, б) локация, в) созданное для этих целей новое устройство. Всё это не реально и нарочито появлением непонятных вылетов в процессе игры.
2. Системные ограничения по размеру нет-пакетов ещё никто не отменял.
3. Они сохраняются и восстанавливаются для реперных точек игры. И, если я восстановлюсь с более ранней сохранки, то мне придётся обновлять свою базу переходов.
4. Требуется серьёзная правка исходных кодов игры, причём для каждого нового мода своя.

С историей на этом всё, переходим к практике.


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

Спросим себя – что такое файл? По определению Википедии - файл это «именованная область данных на носителе информации». Такая область данных может содержать информацию в любом виде, обязательным требованием для которого является только однотипность единицы множества этой информации и наличия описания их параметров. В качестве носителя информации может выступать что угодно, например: книжная полка (единица информации – книга), книга (единица информации – лист), папка для документов (единица информации – документ). Не зря же все иностранцы называют привычные для нас пластиковые папочки с бумажками - файлами. Кто не верит, поверьте мне на слово, т.к. я долго в этом варился. В нашем же случае, носителем информации на вернем уровне является диск, а некой единицей информации на нём - файл. Всё, дальше в это углубляться не будем. Всё остальное все желающие могут найти сами, а нам для дальнейших рассуждений этого вполне хватит.
Что такое текстовый файл? Это область данных, которая хранит информацию в виде набора строк символов. Вся информация о структуре этих строк является служебной и хранится в отдельной области. Нас эта область сейчас ну ни разу не интересует. Нас интересуют только сами строки.
Что такое каталог/папка с файлами? Это область данных, которая хранит информацию в виде набора отдельных файлов, которые для самого каталога представляется только как набор строк с именами этих файлов. Вся служебная информация об этих файлах/строках также хранится в отдельной области. И здесь эта область нас не интересует. Главное мы выяснили – имя файла это строка символов, на которые, правда, накладываются определённые ограничения и то, что каталог это тоже файл.
Разумеется, это не строгое описание, но, чтобы двигаться дальше, этого достаточно.
Итак, договорились, что под обычным «файлом» мы будем понимать каталог, а под «строкой» такого псевдо файла собственно сам физический файл, имя которого и будет являться для нас привычной строкой.
Теперь перейдём к Сталкеру. Что нам в «открытом доступе» оставили Разработчики ТЧ для работы с файлами? Да, собственно говоря, почти ничего полезного для практического использования. Да и это, я думаю, осталось только из-за обычного дефицита времени на подчистку «хвостов» и борьбы за размер дистрибутива. Однако всегда у всех что-то случается, дело до конца не доводится, ну а мы попробуем извлечь из этого выгоду для себя.
И так мы имеем полные возможности читать каталоги с файлами и сохранять их в памяти в виде списков. Замечательно, т.к. половина проблемы решена. С записью хуже. Записать мы можем только определенные структуры данных (сохранки, картинки, логи). Но, поскольку продукт делали наши ребята, то, как часто случается, «хлопнули ушами» и оставили нам дырочку в своём заборе. Я имею в виду метод copy в классе FS. Отлично, значит, мы имеем возможность переименовать любой файл, присвоить ему любое имя и поместить его в любое место. Вам уже понятно, какой был у меня ход мысли?
Тогда вперёд, к реализации наших теоретических изысканий.



Опишу словами то, что потом покажу в коде.

Раз под файлом будем понимать папку, то нам надо её иметь. Для простоты её можно создать в каталоге самой игры, а можно и в любом другом месте. Я создал её там же, где хранятся сохранки, логи и картинки (у меня это папка users в корневой папке игры). В этой папке создается наша рабочая папка (псевдо файл), которую я назвал "scroll", а в ней любым способом создаем файл шаблон нулевой длины с простым коротким именем и без расширения. Я ему дал имя «01» и установил атрибут «только для чтения», чтобы случайно не удалить. Этот файл будет использоваться в скрипте для сохранения наших точек перехода.
При запуске скрипта он будут читать эту папку формировать список имеющихся точек перехода, т.е. имеющихся в ней файлов. На старте же будет фиксироваться текущая позиция ГГ. Эти координаты также будут показаны. Имя выбранной в списке или новой точки перехода выводится в редактируемом поле, которое может быть очищено нажатием соответствующей кнопки. Если в этом поле имеется имя точки, то можно перейти на неё, сохранить
или удалить. При сохранении наш файл шаблон стандартным методом копируется в новый с присвоением ему в качестве имени строки содержащей следующие поля (в качестве разделителя случит запятая):

имя точки, координата X, координата Y, координата Z, угол доворота камеры.имя локации

Заметили, что в качестве имени файла применяется имя локации. Это позволяет нам при формировании списка на старте сразу отфильтровывать и загружать только те точки перехода, которые относятся к текущей локации.
Выход обычным способом или по Esc, или по отдельной кнопке «Выход»



Поскольку я раньше ничем подобным не занимался, то мне было интересно «пощупать», что же это такое текстуры и окна Сталкера. Но можно вполне обойтись и штатными текстурами и диалоговыми окнами.
Как это получилось можно посмотреть
a80367e0c8d66695c628a37f22fabfa85d642420 

И наконец, сама реализация
 

 
-- перемещение по уровню для ТЧ (2014)

local tnam = {"nm","px","py","pz","ag"}
local fs = getFS() -- класс FS
local is_fs = fs:exist("$scroll$","01") -- проверка на начичие шаблона
local full_path = is_fs.name -- запомним абсолютный путь файла-шаблона
local tf = {point} -- внутренняя таблица файлов

-- получаем количество полей и позиции разделителя
local function GetPosSep(str)
if str == "" or str == nil then return {} end
local t, pos, n = {}, 0, 0
while pos ~= nil do
pos = string.find(str, '[%|%,]', pos+1)
n = n + 1
if pos ~= nil then table.insert(t,pos) end
end
return {sp = n, tbl = t}
end

-- получаем поле по индексу
local function GetPol(str,num)
if str == "" then return str end
if num == nil then num = 1 end
local pol, t = nil, GetPosSep(str).tbl
if num == 1 then -- первый
pol = string.sub(str,1,t[num]-1)
elseif num == GetPosSep(str).sp then -- последний
pol = string.sub(str,t[num-1]+1)
else
pol = string.sub(str,t[num-1]+1,t[num]-1)
end
return pol
end

class "load_item" (CUIListItemEx)

function load_item:__init() super()
self.text_name = "name"
self:SetWndRect(0,0,230,22)

self.sn = CUIStatic()
self.sn:SetAutoDelete(true)
self:AttachChild (self.sn)
self.sn:SetWndRect (0,0,200,22)
self.sn:SetText("name")
self.sn:SetFont(GetFontLetterica18Russian())
self.sn:SetTextColor(255,216,186,140)
end

function load_item:__finalize()
end

-- точка входа из внешних скриптов (ui_scroll_wnd.main())
function main()
super_dlg = ui_scroll_wnd.scroll()
level.start_stop_menu(super_dlg, true)
end

class "scroll" (CUIScriptWnd)

function scroll:__init() super()
self:InitControls()
self:InitCallBacks()
end

function scroll:__finalize()
end

-- заполняем список точек перехода на текущей локации
function scroll:FillList()

local flist_ex = fs:file_list_open_ex("$scroll$",FS.FS_ListFiles ,"*."..self.lev)
local f_cnt = flist_ex:Size()
local str, nam
if f_cnt > 0 then
for it = 0, f_cnt-1 do
str = flist_ex:GetAt(it):NameFull()
nam = GetPol(str,1)
self:AddItemToList(nam)
local vp = vector():set(tonumber(GetPol(str,2)),tonumber(GetPol(str,3)),tonumber(GetPol(str,4)))
local age = tonumber(GetPol(string.gsub (str, "."..self.lev, ''),5))
local t = {}; t.fn = str; t.pos = vp; t.dir = age
tf['point'] = nam; tf[nam] = t
end
end

end

function scroll:InitControls()
self:Init(350,100,296,561) -- задаем позицию на экране
local xml = CScriptXmlInit() -- создаем класс для файла описания
xml:ParseFile("ui_scroll_wnd.xml") -- подключаем файл описания элементов окна
xml:InitStatic("scr_background", self) -- устанавливаем статик фона

-- регистрируем элементы окна
-- поле ввода позиции
self.scr_fr_name = xml:InitEditBox("scr_fr_name", self)
self.scr_fr_name:SetFont (GetFontLetterica18Russian())
-- self.scr_fr_name:SetTextColor(255,0,0,0)
self:Register(self.scr_fr_name ,"scr_fr_name")

-- кнопки
self:Register(xml:Init3tButton("scr_btn_1", self),"scr_btn_1") -- перенести
self:Register(xml:Init3tButton("scr_btn_2", self),"scr_btn_2") -- сохранить
self:Register(xml:Init3tButton("scr_btn_3", self),"scr_btn_3") -- удалить
self:Register(xml:Init3tButton("scr_btn_4", self),"scr_btn_4") -- очистить поле ввода
self:Register(xml:Init3tButton("scr_btn_5", self),"scr_btn_5") -- выход

-- получение текущей позиции и направления
self.st = xml:InitStatic("scr_pos", self)
self.lv = xml:InitStatic("scr_lev", self)
self.lev = level.name()
self.pos = db.actor:position() -- позиция ГГ
self.age = -db.actor:direction():getH() -- угол доворота камеры от нулевой точки локации
self.lv:SetFont (GetFontLetterica16Russian())
-- self.lv:SetText(string.format("Локация: %s", ))
self.lv:SetTextST(string.format("Локация: %s", self.lev))
self.st:SetText(string.format("X=%-7.2f Y=%-7.2f Z=%-7.2f", self.pos.x, self.pos.y, self.pos.z))

-- поле списка точек перехода
local ctrl = CUIWindow()
xml:InitWindow ("scr_item:main",0,ctrl)

-- сам список
xml:InitFrame("list_frame",self)
self.list_box = xml:InitList("list",self)
self.list_box:ShowSelectedItem(true)
self:Register(self.list_box, "scr_list")
self:FillList() -- заполняем список
end

function scroll:InitCallBacks()
self:AddCallback("scr_list", ui_events.LIST_ITEM_CLICKED, self.OnListItem_clicked, self)
self:AddCallback("scr_list", ui_events.WINDOW_LBUTTON_DB_CLICK, self.OnListItemDb_clicked, self)
self:AddCallback("scr_btn_1", ui_events.BUTTON_CLICKED, self.OnButton_scroll_clicked, self)
self:AddCallback("scr_btn_2", ui_events.BUTTON_CLICKED, self.OnButton_save_clicked, self)
self:AddCallback("scr_btn_3", ui_events.BUTTON_CLICKED, self.OnButton_del_clicked, self)
self:AddCallback("scr_btn_4", ui_events.BUTTON_CLICKED, self.OnButton_CLEAR_clicked, self)
self:AddCallback("scr_btn_5", ui_events.BUTTON_CLICKED, self.OnButton_CANCEL_clicked, self)
end

-- очистить поле ввода имени
function scroll:OnButton_CLEAR_clicked()
self.scr_fr_name:SetText("")
end

-- Перейти
function scroll:OnButton_scroll_clicked()
local sc_name = self.scr_fr_name:GetText()
if sc_name ~= "" then
db.actor:set_actor_position(tf[sc_name].pos)
db.actor:set_actor_direction(tf[sc_name].dir)
level.start_stop_menu(self)
end
end

-- Сохранить
function scroll:OnButton_save_clicked()
local sc_name = self.scr_fr_name:GetText()
if sc_name == "" then return end
local abs_path = string.gsub(full_path, '01', '') -- получаем абсолютный путь для нового файла
local new_path = abs_path..sc_name..','..tostring(self.pos.x)..','..tostring(self.pos.y)..','..tostring(self.pos.z)..','..tostring(self.age)..'.'..self.lev
fs:file_copy(full_path, new_path) -- создаём новый файл точки перехода
level.start_stop_menu(self)
end

-- Удалить
function scroll:OnButton_del_clicked()
local sc_name = self.scr_fr_name:GetText()
if sc_name == "" then return end
fs:file_delete("$scroll$",tf[sc_name].fn)
level.start_stop_menu(self)
end

-- Выйти
function scroll:OnButton_CANCEL_clicked()
level.start_stop_menu(self)
end

-- Выбор (щелчок в списке)
function scroll:OnListItem_clicked()
local list_box = self:GetListWnd("scr_list")
if list_box:GetSize() == 0 then return end
local item_id = list_box:GetFocusedItem()
local _itm = list_box:GetItem(item_id)
if _itm == nil then return end
local item_text = _itm.sn:GetText()
self.scr_fr_name:SetText(item_text)
end

-- Двойной щелчок
function scroll:OnListItemDb_clicked()
self:OnListItem_clicked()
self:OnButton_scroll_clicked()
level.start_stop_menu(self)
end

-- клавишы
function scroll:OnKeyboard(dik, keyboard_action)
CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
local bind = dik_to_bind(dik)
local console = get_console()
if keyboard_action == ui_events.WINDOW_KEY_PRESSED then
if dik == DIK_keys.DIK_ESCAPE then
level.start_stop_menu(self)
end
end
return true
end

-- добавляем строку в список
function scroll:AddItemToList(text)
local _itm = load_item()
_itm.sn:SetText(text)
local list_box = self:GetListWnd("scr_list")
list_box:AddItem(_itm)
end


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

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

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

Не убраны, а перемещены. Можно взять, отредактировать, и оставив нужное вернуть сюда. Или пусть лежат там, где лежат, в неизменном виде. Очевидно, обсуждение пользователя все же к теме не относится.

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

Ну и вообще, если я правильно понял смысл поста, то это давно делают примерно так:

 

local t_opt = {
	["p"] = { "game_pause", "radio_amk_pause", 1 },
	["r"] = { "show_rad", "radio_amk_rad", 1, bind_stalker.rdet_enable },
	["h"] = { "suit_hud", "radio_amk_hud", 0 },
	["m"] = { "din_music", "radio_amk_mus", 0 },
	["a"] = { "keep_anoms", "radio_amk_anoms", 0 },
	["d"] = { "ch_difficulty", nil, 0 },
	["t"] = { "treasure_type", "radio_amk_treasure", 2 } }

function save_options()
	local fs = getFS()
	local flist = fs:file_list_open_ex("$game_saves$", bit_or(FS.FS_ListFiles, FS.FS_RootOnly), "game_options_*.sta" )
	local s = flist:GetAt( 0 ):NameFull()
	local opt = ""
	local n, fn
	for k, v in pairs( t_opt ) do
		n = v[3]
		if n == 0 then
			game_options[ v[1] ] = false
			fn = v[4]
			if fn then fn( false ) end
		else
			game_options[ v[1] ] = n
			fn = v[4]
			if fn then fn( n ) end
		end
		opt = opt .. k .. tostring( n )
	end
	n = "game_options_" .. opt .. ".sta"
	if s ~= n then fs:file_rename( fs:update_path("$game_saves$", s ),
		fs:update_path("$game_saves$", n ), true)
	end
end

-- здесь, в частности, чтобы сохранить опции игры независимо от сэйвов.

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

@Serge!, тебе все по делу написали. А это извращение было открыто в 2010. Все об это знают я думаю. io в ТЧ нет и не было. К чему ты тут развел это все я не понимаю. Даже сам Artos такое не использовал, а написал se_stor. Подобное нужно лишь в редких случаях (и то - не уверен), например вот как Денис описал.


Обновленная система ивентов: m_events (031214) [~2.5Kb]

P.S.: не трактую свою систему как хорошую и безупречно работающую, но может кому и сгодится ;)

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

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

Только я тогда не знал (я ж любитель), как создать исходный файл-шаблон определенной структуры и поэтому сделал, как мог - просто создал пустой файл и работал с ним.

Сейчас я понял, что могу прогнать такую схему в ЗП и, если всё получится, то работать по ней в ТЧ. Буду пробовать

Где ж Вы раньше были, когда вдвоём уговаривали меня использовать не подходящие средства, а не рассказали про это?

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

Просто метод более ограничен, чем другие. Например, вместе со всеми путями здесь не может быть более 250 символов. На сами символы тоже есть ограничения. То есть, это гораздо ХУЖЕ нетпакета. И хуже внешнего конвертера лога в ltx.

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

@Zander_driver, почему "был"?
Его lua_helper, lua_extension, m_netpk, m_timers, se_stor (соответственно, изменённый _g), ты не поверишь, давно и успешно использую.

Каким боком m_events? Что это? Зачем это (=как использовать)?

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

А скажите пожалуйста, что такого уродского в этом коде:

if string.find( obj:section(), "af_" ) then

Интересно в познавательных целях и чтобы не писать "по уродски".

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

@Barmolini, в секции можно написать my_cool_art и это будет артефактом, однако условие не пройдет. Пытаться чтото идентифицировать не по тому ключевому полю - глупо. Это как пробовать вычислить кошку среди собак по наличию хвоста и отсутствию крыльев. Смекаешь? Если кошка - изволь мяукать, в данном случае надо брать clsid или что там у нас.

ТЧ 1.0004. SAP и Trans mod

github

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

@Barmolini, во-первых да, избавляемся от косяков с секциями, и в некоторых модулях не городим ненужные таблицы напрасно тратя время. А во-вторых проверка t[clsid] работает в ~6-7 раз быстрее чем string.find. На всякий (миллион итераций):

strfind 0.022
table 0.003

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

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

Ребят, умоляю вас! Пожалуйста, напишите функцию убийства определённого НПС через скрипт.
Платформа ТЧ

 

А-ааа !!! npc:kill( npc ) - чем не подходит ? Ну и в поиск это же слово - kill, в смысле. dc

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

function mochim_five_nps()

    if has_alife_info("первый_грохнут") and

        has_alife_info("второй_грохнут") and

        has_alife_info("третий_грохнут") and

        has_alife_info("четвертый_грохнут") and

        has_alife_info("и_пятый_наконец") then

        return true 

    else 

        return false

    end

end

 

Удали несколько строк не нужных, смотря сколько убить нужно.

 

Если нужно одного, то будет вот так:

 


function mochim_five_nps()

    if has_alife_info("первый_грохнут") then

        return true 

    else 

        return false

    end

end

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

 

function mochim_five_nps()
    if has_alife_info("первый_грохнут") and
        has_alife_info("второй_грохнут") and
        has_alife_info("третий_грохнут") and
        has_alife_info("четвертый_грохнут") and
        has_alife_info("и_пятый_наконец") then
        return true 
    else 
        return false
    end
end
 
Удали несколько строк не нужных, смотря сколько убить нужно.
 
Если нужно одного, то будет вот так:
 
function mochim_five_nps()
    if has_alife_info("первый_грохнут") then
        return true 
    else 
        return false
    end
end

 

Спасибо, конечно, за ответ, но я имел в виду немного не то. Возможно, я вопрос не так поставил. Как убить НПС через скрипт - я это имел в виду.

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

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

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

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

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

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

Войти

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

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

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