посиделки, как я помню, как раз через аним поинты и делаются. Но чтобы эта схема заработала, в логике нужно поставить чтобы аним поинт использовал схему посиделок. И если НПС попадает в камп зону, то у него срабатывает эта схема и он рассказывает анекдоты и играет на гитаре.
Это я знаю. Дело не поседелках, а чтобы саму схему xr_kamp воскресить у ЗП, и чтобы работало аналогично как в предыдущих частях (ЧН та ТЧ). Именно это меня интересует.
Сообщение отредактировал stalker-MiX - Четверг, 16.01.2014, 01:52
Есть не только готовый мод где используется динамическое подключение функций, но и, на мой взгляд, достаточно подробное описание работы файла xr_s.script: >>ClicK Me<<.
LaRento, я почему-то никогда не проверял ее. Раз не используют - значит могли удалить, или отключить за ненадобностью, как например схему Сидоровича. Ну, если работает - то хорошо, спс за инфу!!!
Вообще, то тупо что-то обрезать, выключать, удалять. Сначала сделают, а потом удаляют, ну и пысы. Ведь кому оно мешает!!! Закомментировать загрузчик, ну уж, если ест ресурс, и все. А то просто слов не имею на то ...
Сообщение отредактировал stalker-MiX - Пятница, 17.01.2014, 00:20
stalker-MiX, и LaRento, на анимпоинты(они же каверы) перешли сами ПЫС из за того, что нпс усаживались не корректно у костра, иногда даже друг на друге, но основной проблемой этой схемы - то, что при перезагрузке игры или при переходе из локи на локу, нпс под такой схемой спавнятся в центре костра и потому мрут, именно по этому мы могли наблюдать картину множества смертей в кострах, в ЗП это пропало благодаря расставленым анимпоинтам под смартами, и как говорил Artos(мы с ним уже вели разговор по поводу этих двух схем) в идеале объединить бы эти обе схемы camp и kamp, это было бы именно то, что нужно, но что у меня, что у него(как я понял) руки до этого не доходят...
Добавлено (17.01.2014, 01:21) --------------------------------------------- DukeKAn, по поводу первой статьи: в Lua классов, как таковых, нет, это все чудеса Lua bind(на АМК форуме есть его(ну почти оригинальная) реализация от xStream) сами же "классы" являются метатаблицами с метаметодами и не более. class("foo") идентичность function class("foo"), а методы идентичны этому foo = {} foo.print = function (msg) print(msg.." from foo") end foo.print("test msg")
ЗЫ Говорить что в Lua классы, это все равно, что говорить о Lua, как о ООП языке программирования, которым он не является...
ЗЫЫ Я как-то раз используя код реализации Lua bind от xStream в Intellij IDEA реализовал вполне рабочий функционал подобный тому, что есть в Сталкере, "классы" создавались точно так же, с легкостью прикрутилась своя песочница(за основу взята песочница от Artos) регистрировались и отрабатывались коллбэки
помимо Lua bind нашел еще множество реализаций создания подобия классов для чистого Lua...
ЗЫЫЫ Это было адресовано не столько что бы исправить тебя, сколько что бы внести уточнение, и мб кому то это интересно было бы, а вообще подобная тема уже давненько существует на АМК форуме(а даже две) "Скриптование" и "Язык Lua. Общие вопросы программирования."
Сообщение отредактировал Viнt@rь - Пятница, 17.01.2014, 01:23
Viнt@rь, это является весомой причиной, чтобы ее не использовать. Даже здесь проблема в том, что под нее попадает любой нпс с противогазом (шлемом) и использует колбасу, водку, хлеб - что является не правильным.
Плюсом я вижу то, что всем хватает работы, благодаря большому количеству нпс вокруг костра.
Говорить что в Lua классы, это все равно, что говорить о Lua, как о ООП языке программирования, которым он не является...
Да, чисто ОО-языком он не является, но всё-же даёт возможности для написания скриптов в ОО-стиле. Всё-таки и сама игра написана разработчиками в объектном стиле (хотя временами и грязноватом). Поэтому я решил для простоты назвать их классами и сосредоточиться на игровой теории, а не синтаксисе и строении языка.
ЦитатаViнt@rь ()
а вообще подобная тема уже давненько существует на АМК форуме
Да-да, возможно, большую часть знаний я оттуда и почерпнул. Там же и интересная тема про Lua. Но здесь я решил ещё и подкреплять теорию небольшими, но (желательно) интересными маленькими модами, что для новичков, я считаю, будет некоторым стимулом к изучению.
Добавлено (31.01.2014, 17:16) --------------------------------------------- Урок по созданию GUI элементов (окон).
Этот урок не будет содержать теории (а чтобы с ней ознакомиться, настоятельно рекомендую прочитать этот топик : АМК ), разберём мы сегодня создание окон. Конкретнее, разберём мы целых 3 разных применения создания окон в ТЧ – скриптовая эмуляция слота, вывод окна с кнопками и вывод статика на экран во время игры. Ну и создадим мод, в котором все 3 эти возможности применены.
Давайте сделаем так, что у ГГ есть специальный слот, или «карман» для аптечки. Чтобы каждый раз не открывать инвентарь, для того, чтобы узнать, лежит ли там аптечка, мы будем выводить на экран изображение аптечки, если она в кармане. Для создания мода нам понадобится написать 2 класса. Первый – класс для эмуляции слота, второй – для окошка меню взаимодействия с аптечкой в слоте.
Скриншоты
Создаём слот
При создании слота мы схитрим. Слот у нас будет представлять из себя кнопку, на которую мы будем накладывать текстуру аптечки. Это позволит нам реагировать на нажатие на слот (а именно, на двойной клик по слоту). Продумаем наши действия заранее: 1. Раз это слот, то показывать нам его нужно при открытии инвентаря, т.е. нужно как-то узнавать, не открыл ли ГГ инвентарь. 2. Слот должен быть интерактивным, т.е. в него можно вкладывать аптечку, и вынимать её из него.
По первому пункту – чтобы узнать, что ГГ открыл инвентарь, нужно отловить этот момент. Для этого в bind_stalker.script ищем функцию
Код
function actor_binder:info_callback(npc, info_id)
и перед end впишем
Код
ui_new_slots.on_inventory_info(info_id)
Эта функция как раз и будет проверять, не открыт (закрыт) ли инвентарь.
Чтобы вкладывать аптечку в слот, нужно отловить её использование (двойной клик, или контекстное меню по нажатию правой кнопкой по аптечке). Для этого нужно добавить коллбэк на использование предмета в том же bind_stalker.script ищем функцию
Этими действиями мы создали и подписались на коллбэк использования предметов. Однако саму функцию, которая будет вызываться по использованию предмета, ещё не написали. Поэтому в bind_actor.script ПОСЛЕ функции function actor_binder:reinit() (т.е. после end) вставляем
Код
function actor_binder:use_object(obj) local section = obj:section() if section == "medkit" or section == "medkit_army" or section == "medkit_scientic" then ui_new_slots.UseMedkit(section) end end
В ней мы проверяем, использовалась ли одна из аптечек, и если это так, вызываем функцию из нашего (пока не написанного) скрипта.
«Подготовку» закончили, теперь будем писать сам класс слота (в файле gamedata/scripts/ui_new_slots.script) В этом классе всё просто.
1. При написании класса (почти всегда) унаследуемся от уже готового базового класса. В нашем случае – это CUIScriptWnd. 2. Зададим размеры слота и его позицию на экране. 3. Создадим кнопку по этим координатам 4. Навесим на кнопку текстуру 5. Навесим на кнопку коллбэк по двойному клику.
Вот и сам код класса
Код
class "UISlotWnd" (CUIScriptWnd)
function UISlotWnd:__init(owner) super()
self.owner = owner self:Init(0, 0, 1024, 768)
-- задаём функцию, которая будет вызываться при нажатии (двойном) на слот. Она будет создавать нам окошко с действиями аптечки. self.ClickBtn = function() local spwn = ui_new_slots.medicine(get_hud()) level.start_stop_menu(spwn,true) end
-- вот здесь-то и создаётся кнопка-слот local name="medkit_slot" self.btn = CUIButton() self.btn:SetAutoDelete(false) self.btn:SetWindowName(name) -- позиция слота на экране (x,y,width,height) self.btn:Init(30,100,50,50) self:Register(self.btn) self.stat:AttachChild(self.btn) -- сохраним в глобальной переменной, чтобы всегда можно было достучаться до него (до слота) button = self.btn -- отображаем текстуру аптечки в слоте InitSlot() -- устанавливаем каллбек на двойной клик по слоту self:AddCallback(name,ui_events.WINDOW_LBUTTON_DB_CLICK, self.ClickBtn, self)
initial = true end
function UISlotWnd:ShowWnd() if initial and rec_wnd.stat~= nil then rec_wnd.stat:Show(true) end end
function UISlotWnd:HideWnd() if initial and rec_wnd.stat~= nil then rec_wnd.stat:Show(false) end end
function UISlotWnd:DetachWnd() self.owner:DetachChild(self.stat) --self:AttachChild(self.stat) initial = false end
Сам класс написан. Но его объект нигде не создаётся. Вот для этого мы и создавали в bind_stalker.script функции для отлова события открытия инвентаря и использования аптечки.
При использовании аптечки – будем поступать так: Если в слоте уже есть аптечка – то просто лечим ГГ. Если нет – то кладём аптечку в слот. Отображать слот надо как раз по открытию слота. Добавим в начало скрипта функции, которые вызываются из bind_stalker.script + несколько служебных функций.
Код
---------------------------------------------------------------------------------------- -- переменные настройки мода -- сколько здоровья прибавляют аптечки local treat_medkit = 0.35 local treat_medkit_army = 0.65 local treat_medkit_scientic = 1.0 ---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------- -- служебные переменные --------------------------------------------------------------------------------------- local button -- это и есть наш слот local medkit_section local initial = false ---------------------------------------------------------------------------------------
-- функция для отображения текстуры аптечки в слоте function InitSlot(section)
if section == nil then section = load_variable("medkit_slot_section",nil) end if section == nil then return end
-- далее конструкция для загрузки иконки аптечки по её секции из ui_icon_equipment.dds
local btn = button local ini = system_ini() local x = ini:r_u32 (section, "inv_grid_x")*50 local y = ini:r_u32 (section, "inv_grid_y")*50 local width = ini:r_u32 (section, "inv_grid_width")*50 local height = ini:r_u32 (section, "inv_grid_height")*50 -- и устанвливает эиу иконку на кнопку btn:InitTexture ("ui\\ui_icon_equipment") btn:SetOriginalRect(x,y,width,height) -- включаем копку (слот) btn:Show(true)
-- запомнили секцию аптечки в слоте save_variable("medkit_slot_section", section)
if medkit_section ~= nil and get_hud():GetCustomStatic(medkit_section) ~= nil then get_hud():RemoveCustomStatic(medkit_section) end -- теперь ещё надо и отобразить как статик get_hud():AddCustomStatic(section,true) -- запомнили секцию medkit_section = section end
-- эта функция вызывается при использовании аптечки. Если слот пустой - то положить её в слот. Иначе - просто лечить ГГ function UseMedkit(section) local current_slot = load_variable("medkit_slot_section",nil) if current_slot == nil then InitSlot(section) else treat_actor(section) end end
-- функция лечения ГГ function treat_actor(section) if section == "medkit" then db.actor.health = treat_medkit end if section == "medkit_army" then db.actor.health = treat_medkit_army end if section == "medkit_scientic" then db.actor.health = treat_medkit_scientic end end
--записываем переменную function save_variable(variable_name, value) xr_logic.pstor_store(db.actor, variable_name, value) end
--загружаем переменную function load_variable(variable_name, value_if_not_found) return xr_logic.pstor_retrieve(db.actor, variable_name, value_if_not_found) end
--удаляем переменную function del_variable(variable_name) if db.storage[db.actor:id()].pstor[variable_name] then db.storage[db.actor:id()].pstor[variable_name] = nil end end
-- Открытие/закрытие инвентаря function on_inventory_info(info_id) if info_id == "ui_inventory" then if not initial then rec_wnd = UISlotWnd(level.main_input_receiver()) end if not rec_wnd.stat:IsShown() then rec_wnd:ShowWnd() end elseif info_id == "ui_inventory_hide" then if rec_wnd then rec_wnd:DetachWnd() end end end
Создание окна взаимодействия с аптечкой
Вот и всё, класс слота написан. Теперь нужно написать класс для создания окошка действий с аптечкой. Здесь и того проще. Нам нужны только рамка + 3 кнопки, и нужно дописать функции, которые вызываются при нажатиях по кнопке.
Код
class "medicine" (CUIScriptWnd)
function medicine:__init(owner) super() self.owner = owner self:InitControls() self:InitCallBacks() button:Show(false)
end
function medicine:__finalize() end
function medicine:InitControls() -- здесь определяем координаты левого верхнего угла и ширину-высоту self:Init(50,50,600,900)
-- распарсим xml-файл, в котором координаты и названия текстур и пр. local xml = CScriptXmlInit() xml:ParseFile("ui_medkit.xml")
-- рамка xml:InitStatic("background", self)
-- надпись "Аптечка" self:Register(xml:Init3tButton("caption", self),"caption")
-- принять аптечку self:Register(xml:Init3tButton("btn_1", self),"btn_1")
-- вернуть в слот self:Register(xml:Init3tButton("btn_2", self),"btn_2")
-- убрать в рюкзак self:Register(xml:Init3tButton("btn_3", self),"btn_3")
-- кнопка выхода self:Register(xml:Init3tButton("btn_quit", self),"btn_quit") end
function medicine:InitCallBacks() -- тут интерактивные элементы, при определенном действии выполняется заданная ф-ия self:AddCallback("btn_1", ui_events.BUTTON_CLICKED, self.cheat1, self) self:AddCallback("btn_2", ui_events.BUTTON_CLICKED, self.cheat2, self) self:AddCallback("btn_3", ui_events.BUTTON_CLICKED, self.cheat3, self)
self:AddCallback("btn_quit", ui_events.BUTTON_CLICKED, self.on_quit, self) end
function medicine:OnKeyboard(dik, keyboard_action) CUIScriptWnd.OnKeyboard(self,dik,keyboard_action) if keyboard_action == ui_events.WINDOW_KEY_PRESSED then -- на выход повесим Esc if dik == DIK_keys.DIK_ESCAPE then self:on_quit() end if dik == DIK_keys.DIK_NUMPAD1 then self:cheat1() end if dik == DIK_keys.DIK_NUMPAD2 then self:cheat2() end if dik == DIK_keys.DIK_NUMPAD3 then self:cheat3() end end return true end
-- принять аптечку function medicine:cheat1() -- лечим ГГ treat_actor(load_variable("medkit_slot_section")) -- сохраняем, что в слоте ничего нет del_variable("medkit_slot_section") -- убираем индикатор get_hud():RemoveCustomStatic(medkit_section) -- выходим self:GetHolder():start_stop_menu (self,true) end -- вернуть в слот function medicine:cheat2() -- возвращаем в слот button:Show(true) -- выходим self:GetHolder():start_stop_menu (self,true) end -- убрать в рюкзак function medicine:cheat3() -- получаем секцию аптечки в слоте local medkit_section = load_variable("medkit_slot_section",nil) -- спавним аптечку в рюкзак alife():create(medkit_section, db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id(),db.actor:id()) -- сохраняем, что в слоте ничего нет del_variable("medkit_slot_section") -- убираем индикатор get_hud():RemoveCustomStatic(medkit_section) -- выходим self:GetHolder():start_stop_menu (self,true) end
-- выйти function medicine:on_quit() button:Show(true) self:GetHolder():start_stop_menu (self,true) end
Однако, при написании этого класса мы использовали xml Создадим файл gamedata/config/ui/ui_medkit.xml И поместим в него такой текст (это описание координат, шрифта текста и пр.вспомогательных данных).
Вот и всё, окно для кнопок сделано. Остался теперь самый пустяк, нужно вывести на экран во время игры иконку аптечки в слоте. Добавляем в начало скрипта функцию -- функция выводит на экран статик с икнокой аптечки в слоте
Код
function show_medkit_slot_static() local sect = load_variable("medkit_slot_section",nil) if sect ~= nil then get_hud():AddCustomStatic(sect,true) end end
Эта функция должна вызваться при загрузке сэйва, поэтому в bind_stalker.script впишем в функцию actor_binder:net_spawn(data) перед end
Это координаты статика на экране и описание текстур аптечек.
Доделаем последние мелочи – создадим текстурки аптечки и положим их в textures/ui и уберём лечение от аптечек в gamedata/config/misc/items.ltx и мод готов.
Скачать готовый мод для разбора можно здесь Яндекс_диск.
Вообще, мод несколько нелогичен. Зачем нужен такой слот – непонятно. Поэтому будет правильно сделать горячую клавишу для использования аптечки из слота. Но т.к. это уже оффтоп темы про создание окон, то допишу (или напишу новый) урок по установке и использованию перехватчика клавиатуры позже.
Добавлено (02.02.2014, 13:55) --------------------------------------------- Урок по использованию перехватчика клавиатуры.
Перехватчик (использует ASI Loader от Alexander Blade, автор - kstn) позволяет отслеживать нажатия клавиатуры во время игры. В нашем случае, это позволит использовать аптечку в слоте (предыдущий урок) по нажатию быстрой клавиши.
Для начала нужно скачать сам перехватчик здесь Яндекс_диск.
Кинем файлы из папки bin в папку bin директории Сталкера, так же и копируем папку gamedata. Единственный файл, который нужно адаптировать – bind_stalker.script. В функцию actor_binder:update(delta) в конец (перед end) вставим
Код
keylogger.update()
Всё, наш перехватчик мы установили. Перехватчик отслеживает только нажатия тех клавиш, которые прописаны в gamedata/config/keys.cfg.
Видим в начале такую запись
Код
[keys] 74, 75, 85, 97, 98, 99
Здесь – числа, которые соответствуют клавишам клавиатуры, соответствия – даны ниже. В перехватчике уже для примера отслеживаются нажатия клавиш для быстрого использования энергетика, еды и пр. Мы их убирать не будем (если хотите убрать, то просто удалите цифры после [keys]).
Пусть у нас аптечка будет использоваться по нажатию клавиши ‘T’. Находим, что T соответствует число 84. Вписываем его после числа 99, т.е. будет
Код
[keys] 74, 75, 85, 97, 98, 99, 84
Что ж, скрипт будет отслеживать нажатие клавиши T и будет автоматом вызывать функцию _84() из keylogger.script. Напишем её туда (именно _84(), по другому нельзя! Если б выбрали клавишу 71 – название было бы _71()).
После «Съедание итемов» добавим
Код
-- 'T' - использовать аптечку из слота function _84() ui_new_slots.hot_button_treat_actor() end
Функция вызывает функцию лечения ГГ. Добавим такую функцию
В ui_new_slots.script после окончания служебных переменных вставим
Код
-- функция для лечения ГГ по нажатию горячей клавиши клавиши function hot_button_treat_actor() local section = load_variable("medkit_slot_section",nil) if section ~= nil then -- лечим ГГ treat_actor(section) -- сохраняем, что в слоте ничего нет del_variable("medkit_slot_section") -- убираем индикатор get_hud():RemoveCustomStatic(section)
end end
Вот и всё, таким нехитрым способом можно отслеживать нажатия клавиш, и запускать свои действия по их нажатию.
Сообщение отредактировал DukeKAn - Воскресенье, 02.02.2014, 12:57
Требуется помощь. Мне нужно получить из файла actor параметр и сопоставить его с переменной. Проблема в том, что распространенные в модах функции: get_ltx get_ltx_new и read_from_ini в данном случае не подходят, так как секция actor происходит от секции common_ph_friction_params_on_npc_death Все функции жалуются на отсутствие данной секции в конфигах. Мне нужна функция, которая получала бы значение параметра не из секции, а просто из файла. Например, параметр max_weight все равно лишь один в файле и оглядываться на секцию мне нафик не сдалось.
Зачем нужно? Делаю античит-систему, При запуске игра проверяет некоторые параметры, и сели они правились выводит окошко с предупреждением, после чего закрывает игру. Всё готово, кроме способа получить значение из actor.ltx
Max_Warlock, а как ты собираешься свою систему "анти-школоло" защитить? Ведь никто не мешает изменить значение в конфиге и твою проверку на значение, а то и вовсе от неё избавится.
Но раз нужно - могу подсказать. Получить значение можно не привязываясь к какому либо файлу, но один параметр может повторятся в нескольких секциях, поэтому в любом случае нужно указывать в какой именно секции находится нужный тебе параметр - это раз. Два - чтобы получить значение из конфига, нужно определится какого именно типа значение тебе нужно: число (целое, дробное), строка, вектор или логическое значение, т.к. от этого зависит выбор метода для того, чтобы "вытащить" значение из ltx. В качестве универсальной функции могу предложить вот такую:
Код
function GetParamFromConfig (fileName, sectionName, paramName) local ini = ini_file(fileName) local paramValue = ini:section_exist(sectionName) and ini:line_exist(sectionName, paramName) and ini:r_string(sectionName, paramName) or "" return paramValue end
fileName - это полный путь до файла, относительно папки config; sectionName - секция, в которой находится нужный тебе параметр; paramName - собственно название нужного тебе параметра. Возвращает всегда строку(!), поэтому сравнить нужно со строкой. Например, для параметра max_walk_weight проверка будет выглядеть так:
Код
if GetParamFromConfig([[creatures\actor.ltx]], "actor_condition", "max_walk_weight") ~= "60" then --# значение в конфиге отличается от оригинального. end
Сообщение отредактировал ColR_iT - Вторник, 18.02.2014, 14:31
Max_Warlock, а как ты собираешься свою систему "анти-школоло" защитить? Ведь никто не мешает изменить значение в конфиге и твою проверку на значение, а то и вовсе от неё избавится.
Поставить проверку на присутствие самой системы. Запаковать в архив .db 75% школоты, да и не только отсеиваются автоматически