-- Draggable Hud Editor by demonized -- Possibility to set hud values by mouse dragging -- Adjusting world model position and orientation for strapped/unstrapped poses -- 2023 --[[ Script by Tronex Engine edits by Rezy 2019/9/15 Weapon HUD editor Weapon HUD editor allows you to change Weapon HUD position and orientation in real-time. The GUI is interactive and easy to use, with ability to save adjusted pos in a temp file "temp_hud.ltx". Edited values are temporarly cached for the weapons you worked on. You can return to them in case you turned off the editor (Avoid exiting or reloading when you have unsaved values). Keybinds: • Up Arrow: select previous parameter. • Down Arrow: select next parameter. • Left Arrow: reduce\rotate value of selected parameter. • Right Arrow: increase\rotate value of selected parameter. • LShift (hold): x10 value step. • LAlt (hold): x50 value step. • C: copy current section settings. • V: paste/apply current section settings. • Delete: clean temp cache. • H: toggle hint window. • Esc or Home: turn off editor. --]] local EPS = 0.0000100 local function fsimilar(value, to, eps) return math.abs(value - to) < eps end local function generate_orthonormal_basis_normalized(d) local dir = vector():set(d):normalize() local up = vector():set(0,0,0) local right = vector():set(0,0,0) local fInvLength if (fsimilar(dir.y, 1.0, EPS)) then up:set(0, 0, 1) fInvLength = 1 / math.sqrt(dir.x * dir.x + dir.y * dir.y) right.x = -dir.y * fInvLength right.y = dir.x * fInvLength right.z = 0 up.x = -dir.z * right.y up.y = dir.z * right.x up.z = dir.x * right.y - dir.y * right.x else up:set(0, 1, 0) fInvLength = 1 / math.sqrt(dir.x * dir.x + dir.z * dir.z) right.x = dir.z * fInvLength right.y = 0 right.z = -dir.x * fInvLength up.x = dir.y * right.z up.y = dir.z * right.x - dir.x * right.z up.z = -dir.y * right.x end return dir, up, right end local precision = 6 -- allowed number of zeros local t_dir = "items\\weapons\\" local parameters = { -- Type: 0 = string | 1 = number | 2 = 3d vector | 3 = 4d vector | ["hands_position"] = { name = "Hands Position", typ = 2, def = {0,0,0}, indx = 1, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 0, hud = true }, ["hands_orientation"] = { name = "Hands Orientation", typ = 2, def = {0,0,0}, indx = 2, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 0, hud = true }, ["base_hud_offset_pos"] = { name = "Base Position", typ = 2, def = {0,0,0}, indx = 3, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 5, hud = true }, ["base_hud_offset_rot"] = { name = "Base Orientation", typ = 2, def = {0,0,0}, indx = 4, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 5, hud = true }, ["aim_hud_offset_pos"] = { name = "Aim Position", typ = 2, def = {0,0,0}, indx = 5, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 1, hud = true }, ["aim_hud_offset_rot"] = { name = "Aim Orientation", typ = 2, def = {0,0,0}, indx = 6, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 1, hud = true }, ["gl_hud_offset_pos"] = { name = "GL Position", typ = 2, def = {0,0,0}, indx = 7, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 2, hud = true }, ["gl_hud_offset_rot"] = { name = "GL Orientation", typ = 2, def = {0,0,0}, indx = 8, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 2, hud = true }, ["aim_hud_offset_alt_pos"] = { name = "Alt Position", typ = 2, def = {0,0,0}, indx = 9, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 3, hud = true }, ["aim_hud_offset_alt_rot"] = { name = "Alt Orientation", typ = 2, def = {0,0,0}, indx = 10, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 3, hud = true }, ["lowered_hud_offset_pos"] = { name = "Lowered Position", typ = 2, def = {0,0,0}, indx = 11, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 4, hud = true }, ["lowered_hud_offset_rot"] = { name = "Lowered Orientation", typ = 2, def = {0,0,0}, indx = 12, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 4, hud = true }, ["fire_point"] = { name = "Fire Point", typ = 2, def = {0,0,0}, indx = 13, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 10, hud = true, no_16x9 = true }, ["fire_point2"] = { name = "Fire Point 2 (GL)", typ = 2, def = {0,0,0}, indx = 14, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 11, hud = true, no_16x9 = true }, ["fire_direction"] = { name = "Fire Direction", typ = 2, def = {0,0,1}, indx = 15, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 10, hud = true, no_16x9 = true }, ["shell_point"] = { name = "Shell Point", typ = 2, def = {0,0,0}, indx = 16, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 11, hud = true, no_16x9 = true }, ["custom_ui_pos"] = { name = "UI Position", typ = 2, def = {0,0,0}, indx = 17, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 20 }, --idxb 20 is reserved for device ui ["custom_ui_rot"] = { name = "UI Orientation", typ = 2, def = {0,0,0}, indx = 18, min = -180, max = 180, step = 1, idxa = 1, idxb = 20 }, ["item_position"] = { name = "Item Position", typ = 2, def = {0,0,0}, indx = 19, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 12, hud = true, no_16x9 = true }, ["item_orientation"] = { name = "Item Orientation", typ = 2, def = {0,0,0}, indx = 20, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 12, hud = true, no_16x9 = true }, ["scope_zoom_factor"] = { name = "Zoom Factor", typ = 1, def = 0, indx = 21, min = 0, max = 120, step = 0.1 }, ["gl_zoom_factor"] = { name = "GL Zoom Factor", typ = 1, def = 0, indx = 22, min = 0, max = 120, step = 0.1 }, ["scope_zoom_factor_alt"] = { name = "Alt Zoom Factor", typ = 1, def = 0, indx = 23, min = 0, max = 120, step = 0.1 }, } local third_person_parameters = { ["position"] = { name = "Position", typ = 2, def = {0,0,0}, indx = 1, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 0, hud = false, no_16x9 = true }, ["orientation"] = { name = "Orientation", typ = 2, def = {0,0,0}, indx = 2, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 0, hud = false, no_16x9 = true }, ["strap_position"] = { name = "Strap Position", typ = 2, def = {0,0,0}, indx = 3, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 1, hud = false, no_16x9 = true }, ["strap_orientation"] = { name = "Strap Orientation", typ = 2, def = {0,0,0}, indx = 4, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 1, hud = false, no_16x9 = true }, ["fire_point"] = { name = "Fire Point", typ = 2, def = {0,0,0}, indx = 5, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 1, hud = false, no_16x9 = true }, ["fire_point2"] = { name = "Fire Point 2 (GL)", typ = 2, def = {0,0,0}, indx = 6, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 1, hud = false, no_16x9 = true }, ["shell_point"] = { name = "Shell Point", typ = 2, def = {0,0,0}, indx = 7, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 1, hud = false, no_16x9 = true }, } local adjust_active = nil local is_wide = utils_xml.is_widescreen() local _width = (device().width)-400 local _hight = (device().height) local msg_width = is_wide and (_width*0.8) or _width -- general values cache local _memo = {} -- _memo[section] = {...} local _memo_i = {} -- _memo_i[section] = {...} -- cache current values local _cache = {} local _cache_i = {} local _selected = {} local _selected_group = {} local key_timer = 0 local jump_1 = 1 local jump_2 = 10 local jump_3 = 50 local jump = jump_1 local dummy_npc local enable_debug = false function print_dbg(...) if enable_debug then printf(...) end end ------------------------------------------------------------------- GUI = nil -- instance, don't touch function start(owner) -- Hide the owner if owner then if (owner:IsShown()) then print_dbg("~ hide owner: %s", owner.name or "?") owner:HideDialog() owner:Show(false) end else hide_hud_inventory() end local wpn = db.actor:active_item() local det = db.actor:active_detector() if wpn or det then if (not GUI) then GUI = WpnHudEditor(owner, wpn and wpn:section() or det:section()) end if (GUI) and (not GUI:IsShown()) then -- Enable hud adjust mode if (not adjust_active) then adjust_active = true hud_adjust.enabled(true) print_dbg("~ Enabled hud adjust mode") end GUI:ShowDialog(true) GUI.section = wpn and wpn:section() or det:section() GUI:Reset() GUI:Send_MSG("Press H for info") RegisterScriptCallback("on_key_release",on_key_release) RegisterScriptCallback("on_key_hold",on_key_hold) Register_UI("WpnHudEditor","ui_debug_wpn_hud") else ui_debug_launcher.resume() printe("! Ok... Something is fucked...") end else ui_debug_launcher.resume() actor_menu.set_msg(1, "Hold the weapon you want to edit in your hands first!",5) end end function on_key_release(key) -- jump = jump_1 end function on_key_hold(key) -- if GUI then -- if (key == DIK_keys["DIK_LSHIFT"]) then -- jump = jump_2 -- key_timer = time_global() + 200 -- elseif (key == DIK_keys["DIK_LMENU"]) then -- jump = jump_3 -- key_timer = time_global() + 200 -- end -- end end ------------------------------------------------------------------- class "WpnHudEditor" (CUIScriptWnd) function WpnHudEditor:__init(owner,section) super() self.owner = owner self.section = section self.data = {} self.tables = {parameters, third_person_parameters} for i, v in ipairs(self.tables) do _selected[v] = _selected[v] or 1 _selected_group[v] = _selected_group[v] or 1 self.data[v] = {} self.data[v].selected = _selected[v] self.data[v].selected_group = _selected_group[v] self.data[v].cnt = 0 -- parameters counter / index self.data[v].cnt_group = {} -- parameters counter / index per parent parameter self.data[v].cnt_to_group = {} -- parameters counter to group relation self.data[v].name = {} -- parameters name (by index) self.data[v].typ = {} -- parameter type (by index) self.data[v].value = {} -- parameter value (by index) self.data[v].value_i = {} -- parameter value (by parent) self.data[v].parent = {} -- parameter parent, useful for dealing with vector values (by index) self.data[v].index = {} -- list parameter index, to trace list value self.data[v].par = {} -- parameter element (by index) self.data[v].par_cap = {} -- parameter cap (by index) self.data[v].par_hl = {} -- parameter hightlighting (by index) self.data[v].par_list = {} -- parameter list for lists (by index) self.data[v].par_list_n = {} -- parameter numbered list for lists (by index) end self.active_table = parameters self.third_person_mode = false self:ResetNpcCam() self:InitControls() self:InitCallBacks() self:Reset(true) self:Send_MSG("Press H for info") end function WpnHudEditor:__finalize() end function WpnHudEditor:InitControls() self:SetWndRect (Frect():set(0,0,1024,768)) self:SetAutoDelete(true) if self.owner then self.xml = self.owner.xml else self.xml = CScriptXmlInit() end local xml = self.xml if (not self.owner) then xml:ParseFile ("ui_debug_launcher.xml") end -- Guides local width = device().width local hight = device().height local w_fac = (1024/width) local h_fac = (768/hight) local thic = 2 self._w = xml:InitStatic("dbg_wpn_hud_editor:align", self) --self._w:SetWndPos(vector2():set( 0 , ((hight/2 * h_fac) - (thic/2)) )) --self._w:SetWndSize(vector2():set( (width * w_fac) , (thic * h_fac) )) self._w:Show(false) self._h = xml:InitStatic("dbg_wpn_hud_editor:align", self) --self._h:SetWndPos(vector2():set( ((width/2 * w_fac) - (thic/2)) , 0 )) --self._h:SetWndSize(vector2():set( (thic * w_fac) , (hight * h_fac) )) self._h:Show(false) local pos_w = self._w:GetWndPos() --printf("_W | x=%s y=%s w=%s h=%s", pos_w.x, pos_w.y, self._w:GetWidth(), self._w:GetHeight()) local pos_h = self._h:GetWndPos() --printf("_H | x=%s y=%s w=%s h=%s", pos_h.x, pos_h.y, self._h:GetWidth(), self._h:GetHeight()) -- Dialog self.dialog = xml:InitStatic("dbg_wpn_hud_editor", self) xml:InitFrame("dbg_wpn_hud_editor:frame", self.dialog) xml:InitStatic("dbg_wpn_hud_editor:cap",self.dialog) self.txt_section = xml:InitTextWnd("dbg_wpn_hud_editor:section",self.dialog) -- Ratio saving xml:InitStatic("dbg_wpn_hud_editor:cap_ratio", self.dialog) self.btn_ratio = xml:InitCheck("dbg_wpn_hud_editor:check_ratio", self.dialog) self:Register(self.btn_ratio, "btn_ratio") self.btn_ratio:SetCheck(true) -- Show alignment self.btn_align = xml:Init3tButton("dbg_wpn_hud_editor:btn_alignment", self.dialog) self:Register(self.btn_align, "btn_align") -- Third person switch self.btn_third_person = xml:Init3tButton("dbg_wpn_hud_editor:btn_third_person", self.dialog) self:Register(self.btn_third_person, "btn_third_person") -- Copy section settings self.btn_ini_copy = xml:Init3tButton("dbg_wpn_hud_editor:btn_ini_copy", self.dialog) self:Register(self.btn_ini_copy, "btn_copy") -- Paste section settings self.btn_ini_paste = xml:Init3tButton("dbg_wpn_hud_editor:btn_ini_paste", self.dialog) self:Register(self.btn_ini_paste, "btn_paste") -- Save file self.btn_save = xml:Init3tButton("dbg_wpn_hud_editor:btn_save", self.dialog) self:Register(self.btn_save, "btn_save") -- Reload hud values self.btn_reload = xml:Init3tButton("dbg_wpn_hud_editor:btn_reload", self.dialog) self:Register(self.btn_reload, "btn_reload") self:ResetSelects() -- Parameters for i, v in ipairs(self.tables) do self.data[v].scroll_par = xml:InitScrollView("dbg_wpn_hud_editor:scroll_par", self.dialog) self.data[v].scroll_par:Clear() local functor = function(t,a,b) return t[a].indx < t[b].indx end local v1 = v for par,v in spairs(v,functor) do local _st = xml:InitStatic("dbg_wpn_hud_editor:tmp_par", nil) -- Parameter cap local n = #self.data[v1].par_cap + 1 self.data[v1].par_cap[n] = xml:InitTextWnd("dbg_wpn_hud_editor:op_par:cap", _st) self.data[v1].par_cap[n]:SetText(v.name) -- Multi boxes for vectors local par_i = ((v.typ == 2) and 3) or ((v.typ == 3) and 4) or 1 for i=1,par_i do -- Tracers local ext = self:GetStringByType(i,v.typ) local str = par .. ext self.data[v1].cnt = self.data[v1].cnt + 1 self.data[v1].name[self.data[v1].cnt] = str self.data[v1].typ[self.data[v1].cnt] = v.typ self.data[v1].parent[self.data[v1].cnt] = par self.data[v1].cnt_group[n] = self.data[v1].cnt_group[n] or {} local size_cg = #self.data[v1].cnt_group[n] + 1 self.data[v1].cnt_group[n][size_cg] = self.data[v1].cnt self.data[v1].cnt_to_group[self.data[v1].cnt] = n -- Parameter element and hightlight texture if v.typ == 0 then self.data[v1].par[self.data[v1].cnt] = xml:InitComboBox("dbg_wpn_hud_editor:op_par:list", _st) self:Register(self.data[v1].par[self.data[v1].cnt],tostring(v1) .. "par_" .. self.data[v1].cnt) self.data[v1].par_hl[self.data[v1].cnt] = xml:InitStatic("dbg_wpn_hud_editor:op_par:hl_list", _st) else self.data[v1].par[self.data[v1].cnt] = xml:InitEditBox("dbg_wpn_hud_editor:op_par:input" .. ext, _st) self:Register(self.data[v1].par[self.data[v1].cnt],tostring(v1) .. "par_" .. self.data[v1].cnt) if v.typ == 1 then self.data[v1].par[self.data[v1].cnt]:SetText(v.def) else self.data[v1].par[self.data[v1].cnt]:SetText(v.def[i]) end self.data[v1].par_hl[self.data[v1].cnt] = xml:InitStatic("dbg_wpn_hud_editor:op_par:hl" .. ext, _st) end -- Hightlight last selected parameter if (self.data[v1].cnt ~= _selected[v1]) then self.data[v1].par_hl[self.data[v1].cnt]:Show(false) end end self.data[v1].scroll_par:AddWindow(_st, true) _st:SetAutoDelete(false) end if i > 1 then self.data[v].scroll_par:Show(false) end end -- Lines xml:InitStatic("dbg_wpn_hud_editor:line_1",self.dialog) xml:InitStatic("dbg_wpn_hud_editor:line_2",self.dialog) -- Message Window self.msg_wnd = xml:InitFrame("hint_wnd:background",self) self.msg_wnd:SetAutoDelete(false) self.msg_wnd_text = xml:InitTextWnd("hint_wnd:text",self.msg_wnd) self.msg_wnd_text:SetTextAlignment(2) self.msg_wnd:Show(false) self.msg_wnd:SetColor(GetARGB(255,0,0,0)) -- Hint Window self.hint_wnd = xml:InitFrame("help_wnd:background",self) self.hint_wnd:SetAutoDelete(false) self.hint_wnd_text = xml:InitTextWnd("help_wnd:text",self.hint_wnd) --self.hint_wnd_text:SetTextAlignment(2) self.hint_wnd_text:SetText(game.translate_string("st_ui_dbg_hud_about")) self.hint_wnd_text:AdjustHeightToText() self.hint_wnd_text:SetWndSize(vector2():set(msg_width, self.hint_wnd_text:GetHeight()+10)) self.hint_wnd_text:SetWndPos(vector2():set(20,10)) self.hint_wnd:Show(false) self.hint_wnd:SetWndSize(vector2():set(msg_width, 700)) self.hint_wnd:SetWndPos(vector2():set( self.dialog:GetWidth() , 1 )) self.hint_wnd:SetColor(GetARGB(255,0,0,0)) self.disable_drag = true self.pos = nil self.mouse_hold = nil self.save_mode = true end function WpnHudEditor:ResetSelects(state_only) local xml = self.xml -- State Select self.dummy_npc_states = { {"Unstrapped", "guard_na"}, {"Strapped", "wait_na"}, {"Threat", "threat"}, {"Fire", "threat_fire"}, } self.list_dummy_npc_state = self.list_dummy_npc_state or xml:InitComboBox("dbg_wpn_hud_editor:list_dummy_npc_state", self.dialog) self.list_dummy_npc_state:ClearList() for i = 1, #self.dummy_npc_states do self.list_dummy_npc_state:AddItem(self.dummy_npc_states[i][1], i) end self.list_dummy_npc_state:enable_id(1) self.list_dummy_npc_state:SetText(self.dummy_npc_states[1][1]) self.dummy_npc_state = self.dummy_npc_states[1][2] if dummy_npc then db.storage[dummy_npc.id].ui_debug_wpn_hud_dummy.state = self.dummy_npc_state end if state_only then return end -- Time Factor Select self.tf = { {"TF Normal (1)", 1}, {"TF Slow-mo (0.1)", 0.1}, {"TF Freeze (0.01)", 0.01}, } self.tf_select = self.tf_select or xml:InitComboBox("dbg_wpn_hud_editor:list_tf", self.dialog) self.tf_select:ClearList() for i = 1, #self.tf do self.tf_select:AddItem(self.tf[i][1], i) end self.tf_select:enable_id(1) self.tf_select:SetText(self.tf[1][1]) exec_console_cmd("time_factor " .. 1) -- Drag Mode Select (Persistent) if not self.select_registered then self.drag_modes = { {"Drag per-group", true}, {"Drag per-value", false}, } self.list_drag_mode = self.list_drag_mode or xml:InitComboBox("dbg_wpn_hud_editor:list_drag_mode", self.dialog) self.list_drag_mode:ClearList() for i = 1, #self.drag_modes do self.list_drag_mode:AddItem(self.drag_modes[i][1], i) end self.list_drag_mode:enable_id(1) self.list_drag_mode:SetText(self.drag_modes[1][1]) self.drag_mode = self.drag_modes[1][2] end if not self.select_registered then self.select_registered = true self:Register(self.tf_select, "tf_select") self:Register(self.list_dummy_npc_state, "list_dummy_npc_state") self:Register(self.list_drag_mode, "list_drag_mode") end end function WpnHudEditor:InitCallBacks() self:AddCallback("btn_copy", ui_events.BUTTON_CLICKED, self.OnButtonCopy, self) self:AddCallback("btn_paste", ui_events.BUTTON_CLICKED, self.OnButtonPaste, self) self:AddCallback("btn_save", ui_events.BUTTON_CLICKED, self.OnButtonSave, self) self:AddCallback("btn_reload", ui_events.BUTTON_CLICKED, self.OnButtonResume, self) self:AddCallback("btn_align", ui_events.BUTTON_CLICKED, self.OnButtonAlign, self) self:AddCallback("btn_third_person", ui_events.BUTTON_CLICKED, self.OnButtonThirdPerson, self) self:AddCallback("tf_select", ui_events.LIST_ITEM_SELECT, self.OnTfSelect, self) self:AddCallback("list_dummy_npc_state", ui_events.LIST_ITEM_SELECT, self.OnDummyNpcStateChange, self) self:AddCallback("list_drag_mode", ui_events.LIST_ITEM_SELECT, self.OnDragModeChange, self) --self:AddCallback("btn_ratio", ui_events.BUTTON_CLICKED, self.OnButtonRatio, self) for i, v in ipairs(self.tables) do for i=1,self.data[v].cnt do local f = tostring(v).."OnInput_" .. i self[f] = function(self) self:OnInput(i) local group = self.data[v].cnt_to_group[i] self:SwitchParamByIndex(group, i) end self:AddCallback(tostring(v) .. "par_" .. i, ui_events.EDIT_TEXT_COMMIT, self[f], self) end end end function WpnHudEditor:OnTfSelect() local id = self.tf_select:CurrentID() local tf = self.tf[id][2] exec_console_cmd("time_factor " .. tf) end function WpnHudEditor:OnDummyNpcStateChange() local id = self.list_dummy_npc_state:CurrentID() local state = self.dummy_npc_states[id][2] self.dummy_npc_state = state if dummy_npc then db.storage[dummy_npc.id].ui_debug_wpn_hud_dummy.state = self.dummy_npc_state end end function WpnHudEditor:OnDragModeChange() local id = self.list_drag_mode:CurrentID() local state = self.drag_modes[id][2] self.drag_mode = state end function WpnHudEditor:ResetNpcCam() self.dummy_npc_cam_offset = { heading = 0, pitch = 0, height = 1.2, zoom = 1.5, move = -0.2, } self:SetCam() end function WpnHudEditor:SetCam() if dummy_npc then local obj = level.object_by_id(dummy_npc.id) if obj then local pos = obj:position():add(vector():set(0, self.dummy_npc_cam_offset.height, 0)) local obj_pos = vector():set(pos) local dir = vector():setHP(utils_data.deg2rad(self.dummy_npc_cam_offset.heading), self.dummy_npc_cam_offset.pitch):normalize() pos = pos:mad(dir, self.dummy_npc_cam_offset.zoom) dir = vector():set(obj_pos):sub(pos):normalize() -- dir = vector_rotate_y(dir, 15) local d, u, r = generate_orthonormal_basis_normalized(dir) pos:mad(r, self.dummy_npc_cam_offset.move) dir = vector():set( dir:getH(), dir:getP(), 0 ) level.set_cam_custom_position_direction(pos, dir) end end end function WpnHudEditor:ChangeNpcCam(heading, height, zoom, move, pitch) if heading then self.dummy_npc_cam_offset.heading = self.dummy_npc_cam_offset.heading + heading end if pitch then self.dummy_npc_cam_offset.pitch = clamp(self.dummy_npc_cam_offset.pitch + pitch, -math.pi / 2.6, math.pi / 2.05) end if height then self.dummy_npc_cam_offset.height = clamp(self.dummy_npc_cam_offset.height + height, 0.2, 2) end if zoom then self.dummy_npc_cam_offset.zoom = clamp(self.dummy_npc_cam_offset.zoom + zoom, 0.2, 2.5) end if move then self.dummy_npc_cam_offset.move = clamp(self.dummy_npc_cam_offset.move + move, -1.5, 1.5) end self:SetCam() end function WpnHudEditor:StopCam() if dummy_npc then alife():release(dummy_npc) dummy_npc = nil end if level.remove_cam_custom_position_direction then level.remove_cam_custom_position_direction() end RemoveTimeEvent("ui_debug_wpn_hud_dummy", "ui_debug_wpn_hud_dummy_npc") RemoveTimeEvent("ui_debug_wpn_hud_dummy", "ui_debug_wpn_hud_dummy_weapon") self.dummy_npc_state = self.dummy_npc_states[1][2] end function WpnHudEditor:DisableWCT(v) if weapon_cover_tilt and weapon_cover_tilt.set_force_disabled then weapon_cover_tilt.set_force_disabled(v) end end function WpnHudEditor:Reset(force,use_cache) local _use_cache = use_cache and is_not_empty(_cache) and is_not_empty(_cache_i) and true self:DisableWCT(true) local section = self.section local hud_section = ini_sys:r_string_ex(section,"hud") local str = utils_xml.is_widescreen() and "_16x9" or "" local ltx = ini_file("temp_hud.ltx") local section_found = ltx and ltx:section_exist(hud_section) self.txt_section:SetText(section) for i, v in ipairs(self.tables) do empty_table(self.data[v].index) local _use_cache = use_cache and is_not_empty(_cache) and is_not_empty(_cache[v]) and is_not_empty(_cache_i) and is_not_empty(_cache_i[v]) and true -- Read values and convert to numbers if needed if _use_cache then --print_dbg("/ WpnHudEditor:Reset | use cache") copy_table(self.data[v].value_i, _cache_i[v]) if not _memo_i[v] then _memo_i[v] = {} end if (not _memo_i[v][section]) then _memo_i[v][section] = {} end copy_table(_memo_i[v][section], _cache_i[v]) elseif _memo_i[v] and _memo_i[v][section] then --print_dbg("/ WpnHudEditor:Reset | use memo | file: %s - hour: %s", file, hour) copy_table(self.data[v].value_i, _memo_i[v][section]) else local v1 = v for par,v in pairs(v1) do local t = {} if (v.hud) then local par_str = v.no_16x9 and par or (par..str) if section_found and ltx:r_string_ex(hud_section,(par_str)) then t = parse_list(ltx, hud_section, (par_str)) else t = parse_list(ini_sys, hud_section, (par_str)) end else if ltx:r_string_ex(section, par) then t = parse_list(ltx, section, par) else t = parse_list(ini_sys, section, par) end end self.data[v1].value_i[par] = {} for i=1,#t do if (v.typ ~= 0) then self.data[v1].value_i[par][i] = tonumber(t[i]) end end -- use default values if nothing is found if (#self.data[v1].value_i[par] == 0) then if (v.typ == 1) then self.data[v1].value_i[par][1] = v.def else copy_table(self.data[v1].value_i[par], v.def) end end end end -- Fill current values for i=1,self.data[v].cnt do local value if _use_cache then value = _cache[v][i] if (not _memo[v]) then _memo[v] = {} end if (not _memo[v][section]) then _memo[v][section] = {} end _memo[v][section][i] = _cache[v][i] elseif _memo[v] and _memo[v][section] then value = _memo[v][section][i] else value = self:GetParameterValue(i, v) end -- print_dbg("/ WpnHudEditor:Reset | Filler | self.value[%s] (%s) = %s", i, self.parent[i], value) self.data[v].value[i] = value self.data[v].par[i]:SetText(value) end end -- Apply values in-game for i, t in ipairs(self.tables) do if t == third_person_parameters then self:ApplyThirdPersonParameterValue() else for par,v in pairs(t) do self:ApplyParameterValue(v.typ, par, t) end end end -- Crosshair state self.crosshair = get_console_cmd(1,"hud_crosshair") if self._w:IsShown() then exec_console_cmd("hud_crosshair on") end self.disable_drag = true self.pos = nil self.mouse_hold = nil -- self.drag_mode = true end ---------------< Utility >--------------- local indx_str = { [1] = "_x", [2] = "_y", [3] = "_z", [4] = "_a", } function WpnHudEditor:GetStringByType(indx,typ) if (typ == 0) or (typ == 1) then return "" else return indx_str[indx] end end function WpnHudEditor:GetParameterValue(cnt, v) local str = self.data[v].name[cnt] local parent = self.data[v].parent[cnt] local typ = self.data[v].typ[cnt] print_dbg("/ WpnHudEditor:GetParameterValue | str: %s - parent: %s - typ: %s", str, parent, typ) if (typ == 0) or (typ == 1) then return self.data[v].value_i[parent][1] end if (string.sub(str,-2) == "_x") then return self.data[v].value_i[parent][1] elseif (string.sub(str,-2) == "_y") then return self.data[v].value_i[parent][2] elseif (string.sub(str,-2) == "_z") then return self.data[v].value_i[parent][3] elseif (string.sub(str,-2) == "_a") then return self.data[v].value_i[parent][4] end printe("!ERROR no returned value for %s!", parent) end function WpnHudEditor:SetParameterValue(cnt,value) local v = self.active_table local str = self.data[v].name[cnt] local parent = self.data[v].parent[cnt] local typ = self.data[v].typ[cnt] if self:IsInvalidValue(cnt,typ,value) then self:Send_MSG("!ERROR nil value found for parameter [%s]", str) return end if (typ == 1) then self.data[v].value_i[parent][1] = value elseif (string.sub(str,-2) == "_x") then self.data[v].value_i[parent][1] = value elseif (string.sub(str,-2) == "_y") then self.data[v].value_i[parent][2] = value elseif (string.sub(str,-2) == "_z") then self.data[v].value_i[parent][3] = value elseif (string.sub(str,-2) == "_a") then self.data[v].value_i[parent][4] = value end local section = self.section if not _memo[v] then _memo[v] = {} end if not _memo_i[v] then _memo_i[v] = {} end if (not _memo[v][section]) then _memo[v][section] = {} end copy_table(_memo[v][section], self.data[v].value) if (not _memo_i[v][section]) then _memo_i[v][section] = {} end copy_table(_memo_i[v][section], self.data[v].value_i) end function WpnHudEditor:ApplyParameterValue(typ, parent, v) v = v or self.active_table print_dbg("# Setting now: %s",parent) if v == third_person_parameters then self:ApplyThirdPersonParameterValue() else if (typ == 1) then local value = self.data[v].value_i[parent][1] if value and (type(value) == "number") then hud_adjust.set_value(parent, value) print_dbg("-hud_adjust.set_value(%s,%s)",parent,value) else printe("! MISSING VALUE: WpnHudEditor:ApplyParameterValue(%s, %s)", typ ,parent) end elseif (typ == 2) then local value_1 = self.data[v].value_i[parent][1] local value_2 = self.data[v].value_i[parent][2] local value_3 = self.data[v].value_i[parent][3] if value_1 and (type(value_1) == "number") and value_2 and (type(value_2) == "number") and value_3 and (type(value_3) == "number") then hud_adjust.set_vector(v[parent].idxa, v[parent].idxb, value_1, value_2, value_3) print_dbg("-hud_adjust.set_vector[%s]([%s][%s]: %s,%s,%s)", parent, v[parent].idxa, v[parent].idxb, value_1, value_2, value_3) else printe("! MISSING VALUE: WpnHudEditor:ApplyParameterValue(%s, %s)", typ ,parent) end elseif (typ == 3) then local value_1 = self.data[v].value_i[parent][1] local value_2 = self.data[v].value_i[parent][2] local value_3 = self.data[v].value_i[parent][3] local value_4 = self.data[v].value_i[parent][4] if value_1 and (type(value_1) == "number") and value_2 and (type(value_2) == "number") and value_3 and (type(value_3) == "number") and value_4 and (type(value_4) == "number") then --weather.set_value_vector(parent,value_1,value_2,value_3,value_4) print_dbg("-weather.set_value_vector2(%s,%s,%s,%s,%s)",parent,value_1,value_2,value_3,value_4) else printe("! MISSING VALUE: WpnHudEditor:ApplyParameterValue(%s, %s)", typ ,parent) end end end end function WpnHudEditor:ApplyThirdPersonParameterValue() if not self.weapon_cobj then print_dbg("no active weapon to set") return end if not ( self.weapon_cobj.Set_mOffset and self.weapon_cobj.Set_mStrapOffset and self.weapon_cobj.Set_mFirePoint and self.weapon_cobj.Set_mFirePoint2 and self.weapon_cobj.Set_mShellPoint ) then self:Print(nil, "Modded exes not installed, third person editor is unavailable") return end local params = {} for par,v in pairs(third_person_parameters) do local typ = v.typ local parent = par if (typ == 1) then local value = self.data[third_person_parameters].value_i[parent][1] if value and (type(value) == "number") then params[parent] = value print_dbg("-third_person_value(%s,%s)",parent,value) else printe("! MISSING VALUE: third_person_value(%s,%s)", typ ,parent) end elseif (typ == 2) then local value_1 = self.data[third_person_parameters].value_i[parent][1] local value_2 = self.data[third_person_parameters].value_i[parent][2] local value_3 = self.data[third_person_parameters].value_i[parent][3] if value_1 and (type(value_1) == "number") and value_2 and (type(value_2) == "number") and value_3 and (type(value_3) == "number") then params[parent] = vector():set(value_1, value_2, value_3) print_dbg("-third_person_value(%s,%s,%s,%s)",parent,value_1, value_2, value_3) else printe("! MISSING VALUE: third_person_value(%s, %s)", typ ,parent) end end end if params.position and params.orientation then self.weapon_cobj:Set_mOffset(params.position, params.orientation) end if params.strap_position and params.strap_orientation then self.weapon_cobj:Set_mStrapOffset(params.strap_position, params.strap_orientation) end if params.fire_point then self.weapon_cobj:Set_mFirePoint(params.fire_point) end if params.fire_point2 then self.weapon_cobj:Set_mFirePoint2(params.fire_point2) end if params.shell_point then self.weapon_cobj:Set_mShellPoint(params.shell_point) end end function WpnHudEditor:IsInvalidValue(cnt,typ,value) -- Numbers if not (value and value ~= "" and tonumber(value)) then return true end return false end function WpnHudEditor:Send_MSG(text,...) printf(text, ...) self:Print(nil, strformat(text,...)) -- local str = strformat(text,...) -- self.msg_wnd:Show(true) -- self.msg_wnd_text:SetText(str) -- self.msg_wnd_text:AdjustHeightToText() -- self.msg_wnd_text:SetWndSize(vector2():set(msg_width, self.msg_wnd_text:GetHeight()+10)) -- self.msg_wnd_text:SetWndPos(vector2():set(0,20)) -- self.msg_wnd:SetWndSize(vector2():set(msg_width, self.msg_wnd_text:GetHeight()+44)) -- self.msg_wnd:SetWndPos(vector2():set( self.dialog:GetWidth() , (_hight - self.msg_wnd:GetHeight()) )) -- self.msg_wnd_timer = time_global() + 4000 end local toggle_hint = false function WpnHudEditor:ShowHint() toggle_hint = not toggle_hint self.hint_wnd_show = toggle_hint end function WpnHudEditor:Update() CUIScriptWnd.Update(self) if key_state(DIK_keys.DIK_LSHIFT) == 1 then jump = jump_2 elseif key_state(DIK_keys.DIK_LMENU) == 1 then jump = jump_3 else jump = jump_1 end self:SetCam() self:On_Drag() if self.third_person_mode then if key_state(bind_to_dik(key_bindings.kFWD)) == 1 then self:ChangeNpcCam(nil, nil, -0.003) elseif key_state(bind_to_dik(key_bindings.kBACK)) == 1 then self:ChangeNpcCam(nil, nil, 0.003) end if key_state(bind_to_dik(key_bindings.kL_STRAFE)) == 1 then self:ChangeNpcCam(nil, nil, nil, -0.003) elseif key_state(bind_to_dik(key_bindings.kR_STRAFE)) == 1 then self:ChangeNpcCam(nil, nil, nil, 0.003) end -- Lock condition and ammo count in TP if self.weapon_cobj then self.weapon_cobj:SetAmmoElapsed(30) self.weapon:set_condition(1) end end if (self.msg_wnd_timer and time_global() > self.msg_wnd_timer) then self.msg_wnd_timer = nil self.msg_wnd:Show(false) end if (self.hint_wnd_show) then self.hint_wnd:Show(true) else self.hint_wnd:Show(false) end end function WpnHudEditor:Print(s, str, ...) actor_menu.set_msg(1, string.format(str, ...), 2) end local tg = 0 local tg_interval = 20 local tg_print = 0 function WpnHudEditor:On_Drag() if (self.disable_drag) then return end -- local t = time_global() -- if t < tg then return end -- tg = t + tg_interval if not self.pos then self.pos = GetCursorPosition() end local pos = GetCursorPosition() local diff_y = pos.y - self.pos.y local diff_x = pos.x - self.pos.x local rounded_diff_y = math.abs(round_idp(diff_y, 4)) * device().width / 1920 local rounded_diff_x = math.abs(round_idp(diff_x, 4)) * device().width / 1920 -- if time_global() - tg_print > 100 then -- printf("ratio %s", device().width / 1920) -- printf("%s, %s", rounded_diff_x, rounded_diff_y) -- self:Print(nil, rounded_diff_x .. ", " .. rounded_diff_y) -- tg_print = time_global() -- end if self.mouse_hold and self.mouse_hold == DIK_keys.MOUSE_3 and self.third_person_mode then if key_state(DIK_keys.DIK_LCONTROL) == 1 then if diff_y < 0 then self:ChangeNpcCam(nil, nil, 0.003 * -rounded_diff_y) elseif diff_y > 0 then self:ChangeNpcCam(nil, nil, 0.003 * rounded_diff_y) end elseif key_state(DIK_keys.DIK_LSHIFT) == 1 then if diff_x < 0 then self:ChangeNpcCam(nil, nil, nil, 0.0015 * rounded_diff_x) elseif diff_x > 0 then self:ChangeNpcCam(nil, nil, nil, 0.0015 * -rounded_diff_x) end if diff_y < 0 then self:ChangeNpcCam(nil, 0.0015 * -rounded_diff_y) elseif diff_y > 0 then self:ChangeNpcCam(nil, 0.0015 * rounded_diff_y) end else if diff_x < 0 then self:ChangeNpcCam(0.15 * rounded_diff_x) elseif diff_x > 0 then self:ChangeNpcCam(0.15 * -rounded_diff_x) end if diff_y < 0 then self:ChangeNpcCam(nil, nil, nil, nil, 0.0015 * -rounded_diff_y) elseif diff_y > 0 then self:ChangeNpcCam(nil, nil, nil, nil, 0.0015 * rounded_diff_y) end end end local v = self.active_table if self.drag_mode == true then local group = self.data[v].cnt_group[self.data[v].selected_group] if self.mouse_hold and self.mouse_hold == DIK_keys.MOUSE_1 then if self.data[v].selected_group == 2 then -- Hands orientation if diff_y < 0 then self:SwitchValue(true, group[2], 8 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[2], 8 * rounded_diff_y) end if diff_x < 0 then self:SwitchValue(true, group[1], 8 * rounded_diff_x) elseif diff_x > 0 then self:SwitchValue(false, group[1], 8 * rounded_diff_x) end elseif self.data[v].selected_group == 6 and v == third_person_parameters then -- TP Strapped Orientation if diff_y > 0 then self:SwitchValue(true, group[1], 8 * rounded_diff_y) elseif diff_y < 0 then self:SwitchValue(false, group[1], 8 * rounded_diff_y) end if diff_x > 0 then self:SwitchValue(true, group[2], 8 * rounded_diff_x) elseif diff_x < 0 then self:SwitchValue(false, group[2], 8 * rounded_diff_x) end elseif self.data[v].selected_group == 6 then -- Aim orientation if diff_y > 0 then self:SwitchValue(true, group[1], 1 * rounded_diff_y) elseif diff_y < 0 then self:SwitchValue(false, group[1], 1 * rounded_diff_y) end if diff_x > 0 then self:SwitchValue(true, group[2], 1 * rounded_diff_x) elseif diff_x < 0 then self:SwitchValue(false, group[2], 1 * rounded_diff_x) end elseif self.data[v].selected_group == 8 and v ~= third_person_parameters then -- GL orientation if diff_y > 0 then self:SwitchValue(true, group[1], 1 * rounded_diff_y) elseif diff_y < 0 then self:SwitchValue(false, group[1], 1 * rounded_diff_y) end if diff_x > 0 then self:SwitchValue(true, group[2], 1 * rounded_diff_x) elseif diff_x < 0 then self:SwitchValue(false, group[2], 1 * rounded_diff_x) end elseif self.data[v].selected_group == 8 then -- Alt orientation if diff_y > 0 then self:SwitchValue(true, group[1], 1 * rounded_diff_y) elseif diff_y < 0 then self:SwitchValue(false, group[1], 1 * rounded_diff_y) end if diff_x > 0 then self:SwitchValue(true, group[2], 1 * rounded_diff_x) elseif diff_x < 0 then self:SwitchValue(false, group[2], 1 * rounded_diff_x) end elseif self.data[v].selected_group == 19 then -- Item Position if diff_y < 0 then self:SwitchValue(true, group[2], rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[2], rounded_diff_y) end if diff_x > 0 then self:SwitchValue(true, group[1], rounded_diff_x) elseif diff_x < 0 then self:SwitchValue(false, group[1], rounded_diff_x) end elseif self.data[v].selected_group == 20 then -- Item Orientation if diff_y < 0 then self:SwitchValue(true, group[2], 8 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[2], 8 * rounded_diff_y) end if diff_x < 0 then self:SwitchValue(true, group[1], 8 * rounded_diff_x) elseif diff_x > 0 then self:SwitchValue(false, group[1], 8 * rounded_diff_x) end elseif self.data[v].selected_group == 18 then -- UI orientation if diff_y > 0 then self:SwitchValue(true, group[1], round_idp(0.02 * rounded_diff_y, 4)) elseif diff_y < 0 then self:SwitchValue(false, group[1], round_idp(0.02 * rounded_diff_y, 4)) end if diff_x > 0 then self:SwitchValue(true, group[2], round_idp(0.02 * rounded_diff_x, 4)) elseif diff_x < 0 then self:SwitchValue(false, group[2], round_idp(0.02 * rounded_diff_x, 4)) end elseif self.data[v].selected_group == 12 then -- Lowered orientation if diff_y > 0 then self:SwitchValue(true, group[1], 1 * rounded_diff_y) elseif diff_y < 0 then self:SwitchValue(false, group[1], 1 * rounded_diff_y) end if diff_x > 0 then self:SwitchValue(true, group[2], 1 * rounded_diff_x) elseif diff_x < 0 then self:SwitchValue(false, group[2], 1 * rounded_diff_x) end elseif self.data[v].selected_group == 23 or self.data[v].selected_group == 22 or self.data[v].selected_group == 21 then -- Last 3 groups with single value if diff_y < 0 then self:SwitchValue(true, group[1], round_idp(0.2 * rounded_diff_y, 4)) elseif diff_y > 0 then self:SwitchValue(false, group[1], round_idp(0.2 * rounded_diff_y, 4)) end else -- Everything else if diff_y < 0 then self:SwitchValue(true, group[2], 1 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[2], 1 * rounded_diff_y) end if diff_x > 0 then self:SwitchValue(true, group[1], 1 * rounded_diff_x) elseif diff_x < 0 then self:SwitchValue(false, group[1], 1 * rounded_diff_x) end end elseif self.mouse_hold and self.mouse_hold == DIK_keys.MOUSE_2 then if self.data[v].selected_group == 2 then -- Hands orientation if diff_y < 0 then self:SwitchValue(true, group[3], 8 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[3], 8 * rounded_diff_y) end elseif self.data[v].selected_group == 6 and v == third_person_parameters then -- TP Strapped Orientation if diff_y < 0 then self:SwitchValue(true, group[3], 8 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[3], 8 * rounded_diff_y) end elseif self.data[v].selected_group == 18 then -- UI orientation if diff_y < 0 then self:SwitchValue(true, group[3], round_idp(0.02 * rounded_diff_y, 4)) elseif diff_y > 0 then self:SwitchValue(false, group[3], round_idp(0.02 * rounded_diff_y, 4)) end elseif self.data[v].selected_group == 19 then -- Item Position if diff_y < 0 then self:SwitchValue(true, group[3], 2 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[3], 2 * rounded_diff_y) end elseif self.data[v].selected_group == 20 then -- Item Orientation if diff_y < 0 then self:SwitchValue(true, group[3], 8 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[3], 8 * rounded_diff_y) end elseif self.data[v].selected_group == 23 or self.data[v].selected_group == 22 or self.data[v].selected_group == 21 then -- Last 3 groups with single value if diff_y < 0 then self:SwitchValue(true, group[1], round_idp(0.2 * rounded_diff_y, 4)) elseif diff_y > 0 then self:SwitchValue(false, group[1], round_idp(0.2 * rounded_diff_y, 4)) end else -- Everything else if diff_y < 0 then self:SwitchValue(true, group[3], 1 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, group[3], 1 * rounded_diff_y) end end end elseif self.drag_mode == false then if self.mouse_hold and (self.mouse_hold == DIK_keys.MOUSE_1 or self.mouse_hold == DIK_keys.MOUSE_2) then if self.data[v].selected_group == 2 then -- Hands orientation if diff_y < 0 then self:SwitchValue(true, self.data[v].selected, 8 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, self.data[v].selected, 8 * rounded_diff_y) end elseif self.data[v].selected_group == 6 and v == third_person_parameters then -- TP Strapped Orientation if diff_y < 0 then self:SwitchValue(true, self.data[v].selected, 8 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, self.data[v].selected, 8 * rounded_diff_y) end elseif self.data[v].selected_group == 18 then -- UI orientation if diff_y < 0 then self:SwitchValue(true, self.data[v].selected, round_idp(0.02 * rounded_diff_y, 4)) elseif diff_y > 0 then self:SwitchValue(false, self.data[v].selected, round_idp(0.02 * rounded_diff_y, 4)) end elseif self.data[v].selected_group == 23 or self.data[v].selected_group == 22 or self.data[v].selected_group == 21 then -- Last 3 groups with single value if diff_y < 0 then self:SwitchValue(true, self.data[v].selected, round_idp(0.2 * rounded_diff_y, 4)) elseif diff_y > 0 then self:SwitchValue(false, self.data[v].selected, round_idp(0.2 * rounded_diff_y, 4)) end else -- Everything else if diff_y < 0 then self:SwitchValue(true, self.data[v].selected, 1 * rounded_diff_y) elseif diff_y > 0 then self:SwitchValue(false, self.data[v].selected, 1 * rounded_diff_y) end end end end SetCursorPosition(self.pos) end ---------------< Callbacks >--------------- function WpnHudEditor:OnButtonCopy() local v = self.active_table _cache[v] = _cache[v] or {} _cache_i[v] = _cache_i[v] or {} copy_table(_cache[v], self.data[v].value) copy_table(_cache_i[v], self.data[v].value_i) self:Send_MSG("Copied values") end function WpnHudEditor:OnButtonPaste() local v = self.active_table if (_cache and _cache_i and _cache[v] and _cache_i[v]) then self:Reset(false, true) self:Send_MSG("Applied copied values") else self:Send_MSG("No values are copied yet!") end end function WpnHudEditor:SaveCacheDbg() local section = self.section local hud_section = ini_sys:r_string_ex(section,"hud") local prefix = utils_xml.is_widescreen() and "_16x9" or "" local all_prefix = self.btn_ratio:GetCheck() local ini_cc = ui_debug_launcher.ini_cc local function saveParams() local to_save = {} for par,v in pairs(parameters) do local value, value_def if (v.typ == 2) then value = self.data[parameters].value_i[par][1] ..",".. self.data[parameters].value_i[par][2] ..",".. self.data[parameters].value_i[par][3] value_def = v.def[1] ..",".. v.def[2] ..",".. v.def[3] elseif (v.typ == 3) then value = self.data[parameters].value_i[par][1] ..",".. self.data[parameters].value_i[par][2] ..",".. self.data[parameters].value_i[par][3] ..",".. self.data[parameters].value_i[par][4] value_def = v.def[1] ..",".. v.def[2] ..",".. v.def[3] ..",".. v.def[4] else value = self.data[parameters].value_i[par][1] value_def = v.def end value = tostring(value) value_def = tostring(value_def) local par_str = (v.hud and not v.no_16x9) and (par_str) or par local old_value = v.hud and ini_sys:r_string_ex(hud_section, par_str) or ini_sys:r_string_ex(section, par) local c_sec = v.hud and hud_section or section local c_par = par_str if (value ~= old_value) and (value ~= value_def) then if (to_save[c_sec] == nil) then to_save[c_sec] = {} end to_save[c_sec][c_par] = value end end if is_empty(to_save) then self:Print(nil, "No HUD values are modified for %s", section) return end local params_wide = { ["hands_position"] = true, ["hands_orientation"] = true, ["base_hud_offset_pos"] = true, ["base_hud_offset_rot"] = true, ["aim_hud_offset_pos"] = true, ["aim_hud_offset_rot"] = true, ["gl_hud_offset_pos"] = true, ["gl_hud_offset_rot"] = true, ["aim_hud_offset_alt_pos"] = true, ["aim_hud_offset_alt_rot"] = true, ["lowered_hud_offset_pos"] = true, ["lowered_hud_offset_rot"] = true, ["strafe_hud_offset_pos"] = true, ["strafe_hud_offset_rot"] = true, ["strafe_aim_hud_offset_pos"] = true, ["strafe_aim_hud_offset_rot"] = true, } for k, v in pairs(to_save) do for k1, v1 in pairs(v) do ini_cc:w_value(k, k1, v1) if all_prefix then if params_wide[k1] then ini_cc:w_value(k, k1 .. "_16x9", v1) end end end end return true end local function saveThirdPersonParams() local to_save = {} for par,v in pairs(third_person_parameters) do local value, value_def if (v.typ == 2) then value = self.data[third_person_parameters].value_i[par][1] ..",".. self.data[third_person_parameters].value_i[par][2] ..",".. self.data[third_person_parameters].value_i[par][3] value_def = v.def[1] ..",".. v.def[2] ..",".. v.def[3] elseif (v.typ == 3) then value = self.data[third_person_parameters].value_i[par][1] ..",".. self.data[third_person_parameters].value_i[par][2] ..",".. self.data[third_person_parameters].value_i[par][3] ..",".. self.data[third_person_parameters].value_i[par][4] value_def = v.def[1] ..",".. v.def[2] ..",".. v.def[3] ..",".. v.def[4] else value = self.data[third_person_parameters].value_i[par][1] value_def = v.def end value = tostring(value) value_def = tostring(value_def) local par_str = par local old_value = ini_sys:r_string_ex(section, par) local c_sec = section local c_par = par_str if (value ~= old_value) and (value ~= value_def) then if (to_save[c_sec] == nil) then to_save[c_sec] = {} end to_save[c_sec][c_par] = value end end if is_empty(to_save) then self:Print(nil, "No Third Person values are modified for %s", section) return end for k, v in pairs(to_save) do for k1, v1 in pairs(v) do ini_cc:w_value(k, k1, v1) end end return true end local res = saveParams() res = saveThirdPersonParams() or res if res then ini_cc:save() self:Print(nil, "%s section is saved in cache_dbg.ltx", section) else self:Print(nil, "%s no changes were made", section) end end function WpnHudEditor:CleanCacheDbg() local path = getFS():update_path('$game_config$', '') .. "cache_dbg.ltx" local ini_io = io.open(path, 'w') ini_io:close() ui_debug_launcher.ini_cc = ini_file_ex("cache_dbg.ltx",true) self:Print(nil, "cache_dbg.ltx file cleaned") end function WpnHudEditor:OnButtonSave() if self.save_mode then return self:SaveCacheDbg() end local section = self.section local hud_section = ini_sys:r_string_ex(section,"hud") local prefix = utils_xml.is_widescreen() and "_16x9" or "" local all_prefix = self.btn_ratio:GetCheck() local to_save = {} for par,v in pairs(parameters) do local value, value_def if (v.typ == 2) then value = self.data[parameters].value_i[par][1] ..",".. self.data[parameters].value_i[par][2] ..",".. self.data[parameters].value_i[par][3] value_def = v.def[1] ..",".. v.def[2] ..",".. v.def[3] elseif (v.typ == 3) then value = self.data[parameters].value_i[par][1] ..",".. self.data[parameters].value_i[par][2] ..",".. self.data[parameters].value_i[par][3] ..",".. self.data[parameters].value_i[par][4] value_def = v.def[1] ..",".. v.def[2] ..",".. v.def[3] ..",".. v.def[4] else value = self.data[parameters].value_i[par][1] value_def = v.def end value = tostring(value) value_def = tostring(value_def) local par_str = (v.hud and not v.no_16x9) and (par..prefix) or par local old_value = v.hud and ini_sys:r_string_ex(hud_section, par_str) or ini_sys:r_string_ex(section, par) local c_sec = v.hud and hud_section or section local c_par = par_str if (value ~= old_value) and (value ~= value_def) then if (to_save[c_sec] == nil) then to_save[c_sec] = {} end to_save[c_sec][c_par] = value end end if is_empty(to_save) then self:Print(nil, "No HUD values are modified for [%s]", section) return end local function file_exists(path) return io.open(path) ~= nil end local save_done local ini_cc = ui_debug_launcher.ini_cc local function on_execute(path,filename,quit) if is_empty(to_save) then return end local fullpath = path.."\\"..filename local ltx = io.open(fullpath,"rb") if (ltx) then local data = ltx:read("*all") ltx:close() if (data) then for sec,v in pairs(to_save) do if (string.find(data,"["..sec.."]",nil,true)) then ltx = utils_data.cfg_file(fullpath, true) if (ltx) then for par,val in pairs(v) do local p = { par } if all_prefix then local p2 = par if (prefix == "_16x9") then p2 = string.gsub(p2, "_16x9", "") else p2 = p2 .. "_16x9" end p[2] = p2 end for i=1,#p do ltx:SetValue(sec, p[i], val) -- Cache values for the first time so you can return to it upon reseting values local cached_val = ini_cc:r_value(sec, p[i]) if not (cached_val and cached_val ~= "") then local val_old = ini_sys:r_string_ex(sec, p[i]) ini_cc:w_value(sec, p[i], val_old) end printf("% WpnHudEditor | saving [%s]->[%s]->[%s]",sec, p[i], val) end end ltx:SaveExt() to_save[sec] = nil save_done = true self:Print(nil, "Applied and saved the new values for [%s]\\nFile: %s", sec, t_dir .. filename) printf("% WpnHudEditor | saved changes for {%s}", fullpath) end end end end end end local sp = getFS():update_path('$game_config$', t_dir) sp = string.sub(sp,0,string.len(sp)-1) lua_ext.recurse_subdirectories_and_execute(sp,{"ltx"},on_execute) -- Reload system_ini() to adapt the new values in game reload_ini_sys() self:Reset() if (not save_done) then self:Print(nil, "No changes are made on [%s].\\nKeep in mind that configs must be unpacked before applying changes!", section) else -- Cache original values ini_cc:save() -- Remove cached HUD model from engine, so it updates to the new values hud_adjust.remove_hud_model(section) -- Close the UI self:Close() local str = strformat( "Changes are saved to [%s]", section) actor_menu.set_msg(1, str,5) -- Clean memory of this section to adapt the new changes _memo[section] = nil _memo_i[section] = nil end end function WpnHudEditor:OnButtonAlign() if self._h:IsShown() then self._h:Show(false) self._w:Show(false) exec_console_cmd("hud_crosshair off") print_dbg("! WpnHudEditor | Hide alignments") else self._h:Show(true) self._w:Show(true) exec_console_cmd("hud_crosshair on") print_dbg("- WpnHudEditor | Show alignments") end end function WpnHudEditor:OnButtonResume() self:Close() -- Resume normal hud behavior hud_adjust.enabled(false) adjust_active = false actor_menu.set_msg(1, "HUD adjust mode is stopped",5) --ui = nil end function WpnHudEditor:CleanMemo() empty_table(_memo) empty_table(_memo_i) self:Reset(false) self:Send_MSG("Cleared memory!") end function WpnHudEditor:SwitchParam(state, vert) local v = self.active_table local function get_param_index(state, vert) --// Vertical movement if vert then local jumps = ((jump == jump_3) and 3) or ((jump == jump_2) and 2) or 1 if state then self.data[v].selected_group = self.data[v].selected_group + jumps else self.data[v].selected_group = self.data[v].selected_group - jumps end if (self.data[v].selected_group > #self.data[v].cnt_group) then self.data[v].selected_group = 1 elseif (self.data[v].selected_group < 1) then self.data[v].selected_group = #self.data[v].cnt_group end return self.data[v].cnt_group[self.data[v].selected_group][1] --// Horizental movement else print_dbg("selected_group: %s", self.data[v].selected_group) local group = self.data[v].cnt_group[self.data[v].selected_group] if group then local first = group[1] local last = group[#group] if state and (self.data[v].selected < last) then self.data[v].selected = self.data[v].selected + 1 elseif (not state) and (self.data[v].selected > first) then self.data[v].selected = self.data[v].selected - 1 end else printe("! group doesn't exist for selected_group: %s", self.data[v].selected_group) end return self.data[v].selected end end if (not self.data[v].selected) or (not self.data[v].selected_group) then self.data[v].selected = _selected[v] or 1 self.data[v].selected_group = _selected_group[v] or 1 else self.data[v].par_hl[self.data[v].selected]:Show(false) self.data[v].selected = get_param_index(state, vert) end self.data[v].par_hl[self.data[v].selected]:Show(true) _selected[v] = self.data[v].selected _selected_group[v] = self.data[v].selected_group end function WpnHudEditor:SwitchParamByIndex(group, index) local v = self.active_table self.data[v].par_hl[self.data[v].selected]:Show(false) self.data[v].selected_group = group self.data[v].selected = index self.data[v].par_hl[self.data[v].selected]:Show(true) _selected[v] = self.data[v].selected _selected_group[v] = self.data[v].selected_group end function WpnHudEditor:SwitchValue(state, selected, extra_k) local v = self.active_table if not extra_k then extra_k = 1 end if not (selected) then return end local n = selected local typ = self.data[v].typ[n] local parent = self.data[v].parent[n] local val = self.data[v].value[n] local new_val if typ ~= 0 then local input_val = self.data[v].par[n]:GetText() local curr_val = (not self:IsInvalidValue(n,typ,input_val)) and tonumber(input_val) if (not curr_val) then self:Send_MSG("Couldn't read previous input for parameter (%s)", self.data[v].name[n]) return end curr_val = round_idp(curr_val, precision) local max_val = v[parent].max local min_val = v[parent].min local step = v[parent].step * jump * extra_k if state then curr_val = curr_val + step else curr_val = curr_val - step end local precision = precision if curr_val > 100 then precision = precision - 2 elseif curr_val > 10 then precision = precision - 1 end curr_val = round_idp(curr_val, precision) new_val = clamp(curr_val, min_val, max_val) else local curr_val = self.data[v].par[n]:GetText() if (not curr_val) then return end if not (self.data[v].par_list_n[n]) then return end if (not self.data[v].index[n]) then for i=1,#self.data[v].par_list_n[n] do if (self.data[v].par_list_n[n][i] == curr_val) then self.data[v].index[n] = i break end end if (not self.data[v].index[n]) then return end end local num = #self.data[v].par_list_n[n] if state then self.data[v].index[n] = (self.data[v].index[n] < num) and (self.data[v].index[n] + 1) or self.data[v].index[n] else self.data[v].index[n] = (self.data[v].index[n] > 0) and (self.data[v].index[n] - 1) or self.data[v].index[n] end local txt = self.data[v].par_list_n[n][self.data[v].index[n]] if self:IsInvalidValue(n,0,txt) then self:Send_MSG("Invalid path/section for parameter (%s)", self.data[v].name[n]) return end new_val = txt end if new_val then self.data[v].value[n] = new_val self.data[v].par[n]:SetText(new_val) self:SetParameterValue(n,new_val) self:ApplyParameterValue(typ,parent,v) -- self:Send_MSG(self.data[v].name[n] .. " := " .. self.data[v].value[n], 4) else self:Send_MSG("No value can be set for (%s)",self.data[v].name[n]) end end function WpnHudEditor:SwitchValueGroup(state, selected_group) local v = self.active_table if (not selected_group) then return end print_dbg("/ WpnHudEditor | selected_group: %s", selected_group) local group = self.data[v].cnt_group[selected_group] local size = (#group > 3) and 3 or #group -- no need for alpha value for i=1,size do self:SwitchValue(state, group[i]) end end function WpnHudEditor:OnInput(cnt) local v = self.active_table local val = self.data[v].par[cnt]:GetText() local typ = self.data[v].typ[cnt] local parent = self.data[v].parent[cnt] if self:IsInvalidValue(cnt,typ,val) then self:Send_MSG("Error with input for parameter (%s)", self.data[v].name[cnt]) return end if (typ == 0) then val = val or "" else val = tonumber(val) val = round_idp(val, precision) end self.data[v].value[cnt] = val self:SetParameterValue(cnt,val) self:ApplyParameterValue(typ,parent,v) end function WpnHudEditor:Close() local v = self.active_table if v ~= parameters then self.data[v].scroll_par:Show(false) end self.weapon_cobj = nil self.active_table = parameters local v = self.active_table self.data[v].scroll_par:Show(true) self.third_person_mode = false self:ResetSelects() self:StopCam() self:DisableWCT(false) self:HideDialog() self:Show(false) --ui = nil if adjust_active then actor_menu.set_msg(1, "HUD adjust mode is still active",5) end exec_console_cmd("hud_crosshair " .. (self.crosshair and "on" or "off")) UnregisterScriptCallback("on_key_release",on_key_release) UnregisterScriptCallback("on_key_hold",on_key_hold) Unregister_UI("WpnHudEditor") if (level.present()) then printf("- main_menu off") exec_console_cmd("main_menu off") end end local K_M1 = DIK_keys.MOUSE_1 local K_M2 = DIK_keys.MOUSE_2 local K_M3 = DIK_keys.MOUSE_3 local E_PRESS = ui_events.WINDOW_KEY_PRESSED local E_RELEASE = ui_events.WINDOW_KEY_RELEASED function WpnHudEditor:OnKeyboard(dik, keyboard_action) local v = self.active_table local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action) if (res == false) then local bind = dik_to_bind(dik) -- Mouse if dik == K_M1 or dik == K_M2 or (dik == K_M3 and self.third_person_mode) then if (keyboard_action == E_PRESS) then -- printf("mouse hold") self.mouse_hold = dik self.disable_drag = false elseif (keyboard_action == E_RELEASE) then -- printf("mouse release") self.disable_drag = true self.pos = nil self.mouse_hold = nil end elseif keyboard_action == ui_events.WINDOW_KEY_PRESSED then if dik == DIK_keys.DIK_NUMPAD8 then self:SwitchValue(true, self.data[v].selected) elseif dik == DIK_keys.DIK_NUMPAD2 then self:SwitchValue(false, self.data[v].selected) elseif dik == DIK_keys.DIK_NUMPAD9 then self:SwitchValueGroup(true, self.data[v].selected_group) elseif dik == DIK_keys.DIK_NUMPAD3 then self:SwitchValueGroup(false, self.data[v].selected_group) elseif dik == DIK_keys.DIK_NUMPADENTER then self.save_mode = not self.save_mode self:Print(nil, self.save_mode == true and "save to cache_dbg.ltx" or "vanilla save") elseif dik == DIK_keys.DIK_NUMPAD1 then self:CleanCacheDbg() elseif dik == DIK_keys.DIK_UP then self:SwitchParam(false, true) elseif dik == DIK_keys.DIK_DOWN then self:SwitchParam(true, true) elseif dik == DIK_keys.DIK_RIGHT then self:SwitchParam(true, false) elseif dik == DIK_keys.DIK_LEFT then self:SwitchParam(false, false) elseif dik == DIK_keys.DIK_NUMPAD5 then self:OnButtonCopy() elseif dik == DIK_keys.DIK_NUMPAD6 then self:OnButtonPaste() elseif dik == DIK_keys.DIK_G then self:OnButtonAlign() elseif dik == DIK_keys.DIK_DELETE then self:CleanMemo() elseif dik == DIK_keys.DIK_H then self:ShowHint() elseif dik == DIK_keys.DIK_ESCAPE then self:Close() end end end return res end function WpnHudEditor:OnButtonThirdPerson() if not db.actor:active_item() then self:Print(nil, "Actor has no current item, abort") return end if not ( level.set_cam_custom_position_direction and level.remove_cam_custom_position_direction ) then self:Print(nil, "Missing camera functions from modded exes, TP mode is unavailable") return end self.third_person_mode = not self.third_person_mode local v = self.active_table self.data[v].scroll_par:Show(false) if self.third_person_mode then self.active_table = third_person_parameters self:ResetSelects(true) hud_adjust.enabled(false) dummy_npc = alife_create("sim_default_stalker_0", db.actor:position(), db.actor:level_vertex_id(), db.actor:game_vertex_id()) CreateTimeEvent("ui_debug_wpn_hud_dummy", "ui_debug_wpn_hud_dummy_npc", 0, function() local obj = level.object_by_id(dummy_npc.id) if obj then db.storage[obj:id()] = db.storage[obj:id()] or {} db.storage[obj:id()].ui_debug_wpn_hud_dummy = { state = self.dummy_npc_states[1][2] } obj:iterate_inventory(function(owner, item) alife_release_id(item:id()) end, obj) local weapon_sec = db.actor:active_item():section() local weapon = alife_create_item(weapon_sec, obj) CreateTimeEvent("ui_debug_wpn_hud_dummy", "ui_debug_wpn_hud_dummy_weapon", 0, function() local w = level.object_by_id(weapon.id) if w then self.weapon = w self.weapon_cobj = w:cast_Weapon() self:ApplyThirdPersonParameterValue() return true end return false end) self:ResetNpcCam() return true end return false end) -- self.weapon_cobj = db.actor:active_item():cast_Weapon() else self.active_table = parameters self.weapon_cobj = nil self.weapon = nil self:StopCam() hud_adjust.enabled(true) end local v = self.active_table self.data[v].scroll_par:Show(true) end -- NPC Dummy actid = 198122 evaid = 198122 class "evaluator_stalker_ui_debug_wpn_hud_dummy" (property_evaluator) function evaluator_stalker_ui_debug_wpn_hud_dummy:__init(npc,name,storage) super (nil, name) self.st = storage end function evaluator_stalker_ui_debug_wpn_hud_dummy:evaluate() --utils_data.debug_write("eva_panic") local npc = self.object local id = npc:id() db.storage[id] = db.storage[id] or {} local st = db.storage[id] -- printf("checking dummy_npc %s", npc:name()) if st.ui_debug_wpn_hud_dummy then return true end return false end class "action_stalker_ui_debug_wpn_hud_dummy" (action_base) function action_stalker_ui_debug_wpn_hud_dummy:__init (npc,name,storage) super (nil,name) self.st = storage end function action_stalker_ui_debug_wpn_hud_dummy:initialize() action_base.initialize(self) -- local npc = self.object -- npc:set_desired_position() -- npc:set_desired_direction() self.first_update = true end function action_stalker_ui_debug_wpn_hud_dummy:execute() --utils_data.debug_write(strformat("action_stalker_ui_debug_wpn_hud_dummy:execute start")) action_base.execute(self) local npc = self.object --printf("enemy = %s",enemy and enemy:name()) -- ensure and enforce path type -- if (npc:path_type() ~= game_object.level_path) then -- npc:set_path_type(game_object.level_path) -- end -- printf("executing dummy_npc %s", npc:name()) npc:set_desired_position() npc:set_desired_direction() npc:set_dest_level_vertex_id(npc:level_vertex_id()) state_mgr.set_state(npc, db.storage[npc:id()].ui_debug_wpn_hud_dummy.state, nil, nil, { -- look_position = self.st.lvid and level.vertex_position(self.st.lvid) or npc:position(), -- look_object = db.actor, -- look_dir = self.st.lvid and level.vertex_position(self.st.lvid):sub(npc:position()):normalize() or npc:direction(), }, { fast_set = true, animation = true, }) -- First update force movement if self.first_update then -- npc:clear_animations() -- npc:movement_enabled(false) -- npc:set_movement_type(move.stand) -- npc:set_body_state(move.standing) -- npc:set_mental_state(anim.danger) self.first_update = false end end function action_stalker_ui_debug_wpn_hud_dummy:finalize() action_base.finalize(self) self.first_update = true db.storage[self.object:id()].ui_debug_wpn_hud_dummy = nil self.object:clear_animations() self.object:movement_enabled(true) end function setup_generic_scheme(npc,ini,scheme,section,stype,temp) local st = xr_logic.assign_storage_and_bind(npc,ini,"stalker_ui_debug_wpn_hud_dummy",section,temp) end function add_to_binder(npc,ini,scheme,section,storage,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end if not npc:alive() then manager:add_evaluator(evaid,property_evaluator_const(false)) temp.needs_configured = false return end local evaluator = evaluator_stalker_ui_debug_wpn_hud_dummy(npc,"eva_stalker_ui_debug_wpn_hud_dummy",storage) temp.action = action_stalker_ui_debug_wpn_hud_dummy(npc,"act_stalker_ui_debug_wpn_hud_dummy",storage) if not evaluator or not temp.action then return end manager:add_evaluator(evaid,evaluator) temp.action:add_precondition(world_property(stalker_ids.property_alive,true)) temp.action:add_precondition(world_property(stalker_ids.property_danger, false)) temp.action:add_precondition(world_property(evaid,true)) temp.action:add_effect(world_property(evaid,false)) manager:add_action(actid,temp.action) --xr_logic.subscribe_action_for_events(npc, storage, temp.action) end function configure_actions(npc,ini,scheme,section,stype,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager or not temp.action then return end temp.action:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base,false)) -- temp.action:add_precondition(world_property(xr_evaluators_id.wounded_exist,false)) -- if (_G.schemes["rx_ff"]) then -- temp.action:add_precondition(world_property(rx_ff.evaid,false)) -- end if (_G.schemes["gl"]) then temp.action:add_precondition(world_property(rx_gl.evid_gl_reload,false)) end -- if (_G.schemes["facer"]) then -- temp.action:add_precondition(world_property(xrs_facer.evid_facer,false)) -- temp.action:add_precondition(world_property(xrs_facer.evid_steal_up_facer,false)) -- end local action local p = {xr_danger.actid, stalker_ids.action_combat_planner, stalker_ids.action_danger_planner, xr_actions_id.state_mgr + 2, xr_actions_id.alife} for i=1,#p do --printf("ACTION_ALIFE_ID(permaban_material.configure_actions): " .. tostring(p[i])) action = manager:action(p[i]) if (action) then action:add_precondition(world_property(evaid,false)) else printf("axr_panic: no action id p[%s]",i) end end end function disable_generic_scheme(npc,scheme,stype) local st = db.storage[npc:id()][scheme] if st then st.enabled = false end end function npc_add_precondition(action) if not action then return end action:add_precondition(world_property(evaid,false)) end LoadScheme("ui_debug_wpn_hud", "stalker_ui_debug_wpn_hud_dummy", modules.stype_stalker) function on_enemy_eval(obj, enemy, flags) if dummy_npc and (obj:id() == dummy_npc.id or enemy:id() == dummy_npc.id) then flags.override = true flags.result = false end end function npc_on_before_hit(npc,shit,bone_id,flags) if dummy_npc and npc:id() == dummy_npc.id then flags.ret_value = false end end function on_game_start() local function on_localization_change() GUI = nil -- clear ui to force initing again end RegisterScriptCallback("on_localization_change",on_localization_change) RegisterScriptCallback("on_enemy_eval", on_enemy_eval) RegisterScriptCallback("npc_on_before_hit", npc_on_before_hit) end