Divergent/mods/Screen Space Shaders/gamedata/scripts/ui_debug_weather.script

1720 lines
51 KiB
Plaintext

--[[
Script by: Tronex
Engine support by: Rezy
Added: 2019/6/1
Last Edit: 2020/3/23
Weather Editor 2.0
Weather editor allows you to change weather values in real-time. Minute precision.
The GUI is interactive and easy to use, with ability to modify or create new weather files.
Edited values are temporarly cached for the weathers, hours and minutes you worked on. You can return to them in case you turned off the editor (Avoid exiting or reloading when you have unsaved values).
In (Viewer) mode, you can witness weather changes by the time slider.
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.
• Q: previous hour.
• A: next hour.
• W: previous minute.
• S: next minute.
• E: increase selected parameter value.
• D: reduce selected parameter value.
• R: increase selected group value (all parameters within).
• F: reduce selected group value (all parameters within).
• T: next moment.
• G: previous moment.
• C: copy select paramter value.
• C + CTRL: copy current moment settings.
• V: paste/apply copied parameter value.
• V + CTRL: paste/apply to current moment settings.
• Z: viewer mode.
• Delete: Reset current modified moment.
• Delete + CTRL: Reset all modified moments.
• H: toggle hint window.
• Esc or Home: turn off editor.
--]]
local parameters = {
-- Type: 0 = string | 1 = number | 2 = 3d vector | 3 = 4d vector |
["ambient"] = { typ = 0, def = {""}, indx = 1 , ini= "environment\\ambients.ltx" },
["ambient_color"] = { typ = 2, def = {0,0,0}, indx = 2 , min = 0, max = 1, step = 0.001 },
["clouds_color"] = { typ = 3, def = {0,0,0,0}, indx = 3 , min = 0, max = 1, step = 0.001 },
["clouds_texture"] = { typ = 0, def = {""}, indx = 4 , empty_value= true },
["far_plane"] = { typ = 1, def = {0}, indx = 5 , min = 200, max = 1500, step = 1 },
["fog_color"] = { typ = 2, def = {0,0,0}, indx = 6 , min = 0, max = 1, step = 0.001 },
["fog_density"] = { typ = 1, def = {0}, indx = 7 , min = 0, max = 1, step = 0.01 },
["fog_distance"] = { typ = 1, def = {0}, indx = 8 , min = 0, max = 1500, step = 1 },
["hemisphere_color"] = { typ = 3, def = {0,0,0,0}, indx = 9 , min = 0, max = 1, step = 0.001 },
["rain_density"] = { typ = 1, def = {0}, indx = 10, min = 0, max = 1, step = 0.01 },
["rain_color"] = { typ = 2, def = {0,0,0}, indx = 11, min = 0, max = 1, step = 0.001 },
["sky_color"] = { typ = 2, def = {0,0,0}, indx = 12, min = 0, max = 1, step = 0.001 },
["sky_rotation"] = { typ = 1, def = {0}, indx = 13, min = 0, max = 360, step = 0.005 },
["sky_texture"] = { typ = 4, def = {"",""}, indx = 14 , path= "sky\\" },
["sun"] = { typ = 0, def = {""}, indx = 15 , ini= "environment\\suns.ltx" , empty_value= true },
["sun_color"] = { typ = 2, def = {0,0,0}, indx = 16, min = 0, max = 5, step = 0.001 },
-- ["sun_longitude"] = { typ = 1, def = {0}, indx = 17, min = 0, max = 360, step = 1 },
-- ["sun_altitude"] = { typ = 1, def = {0}, indx = 18, min = 0, max = 360, step = 1 },
["sun_shafts_intensity"] = { typ = 1, def = {0}, indx = 19, min = 0, max = 1, step = 0.01 },
["thunderbolt_collection"] = { typ = 0, def = {""}, indx = 20 , ini= "environment\\thunderbolt_collections.ltx" , empty_value= true },
["thunderbolt_duration"] = { typ = 1, def = {0}, indx = 21, min = 0, max = 1, step = 0.01 },
["thunderbolt_period"] = { typ = 1, def = {0}, indx = 22, min = 0, max = 1000, step = 1 },
["water_intensity"] = { typ = 1, def = {0}, indx = 23, min = 0, max = 1, step = 0.01 },
["wind_velocity"] = { typ = 1, def = {0}, indx = 24, min = 0, max = 1000, step = 1 },
["tree_amplitude_intensity"] = { typ = 1, def = {0}, indx = 25, min = 0, max = 1, step = 0.01 },
["wind_direction"] = { typ = 1, def = {0}, indx = 26, min = 0, max = 360, step = 1 },
["bloom_threshold"] = { typ = 1, def = {0}, indx = 50 , min = 1, max = 100, step = 0.01 },
["bloom_exposure"] = { typ = 1, def = {0}, indx = 51 , min = 0, max = 100, step = 0.01 },
["bloom_sky_intensity"] = { typ = 1, def = {0}, indx = 52 , min = 0, max = 100, step = 0.01 },
}
local dir = "environment\\weathers\\"
local precision = 6 -- allowed number of zeros
local clr_list = {
["def"] = GetARGB(255,200,200,200),
["temp"] = GetARGB(255,255,100,100),
["saved"] = GetARGB(255,100,255,100),
}
local string_find = string.find
local string_gsub = string.gsub
local func_small = function(t,a,b) return a < b end
-- Keybinds
local jump_1 = 1
local jump_2 = 10
local jump_3 = 50
local D_JUMP2 = DIK_keys.DIK_LSHIFT
local D_JUMP3 = DIK_keys.DIK_LCONTROL
local D_HOUR_ADD = DIK_keys.DIK_Q
local D_HOUR_SUB = DIK_keys.DIK_A
local D_MIN_ADD = DIK_keys.DIK_W
local D_MIN_SUB = DIK_keys.DIK_S
local D_MOMENT_ADD = DIK_keys.DIK_E
local D_MOMENT_SUB = DIK_keys.DIK_D
local D_VAL_ADD = DIK_keys.DIK_R
local D_VAL_SUB = DIK_keys.DIK_F
local D_VALG_ADD = DIK_keys.DIK_T
local D_VALG_SUB = DIK_keys.DIK_G
local D_COPY = DIK_keys.DIK_C
local D_PASTE = DIK_keys.DIK_V
local D_MOVE_UP = DIK_keys.DIK_UP
local D_MOVE_DOWN = DIK_keys.DIK_DOWN
local D_MOVE_LEFT = DIK_keys.DIK_LEFT
local D_MOVE_RIGHT = DIK_keys.DIK_RIGHT
local D_VIEWER = DIK_keys.DIK_Z
local D_CLEAR = DIK_keys.DIK_DELETE
local D_HELP = DIK_keys.DIK_H
-------------------------------------------------------------------
GUI = nil -- instance, don't touch
function start(owner)
-- Hide the owner
if owner then
if (owner:IsShown()) then
owner:HideDialog()
owner:Show(false)
end
else
hide_hud_inventory()
end
if (not GUI) then
GUI = WeatherEditor(owner)
end
if (GUI) and (not GUI:IsShown()) then
GUI:ShowDialog(true)
GUI:Refresh()
Register_UI("WeatherEditor","ui_debug_weather")
end
end
-------------------------------------------------------------------
class "WeatherEditor" (CUIScriptWnd)
function WeatherEditor:__init(owner) super()
self.owner = owner
self.debug = true
self.help = false
self.select = 1
self.select_g = 1
self.jump = jump_1
-- Indexing
self.idx = 0 -- number of indexes
self.idx_g = {} -- index [per count]
self.idx_i = {} -- inner index [per index]
self.idx_p = {} -- count [per par]
self.name = {} -- parameter name [per index]
self.type = {} -- parameter type [per index]
self.parent = {} -- parent parameter [per index]
self.list = {} -- parameter list [line] = true [per index]
self.list_n = {} -- parameter list [order] = line [per index]
self.list_i = 1 -- to trace list values
self.folder = {} -- parameter list [line] = true [per index]
self.folder_last = {} -- to trace last selected folder
-- current time
self.c_weather = level.get_weather()
self.c_hour = math.floor(level.get_time_hours())
self.c_minute = math.floor(level.get_time_minutes())
-- Storage
self.range = {}
self.memo = {}
self.copy = {}
self.copy_1 = {}
-- Viewer
self.vw_i = {}
self.vw_val = 1
self.vw_width = 0
self.vw_pos = 0
self.vw_autoplay = false
self.vw_tg = 0
self.vw_g_step = 100 -- [ms] autoplay speed (1 min per time step )
self.vw_marks = {}
self.vw_ruler_start = false
self.vw_ruler_min = {
[15] = true,
[30] = true,
[45] = true,
}
-- Weather files
self.weather_files = {}
local fileList = getFS():file_list_open("$game_config$", dir, bit_or(FS.FS_ListFiles, FS.FS_RootOnly))
local count = fileList and fileList:Size() or 0
if count > 0 then
for i = 1, count do
local file_name_full = fileList:GetAt(i - 1)
local file_name = file_name_full:sub(1, -5)
local file_ext = file_name_full:sub(-4, -1)
if file_name and starts_with(file_name,"w_") and (file_ext == ".ltx") then
self.weather_files[#self.weather_files + 1] = file_name
end
end
end
self:InitControls()
self:InitCallBacks()
self:Reset(true)
end
function WeatherEditor:__finalize()
end
function WeatherEditor:InitControls()
self:SetWndRect (Frect():set(0,0,1024,768))
self:SetAutoDelete(true)
self.xml = self.owner and self.owner.xml or CScriptXmlInit()
local xml = self.xml
if (not self.owner) then
xml:ParseFile ("ui_debug_launcher.xml")
end
self.dialog = xml:InitStatic("dbg_weather_editor", self)
self.frame = xml:InitFrame("dbg_weather_editor:frame", self.dialog)
self.cap = xml:InitStatic("dbg_weather_editor:cap",self.dialog)
-- Select weather file
self.cap_weather = xml:InitStatic("dbg_weather_editor:cap_weather",self.dialog)
self.list_weather = xml:InitComboBox("dbg_weather_editor:list_weather", self.dialog)
self.list_weather:SetAutoDelete(true)
self:Register(self.list_weather, "list_weather")
-- Select time
self.cap_time = xml:InitStatic("dbg_weather_editor:cap_time",self.dialog)
self.input_hour = xml:InitEditBox("dbg_weather_editor:input_hour", self.dialog)
self:Register(self.input_hour,"input_hour")
self.input_minute = xml:InitEditBox("dbg_weather_editor:input_minute", self.dialog)
self:Register(self.input_minute,"input_minute")
-- Buttons
self.dialog_btn = xml:InitStatic("dbg_weather_editor:buttons",self.dialog)
-- Copy params
self.btn_copy = xml:Init3tButton("dbg_weather_editor:buttons:btn_copy", self.dialog_btn)
self:Register(self.btn_copy, "btn_copy")
-- Paste params
self.btn_paste = xml:Init3tButton("dbg_weather_editor:buttons:btn_paste", self.dialog_btn)
self:Register(self.btn_paste, "btn_paste")
-- View
self.btn_view = xml:Init3tButton("dbg_weather_editor:buttons:btn_view", self.dialog_btn)
self:Register(self.btn_view, "btn_view")
-- Save
self.btn_save = xml:Init3tButton("dbg_weather_editor:buttons:btn_save", self.dialog_btn)
self:Register(self.btn_save, "btn_save")
-- Resume
self.btn_resume = xml:Init3tButton("dbg_weather_editor:buttons:btn_resume", self.dialog_btn)
self:Register(self.btn_resume, "btn_resume")
-- Clear
self.btn_clear = xml:Init3tButton("dbg_weather_editor:buttons:btn_clear", self.dialog_btn)
self:Register(self.btn_clear, "btn_clear")
-- Help
self.btn_help = xml:Init3tButton("dbg_weather_editor:buttons:btn_help", self.dialog_btn)
self:Register(self.btn_help, "btn_help")
-- Exit
self.btn_exit = xml:Init3tButton("dbg_weather_editor:buttons:btn_exit", self.dialog_btn)
self:Register(self.btn_exit, "btn_exit")
-- Name new preset
self.input_preset = xml:InitEditBox("dbg_weather_editor:input_preset", self.dialog)
self.input_preset:SetText("Enter new file name...")
--self:Register(self.input_preset,"input_preset")
-- Line
xml:InitStatic("dbg_weather_editor:line",self.dialog)
-- Parameters
self.par = {} -- input box [per index]
self.par_cap = {} -- parameter cap [per count]
self.par_hl = {} -- highlight box [per index]
self.scroll_par = xml:InitScrollView("dbg_weather_editor:scroll_par", self.dialog)
self.scroll_par:Clear()
self.st = {}
local cnt = 0
local functor = function(t,a,b) return t[a].indx < t[b].indx end
for par,v in spairs(parameters,functor) do
cnt = cnt + 1
self.st[cnt] = xml:InitStatic("dbg_weather_editor:tmp_par", nil)
self.idx_g[cnt] = {}
self.par_cap[cnt] = xml:InitTextWnd("dbg_weather_editor:op_par:cap", self.st[cnt])
self.par_cap[cnt]:SetText(par)
-- Multi boxes for vectors
local typ = v.typ
local idx = ((typ == 2) and 3) or ((typ == 3) and 4) or ((typ == 4) and 2) or 1
for i=1,idx do
-- Tracing
local ext = self:GetStringByType(i,typ)
self.idx = self.idx + 1
self.idx_g[cnt][i] = self.idx
self.idx_i[self.idx] = i
self.idx_p[par] = cnt
self.name[self.idx] = par .. ext
self.type[self.idx] = typ
self.parent[self.idx] = par
-- Parameter element and hightlight texture
if self:IsList(typ) then
self.par[self.idx] = xml:InitComboBox("dbg_weather_editor:op_par:list" .. ext, self.st[cnt])
self.par_hl[self.idx] = xml:InitStatic("dbg_weather_editor:op_par:hl_list" .. ext, self.st[cnt])
else
self.par[self.idx] = xml:InitEditBox("dbg_weather_editor:op_par:input" .. ext, self.st[cnt])
self.par_hl[self.idx] = xml:InitStatic("dbg_weather_editor:op_par:hl" .. ext, self.st[cnt])
end
-- Hightlight last selected parameter
if (self.idx ~= self.select) then
self.par_hl[self.idx]:Show(false)
end
local ii = self.idx
local _wrapper = function(handler) -- we need wrapper in order to pass ctrl to method
self:On_Param(ii)
end
self:Register(self.par[self.idx], "par_" .. self.idx)
self:AddCallback("par_" .. self.idx, self:IsList(typ) and ui_events.LIST_ITEM_SELECT or ui_events.EDIT_TEXT_COMMIT, _wrapper, self)
end
self.scroll_par:AddWindow(self.st[cnt], true)
self.st[cnt]:SetAutoDelete(false)
end
-- Weather viewer
self.dialog_v = xml:InitStatic("dbg_weather_viewer",self)
self.vw_frame = xml:InitFrame("dbg_weather_viewer:frame", self.dialog_v)
self.vw_scroll = xml:InitScrollView("dbg_weather_viewer:scroll", self.dialog_v)
self.vw_track = xml:InitTrackBar("dbg_weather_viewer:track", self.dialog_v)
self.vw_time = xml:InitTextWnd("dbg_weather_viewer:time", self.dialog_v)
self.vw_cap_autoplay = xml:InitStatic("dbg_weather_viewer:playing",self.dialog_v)
self.vw_btn_play = xml:Init3tButton("dbg_weather_viewer:btn_play", self.dialog_v)
self.vw_btn_pause = xml:Init3tButton("dbg_weather_viewer:btn_pause", self.dialog_v)
self.vw_width = self.vw_track:GetWidth()
self.vw_pos = self.vw_track:GetWndPos()
self:Register(self.vw_btn_play, "btn_play")
self:Register(self.vw_btn_pause, "btn_pause")
self.dialog_v:Show(false)
self.vw_cap_autoplay:Show(false)
--self.vw_btn_play:Show(false)
--self.vw_btn_pause:Show(false)
local is_wide = utils_xml.is_widescreen()
self.width = (device().width) - self.dialog:GetWidth()
self.hight = (device().height)
self.msg_width = is_wide and (self.width * 0.8) or self.width
-- Message boxes
self.message_box_save = CUIMessageBoxEx()
self:Register(self.message_box_save, "mb_save")
-- 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))
-- Help Window
self.help_wnd = xml:InitFrame("help_wnd:background",self)
self.help_wnd:SetAutoDelete(false)
self.help_wnd_text = xml:InitTextWnd("help_wnd:text",self.help_wnd)
--self.help_wnd_text:SetTextAlignment(2)
self.help_wnd_text:SetText(game.translate_string("st_ui_dbg_weather_about"))
self.help_wnd_text:AdjustHeightToText()
self.help_wnd_text:SetWndSize(vector2():set(self.msg_width, self.help_wnd_text:GetHeight()+10))
self.help_wnd_text:SetWndPos(vector2():set(20,10))
self.help_wnd:Show(false)
self.help_wnd:SetWndSize(vector2():set(self.msg_width, 700))
self.help_wnd:SetWndPos(vector2():set( self.dialog:GetWidth() , 1 ))
self.help_wnd:SetColor(GetARGB(255,0,0,0))
-- Hint Window
self.hint_wnd = xml:InitFrame("hint_wnd:background",self)
self.hint_wnd:SetAutoDelete(false)
self.hint_wnd_text = xml:InitTextWnd("hint_wnd:text",self.hint_wnd)
self.hint_wnd:Show(false)
end
function WeatherEditor:InitCallBacks()
self:AddCallback("list_weather", ui_events.LIST_ITEM_SELECT, self.On_Weather, self)
self:AddCallback("input_hour", ui_events.EDIT_TEXT_COMMIT, self.On_Time, self)
self:AddCallback("input_minute", ui_events.EDIT_TEXT_COMMIT, self.On_Time, self)
self:AddCallback("btn_copy", ui_events.BUTTON_CLICKED, self.OnBTN_Copy, self)
self:AddCallback("btn_paste", ui_events.BUTTON_CLICKED, self.OnBTN_Paste, self)
self:AddCallback("btn_view", ui_events.BUTTON_CLICKED, self.Viewer_Start, self)
self:AddCallback("btn_save", ui_events.BUTTON_CLICKED, self.OnBTN_Save, self)
self:AddCallback("btn_resume", ui_events.BUTTON_CLICKED, self.OnBTN_Resume, self)
self:AddCallback("btn_clear", ui_events.BUTTON_CLICKED, self.OnBTN_Clear, self)
self:AddCallback("btn_exit", ui_events.BUTTON_CLICKED, self.OnBTN_Exit, self)
self:AddCallback("btn_help", ui_events.BUTTON_CLICKED, self.OnBTN_Help, self)
self:AddCallback("btn_play", ui_events.BUTTON_CLICKED, self.Viewer_Play, self)
self:AddCallback("btn_pause", ui_events.BUTTON_CLICKED, self.Viewer_Pause, self)
self:AddCallback("mb_save", ui_events.MESSAGE_BOX_YES_CLICKED, self.SaveToFile, self)
self:AddCallback("mb_save", ui_events.MESSAGE_BOX_NO_CLICKED, self.Discard, self)
self:AddCallback("mb_save", ui_events.MESSAGE_BOX_OK_CLICKED, self.Discard, self)
end
function WeatherEditor:Reset(force)
-- Fill weather file list
self.list_weather:ClearList()
for i=1,#self.weather_files do
local file = self.weather_files[i]
self.list_weather:AddItem(file, i)
self:Print("Reset | self.list_weather:AddItem(%s, %s)",file,i)
end
-- Set up current time
self.list_weather:SetText(self.c_weather)
self.input_hour:SetText(self.c_hour)
self.input_minute:SetText(self.c_minute)
if (not force) then
return
end
-- Fill all string parameter lists
for i=1,self.idx do
if (self.type[i] == 0) then
local par = self.parent[i]
self.par[i]:ClearList()
local path = parameters[par].ini
if path then
local ini = ini_file(path)
if ini then
local cnt = {}
if parameters[par].empty_value then
self:AddToList(par, i, cnt, "")
end
ini:section_for_each( function(section)
self:AddToList(par, i, cnt, section)
end)
end
elseif parameters[par].empty_value then
local cnt = {}
self:AddToList(par, i, cnt, "")
end
elseif (self.type[i] == 4) then
local par = self.parent[i]
local path = parameters[par].path
if path and (not self.folder[par]) then -- only once, which is first list
self.folder[par] = {}
local folderList = getFS():file_list_open("$textures$", path, bit_or(FS.FS_ListFolders, FS.FS_RootOnly))
local count1 = folderList and folderList:Size() or 0
if count1 > 0 then
local cnt = {}
for c = 1, count1 do
local folder_name = folderList:GetAt(c - 1)
if folder_name then
local folder_path = folder_name
self:Print("Reset | Filling path [%s] | Folder = %s", par, folder_path)
self.folder[par][folder_path] = {}
self:AddToList(par, i, cnt, folder_path)
end
end
end
for folder_path,t in pairs(self.folder[par]) do
local fileList = getFS():file_list_open("$textures$", path .. folder_path, bit_or(FS.FS_ListFiles, FS.FS_RootOnly))
local count = fileList and fileList:Size() or 0
if count > 0 then
for c = 1, count do
local file_name_full = fileList:GetAt(c - 1)
local file_name = file_name_full:sub(1, -5)
local file_ext = file_name_full:sub(-4, -1)
if file_name and (not string_find(file_name,"#small")) and (file_ext == ".dds") then
self:Print("Reset | Filling path [%s] | Folder = %s , File: %s", par, folder_path, file_name)
self.folder[par][folder_path][file_name] = true
end
end
end
end
end
end
end
end
function WeatherEditor:Refresh(clear)
self:PauseEngine(true)
if (not self.memo[self.c_weather]) then
local fl = self.c_weather
local ltx_name = dir .. fl .. ".ltx"
local ltx = ini_file(ltx_name)
if ltx then
self:Print("Refresh | Reading LTX (%s)", ltx_name)
self.memo[fl] = {}
-- Fill defined moments
ltx:section_for_each(function(sec)
local h, m = self:StringToTime(sec)
self:Print("Refresh | Reading section [%s] | hour: %s - minute: %s", sec, h, m)
if (not self.memo[fl][h]) then self.memo[fl][h] = {} end
self.memo[fl][h][m] = { defined = true } -- "defined" indicate that this moment is a defined section
for par,v in pairs(parameters) do
local val = self:ParseFromString(ltx, sec, par, v.typ)
-- SSS 22 Change - If the value is NIL... Use v.def
if (val[1] == nil) then
val[1] = v.def[1]
end
self.memo[fl][h][m][par] = val
end
end)
self.range = self:GetTimeRange(self.memo[fl])
end
end
self.input_hour:SetText(self.c_hour)
self.input_minute:SetText(self.c_minute)
-- Lerp non-existing moments
local memo = self:CurrentMoment()
if (not memo) then
memo = self:LerpMoment(self.c_weather, self.c_hour, self.c_minute)
if (not memo) then
self:Print("Refresh | can't lerp new moment for file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
return
end
end
-- Clear any temp moments near this moment
if clear then
self:ClearMomentsInRange(self.c_weather, self.c_hour, self.c_minute)
end
-- Adjusting text color
local defined = memo.defined
if (defined == true) then
self.cap_time:SetTextureColor( clr_list["saved"] )
--self.cap_time:TextControl():SetTextColor( clr_list["saved"] )
elseif (defined == false) then
self.cap_time:SetTextureColor( clr_list["temp"] )
--self.cap_time:TextControl():SetTextColor( clr_list["temp"] )
else
self.cap_time:SetTextureColor( clr_list["def"] )
--self.cap_time:TextControl():SetTextColor( clr_list["def"] )
end
-- Apply weather values in-game
for par,v in pairs(parameters) do
self:Apply(par, v.typ, memo)
end
weather.sun_time(self.c_hour, self.c_minute)
end
function WeatherEditor:Update()
CUIScriptWnd.Update(self)
if (self.msg_wnd_timer and time_global() > self.msg_wnd_timer) then
self.msg_wnd_timer = nil
self.msg_wnd:Show(false)
end
self.help_wnd:Show( self.help )
-- Viewer mode
if self.dialog_v:IsShown() then
-- Player input
if self.vw_autoplay or self.vw_track:IsCursorOverWindow() then
local val = self.vw_track:GetFValue()
val = math.floor(val)
if (val ~= self.vw_val) then
self.vw_val = val
local h,m = self:Viewer_Value()
if h and m then
self.c_hour = h
self.c_minute = m
self:Refresh()
self:Viewer_Update()
end
end
end
-- Auto playing
if self.vw_autoplay and (time_global() > self.vw_tg + self.vw_g_step) then
self.vw_tg = time_global()
local val = self.vw_track:GetFValue()
val = math.floor(val) + 1
val = val > #self.vw_i and 0 or val
self.vw_track:SetFValue( val )
end
-- Hint for markers
for cnt,ele in pairs(self.vw_marks) do
if ele and ele:IsCursorOverWindow() then
local t = self.vw_i[cnt]
local str = self:TimeToString(t.h, t.m)
local ln = (t.defined and "Saved" or "New")
self:SetHint(ln .. " \\n" .. str, t.defined and true or false)
return
end
end
end
self.hint_wnd:Show(false)
end
-- Viewer
function WeatherEditor:Viewer_Play()
self.vw_autoplay = true
self.vw_cap_autoplay:Show(true)
end
function WeatherEditor:Viewer_Pause()
self.vw_autoplay = false
self.vw_cap_autoplay:Show(false)
end
function WeatherEditor:Viewer_Exit()
self:Viewer_Pause()
self.dialog_v:Show(false)
self.dialog:Show(true)
self:Refresh()
end
function WeatherEditor:Viewer_Value()
local point = self.vw_track:GetFValue()
local t = self.vw_i[ math.floor(point) ]
if t then
return t.h, t.m
else
--printf(" can't get table of %s", point)
end
end
function WeatherEditor:Viewer_Update()
self.vw_time:SetText( self:TimeToString( self.c_hour , self.c_minute ) )
local memo = self:CurrentMoment()
local defined = memo and memo.defined
self.vw_time:SetTextColor( ((defined == true) and clr_list["saved"]) or ((defined == false) and clr_list["temp"]) or clr_list["def"] )
end
function WeatherEditor:Viewer_Start()
self.vw_scroll:Clear()
local xml = self.xml
local _st = xml:InitStatic("dbg_weather_viewer:tmp", nil)
empty_table(self.vw_i)
--empty_table(self.vw_marks)
if (not self.vw_ruler) then
self.vw_ruler_start = true
self.vw_ruler = xml:InitStatic("dbg_weather_viewer:tmp", nil)
end
-- Lerping for all moments
local cnt = 0
local cnt_tot = 24 * 60
local hh = self.c_hour
local mm = self.c_minute
local memos = self.memo[self.c_weather]
for h=0,23 do
for m=0,59 do
cnt = cnt + 1
self.vw_i[cnt] = { h = h , m = m }
if (h == hh) and (m == mm) then
point = cnt
end
if not (memos[h] and memos[h][m]) then -- look for undefined moments
local ts, te = self:GetNearestMoment(memos, h, m)
local ps = ts and memos[ts.h] and memos[ts.h][ts.m]
local pe = te and memos[te.h] and memos[te.h][te.m]
if ps and pe then
if (not memos[h]) then memos[h] = {} end
if (not memos[h][m]) then memos[h][m] = {} end
memos[h][m] = self:Lerp(h, m, ts, te, ps, pe)
end
-- Mark saved moments
elseif (memos[h][m].defined ~= nil) then
local defined = memos[h][m].defined
self.vw_i[cnt].defined = defined
local pos = self.vw_width * (cnt / cnt_tot)
self.vw_marks[cnt] = xml:InitStatic("dbg_weather_viewer:marker",_st)
self.vw_marks[cnt]:SetTextureColor( defined and clr_list["saved"] or clr_list["temp"] )
self.vw_marks[cnt]:SetWndPos(vector2():set( pos , 0 ))
end
-- Ruler minutes
if self.vw_ruler_start and self.vw_ruler_min[m] then
local pos = self.vw_width * (cnt / cnt_tot)
local mark = xml:InitStatic("dbg_weather_viewer:marker_min",self.vw_ruler)
mark:SetWndPos(vector2():set( pos , 0 ))
end
end
-- Ruler hours
if self.vw_ruler_start then
local pos = self.vw_width * (cnt / cnt_tot)
local mark = xml:InitStatic("dbg_weather_viewer:marker_hr",self.vw_ruler)
mark:SetWndPos(vector2():set( pos , 0 ))
end
end
self.vw_scroll:AddWindow(_st, true)
self.vw_scroll:AddWindow(self.vw_ruler, true)
_st:SetAutoDelete(true)
self.vw_ruler:SetAutoDelete(false)
self.vw_ruler_start = false
self.vw_track:SetOptFBounds(1, #self.vw_i)
self.vw_track:SetStep( 1 )
self.vw_track:SetFValue( point )
self.vw_val = point
self:Viewer_Update()
self.dialog:Show(false)
self.dialog_v:Show(true)
end
-- Callbacks
function WeatherEditor:On_Weather()
local cid = self.list_weather:CurrentID()
cid = cid == 0 and 1 or cid
self.c_weather = self.weather_files[cid]
if not (self.c_weather and self.c_weather ~= "") then
self:Print("On_Weather | ERROR can't get weather file name [%s] from the list | ID: %s", self.c_weather, cid)
end
level.set_weather(self.c_weather,true)
self:Refresh()
self:MSG("Selected weather file: %s", self.c_weather)
end
function WeatherEditor:On_Time(h, m)
-- Get hour
h = h or tonumber(self.input_hour:GetText() or "")
if (not h) then
self.input_hour:SetText(self.c_hour)
return
end
h = (h > 23 and 23) or (h < 0 and 0) or h
self.c_hour = h
-- Get minute
m = m or tonumber(self.input_minute:GetText() or "")
if (not m) then
self.input_minute:SetText(self.c_hour)
return
end
m = (m > 59 and 59) or (m < 0 and 0) or m
self.c_minute = m
self:Refresh()
self:MSG("Selected time [%s] of file: %s", self:TimeToString(h,m), self.c_weather)
end
function WeatherEditor:On_Param(idx, val, ignore)
local val = val or self.par[idx]:GetText()
local typ = self.type[idx]
local i = self.idx_i[idx]
local par = self.parent[idx]
self:Print("On_Param | idx: %s - i: %s - val: %s - typ: %s - par: %s", idx, i, val, typ, par)
if self:IsInvalidValue(idx,typ,val) then
-- Restore old value
local memo = self:CurrentMoment()
if memo and memo[par][i] then
self.par[idx]:SetText( memo[par][i] )
end
self:MSG("%c[pda_red]Error with input for parameter (%s)", self.name[idx])
return
end
if self:IsList(typ) then
val = val or ""
else
val = tonumber(val)
val = clamp(val, parameters[par].min, parameters[par].max)
val = round_idp(val, precision)
end
local memo = self:CurrentMoment()
if (not memo) then
self:Print("On_Param | Can't find cache for file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
return
end
-- Store value, mark as modified moment
memo[par][i] = val
if (memo.defined == nil) then
memo.defined = false
end
self.par[idx]:SetText(val)
if (not ignore) then
self:Refresh(true)
end
end
function WeatherEditor:OnBTN_Copy()
local memo = self:CurrentMoment()
if (not memo) then
self:Print("OnBTN_Copy | Can't find cache for file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
return
end
self.copy = dup_table(memo)
self.copy.defined = false
self:MSG("Copied values of file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
end
function WeatherEditor:OnBTN_Paste()
if is_empty(self.copy) then
self:MSG("%c[pda_red]Nothing is copied yet!")
return
end
local w = self.c_weather
local h = self.c_hour
local m = self.c_minute
if self.memo[w] and self.memo[w][h] and self.memo[w][h][m] then
self.memo[w][h][m] = dup_table(self.copy)
self:Refresh(true)
else
self:Print("OnBTN_Paste | Can't find cache for file: %s - hour: %s - minute: %s", w, h, m)
end
end
function WeatherEditor:OnBTN_Copy_Param()
local memo = self:CurrentMoment()
if (not memo) then
self:Print("OnBTN_Copy_Param | Can't find cache for file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
return
end
local idx = self.select
if (not idx) then
self:MSG("No parameter is selected to copy")
return
end
local par = self.parent[idx]
local i = self.idx_i[idx]
local val = memo[par][i]
if (not val) then
self:Print("OnBTN_Copy_Param | No value is found for parameter [%s]", par)
return
end
self.copy_1.par = par
self.copy_1.idx = idx
self.copy_1.val = type(val) == "table" and dup_table(val) or val
self:MSG("Copied value (%s) from parameter", val, par)
end
function WeatherEditor:OnBTN_Paste_Param()
local memo = self:CurrentMoment()
if (not memo) then
self:Print("OnBTN_Paste_Param | Can't find cache for file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
return
end
local val = self.copy_1.val
if (not val) then
self:MSG("No value is copied yet")
return
end
local par = self.copy_1.par
local idx = self.copy_1.idx
self:On_Param(idx, val)
self:MSG("Pasted value (%s) for parameter", val, par)
end
function WeatherEditor:OnBTN_Save()
local is_new = false
local file_name = self.input_preset:GetText()
if not (file_name and file_name ~= "") then
file_name = self.c_weather
else
local str_w = string.sub(file_name,1,2)
local is_w = (str_w == "w_")
if not (is_w) then
--self:MSG("%c[pda_red]Incorrect name pattern!\\n%c[ui_gray_1]Naming pattern: w_(custom_name)")
self.message_box_save:InitMessageBox("message_box_ok")
self.message_box_save:SetText( strformat( game.translate_string("st_ui_dbg_weather_save_msg_error") , file_name ) )
self.message_box_save:ShowDialog(true)
return
end
is_new = true
for i=1,#self.weather_files do
local file = self.weather_files[i]
if file == file_name then
is_new = false
end
end
end
file_name = dir .. file_name .. ".ltx"
local str = game.translate_string(is_new and "st_ui_dbg_weather_save_msg_new" or "st_ui_dbg_weather_save_msg_old")
self.message_box_save:InitMessageBox("message_box_yes_no")
self.message_box_save:SetText( strformat( str , file_name ) )
self.message_box_save:ShowDialog(true)
end
function WeatherEditor:OnBTN_Resume()
self:PauseEngine(false)
self:Close()
end
function WeatherEditor:OnBTN_Help()
self.help = (not self.help)
end
function WeatherEditor:OnBTN_Clear()
self.memo[self.c_weather] = nil
self:Refresh()
self:MSG("Cleared memory and all modified moments!")
end
function WeatherEditor:OnBTN_Clear_Moment()
local memo = self:CurrentMoment()
if (not memo) then
self:Print("OnBTN_Clear_Moment | Can't find cache for file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
return
end
if (memo.defined == false) then
self.memo[self.c_weather][self.c_hour][self.c_minute] = nil
self:Refresh(true)
self:MSG("Reset moment for file: %s - hour: %s - minute: %s", self.c_weather, self.c_hour, self.c_minute)
else
self:MSG("Can't reset unmodified moment[%s:%s]", self.c_hour, self.c_minute)
end
end
function WeatherEditor:OnBTN_Exit()
self:Close()
end
function WeatherEditor:OnKeyboard(dik, keyboard_action)
local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
if (res == false) then
local bind = dik_to_bind(dik)
if keyboard_action == ui_events.WINDOW_KEY_RELEASED then
if (dik == D_JUMP3) or (dik == D_JUMP2) then
self.jump = jump_1
end
elseif keyboard_action == ui_events.WINDOW_KEY_PRESSED then
if (dik == D_JUMP3) then
self.jump = jump_3
elseif (dik == D_JUMP2) then
self.jump = jump_2
elseif (dik == D_VAL_ADD) then
self:SwitchValue(true)
elseif (dik == D_VAL_SUB) then
self:SwitchValue(false)
elseif (dik == D_VALG_ADD) then
self:SwitchValueGroup(true)
elseif (dik == D_VALG_SUB) then
self:SwitchValueGroup(false)
elseif (dik == D_MOVE_UP) then
self:SwitchParam(false, true)
elseif (dik == D_MOVE_DOWN) then
self:SwitchParam(true, true)
elseif (dik == D_MOVE_RIGHT) then
self:SwitchParam(true, false)
elseif (dik == D_MOVE_LEFT) then
self:SwitchParam(false, false)
elseif (dik == D_HOUR_ADD) then
local h = self.c_hour + (self.jump == jump_2 and 3 or self.jump == jump_2 and 6 or 1)
self:On_Time( self:ReviseTime(h, self.c_minute) )
elseif (dik == D_HOUR_SUB) then
local h = self.c_hour - (self.jump == jump_2 and 3 or self.jump == jump_2 and 6 or 1)
self:On_Time( self:ReviseTime(h, self.c_minute) )
elseif (dik == D_MIN_ADD) then
local m = self.c_minute + (self.jump == jump_2 and 5 or self.jump == jump_2 and 10 or 1)
self:On_Time( self:ReviseTime(self.c_hour, m) )
elseif (dik == D_MIN_SUB) then
local m = self.c_minute - (self.jump == jump_2 and 5 or self.jump == jump_2 and 10 or 1)
self:On_Time( self:ReviseTime(self.c_hour, m) )
elseif (dik == D_MOMENT_ADD) or (dik == D_MOMENT_SUB) then
local ts, te = self:GetNearestMoment(self.memo[self.c_weather], self.c_hour, self.c_minute)
if (dik == D_MOMENT_ADD) and te.h and te.m then
self:On_Time( self:ReviseTime(te.h, te.m) )
elseif (dik == D_MOMENT_SUB) and ts.h and ts.m then
self:On_Time( self:ReviseTime(ts.h, ts.m) )
end
elseif (dik == D_COPY) then
if self.jump == jump_3 then
self:OnBTN_Copy()
else
self:OnBTN_Copy_Param()
end
elseif (dik == D_PASTE) then
if self.jump == jump_3 then
self:OnBTN_Paste()
else
self:OnBTN_Paste_Param()
end
elseif (dik == D_VIEWER) and (not self.dialog_v:IsShown()) then
self:Viewer_Start()
elseif (dik == D_CLEAR) then
if self.jump == jump_3 then
self:OnBTN_Clear()
else
self:OnBTN_Clear_Moment()
end
elseif (dik == D_HELP) then
self:OnBTN_Help()
elseif (dik == DIK_keys.DIK_ESCAPE) then
if self.dialog_v:IsShown() then
self:Viewer_Exit()
else
self:Close()
end
end
end
end
return res
end
-- Functionality
function WeatherEditor:CurrentMoment()
local w = self.c_weather
local h = self.c_hour
local m = self.c_minute
return self.memo[w] and self.memo[w][h] and self.memo[w][h][m]
end
function WeatherEditor:Apply(par, typ, memo)
local val = memo[par][1]
if (not val) then
self:Print("Apply | MISSING VALUE | par: %s - typ: %s", par, typ)
return
end
if (not self:IsList(typ)) and (type(val) ~= "number") then
self:Print("Apply | INCORRECNT VALUE TYPE | par: %s - typ: %s", par, typ)
return
end
local cnt = self.idx_p[par]
if self:IsList(typ) then
local path = parameters[par].path or ""
local str = val
self.par[ self.idx_g[cnt][1] ]:SetText(val)
if (typ == 4) then
self:Reset_FolderList(self.idx_g[cnt][1], par, val, memo[par][2])
local val_2 = memo[par][2]
self.par[ self.idx_g[cnt][2] ]:SetText(val_2)
str = val .. val_2
end
weather.set_value_string(par, path .. str)
elseif (typ == 1) then
weather.set_value_numric(par,val)
self.par[ self.idx_g[cnt][1] ]:SetText(val)
elseif (typ == 2) then
local val_2 = memo[par][2]
local val_3 = memo[par][3]
if (type(val_2) == "number") and (type(val_3) == "number") then
weather.set_value_vector(par, val, val_2, val_3)
self.par[ self.idx_g[cnt][1] ]:SetText(val)
self.par[ self.idx_g[cnt][2] ]:SetText(val_2)
self.par[ self.idx_g[cnt][3] ]:SetText(val_3)
end
elseif (typ == 3) then
local val_2 = memo[par][2]
local val_3 = memo[par][3]
local val_4 = memo[par][4]
if (type(val_2) == "number") and (type(val_3) == "number") and (type(val_4) == "number") then
weather.set_value_vector(par, val, val_2, val_3, val_4)
self.par[ self.idx_g[cnt][1] ]:SetText(val)
self.par[ self.idx_g[cnt][2] ]:SetText(val_2)
self.par[ self.idx_g[cnt][3] ]:SetText(val_3)
self.par[ self.idx_g[cnt][4] ]:SetText(val_4)
end
end
end
function WeatherEditor:LerpMoment(f, h, m)
local ts, te = self:GetNearestMoment(self.memo[f], h, m)
local ps = ts and self.memo[f][ts.h] and self.memo[f][ts.h][ts.m]
local pe = te and self.memo[f][te.h] and self.memo[f][te.h][te.m]
if ps and pe then
if (not self.memo[f][h]) then self.memo[f][h] = {} end
if (not self.memo[f][h][m]) then self.memo[f][h][m] = {} end
self.memo[f][h][m] = self:Lerp(h, m, ts, te, ps, pe)
self:Print("LerpMoment | file: %s - hour: %s - minute: %s", f, h, m)
return self.memo[f][h][m]
end
end
function WeatherEditor:Lerp(h, m, ts, te, ps, pe)
local t = {}
local min_range = ((te.h - ts.h) * 60) + (te.m - ts.m)
local min_mid = ((h - ts.h) * 60) + (m - ts.m)
local min_ratio = min_mid/min_range
--local par = "ambient_color"
--local v = parameters[par]
for par,v in pairs(parameters) do
t[par] = {}
for i=1,#ps[par] do
if self:IsList(v.typ) then
t[par][i] = ps[par][i]
else
local val = ps[par][i] + ((pe[par][i] - ps[par][i]) * min_ratio)
t[par][i] = round_idp(val, precision)
end
end
end
return t
end
function WeatherEditor:ClearMomentsInRange(f, hh, mm)
local ts, te = self:GetNearestMoment(self.memo[f], hh, mm)
local h1 = ts.h
local h2 = te.h
local m_t = {}
if (h1 == h2) then
m_t[h1] = { s = ts.m , e = te.m }
else
for h = h1, h2 do
m_t[h] = (h == h1) and { s = ts.m , e = 59 } or (h == h2) and { s = 0 , e = te.m } or { s = 0 , e = 59 }
end
end
for h = h1, h2 do
for m = m_t[h].s, m_t[h].e do
local memo = self.memo[f][h] and self.memo[f][h][m]
if memo and memo.defined == nil then
if (h ~= hh) or (h == hh and m ~= mm) then
self.memo[f][h][m] = nil
self:Print("ClearMomentsInRange | for [%s][%s][%s] | [%s][%s] is cleared", f, hh, mm, h, m)
end
end
end
end
end
function WeatherEditor:GetTimeRange(t)
local s,e = {},{}
for h,v in spairs(t, func_small) do
for m,vv in spairs(v, func_small) do
if (vv.defined == true) then
if (not s.h) then
s.h = h
s.m = m
end
e.h = h
e.m = m
end
end
end
self:Print("GetTimeRange | START: h(%s) m(%s) | END: h(%s) m(%s)", s.h, s.m, e.h, e.m )
return { s=s , e=e }
end
function WeatherEditor:GetNearestMoment(t, hh, mm)
local ts, te = { h = 0 , m = 0 }, { h = 23 , m = 59 }
local v, found
-- Searching for nearest previous moment
for h=0,hh do
for m=0,59 do
v = t[h] and t[h][m]
if (v and v.defined ~= nil) and ((h < hh) or (h == hh and m < mm)) then
ts.h = h
ts.m = m
end
end
end
-- Searching for nearest next moment
for h=hh,23 do
for m=0,59 do
v = t[h] and t[h][m]
if (v and v.defined ~= nil) and ((h > hh) or (h == hh and m > mm)) then
found = true
te.h = h
te.m = m
end
if found then break end
end
if found then break end
end
-- Rotate on range limit
if (ts.h == 0) and (ts.m == 0) then
ts = dup_table(self.range.e)
end
if (te.h == 23) and (te.m == 59) then
te = dup_table(self.range.s)
end
--self:Print("GetNearestMoment h(%s) m(%s) | previous: h(%s) m(%s) | next: h(%s) m(%s)", hh, mm, ts.h, ts.m, te.h, te.m )
return ts, te
end
function WeatherEditor:SwitchParam(state, vert)
local function get_param_index(state, vert)
--// Vertical movement - Move between parameters
if vert then
local jumps = ((self.jump == jump_3) and 3) or ((self.jump == jump_2) and 2) or 1
if state then
self.select_g = self.select_g + jumps
else
self.select_g = self.select_g - jumps
end
if (self.select_g > #self.idx_g) then
self.select_g = 1
elseif (self.select_g < 1) then
self.select_g = #self.idx_g
end
return self.idx_g[self.select_g][1]
--// Horizental movement - Move inside a parameter
else
local group = self.idx_g[self.select_g]
if group then
local first = group[1]
local last = group[#group]
if state and (self.select < last) then
self.select = self.select + 1
elseif (not state) and (self.select > first) then
self.select = self.select - 1
end
else
printe("! group doesn't exist for selecte_g: %s", self.select_g)
end
return self.select
end
end
-- Move highlight
self.par_hl[self.select]:Show(false)
self.select = get_param_index(state, vert)
self.par_hl[self.select]:Show(true)
-- Scroll to highlighted button
self:ScrollToElement(self.select)
end
function WeatherEditor:SwitchValue(state, idx, ignore)
local idx = idx or self.select
local par = self.parent[idx]
local i = self.idx_i[idx]
local typ = self.type[idx]
local memo = self:CurrentMoment()
local val = memo[par][i]
local val_n
if (not self:IsList(typ)) then
local min_val = parameters[par].min
local max_val = parameters[par].max
local step = parameters[par].step * self.jump
val = state and (val + step) or (val - step)
val_n = clamp(val, min_val, max_val)
else
if not (self.list_n[idx]) then
return
end
local index
local list = self.list_n[idx]
for i=1,#list do
if list[i] == val then
index = i
break
end
end
if (not index) then
return
end
local list_size = #list
index = state and (index + 1) or (index - 1)
if index > list_size then
index = 1
elseif index < 1 then
index = list_size
end
local txt = list[index]
if self:IsInvalidValue(idx, 0, txt) then
self:MSG("%c[pda_red]Invalid path/section for parameter (%s)", self.name[idx])
return
end
val_n = txt
end
if val_n then
self:On_Param(idx, val_n, ignore)
else
self:MSG("%c[pda_red]No value can be set for (%s)",self.name[idx])
end
end
function WeatherEditor:SwitchValueGroup(state)
local group = self.idx_g[self.select_g]
local size = (#group > 3) and 3 or #group -- no need for alpha value
for i=1,size do
self:SwitchValue(state, group[i], true)
end
self:Refresh(true)
end
function WeatherEditor:ReviseTime(h,m)
if m > 59 then
m = m - 60
h = h + 1
elseif m < 0 then
m = m + 60
h = h - 1
end
if h > 23 then
h = h - 24
elseif h < 0 then
h = h + 24
end
return h, m
end
function WeatherEditor:HasChanges()
local cnt = 0
for h,v1 in pairs(self.memo[self.c_weather]) do
for m,v2 in pairs(v1) do
if v2.defined == false then
cnt = cnt + 1
end
end
end
return cnt
end
function WeatherEditor:SaveToFile()
local is_new = false
local file_name = self.input_preset:GetText()
if not (file_name and file_name ~= "") then
file_name = self.c_weather
else
local str_w = string.sub(file_name,1,2)
local is_w = (str_w == "w_")
if not (is_w) then
self:MSG("%c[pda_red]Incorrect name pattern!\\n%c[ui_gray_1]Naming pattern: w_(custom_name)")
return
end
is_new = true
for i=1,#self.weather_files do
local file = self.weather_files[i]
if file == file_name then
is_new = false
end
end
end
local path = dir .. file_name .. ".ltx"
local ltx = ini_file_ex(path, true)
if (not ltx) then
self:MSG("%c[pda_red]No file found or created!")
return
end
local modified = false
for h,v1 in spairs(self.memo[self.c_weather], func_small) do
for m,v2 in spairs(v1, func_small) do
if is_new and (v2.defined == true) or (v2.defined == false) then
for par,v in pairs(parameters) do
if v2[par] then
local val
if (v.typ == 2) then
val = v2[par][1] .. "," .. v2[par][2] .. "," .. v2[par][3]
elseif (v.typ == 3) then
val = v2[par][1] .. "," .. v2[par][2] .. "," .. v2[par][3] .. "," .. v2[par][4]
elseif (v.typ == 4) then
val = (v.path or "") .. v2[par][1] .. v2[par][2]
else
val = v2[par][1]
end
local section = self:TimeToString(h,m)
ltx:w_value(section, par, val)
modified = true
self:Print("OnBTN_Save | new file? %s - file name: %s | section: %s - parameter: %s - value: %s", is_new, path, section, par, val)
else
self:Print("OnBTN_Save | ERROR can't find modified parameter value [%s]", par)
end
end
end
end
end
if (not modified) then
self:MSG("Saved%sfile: nothing is modified for [%s] yet?", is_new and " new " or " ", file_name)
return
end
-- Save to ltx
ltx:save()
self:MSG("Saved%sfile: %s\\nCheck it under weather's directory: %s", is_new and " new " or " ", file_name, dir)
-- Force reloading file and clearning all temp values
if (is_new) then
self.memo[self.c_weather] = nil
local id = #self.weather_files + 1
self.weather_files[id] = file_name
self:Reset()
-- self.list_weather:SetCurrentID(id)
self.list_weather:SetCurrentIdx(id)
self:On_Weather()
else
self.memo[self.c_weather] = nil
self:Refresh()
end
end
function WeatherEditor:Discard()
end
-- Utilities
local indx_str = {
[1] = "_r",
[2] = "_g",
[3] = "_b",
[4] = "_a",
}
function WeatherEditor:GetStringByType(indx,typ)
if (typ == 0) or (typ == 1) then
return ""
else
return indx_str[indx]
end
end
function WeatherEditor:IsList(typ)
return (typ == 0) or (typ == 4)
end
function WeatherEditor:AddToList(par, indx, cnt, value)
cnt[#cnt + 1] = true
self.par[indx]:AddItem(value, #cnt)
if (not self.list[indx]) then self.list[indx] = {} end
if (not self.list_n[indx]) then self.list_n[indx] = {} end
self.list[indx][value] = true
self.list_n[indx][#self.list_n[indx] + 1] = value
self:Print("Reset | Filling list [%s] | self.list[%s] = %s", par, indx, value)
end
function WeatherEditor:Reset_FolderList(indx, par, val, val_2)
self:Print("Reset | Reset_FolderList | indx: %s - par: %s - val: %s - val_2: %s", indx, par, val, val_2)
indx = indx + 1 -- we need the child list (files)
local folder = self.folder[par][val]
if folder and self.par[indx] and (val ~= self.folder_last[par]) then
self.folder_last[par] = val
self.par[indx]:ClearList()
empty_table(self.list[indx])
empty_table(self.list_n[indx])
local cnt = {}
for file_name,_ in pairs(folder) do
self:AddToList(par, indx, cnt, file_name)
end
local val_f = self.list_n[indx][1]
self.par[indx]:SetText(val_f or "")
if (not self.list[indx][val_2]) then
local memo = self:CurrentMoment()
memo[par][2] = self.list_n[indx][1]
self:Print("Reset | Reset_FolderList | file %s\\%s doesn't exist, use first path %s\\%s", val, val_2, val, memo[par][2])
end
self:Print("Reset | Reset_FolderList | updated self.par[%s] = %s", indx, val_f)
end
end
function WeatherEditor:StringToTime(str)
local t = str_explode(str,":")
return tonumber(t[1]), tonumber(t[2])
end
function WeatherEditor:TimeToString(h,m)
if h < 10 then
h = "0" .. h
end
if m < 10 then
m = "0" .. m
end
return (h .. ":" .. m .. ":00")
end
function WeatherEditor:ParseFromString(ltx, sec, par, typ)
local t = parse_list(ltx, sec, par)
if typ == 4 and t[1] then
local p = str_explode(t[1],"\\")
local file = p[#p] or ""
local folder = ""
for i=1,#p-1 do
folder = folder .. p[i] .. "\\"
end
folder = string_gsub(folder, parameters[par].path, "")
t = { folder , file }
elseif (not self:IsList(typ)) then
for i=1,#t do
t[i] = tonumber(t[i])
end
elseif (not t[1]) then
t[1] = ""
end
return t
end
function WeatherEditor:IsInvalidValue(idx, typ, val)
-- Strings
if self:IsList(typ) then
if val == "" then
return false -- it's ok for strings to be empty
elseif val and self.list[idx][val] then
return false
else
return true -- value that doesn't exist in the list
end
-- Numbers
elseif not (val and val ~= "" and tonumber(val)) then
return true -- not number
end
return false
end
function WeatherEditor:ScrollToElement(idx)
if (not idx) then
return
end
local par = self.parent[idx]
local cnt = self.idx_p[par]
local ele = idx and self.st[cnt]
if (not ele) then
return
end
local h = self.scroll_par:GetHeight()
local curr_pos = self.scroll_par:GetCurrentScrollPos()
local ele_pos = ele:GetWndPos().y
if (ele_pos < curr_pos) or (ele_pos >= curr_pos + h) then
self.scroll_par:SetScrollPos( clamp(ele_pos, self.scroll_par:GetMinScrollPos(), self.scroll_par:GetMaxScrollPos()) )
end
end
-- Final
function WeatherEditor:PauseEngine(state)
-- When we want to work on Weather Editor, then engine must stop calculating weather changes by time
if (state == true) and (not weather.is_paused()) then
weather.pause(true)
elseif (state == false) and (weather.is_paused()) then
weather.pause(false)
end
end
function WeatherEditor:Close()
self:HideDialog()
self:Show(false)
Unregister_UI("WeatherEditor")
if (level.present()) then
printf("- main_menu off")
exec_console_cmd("main_menu off")
end
end
function WeatherEditor:SetHint(text,cl)
if (text == "") then
return
end
self.hint_wnd:Show(true)
self.hint_wnd_text:SetText(text)
self.hint_wnd_text:AdjustHeightToText()
self.hint_wnd_text:SetTextColor( (cl == true and clr_list["saved"]) or (cl == false and clr_list["temp"]) or clr_list["def"] )
local w = self.hint_wnd:GetWidth()
w = w >= 96 and w or 96
local h = self.hint_wnd_text:GetHeight()+44
h = h >= 96 and h or 96
self.hint_wnd:SetWndSize(vector2():set(w,h))
local pos = GetCursorPosition()
pos.y = pos.y - self.hint_wnd:GetHeight()
pos.x = pos.x - self.hint_wnd:GetWidth()
self.hint_wnd:SetWndPos(pos)
FitInRect(self.hint_wnd,Frect():set(0,0,1024,768),0,100)
end
function WeatherEditor:MSG(fmt,...)
local str = strformat(fmt,...)
self.msg_wnd:Show(true)
self.msg_wnd_text:SetText(str)
self.msg_wnd_text:AdjustHeightToText()
self.msg_wnd_text:SetWndSize(vector2():set(self.msg_width, self.msg_wnd_text:GetHeight()+10))
self.msg_wnd_text:SetWndPos(vector2():set(0,20))
self.msg_wnd:SetWndSize(vector2():set(self.msg_width, self.msg_wnd_text:GetHeight()+44))
self.msg_wnd:SetWndPos(vector2():set( self.dialog:GetWidth() , (self.hight - self.msg_wnd:GetHeight()) ))
self.msg_wnd_timer = time_global() + 4000
end
function WeatherEditor:Print(fmt, ...)
if self.debug then
printf("WeatherEditor | " .. fmt, ...)
end
end