ID:139357
 
So a couple days ago I was thinking about how I would load an interface during runtime, and well the only way I could think of doing it was by generating a list of everything in the interface such as macros, menus and windows and storing there parameters in the list. I accomplished this somehow but now I have a new problem. Whenever I try to use the interface loader to load these files, the savefile is pretty much emptied by something and it tries to load an empty file and I'm not quite sure why this is happening. Well right now you're probably thinking that this should go in the the code problems section but at this point its already too late. Anyway I just wanted to know if there are any other methods for loading an interface at runtime or if there is a way to fix all of this:

Loader:
//Must have a window named "blank window".

interface_loader
var/list/windows = list() // "window" = list( "Element"=list("attributes") )
var/list/macros = list()
var/list/menus = list()
var/source_file = ""
proc
load_interface(var/client/C)
load_all_windows(C)
load_macros(C)
load_menus(C)
load_all_windows(var/client/C)
for(var/window in windows) load_window(C, window)
load_window(var/client/C, var/window_name)
if(!windows.Find(window_name)) return 0//failed.
winclone(C, "blank window", window_name)
var/list/elements = windows[window_name]
for(var/v in elements)
var/params = elements[v]
if(findtext(params,"type=MAIN")) continue//if its a window don't do it...
winset(src, "[window_name].[v]", params)
load_macros(var/client/C)
var/i = 0
for(var/macro in macros)
var/list/X = macros[macro]
for(var/macro_params in X)
winset(C, "loaded_macro[i]", "parent=[macro]&[macro_params]")//this sets the parent and other shiz
i++
load_menus(var/client/C)
var/i=0
for(var/menu in menus)
var/list/X = menus[menu]
for(var/menu_params in X)
winset(C, "loaded_menu[i]", "parent=[menu]&[menu_params]")//this sets the parent and other shiz
i++
load_to_window(var/client/C, window_name)//coming soon.
load_source_file()
var/savefile/F = new(source_file)
F["macros"] >> macros
F["menus"] >> menus
F["windows"] >> windows
New(new_source_file)
..()
spawn()
if(new_source_file)
source_file = new_source_file
load_source_file()
//loads a specific window on an existing one, I don't recommend this because you may\
get confused with window names if your changing things a lot.


/*
F["macros"] << macros
F["menus"] << menus
F["windows"] << windows
*/

mob
Login()
..()
spawn()
alert(src, "Click Ok To Load Interface...")
var/interface_loader/loader = new("new skin.ilb")
var/t = "Macros:\n"
for(var/v in loader.macros) t += "[v]\n"
t += "\nMenus\n"
for(var/v in loader.menus) t += "[v]\n"
t += "\nWindows\n"
for(var/v in loader.windows) t += "[v]\n"
src<<alert(t, "information")
loader.load_interface(client)


Writer:
proc/getMacros(var/list/interface)//uses get_lines()
var/list/macros = list()
var/current_macro_name = ""
var/a
var/b
var/list/Data = list()
for(var/i=1 to interface.len)
var/line = interface[i]
if(findtext(line, "window \""))
if(current_macro_name && Data)
macros[current_macro_name] += list2params(Data)
Data = list()
break
if(findtext(line, "menu \""))
if(current_macro_name && Data)
macros[current_macro_name] += list2params(Data)
Data = list()
break
if(copytext(line, 1, 8) == "macro \"")
if(current_macro_name && Data && Data.len)
macros[current_macro_name] += list2params(Data)
Data = list()
a = findtext(line, "\"")
b = findtext(line, "\"", a+1)
current_macro_name = copytext(line, a+1, b)
macros.Add(current_macro_name)
macros[current_macro_name] = list()
if(findtext(line, "\telem"))
macros[current_macro_name] += list2params(Data)
Data = new/list()
if(findtext(line, "\t\tname"))
a = findtext(line, "\"")
b = findtext(line, "\"", a+1)
Data.Add("name")
Data["name"] = copytext(line, a+1, b)
if(findtext(line, "\t\tcommand"))
a = findtext(line, "\"")
b = findtext(line, "\"", a+1)
Data.Add("command")
Data["command"] = copytext(line, a+1, b)
if(findtext(line, "\t\tis-disabled"))
a = findtext(line, "=[ascii2text(32)]")+length("=[ascii2text(32)]")
b = findtext(line, "\n")
Data.Add("is-disabled")
Data["is-disabled"] = copytext(line, a, b)
return macros


