-
Notifications
You must be signed in to change notification settings - Fork 88
Подробное описание изменений
Включается в секции features
параметром show_inv_item_condition
. Для работы нужно добавить в inventory_new.xml
:
<condition_progess_bar x="3" y="1" width="29" height="4" horz="1" min="0" max="100" pos="1">
<background x="0" y="0" width="30" height="5">
<texture r="196" g="18" b="18" a="200">ui_mg_progress_efficiency_full</texture>
</background>
<progress>
<texture>ui_mg_progress_efficiency_full</texture>
</progress>
<min_color r="107" g="207" b="119"/>
<middle_color r="255" g="255" b="118"/>
<max_color r="107" g="207" b="119"/>
</condition_progess_bar>
<cell_item_text x="0" y="0" width="35" height="16" stretch="1">
<text x="0" y="0" width="35" height="16" font="letterica16" color="white" align="l" vert_align="t"/>
</cell_item_text>
condition_progess_bar
- это статик полоски состояния.
cell_item_text
- это статик с числом, кол-во предметов.
Аттрибут width
у condition_progess_bar
- не обязательный. Если его нет, то длина полоски будет рассчитана автоматически, на основе ширины ячейки.
Далее dragdrop элементам нужно добавить аттрибут condition_progress_bar="1"
, который включит показ шкал состояния. По умолчанию будет показываться состояние оружия и брони. Что бы показывалось состояние других предметов, им нужно добавить в секцию:
inv_show_condition = true
Дополнительно я добавил аттрибуты cell_sp_x
и cell_sp_y
для dragdrop элементов. Они задают отступы после иконки предмета по горизонтали и вертикали. Для dragdrop_bag использовать не имеет смысла, т.к. несколько неожиданный результат будет для предметов, которые занимают больше одной ячейки в любую сторону. Отступ-то расчитывается для одной ячейки и если их больше, то и отступ будет больше. Т.е. если предмет три ячейки в высоту, то вертикальный отступ у него будет утроенный. А вот для пояса, к примеру, вполне можно использовать.
Прогрессбары состояния предметов анимированные. При открытии инвентаря они плавно увеличиваются до своих значений. Отключить анимации можно через аттрибут animated="0"
.
Примеры использования всего этого можно посмотреть здесь
- Возможность разделения
ui_icon_equipment.dds
на разные полотна. Параметрicon_group
, в секции предмета, задаёт, с какого полотна брать иконку. Если значение параметра0
, а так оно по умолчанию, то используется стандартная текстураui_icon_equipment.dds
. Если значение больше нуля, например
icon_group = 1
то будет использоваться ui_icon_equipment_1.dds
, т.е. к имени стандартной текстуры прибавляется _число
.
- Поддержка
icon_name
в секции предмета. Например:
[vodka]
icon_name = ui_icon_vodka
В этом случае иконка водки будет браться не с полотна по координатам inv_grid_x
и inv_grid_y
, а будет использована текстура ui_icon_vodka
, которая должна быть описана в одном из xml описания текстур, вот так, например:
<ui_texture>
<file_name>ui\ui_icons_items</file_name>
<texture id="ui_icon_vodka" x="0" y="200" width="500" height="200" />
...
</ui_texture>
-
Каждой фразе назначается акселлератор от 1 до 0. Т.е. если нажать соотв. кнопку - будет выбрана соответствующая фраза. Что бы эти номера показывались перед фразами, нужно включить опцию
show_dialog_numbers
в секцииfeatures
, т.к. для этого требуется дополнительный статик"num_text"
вtalk.xml
, как в ЗП. Вот рабочий пример:
<question_item x="0" y="0" width="527" height="20" min_height="13">
<num_text x="0" y="0" width="8" height="20">
<text font="letterica18" r="255" g="232" b="208" align="l" />
</num_text>
<content_text x="15" y="0" width="512" height="20">
<text font="letterica18" r="255" g="232" b="208" align="l" complex_mode="1"/>
<text_color>
<t r="231" g="168" b="12"/> <e r="255" g="232" b="208"/> <h r="255" g="255" b="255"/>
</text_color>
</content_text>
</question_item>
-
Добавил скриптовым кнопкам метод
SetAccelerator( dik, idx )
Позволяет из скрипта назначить кнопке клавишу, при нажатии которой она должна сработать. Тоже самое, что и атрибут"accel"
в xml конфиге. -
Добавил для скриптов возможность получить
CUIButton
изCUIWindow
Позволяет делать например вот так:
-- В данном случае, кнопке выхода из инвентаря назначается акселератор.
local wnd = level.get_inventory_wnd()
local btn_exit = wnd:FindChild( "exit_button" )
if btn_exit then
local btn = btn_exit:GetButton()
if btn then
btn:SetAccelerator( k_inv_open )
end
end
Вызывается перед тем, как будет показан диалог подтверждения перехода на другую локацию. Можно использовать для того, что бы поменять этот самый диалог. Например:
-- Здесь в подтверждение добавляется имя локации, на которую переходим.
function actor_binder:level_changer_action( obj, wnd )
local sobj = obj:get_alife_object()
local lc = sobj:get_level_changer()
local dest_vertex = lc.dest_game_vertex_id
local dest_lname = alife():level_name(
game_graph():vertex( dest_vertex ):level_id()
)
local msg_text_wnd = wnd:FindChild( "message_box_change_level:message_text" )
msg_text_wnd:GetCUIStatic():SetText(
game.translate_string( dest_lname ) .. ": "
.. game.translate_string( "level_changer_invitation" )
)
end
-
Перенес anomaly_detector из
CBaseMonster
вCCustomMonster
, что бы его можно было использовать не только для мутантов, но и для сталкеров тоже. Если кто не знает, это движковый обход аномалий. По умолчанию используется только для мутантов в состоянии покоя. -
Еще экспортировал его для скриптов. Доступ можно получить вот так:
local anomaly_detector = obj:get_custom_monster():anomaly_detector()
log3( "Anomaly_Detect_Radius = %s", anomaly_detector.Anomaly_Detect_Radius )
log3( "Anomaly_Detect_Time_Remember = %s", anomaly_detector.Anomaly_Detect_Time_Remember )
log3( "is_active = %s", anomaly_detector.is_active )
anomaly_detector:activate()
anomaly_detector:deactivate()
-
Добавил
Anomaly_Detect_Probability
Этот параметр определяет вероятность обхода одной аномалии. По умолчанию = 1, т.е. все аномалии обходятся, при активном anomaly_detector. -
Методам
activate()
иdeactivate()
добавил аргументforce
(по умолчанию false), которым можно заблокировать состояние активности. Т.е. если вызватьactivate( true )
, то anomaly_detector будет активен до тех пор, пока не будет вызванdeactivate( true )
и наоборот. При переходе в состояние покоя вызываетсяactivate()
иdeactivate()
при выходе из состояния покоя. Вот что бы это не отключило скриптовый обход или не обход аномалий и нужен указанный аргумент. -
Добавил
CAnomalyDetector::remove_all_restrictions()
Что бы можно было заставить его удалить все ограничения немедленно. -
Добавил
CAnomalyDetector::remove_restriction( anom_id )
Для удаления информации только об одной аномалии. - Добавил возможность задать обход аномалий, созданных активацией артефактов
Это определяется четверым параметром в секции [artefact_spawn_zones]
. Если его нет, то ничего не меняется. Пример:
; Артефакты, которые при активизации создают аномальные зоны
; формат: {секция_артефакта} = {секция_зоны}, radius, power[, eDefaultRestrictorTypeNone]
;
; eDefaultRestrictorTypeNone указывает, что эта аномалия должна обходиться
; мобами. Если этого параметра нет, или он равен 0, то эту аномалию обходить
; не будут. Если этот параметр равен 1, то аномалию обходить будут.
[artefact_spawn_zones]
caps_mosquito_bald = amk_zone_mosquito_bald_average, 3.0, 1.3, 1
caps_gravi = amk_zone_gravi_zone_average, 3.0, 1.2, 1
caps_mincer = amk_zone_mincer_average, 3.5, 1.2, 1
caps_electra = amk_zone_witches_galantine_average, 4.0, 1.2, 1
caps_zharka = amk_zone_zharka_static_average, 2.0, 1.2, 1
caps_ameba = amk_zone_buzz_average, 1.5, 0.5, 1
caps_ice = amk_zone_ice_average, 4.0, 1.0, 1
- В обход аномалий добавил настройку clsid, которые никогда обходить не нужно. Например, если добавить в конфиг моба
Anomaly_Detect_Ignore = zone_radioactive
то этот моб не будет обходить радиоактивные зоны. Несколько clsid задаются через запятую.
- Экспортировал для скриптов
CPatrolPoint
иCPatrolPath
-- Создали путь с одной точкой, совпадающей с позицией актора. Метод add_point() возвращает CPatrolPoint,
-- но особого смысла это не имеет, т.к. это копия, т.е. менять какие-то свойства в этой копии не имеет смысла,
-- т.к. это никак не отразится на пути.
local path = CPatrolPath()
local pp = path:add_point( db.actor:position() )
local pp = path:point( 0 ) -- Возвращает первую точку пути. Опять-таки, менять ее свойства бессмысленно, т.к. это копия.
local pp0 = CPatrolPoint() -- Создали точку
pp0.m_name = "name00" -- и назначили ей имя.
pp0:position( db.actor:position() )
-- Назначили ей текущие координаты актора. Метод position() автоматически вычислит m_level_vertex_id и m_game_vertex_id
-- по этим координатам. При желании можно все назначить в ручную:
pp0.m_position = db.actor:position()
pp0.m_level_vertex_id = db.actor:level_vertex_id()
pp0.m_game_vertex_id = db.actor:game_vertex_id()
pp0.flags = 0
-- На чтение эти свойства тоже доступны.
-- Вот теперь, созданную и инициализированную точку можно добавить в путь:
path:add_vertex( pp0 )
-- Путь готов к использованию.
-
Добавил
obj:set_home( CPatrolPath, ... )
, который принимаетCPatrolPath
, вместо имени пути. Остальные аргументы у него те же самые. -
Добавил метод
at_home()
, который возвращает true/false, если моб находится в пределахmax_radius
, иat_home( pos )
, который проверяет, что pos находится в пределахmax_radius
. - Добавил
level.patrol_path_add( patrol_name, CPatrolPath )
иlevel.patrol_path_remove( patrol_name )
level.patrol_path_add( patrol_name, CPatrolPath ) -- Регистрирует новый или заменяет уже существующий путь с именем patrol_name.
-- Добавленные таким образом пути нужно удалять при удалении актора, иначе будет вылет при выходе из игры или при загрузке сейва.
level.patrol_path_remove( patrol_name ) -- Удаляет путь с именем patrol_name. Нужен как раз для того, что бы удалить пути, добавленные через level.patrol_path_add().
Примеры использования:
db.test_path = CPatrolPath()
db.test_path:add_point( db.actor:position() )
local pp = db.test_path:point( 0 )
log3( "dsh: level_vertex_id = %s", pp.m_level_vertex_id )
log3( "dsh: test_path = %s", level.patrol_path_exists( "test_path" ) )
level.patrol_path_add( "test_path", db.test_path )
log3( "dsh: test_path = %s", level.patrol_path_exists( "test_path" ) )
local path = patrol( "test_path" )
log3( "dsh: level_vertex_id = %s", path:level_vertex_id( 0 ) )
level.patrol_path_remove( "test_path" )
Добавлены варианты спавна:
- При смерти существа внутри аномалии
- При разрыве тела в мясорубке (вот это в движке включено по умолчанию, даже конфиги править не надо)
Добавлены параметры для аномалий:
; это настройки онлайн спавна
spawn_blowout_artefacts = on ; главный переключатель возможности рождения
artefact_spawn_probability = 0.05 ;вероятность, что во время срабатывания аномалии будет рожден артефакт
birth_on_death_probability = 0.2 ; вероятность рождения артефакта после смерти существа в аномалии
birth_on_torn_probability = 0.5 ; вероятность рождения артефакта при разрыве тела в мясорубке
birth_on_nonalive = true ; возможность рождения при срабатывании на предмет
birth_on_alive = true ; возможность рождения при срабатывании на живое существо
birth_on_dead = true ; возможность рождения при срабатывании на труп
Взято отсюда.
-
level.add_call(...)
теперь возвращает управляющий объект, у которого есть метод:set_pause( ms )
. После использования этого метода, функция, заданная вadd_call(...)
, будет вызвана черезms
миллисекунд. Вот простейший пример:
level.add_call(
function()
log3( "condition" )
return true
end,
function() log3( "action" ) end
):set_pause( 3000 )
Через 3 секунды будет выведено:
condition
action
Вот чуть более сложный пример:
local lcall
local cnt = 10
lcall = level.add_call(
function()
log3( "condition" )
cnt = cnt - 1
lcall:set_pause( 1000 )
return cnt == 0
end,
function() end
)
После запуска будет выведено 10 раз:
condition
с частотой раз в секунду.
-
Окончательно починил
level.remove_call(...)
Теперь его можно спокойно использовать даже из обработчика, добавленного черезlevel.add_call(...)
. Обработчики этим вызовом не удаляются, а помечаются, как удаленные. Удалены они уже будут в цикле обработки, вupdate()
. - Заодно починил
level.add_call(...)
вида
level.add_call( self, self.condition, self.action )
т.е. используемые для объекта. level.remove_call(...)
тоже работает с этой формой. level.remove_calls_for_object(...)
тоже работает правильно.
Как использовать? Вот так вот:
local xvars = get_stored_vars()
xvars.test = 123
xvars.test2 = {}
xvars.test2.test1 = 456
Всё просто, использовать, как стандартную таблицу. Есть пара особенностей. Хранить лучше только простые типы: строки, числа, таблицы и т.п, в том числе вложенные. Объекты, вектора, время и т.п., то, что является userdata, лучше не использовать.
Вторая особенность - списки. Для прямого доступа к элементам списка ничего делать не нужно, но если захочется с этим списком работать, как с настоящим списком, т.к. итерировать, к примеру, или подсунуть его в unpack(...)
, то его нужно превратить в lua таблицу. Например:
xvars.test = { 1, 2, 3 }
local t = xvars.test()
for i, v in ipairs( t ) do
end
Операция выполнения, т.е. ()
, возвращает lua table, с которой уже можно делать, что угодно. По умолчанию, преобразование не рекурсивное, т.е. не затронет вложенные таблицы. Что бы сделать его рекурсивным, нужно сделать вот так:
local t = xvars.test( true )
т.е. передать аргумент.
Каждый вызов AddCallback()
добавляет элемент в m_callbacks
. Этот элемент хранит в себе ссылку на функцию и переданный self
. Вся оконная система построена таким образом, что созданное окно нигде принудительно не удаляется. Эта система вообще не рассчитана на то, что окна будут удаляться. Она спроектирована так, что окна будут создаваться один раз и потом только показываться и скрываться. Ну так вот, когда создаются скриптовые окна и в них вызываются AddCallback(...)
, то в элементах m_callbacks
хранятся ссылки на эти скриптовые окна. Так как принудительно окна никто не удаляется, то можно было бы подумать, что их должен удалить уборщик после закрытия окна. Но он этого сделать не может, так как в них, в m_callbacks
, хранятся ссылки на них же. Получаем утечку памяти. Мало того, каждое такое "потерянное" окно замедляет создание новых. Что бы всего этого избежать, нужно использовать метод ClearCallbacks()
, который почистит элементы
списка m_callbacks
и уберет оттуда ссылки на окна. После такой чистки уборщик мусора сможет удалить закрытые окна
и освободить память.
Сначала ищется имя секции, как и раньше. Если не найдено, то проверяются регулярные выражения по очереди. Первое совпавшее
используется. Регулярными выражениями считается все, что начинается на /
и заканчивается /
. Например
/^wpn_/
в секции покупки означает, что непись не будет покупать предметы, имя секции которых начинается на wpn_
. Или
/^wpn_/ = 0.5, 0.8
в той же секции означает, что предметы, имя секции которых начинается на wpn_
, будут покупать с коэффициентами 0.5, 0.8. И т.д.
- Добавил в списки торговли минимальное состояние, при котором предмет будет покупаться
Это третий параметр. Если его нет, то будет по умолчанию, т.е. 0, т.е. предмет будет покупать в любом состоянии. Например:
/^wpn_/ = 0.5, 0.8, 0.3
Все оружие будет покупаться с коэффициентом от 0.5 до 0.8, если его состояние не меньше 0.3. В противном случае он покупаться не будет. Будет закрашено красным цветом.
При отсутствии этого параметра ничего не меняется.
По сути, в CSpaceRestrictor
я добавил функциональность CScriptZone
.
Мне это нужно только для рестрикторов с логикой, которые на апдейте работают. Поэтому по умолчанию, все рестрикторы не выполняют код, который нужен для работы этих коллбеков. Тем более, что рестрикторы такая штука, довольно активно используемая в дебрях движка, что я не уверен, что включение этих коллбеков по умолчанию не сделает
хуже. Итак, что бы активировать эти коллбеки, нужно сделать следующее.
Это пример из скриптового биндера рестрикторов, в net_spawn()
:
self.object:set_callback( callback.zone_enter, self.on_enter, self )
self.object:set_callback( callback.zone_exit, self.on_exit, self )
self.object:get_space_restrictor():schedule_register()
После этого будут вызываться
function restrictor_binder:on_enter( zone, obj )
end
function restrictor_binder:on_exit( zone, obj )
end
Здесь zone - это self.object
, а obj
- это то, что попало в рестриктор или ушло из него.
Для оптимизации и опять же, с точки зрения рестрикторов с логикой, коллбеки вызываются только для живых существ. Ведь в логике, как правило, используются on_actor_inside
и on_npc_in_zone
или как оно там, в общем, проверяется попадание в рестриктор актора или мобов. Так чего лишний раз дергать коллбеки из-за какой-нибудь лампочки, которая находится в рестрикторе. Если и когда понадобится, можно сделать это настраиваемым.
Я добавил еще несколько скриптовых методов:
schedule_unregister() -- отключает код, который скажем так, сканирует периметр рестриктора. Метод обратный schedule_register().
is_scheduled() -- возвращает статус этого сканирования.
active_contact( id ) -- возвращает, если объект с переданным id находится внутри рестриктора.
Все это, повторюсь, работает только если сканирование было включено через schedule_register()
.
Пример скрипта для использования:
log1("texture_find test for name: act\\act_arm_perchatka_cs")
local t1 = texture_find("act\\act_arm_perchatka_cs") -- возвращает таблицу: ключ имя текстуры, значение объект
if t1 then
local tex = t1[ "act\\act_arm_perchatka_cs" ]
log1("found, name=" .. tex:get_name())
local name = "act\\act_arm_perchatka_cs_black"
tex:load(name) -- С этим способом замены текстура будет заменяться везде где используется.
end
Поиск текстур по маске:
log1("texture_find test for name: act\\act_arm_perchatka_cs*")
local t1 = texture_find("act\\act_arm_perchatka_cs*") -- возвращает таблицу: ключ имя текстуры, значение объект
if t1 then
for n, t in pairs(t1) do
log1("found, name=" .. t:get_name())
end
end
Для включения нужно добавить в конфиги инвентаря:
gamedata/config/ui/inventory_new.xml
gamedata/config/ui/inventory_new_16.xml
в секцию outfit_info
новые ноды:
<static_health_restore_speed x="0" y="0" width="180" height="19" complex_mode="1">
<texture>ui_inv_icon_health_restore_speed</texture>
<text font="letterica16" x="17" y="0" color="ui_clr_1"/>
</static_health_restore_speed>
<static_satiety_restore_speed x="0" y="0" width="180" height="19" complex_mode="1">
<texture>ui_inv_icon_satiety_restore_speed</texture>
<text font="letterica16" x="17" y="0" color="ui_clr_1"/>
</static_satiety_restore_speed>
<static_power_restore_speed x="0" y="0" width="180" height="19" complex_mode="1">
<texture>ui_inv_icon_power_restore_speed</texture>
<text font="letterica16" x="17" y="0" color="ui_clr_1"/>
</static_power_restore_speed>
<static_bleeding_restore_speed x="0" y="0" width="180" height="19" complex_mode="1">
<texture>ui_inv_icon_bleeding_restore_speed</texture>
<text font="letterica16" x="17" y="0" color="ui_clr_1"/>
</static_bleeding_restore_speed>
<static_radiation_restore_speed x="0" y="0" width="180" height="19" complex_mode="1">
<texture>ui_inv_icon_radiation_restore_speed</texture>
<text font="letterica16" x="17" y="0" color="ui_clr_1"/>
</static_radiation_restore_speed>
<static_psy_health_restore_speed x="0" y="0" width="180" height="19" complex_mode="1">
<texture>ui_inv_icon_telepatic_immunity</texture>
<text font="letterica16" x="17" y="0" color="ui_clr_1"/>
</static_psy_health_restore_speed>
Пример можно взять из файлов:
gamedata/config/ui/af_params.xml
gamedata/config/ui/af_params_16.xml
- Увеличение переносимого веса. Настраивается так же, как и для брони, параметрами:
additional_inventory_weight = 11
additional_inventory_weight2 = 11
Для отображения доп. веса в свойствах артефактов нужно добавить в af_params.xml
, af_params_16.xml
что-нибудь вроде:
<static_additional_inventory_weight width="257" height="20" complex_mode="1">
<texture>ui_inv_icon_additional_inventory_weight</texture>
<text font="letterica16" x="17" y="0"/>
</static_additional_inventory_weight>
<static_additional_inventory_weight2 width="257" height="20" complex_mode="1">
<texture>ui_inv_icon_additional_inventory_weight2</texture>
<text font="letterica16" x="17" y="0"/>
</static_additional_inventory_weight2>
Если additional_inventory_weight
и additional_inventory_weight2
одинаковые, то отображается только additional_inventory_weight2
. Напоминаю, что в движке перепутан смысл additional_inventory_weight
и additional_inventory_weight2
. additional_inventory_weight
- это вес, до достижения которого еще можно двигаться, а additional_inventory_weight2
- это максимальный переносимый вес, после достижения которого начинается перегруз и применяются другие коэффициенты при расчете выносливости. Т.е. additional_inventory_weight
всегда должен быть
равен или больше additional_inventory_weight2
и указывать нужно оба.
-
Сытость. Включается опцией
af_satiety
в секции[features]
. Включает обработкуsatiety_restore_speed
в секции артефакта. -
Пси-здоровье. Включается опцией
af_psy_health
в секции[features]
. Включает обработкуpsy_health_restore_speed
в секции артефакта. При включении надо не забыть добавить статик для его отображения вaf_params.xml
,af_params_16.xml
:
<static_psy_health_restore_speed width="257" height="20" complex_mode="1">
<texture>ui_inv_icon_telepatic_immunity</texture>
<text font="letterica16" x="17" y="0"/>
</static_psy_health_restore_speed>
-
Опция
objects_radioactive
в секции[features]
включает инвентарную радиоактивность и пси. Т.е. все предметы в инвентаре, у которых положительныйradiation_restore_speed
или отрицательныйpsy_health_restore_speed
(если включенaf_psy_health
), увеличивают радиацию или уменьшают пси-здоровье. -
Опция
af_zero_condition
в секции[features]
ограничивает обработку артефактов так, что обрабатываются только артефакты, у которых состояние больше нуля. Полностью испорченные артефакты игнорируются. -
Опция
af_radiation_immunity_mod
в секции[features]
включает режим, в котором артефакты сradiation_immunity
>1
будут соответствующим образом усиливать радиацию от других артефактов. Т.е. красные 20% усилят радиацию других артефактов на 20%. -
Добавлен каллбек для актора
update_artefacts_on_belt
, который вызывается сразу после обработки артефактов на поясе и ему передается кол-во секунд, которое использовалось при учете артефактов. -
Добавлен метод
get_actor_obj():UpdateArtefactsOnBelt()
, который можно вызывать из цикла сна, что бы учесть влияние артефактов на поясе. Этот же вызов приведет в вызову вышеописанного коллбека.
- Force reload режим, будет вызывать
init_func
при каждом открытии окна диалога. Позволяет перестраивать граф целиком. Работает только если cinit_func
(и соответственно нетуphrase_list
) в xml:
XML: force_reload="1"
LUA: dlg.SetForceReload(true)
- Для фраз установка текста через скрипты:
XML: <script_text>dialog_manager.create_bye_phrase</script_text>
LUA: phrase_script:SetScriptText("travel_manager.squad_action_description")
Параметры такие же как для action\predicate
.
- Новые скриптовые методы для построения диалогов через
init_func
:
dlg.SetPriority(123) -- изменить приоритет диалога
dlg.SetCaption("test") -- изменить заголовок
dlg.SetForceReload(false) -- изменить флаг force_reload (перестать обновлять диалог после какого то уловия)
dlg.GetPhrase("PhraseId") -- получить фразу по id
dlg.GetPhraseScript() -- доступ к CPhraseScript самого диалога. (работают только методы AddPrecondition/AddHasInfo/AddDontHasInfo)
phrase.GetText() -- получить текст фразы
phrase.SetText(str) -- изменить текст фразы
- Поддержка передачи аргументов в функции, вызываемые из диалогов, например:
<precondition>my_script.test_func(123, true, nil, ...)</precondition>
<action>my_script.test_func(123, true, nil, ...)</action>
Если указать в конце переменное кол-во аргументов ...
, то туда будут переданы стандартные аргументы, типа pSpeakerGO1->lua_game_object(), pSpeakerGO2->lua_game_object(), dialog_id, phrase_id
.
Выданные задания сохраняются в сейве и сохраняются полностью: текст, поршни, функции и т.д. Поэтому, изменение соотв. <game_task/>
на уже выданное задание не повлияет. Для того, что бы повлияло, я добавил два аттрибута. Это version
и objectives_version
. По умолчанию оба равны 0. При увеличении version
, например
<game_task id="shax_plennik" prio="0" version="1">
</game_task>
параметры задания из сейва будут проигнорированы, за исключением времени выдачи и т.п, а так же состояний <objective/>
. Т.е. те подзадания, которые уже были выполнены, так и остануться выполненными. Если дополнительно к version
увеличить objectives_version
, например
<game_task id="shax_plennik" prio="0" version="1" objectives_version="1">
</game_task>
то сохраненное состояние подзаданий использоваться не будет. Такие имеет смысл делать в том случае, если в конфиге меняется кол-во подзаданий или их смысл.
Параметры точно такие же как и у CUIEditBox
(xml и lua). Но можно указывать высоту больше одной строки, тогда текст будет автоматически переноситься на новую строчку.
Пример: https://www.youtube.com/watch?v=HNksw-86L_8
Для создания CUIEditBoxEx
подходит точно такой же код как и для обычного CUIEditBox
. Но текстура для подложки должна быть в формате для диалогов или рамок. То есть, должно быть 8 частей для рамки + сама подложка. Пример текстуры из ванильной игры: ui_stroketextbox
.
Создание:
Через lua:
local edit_box1 = CUIEditBoxEx()
edit_box1:SetAutoDelete(true)
edit_box1:Init(120, 200, 43, 90) -- 90 - высота, желательно что то кратное высоте шрифта
edit_box1:InitTexture("ui_stroketextbox")
edit_box1:SetFont(GetFontLetterica18Russian())
self:AttachChild(edit_box1)
Через xml:
<edit_box x="120" y="200" width="43" height="90">
<texture>ui_stroketextbox</texture>
<text font="letterica18" a="255" r="200" g="255" b="200"/>
</edit_box>
lua часть:
local edit_box = xml:InitEditBoxEx("main_dialog:edit_box", dialog)
Для демонстрации много строчный ввод текста добавлен в окно пользовательских меток в ПДА.
По сути жажда - это ещё один параметр и ресурс для ГГ на ровне с голодом. Примерный план адаптации для мода:
- Включить фичу в главном конфиге движка:
[features]
actor_thirst = true
- Добавить в
actor.ltx
:
[actor_condition]
thirst_v = 0.01 ;скорость увеличания жажды
thirst_power_v = 0.2 ;скорость потери сил когда уровень жажды ниже чем thirst_light_limit
thirst_health_v = 0.01 ;скорость восстановления или потери здоровья в зависимости от жажды
thirst_light_limit = 0.5
thirst_critical_limit = 0.3
- Добавить в
maingame.xml
(иmaingame_16.xml
):
<thirst_static x="000" y="000" width="0" height="0" stretch="1">
<texture></texture>
</thirst_static>
- Добавить в
system.ltx
:
[main_ingame_indicators_thresholds]
thirst = 0.5,0.6,0.7,0.8,0.9
[tutorial_conditions_thresholds]
thirst = 0.6
- Добавить в
_g.script
:
function on_actor_thirst() --Туториал на жажду
end
- Так же, добавить в конфиг одному или нескольким съедаемым предметам:
eat_thirst = 1 ;значение утоления жажды
- Для артефактов и костюмов так же добавлена обработка
thirst_restore_speed
.
Включать/выключать жажду можно в любой момент. Сохранения будут совместимые. При отключении просто не будет обрабатываться действие на ГГ и индикаторы на худе, но все скриптовые методы и параметры предметов будут доступны.
Простой и средний детекторы полностью работают как в ЗП, для элитного детектора работает ЗП-стайл вывод UI на экран с настройками в ui_detector_artefact.xml
.
Так же оставлена совместимость с OGSE-стайл системой когда экрану элитного детектора назначается шейдер models_detector_3
, и показом артефактов/аномалий/квестовых штук управляют скрипты. Лимит показываемых объектов там повышен с 8 до 24.
Старый класс детекторов без худа переименован в CCustomDetectorSHOC
, они будут работать как раньше без изменений.
Добавлена возможность детектировать все артефакты добавив в конфиг детектора:
af_class_all = true
af_sound_all_ = detectors\art_beep1
af_freq_all = 0.05, 2
Добавил для скриптов проверку get_actor_obj():IsDetectorActive()
- так можно проверять, находится ли детектор в руках актора в данный момент.
Артефактам в конфиг добавились параметры af_rank
, can_be_controlled
, det_show_particles
, det_hide_particles
, particles_bone
, det_show_snd
, det_hide_snd
из ЗП. Я так понимаю для того чтобы можно было делать арты невидимыми (не тестировалось)
Детектор можно держать в руках месте с болтом, ножом или предметом которому прописан первый слот (пистолетный).
- Добавлены инструменты для настройки UI в пикселах на мониторе конечного юзера, а не в координатной системе 1024х768. Это нужно что бы можно было избежать растянутого UI на широких мониторах, например ( #378 ).
При включении атрибута
as_is="1"
в конфиге, GUI элементы будут читать координаты в пикселах конечного монитора, а не в сетке 1024х768 как было в ванили. Поэтому размеры и координаты в GUI-конфигах придется править, чтобы выглядело нормально и влезало в экран. Скорее всего придется поэкспериментировать. Но зато когда правкой конфигов удастся получить хороший результат, этот результат будет таким же хорошим для всех мониторов. - Для drag-drop элементов так же добавлен атрибут
as_is
, чтобы иконки инвентаря сохраняли правильные пропорции при любом отношении сторон экрана. - Так же для drag-drop элементов добавлен атрибут
autocalc
(0 or 1) управляющий автокалькуляцией размера и количества ячеек. Иконки инвентаря авто-масштабируются в сторону уменьшения, если это необходимо для того чтобы в драгдроп-лист вместилось то число ячеек, что указано в его конфиге. Драгдроп-лист до-заполняется дополнительными ячейками в ширину или высоту, до полного заполнения, если после применения вышеописанных манипуляций в нем осталось свободное место. Размер ячеек регулируется новым атрибутомcell_size
(по умолчанию 50), старые атрибутыcell_width
,cell_height
не используются.
- Добавлено скриптовое управление
demo_record
:
level.demo_record_start()
level.demo_record_stop()
local HPB = level.demo_record_get_HPB()
level.demo_record_set_HPB(HPB)
-- направление
-- HPB.x Left\Right
-- HPB.y Up\Down
-- HPB.z Rotate Left\Rotate Right
local position = level.demo_record_get_position()
level.demo_record_set_position(position)
-- position x y z как обычно на уровне
level.demo_record_set_direct_input(true/false) -- управление прямым вводом
Так же поправлено управление игрой в режиме demo_record
. Включается на *
(звездочку DIK_MULTIPLY
)
- Эффекты ppe теперь должны отображаться в режиме от 3 лица. Так же добавлена команда
g_effects_on_demorecord
что б эффекты были и в режимеdemo_record
.
;Холодец
bend_grass_blowout = true
bend_grass_blowout_speed = 3.0
bend_grass_blowout_radius = 3.0
;----------------------------------
;Воронка
bend_grass_idle_anim = 3
bend_grass_idle_radius = 4.0
bend_grass_idle_str = 2.0
bend_grass_idle_speed = 1.5
bend_grass_whenactive_str = 3.0
bend_grass_whenactive_speed = 3.0
bend_grass_blowout = true
bend_grass_blowout_duration = 2500
bend_grass_blowout_speed = 6.0
bend_grass_blowout_radius = 20.0
;----------------------------------
;Карусель
bend_grass_idle_anim = 2
bend_grass_idle_radius = 4.0
bend_grass_idle_speed = 1.3
bend_grass_whenactive_anim = 3
bend_grass_whenactive_str = 3.5
bend_grass_whenactive_speed = 4.5
bend_grass_blowout = true
bend_grass_blowout_duration = 5500
bend_grass_blowout_speed = 5.0
bend_grass_blowout_radius = 20.0
;----------------------------------
;Трамплин
bend_grass_idle_anim = 3
bend_grass_idle_radius = 4.0
bend_grass_idle_str = 2.0
bend_grass_idle_speed = 1.5
bend_grass_whenactive_str = 3.0
bend_grass_whenactive_speed = 3.0
bend_grass_blowout = true
bend_grass_blowout_duration = 2500
bend_grass_blowout_speed = 6.0
bend_grass_blowout_radius = 20.0
;----------------------------------
;Электра
bend_grass_blowout = true
bend_grass_blowout_speed = 4.0
bend_grass_blowout_radius = 5.0
;----------------------------------
;Жарка
bend_grass_idle_radius = 3.5
bend_grass_whenactive_anim = 4
bend_grass_whenactive_str = 2.0
bend_grass_whenactive_speed = 3.5
bend_grass_blowout = true
bend_grass_blowout_duration = 2500
bend_grass_blowout_speed = 3.0
bend_grass_blowout_radius = 5.0
Большая часть методов, перенесённых из X-Ray Engine by Alpet & KD, здесь не задокументирована. Смысла в переносе описания сотен скриптовых методов оттуда на самом деле нет никакого, т.к. настолько гигантский readme всё равно никто читать не будет. Щас все всё в исходниках смотрят, в основном.
--Флажок у мобов для включения невидимости для аномалий
obj:get_custom_monster().visible_for_zones = false --аномалии не будут будут срабатывать, если этот моб попадет в них.
-- Возвращает кол-во патронов в скрытой части. Т.е. если оружие сейчас в режиме подствольника, то информация о патронах
-- находится в скрытой части и наоборот. Этот метод возвращает кол-во патронов, если оружие в режиме подствольника. В этом
-- случае уже существующий :get_ammo_in_magazine() возвращает кол-во патронов в подствольнике.
obj:get_ammo_in_magazine2()
obj:get_hud_item_state() -- Возвращает текущее состояние худа предмета. Можно использовать соотв. global_flags:
weapon_states = {
idle = global_flags.eIdle,
fire = global_flags.eFire,
fire2 = global_flags.eFire2,
reload = global_flags.eReload,
showing = global_flags.eShowing,
hiding = global_flags.eHiding,
hidden = global_flags.eHidden,
misfire = global_flags.eMisfire,
mag_empty = global_flags.eMagEmpty,
switch = global_flags.eSwitch,
}
obj:radius() -- Возвращает движковый радиус объекта.
-- Это упрощенный аналог из x-ray extensions, без коллбэков и блэк-джека. Я думал, что можно использовать play_cycle(), но
-- нет, play_cycle() запускает анимацию модели, а мне нужно было запустить худовую анимацию.
obj:play_hud_animation( anim_name, bool_mix_in )
sobj.m_level_vertex_id , sobj.m_game_vertex_id -- Теперь доступны для записи.
sobj.m_flags -- Содержит Flags32 с серверным флагами. Это которые UsedAI_Locations и т.п.
sobj.level_id -- id локации этого объекта
sobj.level_name -- Имя локации этого объекта
sobj.is_alive -- true - это живой моб, false - не живой или вовсе не моб.
sobj.set_position( pos ) -- Устанавливает новую позицию объекта.
sobj.cse_get_restrictor_type() -- Для рестрикторов возвращает его тип (u8)
for id, sobj in alife():objects() do -- Перебор всех серверных объектов в игре
-- !!!Ни в коем случае нельзя удалять/спавнить объекты внутри цикла!!!
end
alife():remove_in_restrictions( sobj ) -- Для очистки in-рестрикторов (добавленных для обхода аномалий, например)
alife():remove_out_restrictions( sobj ) -- Для очистки out-рестрикторов
-- Перерисовывает инвентарь или окно обыска. Имеет смысл использовать, если при открытом
-- инвентаре изменилось состояние какого-нибудь предмета, например condition, что должно привести к перегруппировке таких
-- предметов. Если не выполнить update_inventory_window(), что изменения будут видны только при следующем открытии инвентаря.
update_inventory_window()
-- Экспортирован класс главного игрового окна с методом GetStatic. Можно например скрыть миникарту:
local wnd = get_main_window()
local st = wnd:GetStatic( "minimap:background" )
if st then
local r = Frect()
st:GetWndRect( r )
log3("~~ [minimap:background] rect = { %.1f, %.1f, %.1f, %.1f }", r.x1, r.y1, r.x2, r.y2 )
r.x1 = -1000
st:SetWndRect( r )
end
alife().loaded_save_name -- имя загруженного сейва.
alife().save_name -- имя последнего сделанного сейва.
local obj = db.actor:object( "wpn_addon_scope" )
-- К оружию в руках присоединит ПСО-1, который должен быть в инвентаре. Второй аргумент показывает, нужно аддон удалять из инвентаря или нет.
db.actor:active_item():get_weapon_m():attach_addon( obj:get_inventory_item(), false )
-- Обратная ситуация. Отсоединит ПСО-1 с активного оружия. Второй аргумент показывает, нужно спаунить прицел в инвентарь или нет.
db.actor:active_item():get_weapon_m():detach_addon( "wpn_addon_scope", false )
db.actor.conditions:fdelta_time() -- Возвращает кол-во игровых секунд (float), прошедших с последнего апдейта. Именно это время
-- используется при расчете работы артефактов на поясе и при расчете изменения параметров актора: здоровье, сытость и т.п.
db.actor.conditions.has_valid_time -- bool Показывает, что fdelta_time() вернет правильное время. Перед использованием
-- fdelta_time() всегда нужно это проверять. Дело в том, что скриптовый апдейт вызывается перед обновлениями conditions.
-- Из-за этого, на самом первом скриптовом апдейте fdelta_time() вернет мусор, в моем случае - это было очень большое число.
sobj:get_inventory_item().item_condition -- Доступ к состоянию предмета через серверный объект.
CUIWindow:GetCUIStatic() -- Новый метод для получения CUIStatic из CUIWindow
level.get_change_level_wnd() -- Возвращает окно подтверждения перехода. Оно создается один раз и переиспользуется.
level.change_level( game_vertex_id, level_vertex_id, pos, dir ) -- Выполняет переход на другую локацию. Тоже самое, как создать на месте актора level_changer, только без лишних телодвижений.
-- Добавил sobj:get_space_restrictor() для получения доступа к CSE_ALifeSpaceRestrictor
-- На текущий момент можно:
local sr = sobj:get_space_restrictor()
log3( "m_space_restrictor_type = %d", sr.restrictor_type )
-- На запись тоже доступно. Что бы не лезть к этому свойству через нетпакет.
-- Пригодиться, если какой-нибудь аномалии нужно будет поменять тип рестриктора.
-- Например для того, что бы CAnomalyDetector игнорировал ее.
-- Экспортировал прямой доступ к CSE_ALifeItemWeapon:
local s_wpn = sobj:get_weapon()
-- Доступны на чтение/запись:
s_wpn.ammo_current
s_wpn.ammo_elapsed
s_wpn.weapon_state
s_wpn.addon_flags
s_wpn.ammo_type
sobj.custom_data -- Прямой доступ к custom_data
local trader = sobj:get_trader() -- Прямой доступ к свойствам торговца в неписе
log3( "money = %d", trader.money ) -- на чтение/запись
log3( "character_name = %s", trader.character_name ) -- только чтение
-- Добавил возможность прямого изменения custom_data через spawn_ini()
-- Вот пример использования:
local ini = sobj:spawn_ini()
ini.readonly = false
ini:w_string( "test", "dsh_test", "123" )
sobj:save_spawn_ini()
log3( "custom_data = %s", sobj.custom_data )
-- Перед изменением нужно обязательно сбросить флажок "readonly", иначе сработает ASSERT(),
-- т.к. изменение по умолчанию запрещено. Далее можно использовать методы remove_line, w_bool, w_string, w_u32, w_s32,
-- w_float, w_vector.
-- Ну а в конце нужно сохранить изменения обратно в custom_data. Это делает sobj:save_spawn_ini().
ini:get_as_string() -- Возвращает ini в виде строки
ini:remove_section( "section_name" ) -- Удаляет секцию
-- Добавлен итератор секций для CScriptIniFile. Пример использования:
system_ini():iterate_sections(function(sect)
local cls = system_ini():r_clsid(sect, "class")
if cls and cls == clsid.equ_stalker_s then
...
end
end)
level.set_cam_inert( float ) -- для прямого изменения cam_inert
-- Это в дополнение к уже имеющемуся sobj:brain():can_choose_alife_tasks( bool )
sobj:brain():can_choose_alife_tasks() -- bool для получения состояния
sobj:brain().m_time_interval -- u32 Как часто свободного моба предлагать смартам
sobj:brain().m_last_search_time -- u32 Когда это делалось в последний раз.
obj:is_relation_enemy( obj2 ) -- Это аналог проверки obj.relation( obj2 ) == game_object.enemy or obj.relation( obj2 ) == game_object.worst_enemy
-- В скриптах worst_enemy нигде не проверяется. Правда не факт, что кто-то используется -2 в [monster_relations].
level.set_monster_relation( from, to, rel ) -- Эта функция позволяет менять отношения мутантов,
-- т.е. то, что описано в секции [monster_relations] в game_relations.ltx. Пример использования:
level.set_monster_relation( "rat", "actor", 0 ) -- сделать крыс нейтральными к актору
level.set_monster_relation( "rat", "actor", -1 ) -- сделать крыс врагами для актора
-----------------------------------------------------------------------------
npc:ambush_cover( npc_pos, enemy_pos, radius, min_distance ) -- Должен найти вертекс в радиусе radius от npc_pos, который расположен на расстоянии не менее min_distance и максимально прикрыт со стороны enemy_pos и минимально со стороны npc_pos.
npc:angle_cover( pos, radius, enemy_pos, min_enemy_distance, max_enemy_distance, enemy_vertex_id )
-- Ко всем методам npc:*_cover(...) добавлена возможность скриптовой фильтрации. Пример использования:
local cover_cache = {}
local cover_callback = function( cover )
local cover_vertex = cover:level_vertex_id()
local cached = cover_cache[ cover_vertex ]
if cached ~= nil then return cached end
cover_cache[ cover_vertex ] = not (
check_vertex_locked( npc, cover_vertex )
or its_a_trap( npc, cover_vertex )
)
return cover_cache[ cover_vertex ]
end
local cover = npc:angle_cover( npc_pos, r, enemy_pos, min, max, enemy:level_vertex_id(), cover_callback )
-- Т.е. последним параметром можно передать функцию, которая в параметрах
-- принимает cover_point и возвращает true/false, указывающее движку, что
-- этот ковер можно или нельзя использовать в данном случае.
-----------------------------------------------------------------------------
update_inventory_weight() -- Обновляет только статик веса в инвентаре.
-- Если использовать update_inventory_window(), то это приводит не только к обновлению статика веса,
-- но и к прыжкам иконок предметов, если там свободное место образовалось.
-- Например, если мы что-то поместили на пояс или убрали с пояса.
callback.zone_enter, callback.zone_exit -- Каллбеки для аномалий на вход объекта в зону и выход.
-- В аргументах передаются клиентские объекты самой аномалии и объекта, который вошел или вышел.
relation_registry.clear_personal_relations( id ) -- Для очистки реестра отношений моба с этим id.
level.valid_vertex_id( u32 ) -- Проверяет переданный level_vertex_id на валидность и возвращает соотв. bool.
level.is_accessible_vertex_id( u32 ) -- Проверяет не только то, что вертекс существует, как level.valid_vertex_id( u32 ), но и что он не заблокирован.
level.vertex_count() -- u32 Возвращает кол-во вертексов на текущей локации.
level.disable_vertex( u32 ) -- Для блокирования данного level_vertex-а. Т.е. мобы будут его обходить, как если бы там был рестриктор, запрещающий в него заходить.
level.enable_vertex( u32 ) -- Обратный метод
cross_table():vertex( u32 ):game_vertex_id() -- Для переданного level_vertex_id возвращает соответствующий ему game_vertex_id(). Конечно же это работает только для текущей локации.
-- Для полного переименования героя нужно сделать:
db.actor:get_inventory_owner():SetName( "Стрелок" )
local sobj = db.actor:get_alife_object()
local trader = sobj:get_trader()
trader.character_name = "Стрелок"
-- В некоторых местах движка используется имя из InventoryOwner, а в других - из CSE_AlifeTraderAbstract.
sobj:get_weapon_gl().ammo_type_2 -- Получение/изменение типа скрытых зарядов
sobj:get_weapon_gl().ammo_elapsed_2 -- Получение/изменение кол-ва скрытых зарядов
--По аналогии с iterate_inventory добавлены методы:
obj:iterate_belt(func, obj) -- Перебор предметов на поясе
obj:iterate_ruck(func, obj) -- Перебор предметов в рюкзаке
CUIWindow:GetPosLeft() -- возвращает m_wndPos.x из CUIWindow
CUIWindow:GetPosTop() -- возвращает m_wndPos.y из CUIWindow
CUIWindow:DetachAll() -- Удаляет все приаттаченые окна к окну CUIWindow.
CUIStatic:EnableHeading() -- Позволяет сохранять оригинальное соотношение сторон статика на любых разрешениях экрана.
obj:get_inventory_item().always_ungroupable = true -- Задать предмету безусловную негруппируемость в инвентаре
obj:get_physics_shell():set_dynamic_ignore() -- Делать это нужно не раньше первого адейта этого объекта. Физ. объект станет проницаемым для мобов, как трупики при отключенной коллизии с ними.
obj:get_physics_shell():set_ignore_static() -- Включает для этого объекта игнорирование статики.
-----------------------------------------------------------------------------
--Добавил возможность использовать движковый Feel::Touch с любым игровым объектом из скриптов
class "feel_touch"
function feel_touch:__init( obj )
self.object = obj
-- активирует движковый Feel::Touch для этого объекта. Первый аргумент - радиус, в котором обнаруживать появление или
-- исчезновение объектов. Второй аргумент - то, что будет передано первым аргументом в последующие функции.
-- Следующие два аргумента - это указатели на функции ниже.
self.object:add_feel_touch( 5, self, self.feel_touch_new_delete, self.feel_touch_contact )
end
-- Функция будет вызвана при появлении в радиусе нового объекта или его исчезновении.
-- Будут переданы клиентский объект и флаг, показывающий, что объект появился (true) или исчез (false).
function feel_touch:feel_touch_new_delete( obj, bool_is_new )
log3( "%s: %s %s", self.object:name(), obj:name(), ( is_new and "detected" or "gone" ) )
end
-- Функция вызывается для каждого нового объекта и результат определяет, отслеживать этот объект или нет.
-- Если эту функцию не указать, то обрабатываться будут любые мобы.
function feel_touch:feel_touch_contact( obj )
return obj:is_actor()
end
function feel_touch:shutdown()
-- Деактивирует движковый Feel::Touch для этого объекта. Вызывать обязательно тогда, когда он уже не нужен.
-- Аргументы такие же, как и у add_feel_touch(), за исключением радиуса.
self.object:remove_feel_touch( self, self.feel_touch_new_delete, self.feel_touch_contact )
end
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Эти методы заставляют непися захватить объект:
npc:ph_capture_object( obj )
npc:ph_capture_object( obj, capture_bone )
npc:ph_capture_object( obj, bone )
npc:ph_capture_object( obj, bone, capture_bone )
--[=[
Здесь:
capture_bone - имя кости, которой непись будет держать obj. Если не указано, то берется из custom_data визуала этого непися. Обычно там bip01_tail.
bone - id кости obj, которую непись будет держать. Если не указано, то будет использоваться кость, ближайшая к capture_bone.
--]=]
npc:ph_release_object() -- отпускает объект.
local cap = npc:ph_capture() -- возвращает экземпляр CPHCapture, т.е. можно проверить, если захваченные объект или нет.
-- У cap будут доступные для чтения/записи свойства "capture_force", "distance", "pull_distance" и "time_limit". Эти свойства тоже самое, что указано в custom_data визуала, в секции [capture].
cap.pull_force -- соответствует настройке из custom_data визуала, т.е. это сила, с которой предмет притягивается при захвате.
cap.hard_mode -- запрещается ли выпадение предмета при его таскании. Для актора включено по умолчанию, для всех остальных выключено.
cap.e_state -- Показывает текущий режим захвата. Принимает следующие значения:
ph_capture.pulling
ph_capture.captured
ph_capture.released
-----------------------------------------------------------------------------
obj:disable_anomaly( bool_keep_update ) -- теперь принимает опциональный аргумент, который указывает, что нужно продолжать вызывать апдейт при отключенной аномалии.
sobj.angle, sobj.position --Свойства доступны для чтения и записи. Можно напрямую менять direction и position объекта, без извращений с нет-пакетами.
level.get_current_ray_query() -- Получить текущий худовый рейтрейс. Может использоваться для получения подробной информации об объекте/статике, на которую смотрит актер.
level.set_blender_mode_main(mode) -- Устанавливает режим рендеринга изображения в 3D прицеле, где mode должно быть:
0 -- Обычный режим
1 -- Режим ПНВ
2 -- Режим тепловизора (в новых рендерах не работает, скорее всего в будущем сделаю для него ещё один режим ПНВ, другой)
obj:get_weapon().scope_lense_fov_factor -- для скриптовой настройки увеличения в 3D прицелах.
obj:get_weapon():second_vp_enabled() -- проверка что 3Д прицел включен и доступен для активации
-----------------------------------------------------------------------------
level.iterate_vertices_inside( P, R, partially_inside, func ) -- Вызывает func( lvid ) для каждого вертекса, который находится внутри радиуса R от позиции P. parṫtially_inside указывает, что вертексы могут быть частично внутри радиуса. Если false, то будут взяты только те вертексы, который целиком находятся внутри.
--Пример:
local vertexes = {}
level.iterate_vertices_inside( around_pos, radius, false, function( lvid ) table.insert( vertexes, lvid ) end )
--В данном случае, в списке vertexes будут вертексы, которые целиком находятся внутри radius позиции around_pos.
level.iterate_vertices_border( P, R, func ) -- Делает тоже самое, только ищет вертексы, которые расположены по периметру радиуса. Т.е. те, которые расположены внутри частично и при это не полностью.
-----------------------------------------------------------------------------
local icon_p = CIconParams( section )
icon_p:set_shader( b ) -- Устанавливает статику b правильную текстуру для отображения иконки предмета с секцией section. Если в этой секции будет icon_name, использует её, если не будет - возьмет с соотв. полотна.
--Помимо этого, доступны для чтения следующие свойства:
icon_p.icon_group
icon_p.grid_width
icon_p.grid_height
icon_p.grid_x
icon_p.grid_y
icon_p.icon_name
--Пример:
self.item_icon = CUIStatic() --Создали статик для иконки предмета
self.item_icon:SetAutoDelete( true )
CIconParams( ui_item.data.section ):set_shader( self.item_icon ) --Прочитали иконку из секции предмета и поместили её на статик
self:AttachChild( self.item_icon ) --Что-то делаем дальше с этим статиком...
obj:explode_initiator( id ) -- Взрывает взрывчатку с указанием виновника. id - это id виновника, от имени кого взрыв будет наносить хиты.
-----------------------------------------------------------------------------
-- Вот таким образом можно заставить НПС бросить гранату:
local obj = npc:object( "grenade_f1" )
if npc:throw_target( db.actor:position(), db.actor ) then
npc:set_item( object.fire1, obj )
end
--[=[
throw_target() -- возвращает true/false, которое показывает результат проверки, т.е. долетит граната до позиции или столкнется с препятствием раньше. Метод может иметь следующий вид:
thow_target( pos, obj )
throw_target( pos, level_vertex_id, obj )
pos - позиция куда бросать гранату
obj - враг, который не будет считаться препятствием при проверке траектории
level_vertex_id - смещать позицию на случайную величину от двух до шести метров относительно этого вертекса
--]=]
-----------------------------------------------------------------------------
obj:add_wounds( hit_power, hit_type, u16_element ) -- Открыть кровотечение у моба
obj:unload_magazine(true, true) -- первый аргумент - заспавнить ли патроны владельцу, второй аргумент - разрядить ли ПГ, если он есть.
npc:calculate_item_price(item, bool_buying) -- Позволяет получить для торговца цену покупки или продажи предмета у актора.
-- item - клиентский объект предмета
-- bool_buying - флаг покупаем или продаем
obj:controller_psy_hit_active() -- bool Проверка возвращает, производится сейчас атака контроллера или нет. Это когда он к себе притягивает. obj - клиентский объект контроллера.
-- Добавил скриптовый метод drop_item_and_throw(), который выбрасывает предмет из инвентаря в указанном наравлении. Пример использования:
local dir = vector_rotate_y( db.actor:direction(), 180 )
if ( dir.y < 0 ) then dir.y = -dir.y end
dir:normalize():mul( 2 )
db.actor:drop_item_and_throw( obj, dir )
-----------------------------------------------------------------------------
-- Опциональный параметр disp_base в секции сталкера означает его начальную дисперсию при стрельбе, к которой уже прибавляются остальные disp_* дисперсии. По умолчанию равно нулю, т.е. не оказывает никакого влияния. Можно менять находу скриптовыми методами:
local disp_base = npc:stalker_disp_base() -- Возвращает текущее значение.
npc:stalker_disp_base( 0.1 ) -- Устанавливает текущее значение.
npc:stalker_disp_base( range, maxr ) -- Расчитывает и устанавливает текущее значение. range - расстояние до цели, maxr - максимальное расстояние от точки прицеливания, куда может промазать пуля. Т.е. дисперсия устанаваливается такой, что бы на этом расстоянии можно было промазать не более, чем на maxr.
-----------------------------------------------------------------------------
-- Добавлен экспорт clsid_table (таблица) в глобальное пространство. Пример использования:
for name, clsid in pairs(_G.clsid_table) do
log1("--Name: ["..name.."] clsid: ["..clsid.."]")
end
-----------------------------------------------------------------------------
-- Добавил экспорт для движкового psHUD_Flags - level.get_hud_flags() и для HUD().GetUI()->GameIndicatorsShown() - level.game_indicators_shown().
-- Константы для добавления в _g.script (пока не делал экспорт):
HUD_CROSSHAIR = 1 * 2 ^ 0
HUD_CROSSHAIR_DIST = 1 * 2 ^ 1
HUD_WEAPON = 1 * 2 ^ 2
HUD_INFO = 1 * 2 ^ 3
HUD_DRAW = 1 * 2 ^ 4
HUD_CROSSHAIR_RT = 1 * 2 ^ 5
HUD_WEAPON_RT = 1 * 2 ^ 6
HUD_CROSSHAIR_DYNAMIC = 1 * 2 ^ 7
HUD_CROSSHAIR_RT2 = 1 * 2 ^ 9
HUD_DRAW_RT = 1 * 2 ^ 10
HUD_CROSSHAIR_BUILD = 1 * 2 ^ 11
-- Нормальный (копия движкового) способ проверить что игра показывает худ:
function hud_present()
return level.game_indicators_shown() and level.get_hud_flags():is(bit_or(HUD_DRAW, HUD_DRAW_RT))
end
-----------------------------------------------------------------------------
-- Проверки могут пригодиться для того, что бы в net_destroy\on_unregister можно было проверить, объект ли это удаляется или же это уровень выгружается:
alife():is_unloading() -- bool когда выгружаются серверные объекты
level.is_removing_objects() -- bool когда выгружаются клиенские объекты
npc:see_right_now( obj ) -- bool Аналогично npc:see( obj ), что проверяет, виден этот объект прямо сейчас или нет. see() проверяет наличие obj в визуальной памяти.
-- Кастомные статики теперь можно просто скрывать и показывать обратно:
hud():GetCustomStatic( "xxx" ):wnd():Show( false ) -- скрыть
-- ну и true, когда понадобится показать. Т.е. все необходимое можно AddCustomStatic() один раз, а потом, по мере необходимости, скрывать и показывать, вместо того, что бы удалять и добавлять обратно.
level.nearest_vertex_id( Fvector ) -- Возвращает ближайший вертекс к этой позиции. Использует тупой перебор всех вертексов.
-- Каллбек для актора `trade_perform_operation`:
self.object:set_callback( callback.trade_perform_operation, self.trade_perform_operation, self )
-- ...
function actor_binder:trade_perform_operation( trader, money_out, money_in )
-- trader - это с кем торговал актор
-- money_out и money_in - это сколько актор отдал торговцу денег и сколько получил.
end
level.get_character_community_team( "comm" ) -- возвращает team, соответствующий указанной группировке. Этот team можно использовать в obj:change_team(), например.
npc:can_fire_to_enemy( enemy ) -- вернет true или false в зависимости от того, можно в данный момент стрелять в enemy из текущего оружия или нет. Если можно, то можно вызывать object.fire1. Если нет - только object.aim1.
-- Стрелять можно если:
-- Непись видит врага или враг находится на линии огня
-- Если голова непися повернута в направлении врага
-- Если на линии огна находится препятствие, то оно не должно быть ближе 2.5 метра от непися и должно быть ближе к enemy, чем к неписю.
obj:set_money( money ) -- метод для установки денег неписям и актору в том числе.
obj:register_in_combat() / obj:unregister_in_combat() -- combat_planner делается это при входе в бой и выходе из него.
obj:is_exploded() --Возвращает true, если взрывчатка уже взорвалась, но клиентский объект пока еще существует.
obj:is_ready_to_explode() --Возвращает true, когда начинает взрываться взрывчатка.
npc:remove_memory_object( obj ) --Метод для удаления объекта obj из памяти npc.
-- Экспортирован эффектор раскачки в скрипты:
local eff = level.get_effector_bobbing()
-- Доступны следующие свойства:
eff.run_amplitude
eff.walk_amplitude
eff.limp_amplitude
eff.run_speed
eff.walk_speed
eff.limp_speed
-- Методы для запрещения/разрешения вызывать консольные команды. На команды, которые вызываются из скриптов, через get_console():execute(...) данный запрет не действует.
get_console():disable_command("name")
get_console():enable_command("name")
alife():spawn_id( "имя" ) --Возвращает `spawn_id` из `all.spawn` по имени объекта. При пересборке оллспавна spawn_id же постоянно меняются, и их приходится после каждой пересборки в скриптах менять. А так ничего менять не нужно будет.
db.actor.inventory:is_active_slot_blocked() --bool Проверка на блокирование слота ( например на лестнице и тп )
db.actor:active_item():get_weapon():UseScopeTexture() -- показывает, использует ли оружие текстурный прицел в данный момент.
--В скриптовый каллбек `article_info` передается ещё один аргумент - текст артикла.
self:AddCallback(
"control_"..opt,
ui_events.TRACKBAR_CHANGED, --Новый каллбек на изменение позиции тракбара.
function()
--log('TRACKBAR_CHANGED(%s) : %3.3f', opt, ctl:GetTrackValue()) --Новый метод для получения позиции тракбара
end
)
-- Добавлен метод `PlaySound("filename")` в класс меню.
snd_obj:set_start_time(0.5) -- для sound_object добавлен метод для установки позиции начала звука
sobj.visual_name = "новый_визуал" -- для серверных объектов имеющих визуал так можно читать/изменять название визуала
-- Возможность открывать на чтение\запись ltx файлы в любом каталоге: --
local setting_ini = ini_file(full_name, false) -- false тут нужен, значит файл будет искать как указано, без привязки к configs
setting_ini.readonly = false -- обязательно при сохранении
setting_ini:w_string("setting", "string_param", "string_value123")
setting_ini:w_bool("setting", "bool_param", true)
setting_ini:w_float("setting", "float_param", 123.123)
setting_ini:save()
----------------------------------------------------------------------------
sobj:set_health(0.5) -- можно менять здоровье для серверного объекта
obj:setVisible(false) -- отключить рендеринг клиентского объекта, можно так скрывать объекты
local slot_n = obj:get_slot() -- возвращает номер слота (u8), в котором находится этот предмет.
-- Добавлен флаг `FS.FS_NoLower` чтобы file_list_open_ex не приводил имен а файлов к нижнему регистру
local flist = getFS():file_list_open_ex( "$game_saves$", FS.FS_ListFiles + FS.FS_ClampExt + FS.FS_RootOnly + FS.FS_NoLower, "*.sav" )
flist:Sort(FS.FS_sort_by_modif_down)
for file in flist:GetAll() do
wnd:AddItemToList( file:NameFull(), file:Modif() )
end
-- Добавлен третий аргумент в sound_object что б играть звук как музыку (по сути только громкость будет регулироваться через ползунок громкости музыки в меню). Значения: `sound_object.effect` (дефолт) и `sound_object.music`.
local snd = sound_object(path, 0, sound_object.music)
-- Экспорт `CMainMenu:IsActive()` -- не понятно зачем это надо но пусть будет
-- Добавлен каллбек `entity_alive_before_hit` для классов `CCar` & `CHelicopter`
-- Добавлен метод `level.iterate_nearest` и каллбеки `on_footstep`, `on_actor_land`, `on_actor_jump` из Anomaly
-- Новые скриптовые методы для класса ламп:
local lamp = self.object:get_hanging_lamp()
lamp:set_volumetric( true )
lamp:set_volumetric_quality( 0.9 )
lamp:set_volumetric_intensity( 0.05 )
lamp:set_volumetric_distance( 0.3 )
-- Расширен класс скриптовых партиклов. Добавлены методы `set_position`, `set_direction`, `set_orientation`, `last_position` из Anomaly и добавлена возможность играть партиклы в худовом режиме: `:play(true)` / `:play_at_pos(pos, true)`.
-- Добавлен экспорт в скрипты функции `level.is_ray_intersect_sphere` для проверки пересечения луча со сферой:
function is_actor_looking_to_pos( pos, radius )
return level.is_ray_intersect_sphere( device().cam_pos, device().cam_dir, pos, radius ) >= 0
end
--К примеру проверка, смотрит ли актор в область с радиусом 0.5 от object:position()
local looking = is_actor_looking_to_pos( object:position(), 0.5 )