matejcik Posted April 17, 2013 Share Posted April 17, 2013 Fellow programmers, geocachers, Wherigo creators, me and some other guys are playing around with this new concept of doing things in Wherigo. It looks like this: Most of you would be able to create this sort of interaction without much trouble, right? Well, have a look at the Lua code underneath it. function zcharBear:OnTalk() name = UI.Input {zcharBear, "Hi, what's your name?"} UI.Message {zcharBear, "Well, "..name..", I'm hungry."} UI.Message {Player, "Um ... okay, that's ... i'll just ..."} response = UI.Choice {zcharBear, "Do you have anything to eat?", Options={"Sure", "I'm afraid not"}} if response == "Sure" then UI.Message {zcharBear, "Gimme!"} else UI.Message {zcharBear, "Hmm, too bad. Now I'll have to eat you!"} end end You will notice that there are no callbacks, and that instead of creating a ZInput, we just get the response directly and use it right away. Pretty cool, huh? Disclaimer 1: This is only relevant for those of you who write cartridges (at least partially) by hand. There is no builder support. Disclaimer 2: What I'm posting here is closed alpha-release. It's alpha, meaning there are bugs, and it's closed, meaning that i won't post the code here - for now anyway. That said, the code exists and is readily usable, in the form of an author script. I'd like to hear your opinions on the new API model, and possibly your experiences with programming with it. Q: But how can I get the code? A: PM me if you're interested. I'll send it to you - if i judge you worthy /mwa ha ha/. No, seriously, just ask. Q: Then why don't you just post the code here? A: Because I don't want everyone and their grandma to include it in their cartridges before we manage to iron out the kinks. Here's a little secret: something very much like this will end up as part of OpenWIG, WhereYouGo and other modern players. We're trying to design the API right, so we are showing it to you early - but if everyone started using this version now, we would have a compatibility nightmare on our hands. For this reason, I will only send the code to people who have been active on the forums, and i'll want you to promise that you won't give the code to anyone else before it is ready. Q: Ha ha, I bet I could write something like this myself. A: Really? Cool. Why don't you join the discussion here and share your ideas about how it's supposed to work? Q: Okay, I got the code. What now? A: You already got the "installation instructions" in your PM. The next post in this thread contains a short programmer's guide. Have fun! Quote Link to comment
matejcik Posted April 17, 2013 Author Share Posted April 17, 2013 (edited) Programmer's Guide 1. Interaction model Unlike traditional Wherigo API (which i will call legacy API from now on), in the blocking API, there is an implicit message queue. That means that if you send a message to the screen before the user is done with the previous one, nothing bad happens. When the user clicks OK on the first message, your next message will be shown. Also unlike the legacy API, some of our functions are blocking. In legacy API, you would do this: Wherigo.MessageBox{Text="start timer"} ztimer:Start() and the timer would start immediately - before you finished reading the message text. With blocking API, when you do this: UI.Confirm{nil, "Start timer?"} ztimer:Start() the timer will start only after the user clicks OK. In other words, Confirm will block program execution until it is finished. You may be thinking now, "What about timer events? What about zone events? Will they be stuck waiting too?" And the answer is no, they won't. Let's say you have two zones, zone A and zone B: function zoneA:OnEnter() zoneB.Active = true UI.Confirm{nil, "you are in zone A"} end function zoneB:OnEnter() zoneC.Active = true UI.Confirm{nil, "you are in zone B"} end When you enter zone A, zone B will become active and you will see a messagebox. Let's say that you don't click OK and keep it on the screen. When you walk into zone B, zone C will become active (that's the first line), and then the Confirm call will block until you click OK on both the first and the second messagebox. You: (walk into zone A) Cartridge: "you are in zone A" You: (walk into zone <--- at this point, there are two events "running" (they're suspended, actually) at the same time You: (click OK on "you are in zone A") <--- the first OnEnter is unblocked and finishes. only one event is "running" now. Cartridge: "you are in zone B" You: (click OK on "you are in zone B") <--- the second OnEnter can finish To make this possible, a third property of the blocking API is necessary: the same event cannot run twice at the same time. That means that, in the previous example, if you left zone A and returned to it before clicking OK, the OnEnter would not run again. 2. API Overview There are seven functions available: UI.Message, UI.Confirm, UI.Choice, UI.Input, UI.Notify, UI.Cancel, UI.Wait You cannot use these functions and traditional Wherigo functions (MessageBox, Dialog, GetInput) at the same time. Well, you can, but if you do it, everything will break horribly. Stick to the one or the other. First let's look at UI.Message in detail. There are several ways to call it: UI.Message { Sender=zObject, Text="some text", Media=zmediaSomeMedia } UI.Message { zObject, "some text", zmediaSomeMedia } UI.Message { zObject, "some text"} Notice that it's "UI.Message { ... }". It won't work with regular parentheses. (the function signature is actually UI.Message(table). Lua allows you to call UI.Message {something} instead of UI.Message({something}) in this case.) The Sender argument can be a Zone, ZCharacter, ZItem, ZTask or even ZCartridge. In the first example with the bear, I've been using the Bear character as Sender. This is to emphasise that the messages are part of a conversation, and that someone or something is talking to you. Sender can also be Player (if it is something the user is saying), or nil if you want the message "from nobody". At this point, Sender is only used for the picture, but it just might do something interesting in new versions of the player apps. Text is obvious. Media, when specified, overrides Sender's picture. So if you want to show many pictures and you don't have characters to represent them, just use Sender=nil and Media=yourPicture. All functions, except Cancel and Wait, take these first three arguments, but they can usually have more. All functions return nil when canceled (more about that later). Where not specified, the function returns true when not canceled. 3. Functions UI.Message This function is non-blocking. That means that, in the following code, timer will be started immediately, without waiting for the user: UI.Message{nil, "start timer"} timer:Start() The messages from UI.Message are still queued though. Look at the bear example to see them in action. UI.Message is the "normal" message that you would be using most of the time. UI.Confirm Counterpart of Message that is blocking. Useful for waiting before starting timer, activating a zone, whatever. This one has an optional argument, Button, that lets you specify button text. UI.Confirm{nil, "start timer?", Button="start"} timer:Start() UI.Choice Multiple-choice question. Similar to ZInput of type MultipleChoice. Has an argument Options that specifies the available options. Blocks until the user answers, returns text and index of the option: text, index = UI.Choice{zcharBear, "What kind of food do you have?", Options={"fish", "steak", "vegetables"}} -- text == "steak", index == 2 text = UI.Choice{zcharBear, "What kind of food do you have?", Options={"fish", "steak", "vegetables"}} -- text == "steak" UI.Input Text input - similar to ZInput of type Text. Has no more arguments. Blocks until the user answers, then returns the answer text. UI.Cancel Cancels everything on screen, and removes everything from the event queue. Blocks until processing is done. Simply put, you call Cancel to clear the screen for you - as if the user started clicking OK on everything in rapid succession, until nothing remained. (Unfortunately, on legacy players, there is no function to remove a message or input from screen. so instead, it is replaced by a messagebox that says "Canceled." But this one is not queued - if you call UI.Message immediately after UI.Cancel, the users will see your message.) A small example looks like this: function item:OnAskQuestion() UI.Confirm{zcharBear, "you have 10 seconds to answer me"} timer:Start() reply, idx = UI.Choice{zcharBear, "how many heads does a turtle have?", Options={"three", "lemon", "tuesday"}} timer:Stop() if idx == 3 then UI.Message{zcharBear, "that's correct!"} else UI.Message{zcharBear, "sorry, that is wrong"} end zoneCave.Active = false end function timer:OnTick() UI.Cancel() UI.Message{charBear, "too late! now i eat you!"} end What's going on here? After clicking OK on the Confirm, a timer is started. Then the bear asks you a multiple-choice question, and the OnAskQuestion handler is suspended until you answer. If you answer within 10 seconds, timer is stopped and the bear will tell you whether you were right or wrong. If, however, you wait too long, timer's OnTick will fire and call UI.Cancel. At that moment, every UI operation from the suspended OnAskQuestion is considered canceled. We can't just kill the event (well we could but that would be stupid), so we let it finish - but the blocked UI.Choice call will return immediately, and every following UI call will simply be skipped. Let's do it step by step. OnAskQuestion: "reply, idx = UI.Choice{zcharBear, "how many heads does a turtle have?", Options={"three", "lemon", "tuesday"}}" Screen: "how many heads does a turtle have?" <--- OnAskQuestion is now suspended and waiting for user reply <--- 10 seconds pass OnTick: "UI.Cancel" Screen: "canceled" <--- OnTick is suspended, waiting until queued events are finished. <--- OnAskQuestion is resumed. reply and idx are nil OnAskQuestion: "timer:Stop()" <--- nothing happens - the timer is already stopped OnAskQuestion: "UI.Message{zcharBear, "sorry, that is wrong"}" <--- nothing happens - OnAskQuestion is canceled, so all UI operations are ignored. OnAskQuestion: "zoneCave.Active = false" <--- zoneCave is deactivated. This still happens, even though the event was canceled. OnAskQuestion: done <--- all queued events are done. OnTick is resumed OnTick: "UI.Message{charBear, "too late! now i eat you!"}" Screen: "too late! now i eat you!" UI.Wait Takes no arguments, does nothing, but blocks until the screen is clear (or until canceled) I can't come up with an example where this function would be useful. But maybe some exist, and it was easy to implement, so we left it in. UI.Notify Displays a notification message which is not queued. Does not block. The message is displayed immediately, and if you try to display anything after it, the notification will disappear. This is similar to how the original Wherigo.MessageBox works. But with one key difference: when you click OK on a Notify message, you go back to where you were before. Let's go back to the example from UI.Cancel. We will extend the time limit to 30 seconds, and add a second timer: annoyingTimer = ZTimer{Type="interval", Duration=5} function timer:OnStart() annoyingTimer:Start() end function timer:OnStop() annoyingTimer:Stop() end function annyoingTimer:OnTick() UI.Notify{nil, "you have " ..tostring(timer.Remaining).." seconds remaining"} end That's it. During the time the multiple-choice input is shown, you will get a message every 5 seconds. If you click OK on the notification, you go back to the multiple-choice. If you don't, another notification will replace it, showing the new time. Note 1: Due to the way this is implemented, if a Notify box covers an Input, whatever you typed into the input will be lost. I suspect that this will be especially annoying on Garmins. So don't do it. Note 2: If you show a Notify box, then the first UI action that would be shown will make it go away. The result of this rule is a bit weird: If you show a Notify on an empty screen, then the next UI.Message will overwrite it. But if you show Notify on top of a waiting Confirm or something like that, then all other UI events will go to the queue, and the notification will stay visible until the user clicks OK - or until another notification (which would be shown immediately) clears it. Or, obviously, until it is canceled. Edited April 17, 2013 by matejcik Quote Link to comment
+charlenni Posted April 17, 2013 Share Posted April 17, 2013 Very well done. But I thought it calls "Hungry like a wolf" What happens, if another event throws a UI.Message for the interface (you enter the zone). Could I confide, that I get the result from the input? Quote Link to comment
matejcik Posted April 17, 2013 Author Share Posted April 17, 2013 posted the first part of the programmer's guide What happens, if another event throws a UI.Message for the interface (you enter the zone). Could I confide, that I get the result from the input? yes, the input will stay on screen until the user is done with it. a message from another event will be queued. what can happen, though, is that after the input, another message will be displayed before that "I'm hungry" one. I'm not sure whether that's a good thing. we will see. Quote Link to comment
matejcik Posted April 17, 2013 Author Share Posted April 17, 2013 ...and finished the second part of the programmer's guide. hope this is all Quote Link to comment
+Arnyk Posted May 26, 2013 Share Posted May 26, 2013 Any more news on this? Just curious about "production phase". Quote Link to comment
matejcik Posted April 24, 2014 Author Share Posted April 24, 2014 just posting this here, that should force me to keep the schedule :e) more than a year after posting this, I'm close to finishing a bug-free version of the code. In the upcoming weeks, I will probably publish the code on here for the Public Beta. Quote Link to comment
+"Nessmuk" Posted April 26, 2014 Share Posted April 26, 2014 just posting this here, that should force me to keep the schedule :e) more than a year after posting this, I'm close to finishing a bug-free version of the code. In the upcoming weeks, I will probably publish the code on here for the Public Beta. Looking forward to taking this out for a spin. Quote Link to comment
Schnatterfleck Posted June 14, 2014 Share Posted June 14, 2014 just posting this here, that should force me to keep the schedule :e) more than a year after posting this, I'm close to finishing a bug-free version of the code. In the upcoming weeks, I will probably publish the code on here for the Public Beta. Hi matejcik, are you seeing any progress with the Blocking API? I hope there haven't appeared any major obstacles during development? Is there any chance to get a bug-free (well, as bug-free as SW goes... ) version within the next few weeks? I am also really looking forward to play around with the new lib! If you think you could use some outside support in any way, please feel free to ask! Quote Link to comment
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.
Note: Your post will require moderator approval before it will be visible.