proc/getMenus(var/list/interface)//returns list of windows and elements.
var/list/menus = list()
var/a
var/b
var/current_menu_name = ""
var/line = ""
var/retrieved_text

for(var/i=1 to interface.len)
line = interface[i]

if(findtext(line, "window \"")) break//breaks for now
if(findtext(line, "macro \"")) continue//skips macros
if(findtext(line, "menu \""))
a = findtext(line, "\"")
b = findtext(line, "\"", a+1)
current_menu_name = copytext(line, a+1, b)
menus.Add(current_menu_name)
menus[current_menu_name] = list()
if(findtext(line, "\telem") && length(current_menu_name))
retrieved_text = ""
i++
while(copytext(interface[i], 1, 3) == "\t\t")//get first 2 characters, if tabs
retrieved_text += interface[i]+"\n"
i++
menus[current_menu_name] += parse_values(retrieved_text)
return menus

proc/getWindows(var/list/interface)
var/window_name
var/element_name
var/retrieved_text = ""
var/a
var/b
var/line = ""
var/list/windows = list()
for(var/i=1 to interface.len)
line = interface[i]
if(line == "\n") continue
if(findtext(line, "window \""))
a = findtext(line, "\"")
b = findtext(line, "\"", a+1)
window_name = copytext(line, a+1 ,b)
windows += window_name
windows[window_name] = list()
if(findtext(line, "\telem \"") && window_name)
a = findtext(line, "\"")
b = findtext(line, "\"", a+1)
element_name = copytext(line, a+1, b)
windows[window_name] += element_name
retrieved_text = ""
i++
while(copytext(interface[i], 1, 3) == "\t\t")//get first 2 characters, if tabs
if(i > interface.len) break
if(interface[i] == "\n"||copytext(interface[i], 1, 2) == "\n") break
retrieved_text += interface[i]+"\n"
i++
if(i > interface.len) break
retrieved_text = parse_values(retrieved_text)
windows[window_name][element_name] = retrieved_text
if(i > interface.len) break
return windows


proc
GenerateInterfaceFile(var/interface_text, var/new_filename="")
world<<"Creating Lines...\..."
sleep(2)
var/list/interface = get_lines(interface_text)
world<<"Finished."

world<<"Reading Macros...\..."
sleep(2)
var/list/macros = getMacros(interface)
world<<"Finished."

world<<"Reading Menus...\..."
sleep(2)
var/list/menus = getMenus(interface)
world<<"Finished."

world<<"Reading Windows...\..."
sleep(2)
var/list/windows = getWindows(interface)
world<<"Finished."

world<<"Writing File...\..."
var/savefile/F = new
F["macros"] << macros
F["menus"] << menus
F["windows"] << windows
if(fexists(new_filename))
fdel(new_filename)
F.ExportText("/", new_filename)
world<<"Finished."
return 1

