Geep Posted June 15, 2022 Report Posted June 15, 2022 I've been working on prepping some basic info about TDM-specific gui scripting, and there's an issue that has me stumped, having to do with a gui on a world object's surface... NOT an overlay. I'd like to influence what that gui does from a .script (either regular or script object, and by passing either values or using calls), but haven't been successful. I understand that TDM's version of idTech4 has reduced functionality for guis on world surfaces, but still.... If you want to take a wack, here's details. As always, could just be syntax problem. Spoiler As a test case, suppose you have a simple func_static, opaque except for an "Entity GUI" on one face. Give it a spawnarg override "gui_noninteractive 0" (though it doesn't seem to make a difference). Then give it a "gui" spawnarg to point to your gui file. Let's start with this simple gui, which work fine... it makes the active face flash bright green or invisible forever. windowDef test { rect 0, 0, 640, 480 backcolor 0,1,0,1 visible 1 notime 0 onTime 0 { set "visible" "0"; } onTime 1000 { set "visible" "1"; } onTime 2000 { resetTime 0; } } Now, you want to alter it so the visibility change happens instead every time you frob the object (on one of its opaque sides). You might have a frob_action_script with code like: void onFrob(entity me) { // define at file scope: float vis = 0; sys.println("frobbed"); if(vis == 0) { me.setKey("gui_parm1",1); } else { me.setKey("gui_parm1",0); } vis = me.getIntKey("gui_parm1"); // just to confirm it's really changing sys.println("vis = " + vis); }; I can get this toggling code to respond (or a version that forwards the frob request to an equivalent script object function). The problem is that a gui like that shown next (my latest of many iterations here)... windowDef test { rect 0, 0, 640, 480 backcolor 0,1,0,1 visible 0 notime 0 float vis onTime 100 { set "vis" "$gui::gui_parm1"; if("vis" == 1) { set "cmd" "log 'visible 1 branch'"; set "visible" "1"; } else { set "cmd" "log 'visible 0 branch'"; set "visible" "0"; } resetTime 0; } } ...with or without a "$" before the "gui::", never gets a changing value. Logging calls aren't working either, which doesn't help debugging. (BTW, calls like me.setGuiString(handle,"gui_parm1","0"); won't work, because they ignore handles that are not overlays.) As an alternative, I've tried using this in the script sys.handleMissionEvent(self, EVENT_READABLE_OPENED,""); to call this in the gui: onNamedEvent readableOpen {..} but it doesn't appear to fire. Thanks for any help Quote
Nort Posted June 16, 2022 Report Posted June 16, 2022 If nobody else knows, speak to tels about this. Recently I created a patch pack for Dark Radiant 2.14.0, and in the internal shader material, I encountered tels describing the GUI shader as intended to be used with an entity property/spawnarg. ...so ask him to clarify why he wrote that. Quote
datiswous Posted June 16, 2022 Report Posted June 16, 2022 13 hours ago, Geep said: void onFrob(entity me) { // define at file scope: float vis = 0; sys.println("frobbed"); So I'm wondering if this is correct. It seems to me you created the variable vis but then you put // before that line, so that whole line is just a comment. I don't know, maybe that's intended? Quote
Nort Posted June 16, 2022 Report Posted June 16, 2022 (edited) 21 minutes ago, datiswous said: So I'm wondering if this is correct. It seems to me you created the variable vis but then you put // before that line, so that whole line is just a comment. I don't know, maybe that's intended? I don't know enough about this, but at first glance, setting it to 0 (in onFrob() ) would defeat the whole purpose of the script, but maybe the variable scope doesn't reach into this script, and so he needs to at least put "float vis;" in there, or maybe "float vis = me.getIntKey("gui_parm1");". I don't know anything, but I've only seen "set" be used for spawnarg key-value pairs, and those seem different from variables. Edited June 16, 2022 by Nort Quote
Geep Posted June 16, 2022 Author Report Posted June 16, 2022 Sorry if I confused. What I meant was that this: float vis = 0; needs to be declared at the beginning of the .script file that contains the onFrob() [and maybe other functions in general] and outside the body of onFrob, so the value will persist between onFrob invocations, not just be temporary. Like the scoping in C. BTW, I believe tels is no longer active with TDM, so guidance must come from elsewhere. Quote
Nort Posted June 16, 2022 Report Posted June 16, 2022 3 minutes ago, Geep said: Sorry if I confused. What I meant was that this: float vis = 0; needs to be declared at the beginning of the .script file that contains the onFrob() [and maybe other functions in general] and outside the body of onFrob, so the value will persist between onFrob invocations, not just be temporary. Like the scoping in C. BTW, I believe tels is no longer active with TDM, so guidance must come from elsewhere. Why I'm so confused, is that when I write functions that are supposed to take parameters, I write this: Quote void onFrob(entity me, float vis) { // Code using the entity me and the float vis. } I don't just "declare vis outside". ...and then I output return values from a function, in the form of "return vis;", or put "float" instead of "void". It's been a while since I programmed C++, if this is even C++, but that's how I remember doing it. Are you sure you know the basics of how functions work? Quote
Geep Posted June 16, 2022 Author Report Posted June 16, 2022 To use the frob_action_script call, you must supply a function with the signature "void myfrobfunc(entity)". There a multiple ways to persist a value between function calls; I just used one that I like. If instead of a simple function, you were writing a script object, you'd typically make it a variable of the object (again, declared outside the body of any member function). Quote
Nort Posted June 16, 2022 Report Posted June 16, 2022 (edited) 14 minutes ago, Geep said: To use the frob_action_script call, you must supply a function with the signature "void myfrobfunc(entity)". There a multiple ways to persist a value between function calls; I just used one that I like. If instead of a simple function, you were writing a script object, you'd typically make it a variable of the object (again, declared outside the body of any member function). You talking about "objects" sounds like object oriented programming, which I shun, so now I have an excuse to not understand what you're saying. * slinks away.* Edited June 16, 2022 by Nort Quote
Nort Posted June 16, 2022 Report Posted June 16, 2022 On second thought, what is frob_action_script? Is it a function, like frob_action_script()? Is it a spawnarg? ...because if it's a spawnarg, then that's a weird restriction. Why would a spawnarg care how many parameters you put into your function? ...and if it's a function, in some languages you can overload functions, which is that you can just write another frob_action_script(), but which takes two parameters. In any case, maybe your way works. I don't know how objects work. Quote
greebo Posted June 16, 2022 Report Posted June 16, 2022 @GeepIf you care to create a small PK4 for me with that particular set up, I'll have a look. 1 Quote
Geep Posted June 16, 2022 Author Report Posted June 16, 2022 @greebo, here it is: https://drive.google.com/file/d/19sAIHOtVZYkkNtfzwLOB-Fq8NCSsD8a6/view?usp=sharing This is not full fledged, so needs to be started with "map testgui", not through main menus. You'll see a green surface, with brown side walls. If you frob it repeatedly, nothing happens, but you can see in the console that the frob script is trying to toggle the visibility of the green surface. Quote
Geep Posted June 16, 2022 Author Report Posted June 16, 2022 @Nort Quote Is it a spawnarg? ...because if it's a spawnarg, then that's a weird restriction. Why would a spawnarg care how many parameters you put into your function? Yes, it's a spawnarg. In TDM, if you are calling a function from a spawnarg that's designed to do that, it will want a particular form of the function. If you don't give it what it wants, you often get a stack underflow because of a mismatch in parameters. User input parameters and output results are often passed in separate spawnargs, agreed upon by caller and callee. Quote
greebo Posted June 16, 2022 Report Posted June 16, 2022 Ok, if I understood your use case correctly, this should do the trick: Change the GUI to look like this: windowDef test { rect 0, 0, 640, 480 backcolor 0,1,0,1 visible "gui::is_visible" notime 0 } The visible property is now bound to the is_visible GUI state variable. Which is "0" by default, so the green window starts out hidden. Change your script to toggle the visibility by inverting the state flag: void onFrob(entity me) { sys.println("frobbed"); // Invert the is_visible variable that is bound // to the visible property of the test window me.setGuiInt(1, "is_visible", 1 - me.getGuiInt(1, "is_visible")); // Note: The first argument "1" refers to the first entity GUI // as defined by the "gui" "guis/test.gui" spawnarg } Does this help? 1 1 Quote
Nort Posted June 16, 2022 Report Posted June 16, 2022 30 minutes ago, Geep said: @Nort Yes, it's a spawnarg. In TDM, if you are calling a function from a spawnarg that's designed to do that, it will want a particular form of the function. If you don't give it what it wants, you often get a stack underflow because of a mismatch in parameters. User input parameters and output results are often passed in separate spawnargs, agreed upon by caller and callee. Okay. Looking forward to dealing with that next month, when I start scripting. Quote
Geep Posted June 16, 2022 Author Report Posted June 16, 2022 @greebo, confirmed that works. Wonderful! But now some questions, starting with the .script side. I did play around with setGuiInt for a while, but then concluded that it (and other setGui... SDK calls) couldn't work, because inside the C++ code, it looks to confirm that the passed handle is in the m_overlays list. I kinda thought that a handle for a gui that was on a world object's surface would not have an overlay defined for it. So I'm confused again. Does overlay not refer to a 2D pane rendered in the same plane as the screen? When I was working with setGuiInt, since I wasn't instantiating the world surface gui myself, but relying on autoload, I didn't have the handle number, so I would find it using: string s; sys.println("handle hunt"); for (i = 0; i < 10000; i++) { s = me.getGui(i); if(s != "") // could do a better job of string comparison here, but works for test map { handle = i; sys.println(s); break; } } sys.println("handle =" + i); That was returning a value of 1. But what you seem to be saying is, the handle has to be 1 for the gui defined by "gui". True? And presumably that goes for "gui2" (handle = 2) and "gui3" (handle =3), but maybe not beyond that? Quote
Geep Posted June 17, 2022 Author Report Posted June 17, 2022 Playing with my earlier version of the .gui file, to try to understand why it didn't work. Main problem turned out to be that a user variable (in spite of Doom 3 documentation suggesting otherwise) needs to be terminated by either a ";" or a "0". I'm adding that to one of my draft articles, working title "Syntax from Hell". This now works, approximately same behavior as greebo's version (if less elegant): windowDef test { rect 0, 0, 640, 480 backcolor 0,1,0,1 visible 0 notime 0 float vis; // must either end with ";" as here, or 0 onTime 0 { if("vis" != "$gui::is_visible") { set "visible" "$gui::is_visible"; set "vis" "$visible"; set "cmd" "log 'visible changed'"; } } onTime 100 { resetTime 0; } } @greebo, what is still a mystery is why the logging you see here is not working. Not showing up in either qconsole.log or DarkMod.log. Is there some .cfg value that must be set? Or a special log file I'm not aware of? Quote
greebo Posted June 17, 2022 Report Posted June 17, 2022 6 hours ago, Geep said: So I'm confused again. Does overlay not refer to a 2D pane rendered in the same plane as the screen? I think the overlay system was designed to work with the player entity (HUD, effects, etc.), hence the name. It was implemented on the generic entity level, so it replaced the older system for all the entities. That's what I could figure out in the little time; it has been ages since I've touched that code and if I recall correctly, I didn't implement it myself, maybe it's been Ishtvan. 6 hours ago, Geep said: That was returning a value of 1. But what you seem to be saying is, the handle has to be 1 for the gui defined by "gui". True? And presumably that goes for "gui2" (handle = 2) and "gui3" (handle =3), but maybe not beyond that? I think, yes, since the "gui", "gui2", "gui3" spawnargs are processed before any script or game code has a chance to load any additional GUIs and reserve these [1..3] handles. A workaround to not hardcoding the handle might be 1) use the script loop you wrote to find out what handle it has been assigned or 2) load the GUI by the script itself using me.setGui("guis/test.gui") and store the handle somewhere (either a script object field or the "me" func_static entity). 2 hours ago, Geep said: Main problem turned out to be that a user variable (in spite of Doom 3 documentation suggesting otherwise) needs to be terminated by either a ";" or a "0". I'm adding that to one of my draft articles, working title "Syntax from Hell". I know how you feel, weird GUI syntax is weird. I remember having my own fill of figuring out the GUI quirks back when we didn't have any sources to check. The GUI parser also chokes if no onTime block is present after the "float vis" declaration (without semicolon), claims about running into "unexpected end of file". It seems almost everybody hates working with GUIs, but after you've sunk in you'll find can do a lot and there are some really powerful features in there. 2 hours ago, Geep said: what is still a mystery is why the logging you see here is not working. Not showing up in either qconsole.log or DarkMod.log. The logging only works for the main menu GUI, which I assume is where you got that part from. The main menu GUI has special support in the C++ code, in idGameLocal::HandleMainMenuCommand or something like that. IIRC this method was the only place I could connect to the main menu GUI and implement things like the Mission Download GUI (in the days of closed source). set cmd "log" was one of the crucial things I needed for debugging stuff. You can look at the main menu to learn, but some things cannot be applied to regular player or entity GUIs, especially the set "cmd" trickery. 1 Quote
Geep Posted June 17, 2022 Author Report Posted June 17, 2022 (edited) On 6/16/2022 at 11:39 PM, greebo said: The logging only works for the main menu GUI, which I assume is where you got that part from. The main menu GUI has special support in the C++ code, in idGameLocal::HandleMainMenuCommand or something like that. IIRC this method was the only place I could connect to the main menu GUI and implement things like the Mission Download GUI (in the days of closed source). set cmd "log" was one of the crucial things I needed for debugging stuff. You can look at the main menu to learn, but some things cannot be applied to regular player or entity GUIs, especially the set "cmd" trickery. Actually, set "cmd" log... was described in the Doom 3 docs, but I did earlier confirm the syntax as used in the main menu system. Those docs didn't indicate it couldn't be applied to player or entity GUIs. What I'm trying to do now is develop TDM-specific documentation, so knowing what won't work is fine. Although I might put in a bugtracker feature request to get set "cmd" logging more generally available, for the obvious assistance it would give to mapper GUI debugging. From what you say, I guess there's no point in testing set "cmd" runScript... There is the other version (just "runScript...") I need to experiment with. BTW, thanks for clarifying the handle & overlay situation. EDIT: Just runScript, while quietly parsed, doesn't seem to me to try to execute the script function. Edited June 20, 2022 by Geep Quote
Geep Posted June 17, 2022 Author Report Posted June 17, 2022 @greebo, I've updated this page to include your point about set "cmd" not available to mappers: https://wiki.thedarkmod.com/index.php?title=GUI_Scripting:_Parsing_of_Set_'Cmd' Quote
datiswous Posted June 17, 2022 Report Posted June 17, 2022 (edited) Hi sorry for going off topic here a bit, but I noticed that on wiki page https://wiki.thedarkmod.com/index.php?title=GUI_Scripting:_Parsing_of_Set_'Cmd' in section TDM’s Specialized GUI Commands > Within GUIs of the Main Menu System , I think this info is still based on the 2.09 menu, while 2.10 got a big change. Some stuff like eraseSelectedModFromDisk is removed I think (I might be wrong). Although maybe these commands still work, but aren't used in the main menu's anymore. Edited June 17, 2022 by datiswous Quote
Geep Posted June 17, 2022 Author Report Posted June 17, 2022 (edited) Just checked. Yep, that list was actually based on 2.08 data. I'll update to 2.10 (not today tho). Glad you caught that. Edit: Now updated Edited June 18, 2022 by Geep 1 Quote
Geep Posted June 20, 2022 Author Report Posted June 20, 2022 A broader GUI issue: I'm struggling to adequately understand "gui::" variables. I've got a series of questions, but let me start with 2 basic ones, having to do with scoping. I’m going to use the general term “guiDef” to refer to “windowDef”, “choiceDef”, etc. 1. Internally, I think "gui::" variables are those stored in idDict structures. With respect to a given GUI, is there a single idDict for an entire GUI (i.e., top-level on down), or does each guiDef (top-level or nested) have its own? 2. Is there also (or instead) a “global” idDict, across all GUIs in an FM? Looking mainly for TDM-specific answers from our gurus. Thanks. Quote
Geep Posted June 28, 2022 Author Report Posted June 28, 2022 *crickets* Well, that question must of been too obscure. I'll come back to that, but first help me clear up GUI command "runscript", that could appear in an event handler code block. I'm going to claim, despite some engine C++ code to parse it, that - on purpose - this command no longer works in TDM. In either form, "runscript ..." or "set 'cmd' runscript...". If you know about this, please either agree with that claim, or provide a counter-example, such as a working test map or a pointer to an FM that successfully has a GUI file that uses runscript. Thanks! Quote
datiswous Posted June 29, 2022 Report Posted June 29, 2022 Btw. the book Quake 4 mods for dummies can be viewed and downloaded as pdf (for now) here: https://manualzz.com/doc/30169982/quake-4-mods-for-dummies---x#p306 (the link points to page 306, which is where the section about gui editing starts) Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.