proc
parse_values(var/msg as text)
msg = textreplace(msg, "[ascii2text(32)]=[ascii2text(32)]", "=")//removes the spacing between the euals sign
msg = textreplace(msg, "\t", "")//removes tabs
msg = textreplace(msg, "\n", "&")//line breaks become '&'
while(copytext(msg, length(msg), length(msg)+1) == "&") msg = copytext(msg, 1, length(msg))//removes the last '&'
while(copytext(msg, 1, 2) == "&") msg = copytext(msg,2)//removes first '&'
return msg
//From my library, not finished so pasting this:
textreplace(text as text, to_replace as text, replacement as text)
var/pos = findtext(text, to_replace)
var/replacement_length = length(replacement)
var/to_replace_length = length(to_replace)
while(pos)
text = copytext(text, 1, pos)+replacement+copytext(text, pos+to_replace_length)
pos = findtext(text, to_replace, pos+replacement_length)
return text
textreplaceEx(text as text, to_replace as text, replacement as text)
var/pos = findtextEx(text, to_replace)
var/replacement_length = length(replacement)
var/to_replace_length = length(to_replace)
while(pos)
text = copytext(text, 1, pos)+replacement+copytext(text, pos+to_replace_length)
pos = findtextEx(text, to_replace, pos+replacement_length)
return text
get_lines(var/msg as text)
var/pos = 1
var/pos2 = findtext(msg, "\n", pos)
var/list/X = list()
while(pos && pos2)
X += copytext(msg, pos, pos2)
pos = pos2+1
if(pos >= length(msg)) break
pos2 = findtext(msg, "\n", pos)
if(!pos2)
pos2 = length(msg)+1
X += copytext(msg, pos, pos2)
break
return X

mob
verb
Test_Interface_Creator()
var/f = input("choose an interface file...") as file
var/filename = "[f]"
GenerateInterfaceFile(file2text(f), "Generated Interfaces/[to_dot2("[filename]")].ilb")
proc
to_dot2(var/n = "")
var/pos = findtext(n, ".")
var/pos2
while(pos)
pos2 = findtext(n, ".", pos+1)
if(pos2) pos = pos2
else break
if(pos) return copytext(n, 1, pos)
return n
I noticed this a while ago with a game that I had. When you save your character only the objects that are in your mobs contents and your mob itself is saved. The hud usually isn't saved because although it's on the screen, it's not in the mob itself.

What I did is I marked down all the objects on the hud that NEED to be saved and, upon saving it copies every one of these objects into your contents. I should make a not that you'll want to change your stat proc if you're using one to display your items so that it doesn't display the saved hud objects.

Another way to do this is for the game to create a temporary mob and move the objects into that mob, then create a savefile with a name like "[playername]-HUD.sav" Then deleting the mob. Upon loading your savefile it will also load the hud save to another temporary mob and pulling the object from it onto the screen in their necessary locations.

You can do it many ways really, but you have to move objects on the hud into a mob before they will be saved.

Also, just as a syntax thing, when talking about an on-map based interface, call it a HUD and when it's part of the byond window itself, call it the interface. This confused me at first until I understood it was a HUD.
In response to Bravo1 (#1)
You can actually save the client.screen variable itself, but it's best not to save the actual screen objects, you don't want actual objects bloating your savefiles too much. Best to save a list of type-paths or something similar and re-create the objects based on that list when the save is loaded.

It's the same reason you should try not to save the icon variable, it gets saved as a lot more data than it needs to be if there have been any runtime modifications to the icon, including changing icon_state. The better idea is saving the general data relating to the icon and loading from that.
In response to Nadrew (#2)
True, form my point was that if these hud objects have variables themselves then they can't be saved unless you either A: make a huge list of variables or B: save the client.screen

Saving the types in a list is better yes, but they'd lose the variables as they'd be generated anew upon relogging. The issues that I have unfortunately -__-
In response to Bravo1 (#3)
I'm talking about loading actual .dmf files, loading a screen is easy enough for me but when I tried the load_source_file() proc every one of the lists was null, seemed like we were getting off topic here.
I think I figured out the problem, I've been using ExportText on the savefiles the whole time (stupid me), I'm going to try now and see if I can actually load an interface file into the game.
In response to ExPixel (#5)
Interface loader works now finally, and I'm able to load interface files at runtime, parsing isn't recommended during runtime though. Gonna release the new source at some point I guess. Some screenshots here:

-Before Load
-After Load