<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5937183399046864464</id><updated>2011-07-08T06:50:55.720-06:00</updated><category term='MetaMarkup'/><category term='moving'/><category term='introduction'/><category term='sounds'/><category term='OutputToUser'/><category term='UI'/><category term='screen effects'/><category term='advertising'/><category term='events'/><category term='solutions'/><category term='functions'/><category term='lua'/><category term='collision'/><category term='latency'/><category term='triggers'/><category term='motivation'/><category term='tables'/><category term='effects'/><category term='mouse'/><category term='commands'/><category term='Ultima Online'/><category term='MoveObject'/><category term='metatables'/><category term='animation'/><category term='attributes'/><category term='keyboard'/><category term='physics'/><category term='sprites'/><category term='teaching'/><category term='DoCommand'/><category term='HTML5'/><category term='screenspace'/><category term='avatars'/><category term='customization'/><category term='data binding'/><category term='scripting'/><category term='sample code'/><category term='Go'/><category term='colour'/><category term='MakeInput'/><category term='screen size'/><category term='Javascript'/><category term='patterns'/><category term='programming'/><category term='matrices'/><category term='usedata'/><category term='parameters'/><category term='XML'/><category term='tinting'/><category term='modules'/><category term='embedding'/><category term='sliding'/><category term='Java'/><category term='ideas'/><category term='worldspace'/><category term='lost loves'/><category term='properties'/><category term='client writing'/><category term='alpha'/><category term='malicious code'/><category term='problems'/><category term='RIP'/><category term='per-user'/><category term='namespace'/><category term='booleans'/><category term='color'/><category term='MMORF'/><category term='command-line'/><category term='pathfinding'/><category term='content'/><category term='recursion'/><category term='metascript'/><category term='workarounds'/><category term='SlideObject'/><title type='text'>Camel Pâté</title><subtitle type='html'>My thoughts and experiences in the world of &lt;a href="http://metaplace.com"&gt;Metaplace&lt;/a&gt;.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>19</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-1783011735723833087</id><published>2009-12-21T19:45:00.004-07:00</published><updated>2009-12-21T20:59:17.060-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RIP'/><title type='text'>RIP Metaplace.com</title><content type='html'>This is definitely a sad day for me.  One of the most interesting things I've ever been involved in is Metaplace, and it has been &lt;a href="http://metaplace.com/community"&gt;announced today&lt;/a&gt; that metaplace.com, the User-Generated Content portion of their business, will be closing as of January 1st.&lt;br /&gt;&lt;br /&gt;Anyone reading this blog will likely know of Metaplace, for that's its whole purpose; I might have been critical about some of the things in Metaplace, but for the last two years, it has been one of my biggest distractions. It is (was) a platform for which I often thought, "what else can I do with this?", or when a new feature came out, "what can I create with this new feature", or, if I had any idea for a game/virtual environment, the first thought was "how can I implement this in Metaplace?"&lt;br /&gt;&lt;br /&gt;From a technological point-of-view, I think Metaplace did it right; I've said to others that it's very much the way that I would have done it, &lt;a href="http://mmorf.crwth.org/"&gt;had I written my own engine&lt;/a&gt;. I hope the code is preserved for a future project, because while it wasn't "done", it was "right so far". &lt;br /&gt;&lt;br /&gt;I realize, as I chat in MSN with KStarfire, a friend I've made from Metaplace, that this announcement has left a larger hole than I realized 20 minutes ago, because so much of my free programming has been around Metaplace, whether in it proper, or external tools.  One of the tools that I started working on, as a way to learn Google's Go language, was a tool to download all of the assets of a world.  Now it looks like I have two weeks to get this going, to salvage the work I've put into Metaplace, if only for nostalgia.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I sat down tonight to continue work on the bitmap font module, something that I recently picked up again, and was making really good headway with.&lt;br /&gt;&lt;br /&gt;I also planned on whipping up a small module for a user on the forums for togglable MP3 music playing, something that would have taken me only an hour or so to write.&lt;br /&gt;&lt;br /&gt;Over the last week, as my two-and-a-half year-old daughter keeps asking to "sit the internet" to play her Disney computer games, I've been thinking about how to make some toddler games in Metaplace to let them learn their colours, shapes, numbers, letters; their keyboard and mouse skills.&lt;br /&gt;&lt;br /&gt;I was thinking, as I was out and about today, about Ultima Online (as I often do), and how I really need to get back to my Metaplace implementation of it, perhaps tonight after I work on the bitmap fonts and sound player...&lt;br /&gt;&lt;br /&gt;And now, after reading the announcement, I think about the four Metaplace clients I've started, now all dead ends.  About all of the worlds that I created (I think I hit 102), many which were mere placeholders for ideas that I had come up with, but never got off the ground. I think of the rest of them, most which got a start with coding, seen only by me, and the handful that might be considered worth visiting, mostly demo worlds of the latest Metaplace feature, and one, &lt;a href="http://metaplace.com/Sniper"&gt;Sniper&lt;/a&gt;, being the only "done" world, which has had a bizarre resurgence of interest lately, getting Favorited once or twice a week.  Please, go give it (another) try in the next two weeks, before it's gone.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I think about the people I've met through Metaplace, whether online acquaintances that I only know in Metaplace worlds to those with which I've got a non-Metaplace relationship, through Twitter or blogs or email.  And then there are those that I've met in-person through Metaplace - fellow testers Scopique, Chooseareality and Rboehme, and the employees at the time -- some who had since left, some who are leaving as of today, and some who will stay with what Metaplace-the-company will become. I met these people because Metaplace flew me down to meet them, something that spoke a lot about the personality of the company and the people within it, that they would do this for an unknown alpha-tester and some-time pain in the ass.  I got to meet Raph Koster.  I'm a fanboi, I admit it.&lt;br /&gt;&lt;br /&gt;I wore my Areae shirt two days ago.&lt;br /&gt;&lt;br /&gt;I use my Metaplace coffee mug every day at work.&lt;br /&gt;&lt;br /&gt;And no matter what Metaplace does in the future, it has made its mark on me; on my programming skills, on my programming ideas, on my programming direction. It has stimulated the game designer and game developer in me, from the sideline dreamer to a nascent latecomer.&lt;br /&gt;&lt;br /&gt;Thank you, Metaplace.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-1783011735723833087?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/1783011735723833087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/12/rip-metaplacecom.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/1783011735723833087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/1783011735723833087'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/12/rip-metaplacecom.html' title='RIP Metaplace.com'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-4996641945992722832</id><published>2009-12-01T10:42:00.003-07:00</published><updated>2009-12-01T11:30:14.670-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='client writing'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML5'/><category scheme='http://www.blogger.com/atom/ns#' term='MetaMarkup'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><category scheme='http://www.blogger.com/atom/ns#' term='lua'/><title type='text'>Client writing</title><content type='html'>One of the earliest projects I started when I got into the Metaplace alpha, apart from a handful of demo worlds to try out the API, was a client.  Why write a client when the Flash one was available?  To see if I could, of course.  For me, it's not so much the "doing" as the "can I do" when it comes to programming.&lt;br /&gt;&lt;br /&gt;If you're a programmer, you should definitely give the &lt;a href="http://metaplace.com/wiki"&gt;Metaplace wiki&lt;/a&gt; a peek; it has a lot of information about how a client connects and all of the &lt;a href="http://metaplace.com/wiki/index.php/MetaMarkup"&gt;Metatags&lt;/a&gt; that come down the pipe whenever something happens in a Metaplace world.  Even if you have no interest in writing your own Metaplace client, seeing how the communication between the client and server happens can lend some insight to what kind of information is available to the client, and when, and thus can help you design scripts more efficiently for yourself or for modules you're looking to publish.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Lua&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;My first attempt at a client was in Lua.  Because I was learning (and loving) Lua at the time, mainly to learn how to script in Metaplace, I was attempting to apply it to all programming problems that arose -- I find this a very good way to learn a language, even if the language is not necessarily suited to the problem domain.  This is a perfect example of that, because while some of the data structures of Lua lend themselves well to writing a Metaplace client, the basics such as encryption and secure HTTP are not supported without adding external libraries, and Lua comes with no standard graphics library.  This was where the project stalled -- there were a handful of choices out there, C libraries that could be linked into Lua to provide the visual part of the client (which, admittedly, is really the point).  But these were large libraries that I had no interest in learning, especially in a manner that requires calling them awkwardly from within Lua.  &lt;br /&gt;&lt;br /&gt;In the end, my Lua client was just a text-based client, showing me the tags as they came in (and eventually you develop the ability to "read" them), with a simple command-line interface for querying the state of a world, such as the objects within, the users within, and, perhaps most usefully, for calling some built-in scripts that I had written; the rudimentary beginnings of a Metaplace bot.  Those who were around in the alpha days might remember the statue in the earliest Central getting dropped on people's heads, or being tomatoed mercilessly by my auTOMATOn script.  The only other use for the Lua client -- one that I've actually used quite a bit -- is as a way of introducing many commands to a world at once.  This allowed me to generate the instructions for placing tiles and objects offline, into a file, and then upload the whole series of commands in one fell swoop, mimicking the effect that would otherwise require me to painstakingly do so through the editor tools.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Java/Android&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;My next attempt was to make a client for Google's Android platform. Not that I had (or have) an Android device, but it sure sounded like an interesting way to learn how to develop for that platform AND still do Metaplace stuff. Because Android is essentially a Java platform, I would be in familiar territory, having coded lots of Java over the years, and my initial trial with the networking in Android (using the emulator) showed that the most worrisome part of writing a Metaplace client on Android -- the actual connectivity -- wasn't going to be a problem. &lt;br /&gt;&lt;br /&gt;The approach I took, then, was to write a Java client, and then worry about the Android-specifics after the fact.  This may not be the most efficient way to write a Android client, but remember I really just wanted to write "a client", and if I ended up with a Java one first, and later an Android one, that was fine with me.  Shortly after I started the project, Akidan, who had been fiddling with Tachevert's short foray into client-writing in .NET, joined the Java project, and he ended up getting it further along than the Lua client got.  Unfortunately, Akidan left the Metaplace scene, and while I'm not usually a group-programming kind of guy, it kind of took the steam out of the effort, not having him around as a motivator to continue work made the project stall.  This client got so far as to display some rudimentary UI, and the world tiles, and in the end could function as a very simple chat client, something that was only possible in the Lua client if you hand-crafted your conversation into Metaplace commands.&lt;br /&gt;&lt;br /&gt;It's too bad that the project stopped -- there are a few Metaplace developers with Android devices that I feel I let down by not coming up with something useable for them.  Though, unlike the Lua project, this one isn't "dead" so much as "abandoned", and could still be resurrected, if time permits.  However...&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Javascript&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;HTML5, and CSS3, and all of these new Web technologies that are coming out have finally given me reason to give this "web programming" a try.  I've never had a need to do it before, and although I've read the occasional Javascript book in the past, I had never had a need to use it, so never really "knew" Javascript.  As HTML5 and CSS3 are being developed and standardized, however, I'm trying to stay on top of it, figuring that now is as good a time as any to learn them.  And what better way to learn than to write something big -- like a Metaplace client!&lt;br /&gt;&lt;br /&gt;I have to say that, by far, this was the easiest client to get going, at least to the state that the other two did.  Because (at the time of me writing it) the WebSocket part of HTML5 wasn't supported in any browser, it relies on a Java Applet to do the networking, but otherwise, in no time at all, it caught up to, and surpassed, the functionality of the Lua client.  Perhaps two or three days?  It's a good testament to either Javascript or the Metaplace client model.  Or perhaps to the fact that it was my third client attempt.  &lt;br /&gt;&lt;br /&gt;The best part about this client is that &lt;a href="http://pages.cpsc.ucalgary.ca/~crwth/js/chump/"&gt;you can actually try it!&lt;/a&gt; I will point out that the username/password become part of the URL, so do get stored in my server logs, but I assure you, I have no interest in them.  I don't blame you if that prevents you from giving it a try, but you could also just make another Metaplace account if you really want to see.  Now, it's not like it's finished or anything, so you might want to stick to simple worlds, such as the ClientTestWorld, and I think doesn't work on Safari or Opera (and definitely not IE!) so Firefox and Chrome are your best bet.  It draws the background, tiles, and objects too, even animating them!  And simple keyboard support also works, so you can walk around in the world a little.&lt;br /&gt;&lt;br /&gt;This project only paused because this semester has become way too busy (thus my lack of anything Metaplace at all, and lack of posts here).  Family, work, teaching and classes doesn't leave much time for anything, even fun Javascript-based Metaplace clients.  This project is certainly not dead, just on hold, because it's still a great way for me to learn Javascript and some other HTML5 tags.  The best part of this one, though, is that there's nothing stopping you from using View Source and just continuing on with it, if you so choose. Hey, if you do, let me know how far you get!&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Go&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Of course, no matter how busy I am, I find it hard to ignore when Google comes out with something new and interesting, such as their new &lt;a href="http://golang.org"&gt;Go language&lt;/a&gt;. Being an old C hacker and a compiler writer in another life, something like this really got my attention, and after a few little projects to warm up with the language (mainly ports of Lua projects that I had done to learn THAT language), I started thinking about what I could do for a large project in Go, to really test it out. You can see where this is going...&lt;br /&gt;&lt;br /&gt;And that's where I am today; my current Metaplace time, which is still very slight for another week or so this semester, is taken up with starting my fourth Metaplace client.  Go is very young, still under development, which makes things a little awkward (the first day I used the Vector library, they changed it on me), so things such as encryption and secure HTTP are missing from it as they were in Lua, as are anything but the most basic graphic primitives, but I feel have a better chance of appearing, and soon. Even so, connecting to external libraries is a little cleaner with Go than it was with Lua, and in just a few days, I've put together the start of client #4.  Not as fast as the Javascript one, mind you -- I &lt;span style="font-weight:bold;"&gt;did&lt;/span&gt; have years of reading of Javascript behind me -- but it's getting there, slowly but surely.&lt;br /&gt;&lt;br /&gt;Until the next language comes along, I guess.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-4996641945992722832?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/4996641945992722832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/12/client-writing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/4996641945992722832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/4996641945992722832'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/12/client-writing.html' title='Client writing'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-7834364598668653183</id><published>2009-08-31T12:06:00.003-06:00</published><updated>2009-08-31T13:12:41.163-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='command-line'/><category scheme='http://www.blogger.com/atom/ns#' term='patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='commands'/><category scheme='http://www.blogger.com/atom/ns#' term='scripting'/><title type='text'>Patterns in programming</title><content type='html'>Just managed to sneak in ONE post for August...&lt;br /&gt;&lt;br /&gt;It's not that I haven't been playing with Metaplace lately (though not nearly as often as I'd like), but a lot of the stuff I've been doing hasn't been groundbreaking or, really, that interesting.  Not in its current stage, anyway.&lt;br /&gt;&lt;br /&gt;Ever since I &lt;a href="http://camelpate.blogspot.com/2009/07/sounds.html"&gt;added sounds&lt;/a&gt; to my Ultima Online world, I've been drawn back to getting that large project going again, happily ignoring the fact that custom avatars are the bane of my Metaplace existence.&lt;br /&gt;&lt;br /&gt;While the footstep sounds were interesting, the most memorable sounds in UO were the spells, so I decided to focus on that subsystem next.  As is typical of my programming history, I'm much better off at the behind-the-scenes coding than the user interface portion, and since UO spells (more specifically, the spellbooks) can require a bit of UI that I shudder at creating (and I won't even bring up UiXml()... well, except there), I went to work on the back-end portion of spellcasting.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;One of the most common things I do in Metaplace, whether it's designing a new module, a new system, or just testing out new functionality, is to write a command-line interface to the code I'm developing.  This is useful for testing things that would otherwise require buttons and sliders and textboxes, but don't have them yet, and for quickly trying different values in different situations.  This is something I do so often, in fact, that I've got a routine when I first create a script, that sets me up for development.  For instance, when I decided I was going to work on spellcasting in UO (specifically, Magery, which is actually a skill...) I created a script called "magery", and right off the bat, wrote the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Define Properties()&lt;br /&gt;  magery={}&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Define Commands()&lt;br /&gt;  MakeCommand("magery", "magery interface", "cmd:sentence")&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Command magery(cmd)&lt;br /&gt;  local params=string.gmatch(cmd,"%S+")&lt;br /&gt;  local subcommand=params()&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This sets me up for a few things:  it gives me some local storage for anything I create during testing, such as window IDs, sound IDs, state variables, etc. all within the self.magery table.  It also gives me a quick way to pop up the command-line and start sending commands to my code, taking advantage of the handy "sentence" type in Metaplace.  In case you've not seen it, it lets you type, say&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  magery cast gate travel&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And it'll take everything after the command name and pass it as a single string, spaces and all.  This allows for parameters with spaces (such as "gate travel" above), and for sub-commands that have varying or variable numbers of parameters.  &lt;br /&gt;The little bit of code at the start of the magery Command helps me tokenize the subcommand and the parameters.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;After throwing together a little bit of magic in my UO world, I decided that I should really add in skill support (because, as I mentioned, Magery is technically a skill that you use), so I made a script called "skills" and started it with&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Define Properties()&lt;br /&gt;  skill={}&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Define Commands()&lt;br /&gt;  MakeCommand("skill", "skill interface", "cmd:sentence")&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Command skill(cmd)&lt;br /&gt;  local params=string.gmatch(cmd,"%S+")&lt;br /&gt;  local subcommand=params()&lt;br /&gt;  local skillname=params()&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Almost the same as before.  Then I could quickly add&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  if subcommand=="use" then&lt;br /&gt;    SendTo(self,"use_"..skillname,0,params(),params(),params(),params(),params())&lt;br /&gt;  elseif subcommand=="set" then&lt;br /&gt;    self.skill[skillname]=params()&lt;br /&gt;  else&lt;br /&gt;    AlertToUser(self,2000,"Unknown skill command: "..subcommand)&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, admittedly, I kind of lose some of the nicety of the sentence type by coding my skillnames without spaces (AnimalTaming instead of Animal Taming).  And the ugly bit with the repeated calls to params(), to pull off each of the extra parameters (if they exist), works because the function returned by string.gmatch() will just continue to return nils once it's done, and sending extra nils through to the Trigger is harmless (provided the Trigger didn't expect anything there, of course).  I could spend the time and write code that says, "if the skillname is Animal Taming, then there's just one extra parameter, the target, so I'll only pass one extra params(); but if it's Provocation, then there are two...", but this lets me change the rule in their own individual handler functions, making for very rapid prototyping.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The other day I got distracted from UO development by a conversation with LunarRaid, which made me want to try out implementing drag-and-drop functionality using the UiEvent() system.  Replace the "magery" with "dndui" and you have my starting block of code, ready to change settings, pop up windows, or whatever else I might want to change while testing.  The nice thing about this setup is that many UI elements call Commands when pressed or used, and thus the same single interface can be used by them.  Also, I tend to have all of the conditional code in the Command just fire Triggers to &lt;span style="font-style:italic;"&gt;self&lt;/span&gt; so other code I write can easily duplicate, with a SendTo(), the functionality that I've been testing from the command-line.&lt;br /&gt;&lt;br /&gt;For just testing concepts, or to avoid fiddling with buttons, this is a great way to just get coding.  If I didn't have a nice way of quickly prototyping the code I come up with, I'd probably still be fiddling with a spellbook interface and have nothing but a few sprites to show for it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-7834364598668653183?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/7834364598668653183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/08/patterns-in-programming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/7834364598668653183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/7834364598668653183'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/08/patterns-in-programming.html' title='Patterns in programming'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-4438793608721730422</id><published>2009-07-26T14:27:00.005-06:00</published><updated>2009-07-26T15:55:57.949-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sounds'/><category scheme='http://www.blogger.com/atom/ns#' term='recursion'/><category scheme='http://www.blogger.com/atom/ns#' term='triggers'/><category scheme='http://www.blogger.com/atom/ns#' term='latency'/><title type='text'>Sounds</title><content type='html'>After a year-and-a-half of taking part in Metaplace during alpha and beta, I've finally added sounds to one of my worlds (I don't count the crwth_effects world, as the sounds are only played when the "player" tries them out, and not as part of the world itself.)  I'd say the biggest reason is that I rarely finish a world, and something like sound is a finishing touch.&lt;br /&gt;&lt;br /&gt;I'm not really sure what made me do it, since the world I've added them to, &lt;a href="http://metaplace.com/UO"&gt;my Ultima Online workspace world&lt;/a&gt;, is nowhere near done (being a lab world, it'll never be done as such).  For some reason this past week, I got to thinking about the music of UO, or the sounds of UO... I'm not sure, but I decided to figure out how to pull all of the UO sounds from the datafiles.  Once I had those, I browsed through them, playing them to bring back memories (which is silly, since my UO accounts only just expired) and to see the large portion of them that never appeared in the game.  Some of the sounds include the player's footsteps on a variety of terrain, and I was thus compelled to add them into the UO world.&lt;br /&gt;&lt;br /&gt;The funny thing is that right now, my UO world doesn't have the UO avatars for the players, but the Metaplace ones instead -- the only non-UO thing in the world gets the only UO sounds!  For each terrain type (grass, pavement, sand, etc.) there are two sound files, allowing you to alternate the foot falls per-foot; they could have taken the approach of a sound file that played a two-step sound, but it wouldn't match up if you took a single step with your avatar.&lt;br /&gt;&lt;br /&gt;This poses a challenge right off the bat, since there's no way in Metaplace to say "play these two sounds in a loop"; all sounds, whether to an individual or the whole Place, or whether always-on or based on distance, are on their own.  Because we want to play a different sound right after one finishes -- to give the clip-clop walking effect, we have one of two tools available to us:  we can either ask to be informed when a sound ends (and then act to start up the next one), or we can try to time playing the next sound based on the length of the current one.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Trigger sound_done&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As part of the PlaySound*() series of API functions (but not exhaustively documented), you may provide an object pointer as a callback object, telling the system that once this sound finishes, this callback object will have a "sound_done" Trigger fire at it.  This lets you play the next sound at the right time -- in theory.  Of course, being an online world, you have the latency of the internet to contend with, which means that once the sound finishes, your client tells the server (after X milliseconds), the server acts upon this information by firing the sound_done trigger at the specified object, the object decides to play a new sound, and the client is told, again after X milliseconds.  All told, these milliseconds can add up, between the latency and the script logic to determine that another sound should be played.  This delay is probably fine for playing background music (which I also added to the UO world), because a half-second delay between soundtracks is fine.  A half-second between footsteps, when you're stepping every quarter-second, is not.&lt;br /&gt;&lt;br /&gt;Here are the basics of this method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger sound_done()&lt;br /&gt;    local movesounds={&lt;br /&gt;        {"feetpvmta","0:19"},&lt;br /&gt;        {"feetpvmtb","0:18"}&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    self.move_number=self.move_number+1&lt;br /&gt;    if self.move_number&gt;#movesounds then&lt;br /&gt;        self.move_number=1&lt;br /&gt;    end&lt;br /&gt;    local moverec=movesounds[self.move_number]&lt;br /&gt;    self.move_sound=PlaySoundTo(self,moverec[2],255,0,self)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;SendTo()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;While we can't completely eliminate the latency problem, we can at least reduce it by not waiting to react to a message from the client, stating that the current sound has finished playing.  Instead, after starting the current sound, we'll fire a delayed Trigger to our sound-playing code to just start playing when it makes sense to do so.   This means that we need to know how long our sounds are to know how long to wait (for playing a series of background tracks), or we have to know that a given delay will be plenty of time for each of the sounds to play and finish (such as short footstep sounds).&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger movesound()&lt;br /&gt;    local movesounds={&lt;br /&gt;        {"feetpvmta","0:19"},&lt;br /&gt;        {"feetpvmtb","0:18"}&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    self.move_number=self.move_number+1&lt;br /&gt;    if self.move_number&gt;#movesounds then&lt;br /&gt;        self.move_number=1&lt;br /&gt;    end&lt;br /&gt;    local moverec=movesounds[self.move_number]&lt;br /&gt;    self.move_sound=PlaySoundTo(self,moverec[2],255,0)&lt;br /&gt;    self.soundtrigger=SendTo(self,"movesound",250)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I have to admit, I was prepared to trial-and-error the delay value to see what looked good -- in theory, it should be based off of the framerate of your animation, and the number of frames between each foot lands on the ground in the animation), but I really lucked out with my first attempt at 250ms -- go take a look at the world and see if you agree.&lt;br /&gt;&lt;br /&gt;That's right, I've gone with this second approach, for the reasons specified -- I've reduced the effect of latency on my sounds loop by not relying on the client to report when the sounds finish, and I'm able to do this because the clip-clop of walking is very regular.  And what about my background music?  Well, in the end I used to delayed-Trigger method, there, too:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger playmusic()&lt;br /&gt;    local music={&lt;br /&gt;        {"britain1","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Britain1.mp3",39},&lt;br /&gt;        {"Britainpos","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Britainpos.mp3",53},&lt;br /&gt;        {"Stones1","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Stones1.mp3",135},&lt;br /&gt;        {"Walking","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Walking.mp3",343},&lt;br /&gt;        {"Medieval","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Medieval.mp3",150},&lt;br /&gt;        {"Festival","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Festival.mp3",128},&lt;br /&gt;    }&lt;br /&gt;    self.music_number=self.music_number+1&lt;br /&gt;    if self.music_number&gt;#music then&lt;br /&gt;        self.music_number=1&lt;br /&gt;    end&lt;br /&gt;    local musicrec=music[self.music_number]&lt;br /&gt;    self.background_music=PlaySoundRefTo(self,musicrec[2],100,0,self)&lt;br /&gt;    SendTo(self,"playmusic",(musicrec[3]+1)*1000)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note the list of tracks also contains the length, needed because they're of variable length, unlike the footsteps.  Why use this method instead of the sound_done() Trigger?  I think the main reason is because, when trying to handle both the music and the sound effects, I had code that looked like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger sound_done(userid,handleid)&lt;br /&gt;    if handleid==self.background_music then&lt;br /&gt;        SendTo(self,"playmusic",1000)&lt;br /&gt;    elseif handleid==self.move_sound then&lt;br /&gt;        SendTo(self,"movesound",0)&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I can't imagine all the conditionals when I've got dozens of sound effects, such as spell effects and combat sounds.  Sure, I could separate them into different scripts each with their own sound_done() Trigger, but the conditional code would still be the same.  One way I see this being a bit more useful is being able to supply not only the callback object, but the callback Trigger as well.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Dynamic footsteps&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The other thing I decided to do, because of the variety of footstep sounds available, was to change the sound based on the terrain the player walked upon.  &lt;br /&gt;&lt;br /&gt;In general, having different events based on the tiletype that a player stands on isn't "easy", because tiles themselves don't support Triggers or events, so you can't just attach a script to a grass tile that sets the player's footstep sound to "feetgrssa" and "feetgrssb".  For many cases of tile-based effects, you'd have to have some sort of tick-based Trigger, checking if the tile has changed and then changing the effect in question.  &lt;br /&gt;&lt;br /&gt;The handy thing about the footsteps firing every 250ms, however, is that this provides its own "tick", and thus every time we're to start a new sound, we can decide if the sound should change.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger movesound()&lt;br /&gt;    if self.moving==0 then return end&lt;br /&gt;&lt;br /&gt;    local tile=GetTileAt(self.x,self.y)&lt;br /&gt;    local tilename=stylesheet.places["0:"..GetPlace().placeId].tiles[tile].name&lt;br /&gt;    local movesounds={&lt;br /&gt;        stone={&lt;br /&gt;        {"feetpvmta","0:19"},&lt;br /&gt;        {"feetpvmtb","0:18"}&lt;br /&gt;        },&lt;br /&gt;        grass={&lt;br /&gt;        {"feetgrssa","0:21"},&lt;br /&gt;        {"feetgrssb","0:20"}&lt;br /&gt;        },&lt;br /&gt;        dirt={&lt;br /&gt;        {"feetgrvla","0:25"},&lt;br /&gt;        {"feetgrvlb","0:22"}&lt;br /&gt;        },&lt;br /&gt;        sand={&lt;br /&gt;        {"feetsanda","0:23"},&lt;br /&gt;        {"feetsandb","0:24"}&lt;br /&gt;        },&lt;br /&gt;        water={&lt;br /&gt;        {"feet15a","0:27"},&lt;br /&gt;        {"feet15b","0:28"},&lt;br /&gt;        {"feet15c","0:29"},&lt;br /&gt;        {"feet15d","0:26"},&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    local tilesounds=movesounds[tilename]&lt;br /&gt;    if tilesounds then&lt;br /&gt;        self.move_number=self.move_number+1&lt;br /&gt;        if self.move_number&gt;#tilesounds then&lt;br /&gt;            self.move_number=1&lt;br /&gt;        end&lt;br /&gt;        local moverec=tilesounds[self.move_number]&lt;br /&gt;        self.move_sound=PlaySoundTo(self,moverec[2],255,0,self)&lt;br /&gt;    end&lt;br /&gt;    self.soundtrigger=SendTo(self,"movesound",250)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This assumes that your tiles are nicely named, as mine are (they're computationally-generated from the Ultima Online datafiles, which conveniently provide names), though this is all in the hands of the world builder anyway.  The above is the exact function I use for my footsteps.&lt;br /&gt;&lt;br /&gt;Of course, this solution wouldn't work for sounds that have a long "tick" -- Ultima Online played different background music depending on your region, so you'd get some spookier music in the jungle or a dungeon, compared to a forest or city.  You wouldn't want to wait three minutes for your current cycle of "city" themed music to end before realizing that you're in a dungeon and should be a little more on edge.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;When to walk&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The only thing missing is knowing when to start and stop the walking sound loop at all.  The last snippet gave a hint on how the stopping is done -- using a property called &lt;span style="font-style:italic;"&gt;self.walking&lt;/span&gt;.  It's used to stop the sound-playing loop, above, but how is it set?  By patching into the path_begin()/path_not_found()/path_end() series of Triggers that are part of the pathfinding system.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger path_begin()&lt;br /&gt;    StopSound(self,self.move_sound)&lt;br /&gt;    if self.soundtrigger~=0 then CancelSend(self,self.soundtrigger) end&lt;br /&gt;    self.moving=1&lt;br /&gt;    SendTo(self,"movesound",0)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Trigger path_end()&lt;br /&gt;    self.moving=0&lt;br /&gt;    StopSound(self,self.move_sound)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;Trigger path_not_found()&lt;br /&gt;    self.moving=0&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The extra code in the path_begin() Trigger is necessary because of the delayed-Trigger method being used; if someone was walking and then clicked elsewhere, they'd start a new pathfinding session but wouldn't have stopped the last delayed-Trigger, causing two (or three, or four) sound loops to play until the player finally stopped moving.  This was an interesting effect, but definitely not desired!&lt;br /&gt;&lt;br /&gt;Some readers might be thinking that such a problem could be avoided if I had just used SendToRepeat() instead of a recursive delayed Trigger.  Then I could just let the SendToRepeat() continue as it would, but I could really do the same thing with the recursive Trigger.  The reason I didn't use the SendToRepeat() is because of the possibility of a variable delay for the sounds; while the footsteps are a regular 250ms apart, I might get sounds that aren't so regular, and would thus want to be able to vary the delay, much as done in the music loop.  For this use, I could go either way, but I like to code generally, even if Dorian and Sean would rather I didn't.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I'm happy with how the sound turned out, considering it was really a whim.  It makes me want to get more sounds into the world, even without the mechanics that really require them (the sounds of combat, the sounds of crafting, monsters roaring...)  I also realized, as writing this post, that the footstep sounds are solely for the ears of the maker -- this is a bit silly, since we have PlaySoundRefRadius() to emanate sound from an object and to get the feature of distance affecting the volume built-in.  Perhaps I'll go take a look at that now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-4438793608721730422?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/4438793608721730422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/07/sounds.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/4438793608721730422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/4438793608721730422'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/07/sounds.html' title='Sounds'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-6087780983404174328</id><published>2009-07-10T21:00:00.005-06:00</published><updated>2009-07-10T22:40:53.709-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='parameters'/><category scheme='http://www.blogger.com/atom/ns#' term='lost loves'/><category scheme='http://www.blogger.com/atom/ns#' term='data binding'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><title type='text'>UiXML</title><content type='html'>A year ago this week, the Metaplace folks flew four alpha testers, myself included, down to San Diego to meet the team and get a little insight into what was coming.  It was a fantastic experience, very much appreciated, not only to meet the team (and the other three testers -- Chooseareality, Rboehme, and Scopique) but to see where they work (though they've moved since then) and how they worked (in which we got to participate).  We also got a sneak-peak at a new feature that was released shortly after that, which was the UiXml() system.&lt;br /&gt;&lt;br /&gt;The idea of this system is that instead of calling lots of UI API functions, you can create an XML segment, in a string, that will be passed to a single UiXml() function and will generate all of your UI elements for you.  So, instead of typing &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  local outerrect=UiRect(0, "outer rect", 10, 10, 220, 140)&lt;br /&gt;  UiColor(outerrect, 125, 2, 2, 0.9)&lt;br /&gt;  local innerrect=UiRect(outerrect, "inner rect", 1, 1, 218, 138)&lt;br /&gt;  UiColor(innerrect, 50, 130, 130, 0.8)&lt;br /&gt;  local label=UiLabel(innerrect, "name", 1, 1, "label text")&lt;br /&gt;  UiColor(label, 255, 234, 0, 1)&lt;br /&gt;  UiAttachUser(self,outerrect)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;you can type&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  local rectxml=[[&lt;br /&gt;  &amp;lt;rect name="outer rect" x="10" y="10" width="220" height="140" red="125" green="2" blue="2" alpha="0.9"&gt;&lt;br /&gt;    &amp;lt;rect name="inner rect" x="1" y="1" width="218" height="138" red="50" green="130" blue="130" alpha="0.8"&gt;&lt;br /&gt;      &amp;lt;label name="name" x="1" y="1" text="label text" red="255" green="234" blue="0" alpha="1"/&gt;&lt;br /&gt;    &amp;lt;/rect&gt;&lt;br /&gt;  &amp;lt;/rect&gt;&lt;br /&gt;  ]]&lt;br /&gt;  UiAttachUser(self,UiXml(rectxml))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What does this gain you?  Quite a few things, actually.  As you can see, you can set the colour of a UI element at the same time as defining it, instead of as a separate command.  Not really a biggie.  But the above rectxml string doesn't have to be created all in one go, and THAT makes things powerful.&lt;br /&gt;&lt;br /&gt;For instance, you could have a loop that builds up the string, based on ... well, based on whatever:  the number of entries in a table, whether a checkbox is or isn't checked, or the user's name.  But, to be honest, you could do the same thing with if-, while- or for-statements and the standard Ui*() functions.&lt;br /&gt;&lt;br /&gt;You could have objects which themselves know how to render the pertinent UI elements, and each might just have a property, "myui", which you can read at any time to insert into a block of the XML.  But, of course, you could have a function or Trigger on an object which calls the related Ui*() functions.&lt;br /&gt;&lt;br /&gt;Okay, you could call an external web service, which would supply the UI XML to render whateveritis that that web service wants you to render -- instead of having to fetch some text or JSON data or XML and then process it in your world, wouldn't it be nice if that service knew how to "talk UiXml()", and could give you a window, with buttons, and sliders, and textboxes, all laying out the data?  Yes, even THIS could be done with Ui*() functions, but we're seeing some usefulness...&lt;br /&gt;&lt;br /&gt;XML is processable; with an appropriate XML/XSLT library, or hell, even some interesting use of string.gsub(), I can change all of my UiRect()s to UiOval()s; decrease all of my red colours by 10; or widen all of my elements by 10%.  Yes, some of these could be variables that get modified, and others could be done with if-chains or lookup tables, but I still say processing a string is easier...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Still not convinced?  &lt;br /&gt;&lt;br /&gt;UiXML supports "layouts"; these are basically containers for arranging UI elements (similar to Java Layouts if you know those).  From &lt;a href="http://metaplace.com/wiki"&gt;the wiki&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  &amp;lt;layout style="grid" grid_x="2" grid_y="1" padding_x="1" padding_y="2"&gt;&lt;br /&gt;    &amp;lt;image name="itemimage" width="32" height="32" spriteId="0:2"&gt;&amp;lt;/image&gt;&lt;br /&gt;      &amp;lt;rect width="68"&gt;&lt;br /&gt;        &amp;lt;layout style="grid" grid_x="1" grid_y="2" padding_x="0" padding_y="0"&gt;&lt;br /&gt;          &amp;lt;label name="itemname" text="Name" red="0" green="0" blue="0" &gt;&amp;lt;/label&gt;&lt;br /&gt;          &amp;lt;label name="itemqty" text="Qty" red="0" green="0" blue="0"&gt;&amp;lt;/label&gt;&lt;br /&gt;        &amp;lt;/layout&gt;&lt;br /&gt;      &amp;lt;/rect&gt;&lt;br /&gt;  &amp;lt;layout&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;See the grid_x and grid_y?  This set up the organization of the elements as they were added.  They even nested, so inside one of the grid elements was another layout.  Oh, but there's a UiLayout() function (I bet I'm the only one who has ever used it).&lt;br /&gt;&lt;br /&gt;Fine.  How about "components"?  Again, from the wiki:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  component = [[&lt;br /&gt;    &amp;lt;ui xmlns:mp='http://www.metaplace.com/schema/ui'&gt;   &lt;br /&gt;    &amp;lt;define_component name="data_field"&gt;&lt;br /&gt;    &amp;lt;rect name="back" width="100" height="20" red="131" green="131" blue="131" expand="true"&gt;&lt;br /&gt;      &amp;lt;layout style="grid" grid_x="2" grid_y="1" padding_x="1" padding_y="1"&gt;&lt;br /&gt;        &amp;lt;label name="data_name" text="name:" width="100" /&gt;&lt;br /&gt;        &amp;lt;text_field name="data_value" command=" " text="value" red="0" green="0" blue="0"/&gt;&lt;br /&gt;      &amp;lt;/layout&gt;&lt;br /&gt;    &amp;lt;/rect&gt;&lt;br /&gt;    &amp;lt;/define_component&gt;&lt;br /&gt;    &amp;lt;/ui&gt;&lt;br /&gt;]]&lt;br /&gt;&lt;br /&gt;-- Add this XML to the UiXml stack&lt;br /&gt;UiXml(component)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Using A Component:&lt;br /&gt;myUI=[[&lt;br /&gt;    &amp;lt;ui xmlns:mp='http://www.metaplace.com/schema/ui'&gt;&lt;br /&gt;      &amp;lt;window name="item_detail" x="10" y="10" width="400" height="140" style="20"&gt;&lt;br /&gt;       &amp;lt;layout style="grid" grid_x="2" grid_y="2" padding_x="1" padding_y="5"&gt;&lt;br /&gt;          &amp;lt;component type="data_field" name="df1" height="20" width="200"/&gt;&lt;br /&gt;          &amp;lt;component type="data_field" name="df2" height="20" width="200"/&gt;&lt;br /&gt;          &amp;lt;component type="data_field" name="df3" height="20" width="200"/&gt;&lt;br /&gt;          &amp;lt;component type="data_field" name="df4" height="20" width="200"/&gt;&lt;br /&gt;       &amp;lt;/layout&gt;&lt;br /&gt;     &amp;lt;/window&gt;&lt;br /&gt;    &amp;lt;/ui&gt;&lt;br /&gt;]]&lt;br /&gt;UiXml(myUI)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Basically, components are "templates" for UI layout, allowing you to group together multiple UI items into a single conceptual block, and then reuse them as often as you like.  This example provides a rectangle with a label and a textfield as a nice convenient unit that can be placed anywhere with a single line.  Note how the height and width can be supplied afterwards, leaving some variability to the component's configuration.  &lt;br /&gt;&lt;br /&gt;In fact, you could go even further with the &amp;lt;override/&gt; tag:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  &amp;lt;component type="data_field" height="20"&gt;&lt;br /&gt;    &amp;lt;override target="data_name" text="Strength:"/&gt;&lt;br /&gt;    &amp;lt;override target="data_value" text="100"/&gt;&lt;br /&gt;  &amp;lt;/component&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Fantastic!  Customized label/field pairs, at your fingertips.  Sure, I could write a function that provided this functionality -- the function could take any number of parameters that it would then use to make a specific component, and return a handle... which do you think is easier to use?&lt;br /&gt;&lt;br /&gt;Okay, if you're still not convinced about the power of UiXML over regular UI function calls, Data Binding will win you over.  Data binding allows you to specify an alternate source to a constant for filling in values.  So instead of specifying a static value of "100" for an element's colour,&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  &amp;lt;rect name="red rect" x="0" y="0" width="100" height="100" red="100" blue="0" green="0"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;we can use a constant in our script:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  &amp;lt;rect name="red rect" x="0" y="0" width="100" height="100" red="{RED_VALUE}" blue="0" green="0"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or even something more advanced:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  &amp;lt;rect name="red rect" x="0" y="0" width="100" height="100" red="{stylesheet.places["0:0"].red_value}" blue="0" green="0"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Anything you can imagine that you can code, could be put into that { } definition.  (This has the unfortunate distinction, however, of being one of the few ways to hide malicious code -- a future blog post).  I hope you can get an idea of how powerful that is.  Sure, whatever code you put in there could also be run separately, and then passed to a Ui*() function as a parameter, but &lt;b&gt;this string of code itself can be generated dynamically&lt;/b&gt; - which is why it can also be dangerous.&lt;br /&gt;&lt;br /&gt;There are also other types of data binding.  Use "% %" for values passed in during the UiXml() call, such as &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &amp;lt;ui xmlns:mp='http://www.metaplace.com/schema/ui' scriptId='0:8'&gt;  &lt;br /&gt;      &amp;lt;component type="data" height="20"&gt;&lt;br /&gt;        &amp;lt;override target="data_name" text="Strength:"/&gt;&lt;br /&gt;        &amp;lt;override target="data_value" text="%foo%"/&gt;&lt;br /&gt;      &amp;lt;/component&gt;&lt;br /&gt;    &amp;lt;/ui&gt;&lt;br /&gt;&lt;br /&gt;  values = {foo='a value here'}&lt;br /&gt;  winId = UiXml(xml, parentWindowId, values)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And even better, use "# #" to pull data from object properties:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &amp;lt;ui xmlns:mp='http://www.metaplace.com/schema/ui'&gt;  &lt;br /&gt;      &amp;lt;component type="data" height="20"&gt;&lt;br /&gt;        &amp;lt;override target="data_name" text="Health:"/&gt;&lt;br /&gt;        &amp;lt;override target="data_value" text="#health#"/&gt;&lt;br /&gt;      &amp;lt;/component&gt;&lt;br /&gt;      &amp;lt;component type="data" height="20"&gt;&lt;br /&gt;        &amp;lt;override target="data_name" text="Mana:"/&gt;&lt;br /&gt;        &amp;lt;override target="data_value" text="#mana#"/&gt;&lt;br /&gt;      &amp;lt;/component&gt;&lt;br /&gt;    &amp;lt;/ui&gt;&lt;br /&gt;&lt;br /&gt;  winId = UiXml(xml)&lt;br /&gt;  UiBindObject(winId, self)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;the only thing that would make the above better is if the #values# automatically updated any time the properties changed; as it stands, you have to call UiBindObject() every so often to have the changes reflected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, have I convinced you that UiXml() is THE way to do UI in Metaplace?  I hope not.  That's right, I hope not, because... they just removed it.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Apparently I was the only person using it, and instead of letting me go on doing so, it got removed.  I agree, it had some problems - some of the later UI elements, such as sliders, weren't supported, and you couldn't set the text on certain items from within the XML - but it also worked well for the parts that did work.  Even more stinging is the fact that a new system, UI Styles, was introduced as a replacement to UI XML.  It's not the same thing at all (though it's an interesting system in its own right, one that I may blog about).&lt;br /&gt;&lt;br /&gt;So why did I blog about it at all?  Well, I have a Google Doc which is a long list of future blog posts, and that was on it from before they stole my baby from me.  And, to be honest, I had already started working on a replacement version which DOES support sliders, and text initialization (but which, can't do the cool things such as the data binding) before it was taken away, so those who might want this functionality can have it once I publish my version.  And, perhaps, just perhaps, all of the bitching I do about this (and other things) will get others to bitch (about this, and other things), and my beloved UiXml() might be returned to me.&lt;br /&gt;&lt;br /&gt;Hey, stop laughing.  I can dream.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-6087780983404174328?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/6087780983404174328/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/07/uixml.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/6087780983404174328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/6087780983404174328'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/07/uixml.html' title='UiXML'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-8979888240118507358</id><published>2009-07-08T09:25:00.003-06:00</published><updated>2009-07-08T09:57:18.865-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='screen size'/><category scheme='http://www.blogger.com/atom/ns#' term='advertising'/><category scheme='http://www.blogger.com/atom/ns#' term='embedding'/><title type='text'>Embedding</title><content type='html'>A week or so ago, Metaplace released the ability to embed your Metaplace world into pretty much any webpage.  As long as your page can support the IFRAME tag, you're probably set; there has been mention of various modules for Wordpress and other blogs, and I now regularly sit in chat from an embedded version of the PlainOldChat world inside an iGoogle gadget.  You can also see the PlainOldChat world embedded at the bottom of this page (I'm too lazy to figure out how to change the blog's template to make it fit up near the top).&lt;br /&gt;&lt;br /&gt;The main purpose of this embed, I suppose, is to allow people to share their worlds in a different environment:  instead of having people "go" to Metaplace to see your world, they can find it right at your blog, or your company's website, or on your guild's page.  This is a nice way to get people with common interests together in a "live" setting, giving a virtual environment that's a little more interactive than your flat webpage or forum.&lt;br /&gt;&lt;br /&gt;Not long after the embed was introduced, there was talk about using them as banner ads.  I'm surprised we haven't seen this yet, actually.  Of course, it would be nice to get rid of the little bit of non-world stuff from the embed, like the Help/Logout stuff, so that JUST the world is shown.  Also, there's currently no automatic anonymous or guest user support as yet, so only people who choose to create an account, or choose to log in, are going to be subjected to the advertising.  &lt;br /&gt;&lt;br /&gt;Adding support for anonymous or guest access would also allow for mini-games to be added to a web page; nothing as elaborate as a full virtual world, but just a casual game of slot machines, or a shoot-em-up, or sudoku, or a &lt;a href="http://www.squidi.net/three/entry.php?id=22"&gt; little RPG&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;But the one thing I think will make embedding fun, cool, and powerful is that the surrounding webpage can communicate with the embedded world, by using Metaplace's design of every world being its own web server.  In this way, the encompassing website, using AJAX or something similar, could display information about the world or about the user, right in the page instead of in the embedded view.  You could have your health bar outside of the play area, the high score list, the help commands, your character's inventory...&lt;br /&gt;&lt;br /&gt;Why not just put these things inside the Metaplace world, you ask?  Because they take up screenspace.  But don't they still take up screenspace, just in the webpage portion, you ask?  Yes, but:  one of the great things about the embed is that you can finally force a size for your world view; back in alpha, we started off with a 640x480 view, and could expand to fullscreen, but now it's the other way around, and there's no way to make someone leave fullscreen view.  Some world- and game ideas can rely on the player only seeing a certain amount of the world, and while we do have a little bit of distance culling in Metaplace, it's done as a radius distance instead of a square, and it doesn't affect tiles.  Being able to restrict the size of the world means that every player is on even footing, and no one benefits from having a 30" widescreen monitor over those of us with just a 20".&lt;br /&gt;&lt;br /&gt;Displaying information from the world is only the beginning of what can be done with the embeds and the web triggers, but I'll save other ideas for another post.  For now, I'll enjoy being able to chat in Metaplace alongside my Twitter feed alongside my email, all in one browser tab, and will save the interesting embed tricks for another day (one in which I've brushed up on a some Javascript).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-8979888240118507358?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/8979888240118507358/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/07/embedding.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/8979888240118507358'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/8979888240118507358'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/07/embedding.html' title='Embedding'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-1139518729358761686</id><published>2009-06-29T07:33:00.004-06:00</published><updated>2009-06-29T09:22:46.859-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OutputToUser'/><category scheme='http://www.blogger.com/atom/ns#' term='workarounds'/><category scheme='http://www.blogger.com/atom/ns#' term='per-user'/><category scheme='http://www.blogger.com/atom/ns#' term='MetaMarkup'/><title type='text'>OutputToUser()</title><content type='html'>Back from vacation, and it looks like our last server release has &lt;a href="http://beta.metaplace.com/forums/posts/listing/4190"&gt;lots of goodies&lt;/a&gt; in it.  One of the best ones, in my opinion, is "Added support for per-user camera control from script."  It's the "per-user" part that makes me happy.  I have &lt;a href="http://camelpate.blogspot.com/2009/06/happy-accidents.html"&gt;mentioned&lt;/a&gt; &lt;a href="http://camelpate.blogspot.com/2009/06/worldspace-versus-screenspace.html"&gt;before&lt;/a&gt; the loss of the OutputToUser() function, which was the ultimate way to do per-user effects, so anything that adds them back is welcome indeed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MetaMarkup&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;The Metaplace servers, and specifically, a Metaplace world, communicates with a user's client by way of MetaMarkup tags, also occasionally called "game markup language."  These are plain-text messages such as these samples pulled from my time in the PlainOldChat world:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; [O_HERE]|10013|0:308|5|4|0|0|Crwth|0:1|0| |0&lt;br /&gt; [P_ZOOM]|1.000000&lt;br /&gt; [W_SPRITE]|24080:135|0.375|0.140625|255|255|255|http://assets.metaplace.com/worlds/0/24/24079/assets/images/dwnbtnpress.png|dwnbtnpresspng_|2|0|.|4|0|0&lt;br /&gt; [S_CONFIRM]|Sprite data fetched successfully.&lt;br /&gt; [UI_CAPABILITY]|2165|drag&lt;br /&gt; [INV_GOLD]|241|13829|fc239cc9bf4ef7e148aae958c258bbb4|25|284503&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first part in the [brackets] defines the type of the tag, and the rest of the data, separated by |pipes| make up the parameters of the tag.  If you're curious, you can visit any Metaplace world in which you're an administrator and go to Advanced|Debug and click on the "log" tab; perhaps uncheck the "commands" box as well.  Everything that the client needs to know -- appearance/disappearance of objects, movement of objects, UI popping in and out -- comes through here.&lt;br /&gt;&lt;br /&gt;As a world-builder, we have control over most of what comes through by using the API in scripts attached to objects, or by just building our world with the tools.  Thus, calling CreateObject() will make a [O_HERE] tag appear (to most people - see below), and anything UI-based will give you one or more [UI_ ...] type tags.&lt;br /&gt;&lt;br /&gt;OutputToUser() let you "hand-craft" these tags, such as &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  OutputToUser(self,"[P_ZOOM]|"..self.myzoom)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  for _,user in ipairs(GetUsersInPlace()) do&lt;br /&gt;    if user.cansee then &lt;br /&gt;      OutputToUser(user,"[O_HERE]|"..getHereParams(self))&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Why is this useful?  Right now, the Metaplace API and system is setup mainly to support the idea of a shared-world view.  If you go to &lt;a href="http://metaplace.com/MPCentralLive/play"&gt;Metaplace Central&lt;/a&gt;, everyone gets to see the same tiles on the ground, the same stationary objects, and see the same look for everyone they encounter.  But what if this isn't what you want?  Two types of games that easily come to mind, where players should have different views, are RPGs (Role Playing Games) and RTSes (Real Time Strategy).  Both of these can have requirements that certain players have different/extra knowledge about the game world than others.  With OutputToUser(), you could, with some work, code this up any way you wanted.  Now, you're at the mercy of what the API permits.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UI&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;So what DOES the API permit, on a per-user basis?  Well from the beginning, UI has always been able to be done per-user, which &lt;a href="http://camelpate.blogspot.com/2009/06/worldspace-versus-screenspace.html"&gt;I've mentioned previously&lt;/a&gt;.  This makes sense, as support for things such as pop-up dialogs are important pretty much anywhere, and just because I'm being asked "are you sure?" doesn't mean that everyone else should also see that message.  So from day one (at least, &lt;em&gt;my&lt;/em&gt; day one in beta) we've had per-user UI, so there was no need to use OutputToUser() for it.  Right?  &lt;br /&gt;&lt;br /&gt;Perhaps, but I can actually think of reasons you might want to hand-craft the [UI_...] tags; sometimes it's easier to have strings premade which have drop-in values (using string formatting) or to send a variable amount of UI commands based on computations and iteration through tables instead of a bunch of conditional code.  Fellow tester LunarRaid, as I recall, was doing something fancy with OutputToUser() and changing the art of UI elements.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Place Settings&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;There are &lt;a href="http://beta.metaplace.com/wiki/index.php/MetaMarkup#Place"&gt;a bunch&lt;/a&gt; of MetaMarkup tags that tell the user's client about the Place they're in, such as the location of the camera, the zoom level, and the type of View (top-down, isomorphic, etc.)  Some of these, such as the View, probably make sense as a shared, universal settings for all users (though, I did hand-craft [P_VIEW] tags in a world to allow visitors to change the View -- for themselves alone -- to see how certain code behaved in different Views).  Others, though, might have legitimate need to be different between users.  &lt;br /&gt;&lt;br /&gt;I mentioned in &lt;a href="http://camelpate.blogspot.com/2009/06/worldspace-versus-screenspace.html"&gt;a previous post&lt;/a&gt; that certain calculations depended on knowing what the zoom level was of a Place, and thus it was strongly suggested that the zoom was locked, preventing the user from scrolling with the mouse wheel.  But if you could set the zoom level, per user, you could not only override any mouse wheel zooming by forcing the zoom every second, half-second or quarter-second, but you could also provide a little zoom bar with which the player can legitimately change their zoom level in a way that code that depends on it will know (zooming with the mouse wheel is all client-side, so nothing gets sent to the server, and thus scripts can't know that it has been done.)&lt;br /&gt;&lt;br /&gt;Playing with the camera can also provide some interesting effects: right now, the camera is usually locked to a user's avatar, or locked to a given location in the world.  While we've had a MoveCamera() function for a long time, to change that location-based camera position, we never had the flexibility to change the camera behaviour between the two on a per-user basis.  (Locking the camera to the user, and hiding the user, allows for interesting effects such as &lt;a href="http://metaplace.com/crwth_followcamera/play"&gt;my follow camera experiment&lt;/a&gt;, based on a discussion with LunarRaid; he &lt;a href="http://beta.metaplace.com/forums/posts/listing/3784/1#1252516"&gt;recently requested some new functionality&lt;/a&gt; that I would also like, as can be seen by the jerkiness &lt;a href="http://metaplace.com/Instancing/play"&gt;here&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;One concept that can play a big part in RTSes is the "fog of war", where the map is unknown to you until you've been there, and even afterwards, parts of the map that you don't currently see can change - often, RTSes would have these "old" areas greyed out, with the scene as last seen.  Of course, "what you see" changes from each player's point-of-view, and this includes the tiles themselves; my world &lt;a href="http://metaplace.com/BITN/play"&gt;BITN&lt;/a&gt; (currently suffering from an art issue) was a testbed for such tricks, where the world was dark except for a few lightsources or the special ability of the user to see in the dark, and thus tiles were revealed based on user-specific data.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This last server release has given us some of the functionality that I used to have relating to Place settings, but there are still some calls that we could use...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Objects&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Along with the selective viewing of tiles in BITN was also the idea of selective views of objects.  In fantasy RPGs, you might have spells such as Invisibility, Illusion or Polymorph, which change your outward appearance.  These on their own are easy enough to implement, by changing the player's sprite.  But such games might also have the idea of different "vision" types (perhaps granted by other spells, perhaps innate to the player's character's race), such as See Invisibility, See Illusion, or True Sight (which might see through all of these tricks).&lt;br /&gt;&lt;br /&gt;Changing the player sprite is a good solution for Invisibility or Illusion if everyone is affected (although it could even be argued that the player of the invisible or illusionary character might want to see their true form), but as soon as we have different players who should see different things, the sprite-change method isn't suitable.  When we were able to handcraft MetaMarkup, we could send different [O_SPRITE] tags to different users, depending on what they should see.  This is exactly what BITN did, where I had all of the above spells and visions; if someone had cast illusion on their usual rogue form, they would appear as a fighter to the commonfolk, but anyone who had either See Illusion or True Sight would see the character as the rogue he or she really was.&lt;br /&gt;&lt;br /&gt;So seeing an object differently can be useful.  How about location?  In the fog-of-war idea, the greyed out "old information" might show that there were some enemy units there, but they have since moved on since you last looked; on your screen, those objects should still be there, but for that enemy player, he sees them for where they truly are.  Right now, you can't do this - as soon as you move an object, it moves -- there are no selective [O_HERE] tags based on whether or not you should have accurate knowledge of an object's location.&lt;br /&gt;&lt;br /&gt;(I should point out that we &lt;em&gt;do&lt;/em&gt; have a SetUserVisibility() function, which lets you set how far from a user objects can be seen - when objects leave this radius, either because they or the user moves, [O_GONE] tags are sent, even though some other user might still see them.  It's a limited version of per-user visibility, which does solve some problems, but it only allows "see it or don't", not "see it here or see it there".  There's also the gmVisible setting on templates, which set whether objects of this type can be seen by only administrators of a world, or by everyone -- again, it has it's uses (my &lt;a href="http://alpha.metaplace.com/module/camera_marker"&gt;camera marker&lt;/a&gt; module uses this functionality), but isn't a game play tool, just a game design tool.)&lt;br /&gt;&lt;br /&gt;Look and location are just two examples of object settings that you might want to have different per-user; you can browse through &lt;a href="http://beta.metaplace.com/wiki/index.php/MetaMarkup"&gt;the MetaMarkup page&lt;/a&gt; and look at each tag, perhaps coming up with all sorts of interesting game mechanics that could be implemented if only you could hand-craft how these tags were being sent to different users (just looking at the page now, I thought of having "x-ray specs" where you could have another player's avatar's clothing vanish, but only if you have the x-ray specs -- oo la la!)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Solution&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Of course, the easiest "solution" would be to just give back OutputToUser().  From what I can gather, the reason it was removed was to prevent malicious use; the last example MetaMarkup tag I showed above, [INV_GOLD], represents something "meta" from the gameworld you're in, something at the Metaplace level instead of the world level.  Perhaps forging these, making people think that they got gold that they shouldn't, is the issue?  Regardless of the reason, we've lost it.&lt;br /&gt;&lt;br /&gt;So far, we've been getting new API calls to replace some of the most-often cited functionality of OutputToUser().  The per-user zoom is definitely a good one; we also recently got AddEffectForUser() added to the mix.  And I can hope that, as long as I keep pestering/asking for the other users, we'll see the API calls appear.  &lt;br /&gt;&lt;br /&gt;A different solution, though, would be to still allow hand-crafting of tags, but perhaps only certain ones -- allow something like &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  OutputToUser(user,tag,params)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;where I would call&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  OutputToUser(self,"O_HERE","10013|0:308|5|4|0|0|Crwth|0:1|0| |0")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and the function can decide if "O_HERE" is one I'm allowed to hand-craft.  This would prevent me from faking INV_ tags, if that's the concern.  Without knowing the full range of concerns regarding the old OutputToUser(), I don't know if this new one would be feasible.  It would, however, be a one-stop solution to all of the other useful features lost and now pending API additions.  Even if the goal is to have API functions for all of the imaginable per-user needs, something like this might be a nice temporary fix?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-1139518729358761686?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/1139518729358761686/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/outputtouser.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/1139518729358761686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/1139518729358761686'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/outputtouser.html' title='OutputToUser()'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-3502869868495979449</id><published>2009-06-15T19:29:00.004-06:00</published><updated>2009-06-15T19:57:14.201-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='problems'/><category scheme='http://www.blogger.com/atom/ns#' term='workarounds'/><category scheme='http://www.blogger.com/atom/ns#' term='solutions'/><category scheme='http://www.blogger.com/atom/ns#' term='effects'/><title type='text'>Happy accidents</title><content type='html'>One of the interesting things about working with Metaplace is that, with all of the different API calls and graphic effects, you can do all sorts of things -- some planned, some not!&lt;br /&gt;&lt;br /&gt;For instance, &lt;a href="http://beta.metaplace.com/module/circleoftransparency"&gt;the module that I just published today&lt;/a&gt; provided something like a "circle of transparency", which, from Ultima Online, would make objects that blocked your view of yourself transparent, so you could still see things near you on the ground, or that are attacking you, or just to see yourself for whatever reason. This is a client-side effect, where the client just clears out a circle of user-specified radius based on the objects it renders.  Of course, I don't have access to the Metaplace client, nor does it have support for this, so I had to write a workaround, as I usually do.&lt;br /&gt;&lt;br /&gt;An actual circle was out of the question (or is it? Hrm...), so instead, I went for a method of making any object whose sprite overlapped with the user's become invisible.  This requires a lot of the &lt;a href="http://camelpate.blogspot.com/2009/06/worldspace-versus-screenspace.html"&gt;screenspace/worldspace&lt;/a&gt; calculations that I've talked about.  My original plan was to just make the sprite invisible to the user by changing the sprite, or through some sort of effect.  As I was working on it, testing the algorithm for determining sprite overlap, I thought, "I'll just have it show a glow effect on the objects that it wants to turn transparent".  This would be a temporary effect, just to help me visually see what the code wanted to hide.  The effect didn't seem to be appearing on the objects it should, so I started messing around with it, changing values and effect types to see if the glow was perhaps broken.  Once I figured out the issue (and I unfortunately cannot remember what it was), I had the effect set to the "bevel" effect, and had the object hiding itself.&lt;br /&gt;&lt;br /&gt;Well, let me tell you, this effect was even better than I had planned.  I had *planned* to just have the object vanish!  But with the bevel effect, you can see through it (since I turned on the "hideObject" setting) but still get a sense of what it is you're seeing through, because the edges still have the bevel effect applied.  Fantastic!  If you want to see the effect, go take a stroll around my &lt;a href="http://metaplace.com/crwth_rotateplace/play"&gt;rotateplace&lt;/a&gt; world, stepping near the trees.  &lt;br /&gt;&lt;br /&gt;This lucky stumbling on a solution was a bit surprising, since I had previously made a &lt;a href="http://metaplace.com/crwth_effects/play"&gt;demo world&lt;/a&gt; where you can play with all of the effects (I'll talk about them in a future post).  But even after having all of that hands-on experience with bevelling and effects in general, I never thought of using it as a transparency system that including the "cloaking border", as fellow-tester Dalian described it.  &lt;br /&gt;&lt;br /&gt;This is what made me think of writing this post:  there are all sorts of things that you can do in Metaplace, whether it's modules people have written to single lines of code, that you're able to put to uses that no one likely thought of.  Once in a while, I've looked at the latest release notes, read about the latest functionality that was introduced, and said, "hey, not only can it do this, but you could use it to do THAT, too."  But generally, I read about new functionality and think either "ahh, they finally added that feature we've been asking for", knowing what the original request was for, or, "hrm, they added this feature out of the blue; I wonder what the Metaplace content folks needed it for," and come up with a reasonable idea.  But who knows what kind of interesting uses are still undiscovered, because we read about these features and just recognize the "intended" use?&lt;br /&gt;&lt;br /&gt;And that brings up the other side:  having a need for some feature, effect or functionality, and not having it... what do you do?  Well, I kinda pride myself as the King of Workarounds, and I can usually find a way to do pretty much anything needed in Metaplace, even if there's nothing close to an obvious way to do it.  The workaround may be ugly, may be laggy, may be considered illegal in thirteen states, but I can usually find one.  And I think that, too, speaks to the beauty, the flexibility of the Metaplace platform:  even if you can't do something, you can still do it.&lt;br /&gt;&lt;br /&gt;Edit: I just realized that this post probably had me sounding like a huge Metaplace fanboy -- I didn't say anything bad!  How's this:  the best tool we had for workarounds, the OutputToUser() function, was taken from us.  So there!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-3502869868495979449?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/3502869868495979449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/happy-accidents.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3502869868495979449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3502869868495979449'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/happy-accidents.html' title='Happy accidents'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-3132229719112436256</id><published>2009-06-12T08:28:00.005-06:00</published><updated>2009-06-12T11:01:17.933-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usedata'/><category scheme='http://www.blogger.com/atom/ns#' term='metascript'/><category scheme='http://www.blogger.com/atom/ns#' term='properties'/><category scheme='http://www.blogger.com/atom/ns#' term='triggers'/><category scheme='http://www.blogger.com/atom/ns#' term='commands'/><category scheme='http://www.blogger.com/atom/ns#' term='booleans'/><category scheme='http://www.blogger.com/atom/ns#' term='metatables'/><category scheme='http://www.blogger.com/atom/ns#' term='lua'/><category scheme='http://www.blogger.com/atom/ns#' term='tables'/><category scheme='http://www.blogger.com/atom/ns#' term='functions'/><title type='text'>Metascript</title><content type='html'>The scripting language for Metaplace, Metascript, is strongly based off of Lua, so much so that one might think that it WAS Lua.  If you don't know Lua before diving into Metaplace, then that's fine, but if you've got some Lua under your belt, you might hit some hurdles.&lt;br /&gt;&lt;br /&gt;This post isn't about learning Metascript, though, or about the differences between it and other programming languages, or how Lua/Metascript sucks compared to your favorite language.  Just because you come from a background where arrays are indexed from zero doesn't mean that indexing from one is wrong. And if you want to argue the point, then I'll point out that &lt;a href="http://metaplace.com/wiki/index.php/Lua_Arrays"&gt;Lua doesn't have arrays anyway&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Syntactic sugar&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;One of the things that will strike Lua programmers is the extra set of definitions available in Metascript:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Define Properties()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Define Commands()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Trigger foo()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Command bar()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;WebTrigger baz()&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;These all start "special" functions in Metascript, and aren't standard Lua.  What post-alpha users might not know, though, is that these are just colourful candy coatings for some mundane-looking counterparts:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;function def_properties()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;function def_commands()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;function trg_foo()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;function cmd_bar()&lt;/li&gt;&lt;br /&gt;&lt;li&gt;function trg_http_baz()&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;In fact, way back when we also included the "trg_" prefix in the SendTo() calls.  As far as I know, these old methods still work; I assume this because&lt;br /&gt;&lt;ol type='a'&gt;&lt;br /&gt;&lt;li&gt;I still have old worlds that use it (thought I haven't visited them in a while)&lt;br /&gt;&lt;li&gt;We were told that they wouldn't stop working&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Kinda takes some of the mystery away, doesn't it?  I can see why the change was made:  it helps to emphasize the special nature of these functions from others defined with "function XXX()"; and it hides the unfortunate fact that variable prefixes are used to denote semantic meaning.  Don't consider them variables?  In Lua, &lt;br /&gt;&lt;code&gt;&lt;br /&gt;function foo_bar() &lt;br /&gt; ...&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;is equivalent to&lt;br /&gt;&lt;code&gt;&lt;br /&gt;foo_bar=function() &lt;br /&gt; ...&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Hrm.  I'm now curious whether I could write&lt;br /&gt;&lt;code&gt;&lt;br /&gt;def_properties=function() foo_bar=0 end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;in Metascript and have it work.  I'm going to guess not, which I'll talk about shortly.&lt;br /&gt;&lt;br /&gt;So, should you use &lt;span style="font-style:italic;"&gt;function trg_foo()&lt;/span&gt; instead of &lt;span style="font-style:italic;"&gt;Trigger foo()&lt;/span&gt; ?  One reason you might is to do some offline programming, so you have code that compiles under a standard Lua interpreter, but can still be tested (by implementing your own backend library that knows how to handle Triggers and Commands).  I've used this in the past to do some rapid development and to avoid some editor idiosyncrasies.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Define Properties() and Define Commands() (or function def_properties()...)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've made no secret my dislike for these "functions".  Define Properties() is where you define your script properties (where &lt;a href="http://camelpate.blogspot.com/2009/06/namespaces.html"&gt;they might collide&lt;/a&gt; with others on the object), include scripts (similar to Lua's dofile()), expose properties and export functions.&lt;br /&gt;&lt;br /&gt;Define Commands() is where you define your &lt;a href="http://camelpate.blogspot.com/2009/06/user-inputs.html"&gt;MakeInput()s&lt;/a&gt; and your MakeCommand()s.  (There used to be a separate &lt;span style="font-style:italic;"&gt;function def_inputs()&lt;/span&gt; -- I wonder if that still works.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;These functions are handled "specially"; they're actually executed at compile time, which can reveal bugs earlier than runtime, such as attempting to access functions that aren't available (which is almost all of them).  That means no Debug(), no AlertToPlace() - no pairs() or ipairs(). &lt;br /&gt;&lt;br /&gt;They're actually code, though; you can have for loops in here, if there's anything worth looping over, and conditionals, if there's anything to compare.  But generally, these functions act as definition blocks, and might better be implemented as such, extending the Metascript further from standard Lua.  Then we could have some alternate notation for defining properties, such as &lt;br /&gt;&lt;code&gt;&lt;br /&gt;float value;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;instead of relying on&lt;br /&gt;&lt;code&gt;&lt;br /&gt;float=0.0&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to work.  Which it won't.  This is because behind-the-scenes, any local variables defined in this block are being specially processed into member variables on a C++ object on the server (all supposition -- I'm not actually privy to the source code).  Being C++, types need to be known and once defined, permanent.  This means that, unlike Lua, Metascript won't let you redefine the type of a property once it has been set (and this is a reason why &lt;a href="http://camelpate.blogspot.com/2009/06/namespaces.html"&gt;I advocate table properties&lt;/a&gt;).  Not only that, but it means that certain Lua types - ones that aren't available in C++ - aren't allowed as properties, such as booleans and functions.  I would expect that supporting table properties had to require a bit of work, having the Lua form of the table being converted into something that C++ understands, and can hold, so why not a Lua function?  Why not the lowly boolean?  Also, this desire to define property type using Lua notation, instead of just creating a custom definition block, leads to ugliness such as&lt;br /&gt;&lt;code&gt;&lt;br /&gt;child="_object_"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to define a property as an object.  Ouch.  &lt;br /&gt;&lt;br /&gt;As for Define Commands(), I'll just re-iterate from my previous post that I don't understand why MakeInput() and MakeCommand() cannot be used outside of Define Commands().  Sure, some compile-time checking is nice, but you're not saving me from all sorts of other scripting bugs, so why this?  Also, these definitions check whether a Command specified in MakeCommand() is actually defined.  Why?  What's so bad about a Command being sent that doesn't have a handler?  Triggers allow this (and allow multiple handlers), yet Commands must have specifically 1.&lt;br /&gt;&lt;br /&gt;And because I just can't let it go:  why are these two scripts run in such a tight sandbox?  Fine, it might make no sense to call SendTo() while defining properties, or perhaps not even Debug() while defining an input, but no pairs()/ipairs()?  Am I really the first person to write "code" in these blocks, instead of just a handful of definitions and pre-structured function calls?  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Scope&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;In Lua, everything is a global variable unless you define it as "local".  In Metascript, too, your definitions have a global scope &lt;span style="font-style:italic;"&gt;within a script&lt;/span&gt; -- this probably bites me in the ass once a week, since I tend to write a lot of recursive functions.  The "within a script" is important, though.  When first looking at Metascript, and Metaplace, and the idea that multiple scripts are attached to an object, you might want to think that these are all loaded into a shared environment as far as the object is concerned.  However, this isn't true, and rightfully so.  &lt;br /&gt;&lt;br /&gt;Remember how &lt;br /&gt;&lt;code&gt;&lt;br /&gt;Trigger foo()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;is the same as &lt;br /&gt;&lt;code&gt;&lt;br /&gt;function trg_foo()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;is the same as &lt;br /&gt;&lt;code&gt;&lt;br /&gt;trg_foo=function() ...&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;?  Well this would be problematic if all scripts shared the same scope, because it would mean that an object couldn't have definitions for a Trigger function more than once, as the latter ones would overwrite the former, and having multiple definitions for the same Trigger is a key, powerful part of Metaplace.  This scoping is unfortunate, however, because this means if we use IncludeScript() to import a set of functions, we have to do it for every script that needs them, instead of just having it imported once for the object.  This makes it awkward to have a library that's used throughout a set of scripts.&lt;br /&gt;&lt;br /&gt;Also, the scope affects the idea of "self".  Commands and Triggers, usually the largest portion of a script, have an inherent sense of self.  But functions do not.  Why?  Well, they actually used to, I believe before we had IncludeScript(), so there wasn't a question of the context in which a function was being run.  Not being privy to the way the Lua sandbox is being run, I'm not exactly sure why "self" can't still be defined in the environment of a function call, but it is no longer supported - you must now pass it in explicitly.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Userdata&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Stock Lua also has a userdata type -- it's basically a C++ object -- so this doesn't make Metascript different in that regard.  However, because every object in Metaplace is represented by a userdata object, how they interact in script is important.  &lt;br /&gt;&lt;br /&gt;Knowing the structure of the userdata is usually required, because there's no reflection or introspection by default; you can access self.foo if you know it's there, but if you don't, you have no way to ask (not exactly true, see below).  Why is this important?  If I don't know that self.foo is there, should I really be using it?&lt;br /&gt;&lt;br /&gt;Well, yes, sometimes there are cases where iterating over everything is a good idea.  One case is the &lt;a href="http://metaplace.com/wiki/index.php/API_Functions#Stylesheet"&gt;set of stylesheet API functions&lt;/a&gt; that Metaplace provides.  These let us look at the stylesheet (basically, the static portion of a world), to peruse the templates, places, sprites, scripts and modules.  To do this without any prior knowledge, we need to be able to iterate over them all, much the same way that pairs() and ipairs() allow us to iterate through a table.  In some cases, we have a special ._all_ property on the userdata, which returns a table which can indeed be iterated over with ipairs().  But why not all the time?  &lt;br /&gt;&lt;br /&gt;For instance, if I want to browse the Places in my world, I can access a userdata object with &lt;span style="font-style:italic;"&gt;stylesheet.places&lt;/span&gt;, and if I know the name of one, I can index into this userdata, such as &lt;span style="font-style:italic;"&gt;stylesheet.places["0:1"]&lt;/span&gt;.  Alternately, I can use &lt;span style="font-style:italic;"&gt;stylesheet.places._all_&lt;/span&gt; and loop through them all, finding the one I want.  And once I have a specific Place, I can get another userdata from it with &lt;span style="font-style:italic;"&gt;someplace.tiles&lt;/span&gt;.  Not knowing anything about how many tiles there might be, I don't know what to ask for specifically, so I try &lt;span style="font-style:italic;"&gt;someplace.tiles._all_&lt;/span&gt;, and get back ... nothing.  It turns out that instead of a nice table to iterate through, I have to basically guess the indexes of the tiles, with something like&lt;br /&gt;&lt;code&gt;&lt;br /&gt;t=0&lt;br /&gt;while someplace.tiles[t] do&lt;br /&gt;...&lt;br /&gt;t=t+1&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;And worse, there are other objects, such as the Place itself, that neither have a way to iterate (with ._all_) nor to enumerate (with a 0-&gt;n loop), but that you just have to guess/know the properties of, hoping that the wiki documentation is up-to-date.  Again, this isn't something specific to Metascript -- Lua userdata can have this problem too -- but it would be nice if we had the ._all_ table available on all accessible userdata objects.  Alternately, all userdata objects could act as tables for purposes of using pairs() or ipairs(), if they really wanted to.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Metatables&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Metatables (the "meta" being unrelated to Metaplace) of Lua are a very powerful feature, one that, in my opinion, turns Lua from a simple toy language into a powerful full-featured one.  Metatables allow us to redefine our environment, creating a sandbox where a limited set of functions might be available.  Metatables allow tables to take on new functionality, such as addition of two tables, or different handling of accessing values that don't exist.  And metatables ... aren't available in Metascript.&lt;br /&gt;&lt;br /&gt;My only guess is that the current sandboxing provided to the Metaplace scripts makes further access to metatables ... hard?  Unwise?  Dangerous?  Confusing?  I'm not sure, but it's a real shame that we don't have them.  Granted, they're an advanced feature, and it's quite likely that very few Metaplace users will notice their absence, but heavier coders like myself certainly do, and working around them can be difficult, awkward, or near impossible.&lt;br /&gt;&lt;br /&gt;Case in point:  I'm writing a vector/matrix library for Metaplace, to help implement a new physics engine that I'm writing.  Such a library must exist for Lua somewhere, right?  Sure, there are some out there, but most (all?) wisely take advantage of metatables to overwrite the built-in operators, since it makes for much nicer and cleaner usage of such a library if I can say&lt;br /&gt;&lt;code&gt;&lt;br /&gt;newvector=v1+v2&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;instead of&lt;br /&gt;&lt;code&gt;&lt;br /&gt;newvector=v1.sum(v2)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;which I have to do now.  Even something as simple as making the vectors printable:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Debug(newvector)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;is a lot nicer than&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Debug(newvector.tostring())&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Small things?  Yes, but these only touch on what metatables can allow.  Frankly, I'd like to stop there because I don't want to realize what other powerful things I cannot do in Metascript because of their absence.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Verdict?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;All that being said, Metascript isn't a bad language.  It's Lua at its heart, with a few surgeries to make it tick a little differently, some of which might have been required, or some only elective.  This customized version of Lua hasn't itself prevented me from doing anything in Metaplace, apart from quickly porting in pure-Lua code from the internet; any walls I've hit have been with the Metaplace platform itself, and not the scripting language.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-3132229719112436256?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/3132229719112436256/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/metascript.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3132229719112436256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3132229719112436256'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/metascript.html' title='Metascript'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-7225305289201621475</id><published>2009-06-08T16:09:00.004-06:00</published><updated>2009-06-08T20:58:37.223-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='namespace'/><category scheme='http://www.blogger.com/atom/ns#' term='properties'/><category scheme='http://www.blogger.com/atom/ns#' term='triggers'/><category scheme='http://www.blogger.com/atom/ns#' term='commands'/><category scheme='http://www.blogger.com/atom/ns#' term='attributes'/><category scheme='http://www.blogger.com/atom/ns#' term='tables'/><title type='text'>Namespaces</title><content type='html'>Metaplace's biggest strength has to be the Marketplace, where the community can contribute their own modules to everyone else, either free or for a cost of Metacoins, the current virtual currency being tested during Open Beta. This biggest strength, however, is going to become a problem if something isn't done to deal with the ever-crowding realm of namespaces.&lt;br /&gt;&lt;br /&gt;In case you're not aware, a "namespace" is all of the available possibilities for naming something in a certain context.  The names of the Metaplace worlds make up a namespace -- no two people can have the same "cleanname" world name (so, for instance, you cannot make a world called "MPCentralLive", or "UO", or any of the tens of thousands of other worlds that are made.)  On the other hand, the display names are probably not strictly a namespace; I've not tried, but I do wonder if I can have the same display name on my world as someone else's.  &lt;br /&gt;&lt;br /&gt;The world names aren't TOO big of a deal. Sure, there might eventually be issues with copyright, or "prior art" in the case of names:  if Metaplace is going to take over the world, then I'm sure worlds named "McDonald's", "Microsoft" and the like are going to be fought over, in the same way that domain names (mcdonalds.com, microsoft.com) were when the internet started growing.  But the more pressing namespaces are from the scripting point-of-view, as more and more content becomes available, and the likelihood of problems increase.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Properties&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Metaplace objects are all about their properties.  Objects have a &lt;a href="https://alpha.metaplace.com/wiki/index.php/Objects"&gt;set of default ones&lt;/a&gt;, and different ones can be available if an object is a Physical object, or a Place, or a World.  All of these built-in properties tend to be set upon creation of the object (such as "id"), or as they operate inside the Metaplace environment ("x", "y", "z"). These properties are accessed by having a reference to the object and then using dot- or bracket-notation to access it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  self.id&lt;br /&gt;  self["spriteId"]&lt;br /&gt;  GetObjectById(10003).vx&lt;br /&gt;  etc.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Attributes, which can be thought of as properties that are configurable for a template (from which objects are created), are also accessed with this same notation.  This means, then, that you cannot add an attribute to your templates called "id", or "name", or "speed".  But you could have your RPG game where all of the objects need to have a damage value, so after checking the Properties pages on the wiki, you can have that:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  self.damage&lt;br /&gt;  monster[4].damage&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Also, scripts attached to an object can also add properties.  A module might define these in one of its scripts to allow passing of values between all of its component scripts.  It might also use them to persist values, such as an RPG module that provides the ability scores such as "strength" and "dexterity".  These are added in with the Define Properties() block, typically found at the top of a script.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  Define Properties() &lt;br /&gt;    strength=10&lt;br /&gt;    intelligence=10&lt;br /&gt;    dexterity=10&lt;br /&gt;    constitution=10&lt;br /&gt;    wisdom=10&lt;br /&gt;    charisma=10&lt;br /&gt;    PersistProperty("strength") -- etc.&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  self.strength=self.strength+1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We're okay unless our RPG game has ability scores for "speed" or "lifetime" or a "type" feature.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;As you can see we've already got concerns, so if you don't read the wiki property pages daily, you might, say, want to write your own physics library, and try to make script properties called "physics" or "speed" -- like I did.  Or, you might want to write your own containment library, and feel that objects should each keep track of where their "container" and what they "contained" -- like I did.  &lt;br /&gt;&lt;br /&gt;Now I believe they've helped the issue a bit by having the compiler generate errors if you try to define script properties that already exist as template properties, and perhaps even attributes?  I can't remember, because you'd think that once you'd made this mistake twice, and have wasted countless minutes, perhaps hours, debugging it.  But while the script compiler can save you from attempting to use built-in property names, it can do nothing to prevent you from using properties that someone else is also using.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And this fact is actually taken advantage of, in a way I don't much care for.  The Behavior Tool, in its latest form, recommends that any behaviour should have, in its script (and that's really all behaviours are -- scripts), &lt;a href="https://alpha.metaplace.com/wiki/index.php/Behavior_Tool#Behavior_Properties"&gt;a handful of properties&lt;/a&gt; defining that they are indeed a behaviour, name, description, and a few other future properties.  But... wouldn't this be a problem once you have more than one behaviour?  &lt;br /&gt;&lt;br /&gt;It would, if these properties were used for anything other than identification.  It's possible to pull out the values of these from the original script, even if these values get lost on the actual object due to being overwritten by other properties of the same name.  In fact, these values are retrievable without the script actually being attached to an object, quite different from accessing properties via the dot- or bracket-notation.&lt;br /&gt;&lt;br /&gt;I don't care for this "trick" to get values about the module.  I think other methods should be used, such as labels, instead of this series of faux properties.  I suppose it works, but I think it only fosters misuse of properties.  Especially for those that are meant to take on per-object values.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Commands and Triggers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Properties aren't the only namespace to worry about - as was emphasized today, in fact.  Newcomer tester Karkacabra was hitting a strange error, which quickly revealed itself to be a namespace collision -- with one of my own modules, no less.  He had defined a Command called "close", which collided with the one that I had used in my &lt;a href="http://metaplace.com/module/languagewindows"&gt;languagewindows&lt;/a&gt; module. Admittedly, (especially since I'm writing this blogpost,) I should have used a better name, such as "languagewindow_close".  And in my defense, Karkacabra should have also. *:^)  In fact, we should ALL be doing so; the content from the Metaplace team is actually pretty good at this, prefixing Commands and Triggers with a something meaningful and hopefully less likely to collide.&lt;br /&gt;&lt;br /&gt;Triggers are actually more of a problem for collision, because technically they DON'T collide -- it is perfectly valid for multiple scripts to define the same Trigger name, and they will each get called, with the order based on their attachment order.  This is intended, and a very useful function -- but ONLY if it's intended.  If I write a "use" Trigger on one of my objects, I had better hope that I had intended it for use with the Smart Object system in Metaplace, or else I'm going to see some odd behaviour -- not the wrong behaviour, as you're likely to see with colliding Command names, but additional behaviour, as all of the like-named Triggers get fired.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Workarounds&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So, we know that if we start using verbose prefixes on our Command and Trigger names, we're going to help reduce the collision in their namespaces;  the namespace is rather large, what with 50-60 or so characters per position in the name, and more length from a prefix just means more possibilities.  But what about the properties?&lt;br /&gt;&lt;br /&gt;Of course, the prefix system works just fine there, too, and I believe the Content Team uses it there.  But I prefer a different method, which works, in my opinion, better.&lt;br /&gt;&lt;br /&gt;Instead of defining each of the various or numerous properties individually, I define a single table as a property, with a suitably namespace-safe name, and then insert all of my properties within.  Here are some of the benefits:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;automatically grouped together by name, using foobar.propname instead of foobar_propname&lt;br /&gt;&lt;li&gt;requires only one PersistRuntimeProperty() call to keep all of the properties persistent (which also means none are accidentally forgotten&lt;br /&gt;&lt;li&gt;the property types do not need to be specified in the Define Properties() block&lt;br /&gt;&lt;li&gt;the table method allows easy deletion of properties, instead of setting to a "nonce" value&lt;br /&gt;&lt;li&gt;all properties for a module are easily iterated through&lt;br /&gt;&lt;li&gt;properties can be booleans, or functions&lt;br /&gt;&lt;li&gt;two modules, if they both try to define the same table property, won't fatally collide unless those tables predefine the properties (which is only necesary for default values)&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The only drawbacks that come to mind are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;reading the script's Define Properties() doesn't tell you the properties used&lt;br /&gt;&lt;li&gt;reliance on the PersistRuntimeProperty() of a table, which has had a history with a few problems&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I've been lightly pushing people in Metaplace to use the table property approach, only because I think it's nicer.  What I will start pushing harder for, however, is the namespace consideration -- and I'll be the first to admit that I'm quite the culprit.  In my defense, a lot of my code started off as "for me", so I would be aware of my own namespace usage, but of course as soon as I decide to publish it, or I start using others' modules, I need to be responsible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-7225305289201621475?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/7225305289201621475/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/namespaces.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/7225305289201621475'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/7225305289201621475'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/namespaces.html' title='Namespaces'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-5748993183158393516</id><published>2009-06-04T11:03:00.008-06:00</published><updated>2009-06-04T22:43:27.426-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='keyboard'/><category scheme='http://www.blogger.com/atom/ns#' term='modules'/><category scheme='http://www.blogger.com/atom/ns#' term='sample code'/><category scheme='http://www.blogger.com/atom/ns#' term='customization'/><category scheme='http://www.blogger.com/atom/ns#' term='mouse'/><category scheme='http://www.blogger.com/atom/ns#' term='triggers'/><category scheme='http://www.blogger.com/atom/ns#' term='MakeInput'/><title type='text'>User inputs</title><content type='html'>I had &lt;a href="http://camelpate.blogspot.com/2009/06/movement.html"&gt;mentioned previously&lt;/a&gt; that players have two inputs to their computer, and thus to Metaplace -- the keyboard and the mouse.  Fellow beta tester KStarfire pointed out that I missed the joystick/gamepad.  Does Flash support a joystick?  I'm not sure, but I'll concede the point to him.&lt;br /&gt;&lt;br /&gt;Metaplace uses a single method, the MakeInput() function, to define all of the inputs that a world understands.  These are parsed at compile time to ensure they're valid, and passed to the client as a bunch of tags to tell it which inputs it should bother to send to the server.  Here's its definition:&lt;br /&gt;&lt;br /&gt;MakeInput(input description, input code, input event, input modifier, command string)&lt;br /&gt;&lt;br /&gt;Where &lt;em&gt;input description&lt;/em&gt; is something like "press I to open the inventory", the &lt;em&gt;input code&lt;/em&gt; is "i", the &lt;em&gt;input event&lt;/em&gt; is "down", the &lt;em&gt;input modifier&lt;/em&gt; is "control" and the &lt;em&gt;command string&lt;/em&gt; is "inventory".&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Keyboard&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Whether you're going old-school RPG with arrow keys or WASD movement, or just need a few keys to support inventory and status windows, the keyboard is a must.  MakeInput() lets us define an event - that is, send a Command - upon a specific key press (up or down) with a modifier (shift, alt, none) for every need we have.  Some of the keys have bigger "codes" than the letter they represent, such as "left" for the left-arrow key.  This means we can do one-step-at-a-time walking, by just listening for "down" events for our arrow keys, or we can do walk-until-I-let-go walking, where we start walking on "down" event, and stop on "up".  This sounds like everything we could want, but there are some limitations.  &lt;br /&gt;&lt;br /&gt;There are a bunch of keys that aren't supported.  I won't spell them out here, but there's a bug filed (by me) in the Metaplace forums for certain punctuation keys that you just aren't able to listen for.  This is problematic for me in my Ultima Online world, where I want to emulate the speech system of UO, where there's no chat box in which you click to start typing.  This means that my current implementation (which you can &lt;a href="http://metaplace.com/UO/play"&gt;try out&lt;/a&gt;) lets you type letters, numbers and a space, but none of your punctuation comes through, which makes you type like a ... well, like most of the people on the internet.&lt;br /&gt;&lt;br /&gt;My UO world also points out another issue:  to get this working, I had to make 26 lines of MakeInput() to handle each letter; another 26 for when I press shift (to get a capital version); 10 more for the digits; space, enter... that's 64 right there.  If the punctuation was working, I'd have even more.  And what if I wanted to catch every keypress possible?  UO allowed you to map keypresses to macros, such as "control-e" for the allnames macro (which happens to work in my UO world, if you want to try it.)  This means four modifiers (none, shift, control, alt), two events (down and up), and ... 100+ keys?  That's 800 MakeInput() lines!  In my UO world, I only have half of them (I wasn't interested in key-up events).  Rest assured that I wrote a program to write those all out for me.  The problem that this points out:  there's no way to say "any" for the "input code", to say "for any keypress, send this command" or "for any shift-keyrelease, send this command".  &lt;br /&gt;&lt;br /&gt;Another problem that arises is that a given code/event/modifier combination can only have one possible Command that it will send -- you can't have "i" both bring up your inventory and cast the Invisibility spell.  If you define a MakeInput() more than once for the same combination, it used to be the case that the client used a random one.  Now it looks like the last-defined one -- the one in the script loaded last -- is the one that wins.  &lt;br /&gt;&lt;br /&gt;For a user creating their world completely from scratch, this shouldn't be a problem - they know what they want each key to do, and aren't likely to re-define the same combination again for another purpose (though I'll come back to this).  The problem is more likely to appear when the Marketplace becomes involved, when a world-builder buys off-the-shelf modules that define MakeInput()s.  As more and more content becomes available, the chances of these modules colliding is going to grow.  &lt;br /&gt;&lt;br /&gt;This has already occurred.  KStarfire had a world where the spacebar was the "throw" command; after he had created that functionality, the avatar module -- probably the most ubiquitous module in Metaplace -- added a jump action to the avatars.  And what key did they choose for that?  That's right, the spacebar.&lt;br /&gt;&lt;br /&gt;KStarfire, at that point, had two choices: either edit the avatar module, change the MakeInput() line where the jump was defined, &lt;strong&gt;&lt;em&gt;and have to do this every time the module updated&lt;/em&gt;&lt;/strong&gt;; or change his own code.  But what if the "throw" command was one that he had purchased, instead of written himself?  Another solution would be to allow the keys for every module to be defined by the user or world-builder, to have the MakeInput() commands pull their values from a user or template property.  Unfortunately, the MakeInput() command doesn't allow this (future blog post).&lt;br /&gt;&lt;br /&gt;So is there a solution?  I think so.  I think that a module needs to be developed that defines &lt;strong&gt;every single combination &lt;/strong&gt; in MakeInput() and sends a single command from the client to the server when it happens.  This Command would then fire a Trigger to the the user object, and at that point, any interested parties could listen for the Trigger, and based on configuration, decide if that matters to them.  So the avatar system could allow the user, or world admin, to say that "control-spacebar is jump" and "spacebar is throw", and each module would handle things the way they should.  Additionally, modules could, if they wished, share the same keypress.&lt;br /&gt;&lt;br /&gt;I believe in this solution so much, in fact, that I've implemented it.  Also, I was able to work around the limited environment of the Define Commands() block to come up with this little gem to save myself from typing an enormous list of MakeInput()s:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;keys={"numpad0","numpad1","numpad2","numpad3","numpad4","numpad5","numpad6","numpad7","numpad8","numpad9",&lt;br /&gt;        "f1","f2","f3","f4","f5","f6","f7","f8","f9","f10","f11","f12",&lt;br /&gt;        "backspace","tab","return","pause","capslock","escape","space","pageup","pagedown",&lt;br /&gt;        "end","home","left","up","right","down","print","printscrn","insert","delete","help","numlock","scroll",&lt;br /&gt;        '-','=','\\','.','/','0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i',&lt;br /&gt;        'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',&lt;br /&gt;         "shift","control","alt","lshift","rshift","lcontrol","rcontrol"}&lt;br /&gt;    states={"down","up"}&lt;br /&gt;    modifiers={"none","shift","control","alt"}&lt;br /&gt;&lt;br /&gt;    for x=1,#keys do&lt;br /&gt;        key=keys[x]&lt;br /&gt;        for y=1,#states do&lt;br /&gt;        state=states[y]&lt;br /&gt;            for z=1,#modifiers do&lt;br /&gt;            modifier=modifiers[z]&lt;br /&gt;                MakeInput('keypress '..modifier..'-'..key..' '..state,key,state,modifier,(state=='up' and 'un' or '')..'key '..modifier..' '..key)&lt;br /&gt;            end&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This generates every possible MakeInput() that Metaplace supports right now (with all of the missing punctation).  It fires a Trigger key(modifier,code) for key down, and unkey(modifier,code) for key up.  Now I can have code such as &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger key(modifier,code)&lt;br /&gt;  if code==self.jumpkey and code==self.jumpmodifier then&lt;br /&gt;    SendTo(self,"jump")&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;or even have a way to encode the modifier and code into a single value so there aren't two values to store and compare, and have a comparison function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Trigger key(modifier,code)&lt;br /&gt;  if self.jumpkey==keypair(modifier,code) then&lt;br /&gt;    SendTo(self,"jump")&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I really think that this approach is going to be inevitable, because the colliding modules is going to happen more and more, and the need for customizable keypresses, either to prevent collisions or to allow flexible worldbuilding (not to mention, to be able to support key macros) is going to be in demand.  And, as I hinted at before, we might want to change the meaning of a key halfway through gameplay; for instance, I might want "i" to open the inventory in a non-combat mode, but when I'm in combat mode, "i" might cast the Invisibility spell.  This could be one Trigger that handles the keypress, that understands both modes, or two separate Triggers (in different modules) that each handle a specific case, unaware (and uncaring) that the other exists.&lt;br /&gt;&lt;br /&gt;I have one last thing that I'd like to see support for -- held-down keys generating repeated key-presses, much as we're used to seeing on computers when we hold down a key in our word processor.  Some world might need this functionality (such as my UO speech), but others probably won't; once I add this as an option, I'll publish the above code as a small module for universal key handling.&lt;br /&gt;&lt;br /&gt;One last thing about the keyboard:  even with the Flash client in focus, some browsers out there rudely capture keypresses instead of passing them on to Metaplace.  Specifically, the "control-c" in UO is meant to refresh your mouse cursor (there's a bug there I have yet to file), but in Internet Explorer 8, this brings up a menu or something.  I've seen this on some of the other browsers, with certain keypresses, whether it toggles bookmarks or who-knows-what.  This is an unfortunate issue that world- and module-builders will have to keep in mind -- the browser compatibility issues reach further than the web page!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Mouse&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The mouse is a must in today's computing, giving us near-instant pointing and selection.  Very few worlds are likely to get by without its use, largely in part to the graphical nature of the worlds (plain worlds such as &lt;a href="http://metaplace.com/PlainOldChat/play"&gt;Plain Old Chat&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;Unlike the keyboard, there are only a few different input codes available for the mouse: &lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;mouse-terrain -- clicking on the ground (on a tile)&lt;br /&gt;&lt;li&gt;mouse-object -- clicking on an object&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;"click" and "double-click" are the only two events supported. Embarrassingly, I've never tried the "double-click" event, since the only one of my worlds that needs it (UO) was created before this event was added, and I ended up writing my own (in a similar manner to how I'll write the repeating key code).  All of the modifiers are supported (none, shift, control, alt).&lt;br /&gt;&lt;br /&gt;There are lots of things missing here.  There's no way to distinguish where the mouse-click went down, and where it was released (I believe the location of release is what is sent to the server).  This is one that I think could add some extensibility to a world's interface, and wouldn't increase the traffic at all.&lt;br /&gt;&lt;br /&gt;Having the mouse location, either while holding down the mouse button or not, would of course be valuable.  This would let us have draggind-and-dropping, of handling mouse-over/hover (future blog post) very easily, and to have the mouse cursor itself act as an agent in the game, as the avatar itself.  We're told that the traffic between the client and server would be too large to allow this feature.  I can certainly see that there *could* be a lot of traffic, depending on how often you sent the mouse data.  But I propose that this is a setting we should be able to send to the client,&lt;br /&gt;&lt;br /&gt;  SetMouseLocationUpdateTime()&lt;br /&gt;&lt;br /&gt;as well as provide a way to enable and disable the flow of this data, so worlds that need it can request it:&lt;br /&gt;&lt;br /&gt;  StartMouseLocationUpdates()&lt;br /&gt;  StopMouseLocationUpdates()&lt;br /&gt;&lt;br /&gt;And of course, the client could have a hardcoded maximum of tags being sent in.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And last but not least, there's the fact that all mouse events currently represent the left-mouse-button only.  No right click (nor middle click, nor sideclick?).  This is a Flash problem, as I mentioned in the post on movement, but indeed a problem.  As I had said, Ultima Online used right-mouse-hold (or right-mouse-doubleclick) for movement, and everyone associates right-clicking with a context menu.  Other clients (a future blog post) could implement this support, of course, but I think it unlikely that we're going to see support in the Flash client.  And, of course, even if other clients COULD support it, there's no way for the world to state that it's interested, because it's not an option in MakeInput().&lt;br /&gt;&lt;br /&gt;So for now, workarounds need to be made.  Luckily (perhaps oddly), double-left-click on terrain had no meaning in Ultima Online, so I've used that as my walk-to command (I use single-left-click as a "look" command.)  As for workarounds for other missing things, I use ctrl-left-click as pickup/drop (instead of drag-and-drop).  Though I also have a little hack for this, too, which I'll mention ... in a future post.  Shift-left-click is, I believe, used to bring up the Behavior Tool, which acts in place of a right-click for a context menu, and the current avatar behaviour seems to go with single-left-click for bringing up a context menu on other avatars (for meeping, sending friend requests, offering gameplay, etc.)&lt;br /&gt;&lt;br /&gt;I can also see a need for a mouse equivalent to the "every key" module I mentioned above, letting world owners define the mouse meaning they desire to various actions:  if I want single click to be "look", and double click to be use, I should be able to set that instead of be forced to use whatever the module-writer decided.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Overall, I'm a bit disappointed with the mouse support, because it's really the primary interface for many.  The modifiers can help us for now, but I really hope that, once all of the bigger pieces of Metaplace get done, the mouse support can be revisited.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Joystick?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Is this so far-fetched?  Not really; I have no idea if Flash can support joysticks, or gamepads, but custom clients sure can, but we still hit the problem of having no support for them in the MakeInput() call.  And this brings up something that has stuck in craw for a bit:  the special nature of MakeInput().&lt;br /&gt;&lt;br /&gt;I had planned on talking about this in a future post, but I really don't understand why MakeInput() must be in the Define Commands() block.  Or at least, why it must ONLY be in that block. I can see a desire to precompile this, to ensure that the definition is done correctly.  Or can I?  Every other API call checks this at runtime, not at compile time, and lets the world owner know in the Debug or Log window, and lets the user know by just not working.  Why can't MakeInput do the same thing?  Why can't I call MakeInput() later in my script, after asking the user to define some keys, of after the user's preferences have loaded?  Why can't we call DeleteInput() -- there's a tag ([W_DELINPUT]) that exists for when a script is unloaded, which removes a defined input, so doing so after the world is running, and a user is connected, is certainly possible.  Why can't we add inputs after we're up and running?&lt;br /&gt;&lt;br /&gt;Maybe there's a good reason, maybe it goes against the design of the system, or maybe this was just oversight.  Some day I hope to know!  Until then, though, I hope my module will be a viable workaround, or that the collision of inputs doesn't become too big of a problem, too soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-5748993183158393516?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/5748993183158393516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/user-inputs.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/5748993183158393516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/5748993183158393516'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/user-inputs.html' title='User inputs'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-1174709747362417268</id><published>2009-06-03T07:56:00.004-06:00</published><updated>2009-06-03T11:02:12.855-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='screenspace'/><category scheme='http://www.blogger.com/atom/ns#' term='modules'/><category scheme='http://www.blogger.com/atom/ns#' term='worldspace'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><category scheme='http://www.blogger.com/atom/ns#' term='sprites'/><title type='text'>Worldspace versus Screenspace</title><content type='html'>In Metaplace, you have two canvasses on which to work:  the "worldspace", where all of your objects sit, and the "screenspace", which is where the UI elements sit.  There are a few key differences to note about them:  worldspace is seen by everyone, where screenspace may or may not be shared; worldspace is affected by zoom, where screenspace is not; and screenspace is always on top of worldspace.&lt;br /&gt;&lt;br /&gt;These three differences can be utilized to make all sorts of interesting effects that aren't immediately obvious to world builders.  The fact that screenspace can be on a per-user basis means that it can help implement worlds where not everyone has the same knowledge -- fog-of-war and different kinds of "vision" (infravision, see-invisibility) can be supported through interesting uses of screenspace instead of worldspace.  Basic menus and dialog boxes inherently "take advantage" of the fact that the UI doesn't zoom while the worldspace does, although the opposite can also be true -- zoom way out in a world (that supports it) with nametags on the avatar:  the nametag soon dwarfs the puny little figure.  &lt;br /&gt;&lt;br /&gt;There might also be times when you want to have an effect between two objects:  my &lt;a href="http://metaplace.com/massesandsprings/play"&gt;Masses and Springs&lt;/a&gt; world needed to draw a line between two objects, but a line is in screenspace while the objects were in worldspace (if you go see the world, it currently suffers from a bug, which I've just reported -- it works okay if you click down-and-right of the face.  Also, it only really works if you get out of full-screen mode, for reasons mentioned below).&lt;br /&gt;&lt;br /&gt;So how do you go about matching up screenspace to worldspace?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;There are four methods to attach UI (our screenspace objects) in Metaplace, two that are seen by everyone, and two that are per-user.  UiAttachWorld() is attached to the viewport itself, treating the top-left corner as the origin, and is seen by everyone.   UiAttachUser() is the per-user equivalent.  UiAttachObject() attached UI to a specific worldspace object, &lt;span style="font-style:italic;"&gt;based on the origin of that object&lt;/span&gt; (this is important later), and is seen by anyone who has that object on-screen.  UiAttachUserObject() is the per-user equivalent.&lt;br /&gt;&lt;br /&gt;Each of these functions has their use:  UiAttachWorld() is good for a high score board, or a population monitor; UiAttachUser() is good for a HUD with user-specific values; UiAttachObject() is good for nametags; and UiAttachUserObject() is good for context menus.  For the latter two, the UI "follows" the object as it moves (or as you move away), which is what you likely want, and is all nicely left up to the client; if the object moves off-screen, you don't see the UI, and that's probably a good design.  Any UI attached to an object will hopefully be an appropriate size, such as a context menu that is reasonably sized to fit menu choices, or a nametag that is readable but not covering up half of the screen.&lt;br /&gt;&lt;br /&gt;But is the nametag an appropriate size?  What about when you zoom way out, like I mentioned before?  And in the case of UiAttachWorld() and UiAttachUser(), the screenspace objects don't follow a specific object, but sit on-screen until removed, taking up real estate -- but how much?  &lt;br /&gt;&lt;br /&gt;The scaling and positioning of screenspace objects to worldspace objects is a two-part problem:  zoom and screen size.  The zoom can be read with GetPlace().zoom , but that only retrieves the "set" value of zoom, and cannot take into account any mouse-wheeling done by the user.  This means that any screenspace-to-worldspace mapping requires the world to enable zoom lock.  There's no way to set zoom from script (anymore - we used to be able to fake it with OutputToUser()), so we can't even force a zoom level on users if they change it, or if we want to have different zooms for different cases.  Screensize is even worse:  there's no function to request a user's screen size (which could vary like the zoom could), and there's no support for the undocumented P_VIEWPORT tag to force a client to a specific size.&lt;br /&gt;&lt;br /&gt;Why is this such a problem?  To do any sort of mapping between screenspace and worldspace, whether it's position or sizing, we need one or both of these values.  Sizing, of course, only requires knowing zoom, so provided we enable zoom lock, we could ensure that or nametags are scaled appropriately with our avatars by using GetPlace().zoom to resize them.  But since we have no way of changing zoom, I suppose this is really a one-time calculation anyway.  Not too useful.  But for positioning, and especially for screenspace items that might span between two worldspace objects, both position and size matter.&lt;br /&gt;&lt;br /&gt;To map position between screenspace and worldspace, we need to know at least one point that maps between the two.  Metaplace has the idea of a "camera", which is where the center of the viewport is focused; for most worlds, this is usually centered on the player's avatar, but Metaplace also allows the camera to be focused on a specific point in the world.  The important thing to note is that this value -- either the player's position in the world or the camera's focal point -- is a worldspace coordinate that we know, and it maps to the &lt;span style="font-style:italic;"&gt;center of the viewport&lt;/span&gt;.  The fact that this is the center is important because we don't know how tall or wide our viewport is, which means that we still have no way of determining where in the worldspace the top-left of our viewport maps to.  This means that we must always attach the screenspace items based on the center... but we don't know the center of the viewport, either!&lt;br /&gt;&lt;br /&gt;So what do we do?  If the camera is focused on the player, then we just use them as our center, using UiAttachObject() or UiAttachUserObject(); and if the camera is focused on a specific worldspace coordinate, we have to figure out how far away a known object is from that worldspace location (the player, or perhaps a more stationary object), convert this distance to screenspace (which we still haven't figured out how to do), and then attach to that object.  &lt;br /&gt;&lt;br /&gt;Sounds easy, right?  Let me make it worse:  the view type will also affect your calculations!  Metaplace currently supports a bunch of views:  top-down and side-view (which thankfully use the same scaling); isomorphic, stepped and sloped isomorphic; and rotated (or "UO") view.  Also, as we hinted at earlier, the origin of the object to which we're attaching can matter, if our screenspace effect is related to the object itself.&lt;br /&gt;&lt;br /&gt;Let's try to make sense of all this.  First, let's think about scale.  A zoom of 1.0 in Metaplace means that a tile is 64 pixels wide, regardless of which view we're in.  Hurray for small blessings!  This means that for every 64 pixels on the x-axis that we move something in screenspace, it will shift the equivalent of one tile over in worldspace, at zoom 1.0.  A zoom of 2.0 makes everything twice as big, which means that one tile is now 128 pixels wide, and so our general formula is &lt;br /&gt;&lt;br /&gt;screenspacewidth = (number_of_tiles_wide)*64*zoom&lt;br /&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;number_of_tiles_wide = screenspacewidth / (64*zoom)&lt;br /&gt;&lt;br /&gt;The height of a tile depends on which view we're in:  top-down and side-view use square tiles, so the size is also 64 pixels high, at zoom 1.0; rotated view's tiles are diamonds, but are proportional, so they, too, are 64-pixels high; and the isomorphic views are nicely at an angle where the tiles are half the height of their width, so 32 pixels high, at zoom 1.0.  Given this, we now know how to scale screenspace elements to their worldspace counterparts; we know that if we want a UI window, button, etc. to be two tiles wide and two tiles high, we could write a function like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  function tiles_to_pixels(user,w,h)&lt;br /&gt;    local tilewidth=64&lt;br /&gt;    local tileheight=64&lt;br /&gt;&lt;br /&gt;    local view=user.place.view&lt;br /&gt;    if view==2 or view==3 or view==4 then -- isomorphic&lt;br /&gt;      tileheight=32&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    local zoom=user.place.zoom&lt;br /&gt;    &lt;br /&gt;    return w*zoom*tilewidth,h*zoom*tileheight&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and then might use this function in such a way:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  w,h=tiles_to_pixels(2,2)&lt;br /&gt;  local win=UiRect(0,"blank window",w/2,h/2,w,h)&lt;br /&gt;  UiAttachUserObject(self,self,win)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;to draw a big square on top of the player.  Big deal?  It will be the same relative size, no matter what you set the Place's zoom level to.  That's the big deal.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;But what about position?  This is all fine and good if we want scaled screenspace items that are a given distance from the player, but what if we don't know that distance all the time, because the player can move about?&lt;br /&gt;&lt;br /&gt;Let's say that the player is at (10,10) in our world, but we want a screenspace item to appear at (4,6), because, perhaps, they have a treasure map marking X, and the player's Treasure Spotting skill is high enough that a glowing X should appear in the distance.  One way would be to just drop a glowing X object into the world and be done with it, but other players would then be able to see it, even if they don't have the Treasure Spotting skill.  Alternately, we could drop an invisible object in that location, and then attach a glowing X to it using UiAttachUserObject(), so only the Treasure Spotting player sees it; this would work for most cases, but some people will point out that the object information is still being sent to everyone else's client, and thus they can still know that the object is there, even without a glowing X.  If that's not enough of a concern for you, let's say instead that we want a glowing line drawn from the player to the location of the treasure -- there's no UI function to attach a line from one object to another, so we're back to going through all of this nonsense anyway.&lt;br /&gt;&lt;br /&gt;It sounds easy, that we can take our current position (10,10), and the desired position (4,6), find the difference (-6,-4), turn those into pixels (-384,-256) or (-384,-128) depending on view (at zoom 1.0), and voila, we have our offsets from our player's position in screenspace.  And that's true -- if we're in top-down or side-view.&lt;br /&gt;&lt;br /&gt;But this is not the case for any of the other views:&lt;br /&gt;&lt;br /&gt;rotated (UO) view&lt;br /&gt;&lt;img src="http://pages.cpsc.ucalgary.ca/~crwth/metaplace/UOview2.png"/&gt;&lt;br /&gt;&lt;br /&gt;iso view&lt;br /&gt;&lt;img src="http://pages.cpsc.ucalgary.ca/~crwth/metaplace/ISOview2.png"/&gt;&lt;br /&gt;&lt;br /&gt;See how going from (10,10) to (9,10), while it sounds like we're moving one tile "left", we're actually moving left and up.  This is the "rotated" part of the view, where the X and Y axes of the worldspace don't align with the X/Y axes of screenspace.  This means even more figuring, to determine how much each of our tiles in worldspace slide along these diagonals in screenspace.&lt;br /&gt;&lt;br /&gt;Let's look at the rotated view first.  I think it's pretty clear from the diagram that as we move along the X axis (from (9,10) to (10,10)), we're moving half-a-tile to the right, and half a tile down, or (32,32) pixels in screenspace (at zoom 1.0) for every increase along the X axis in worldspace.  And, as we move along the Y axis (from (10,9) to (10,10)), we move half-a-tile to the left, and half-a-tile down, or (-32,32) pixels in screenspace for every increase along the Y axis in worldspace.&lt;br /&gt;&lt;br /&gt;This means that our previous attempt, where we determined that our offset (our difference) was (-6,-4), can use these offsets to figure out where we're going:  since the difference was -6 on the X axis, we can multiply that by the (32,32) we figured out and get (-192,-192); the difference on the Y axis was -4, multiplied by our (-32,32) gives us (128,-128).  Add these two together, and we get (-64,-320) instead of the (-384,-256) we originally (naively) came up with.  If you're still not convinced, try it again going from our (10,10) to (6,6) -- looking at the diagrams above, you should realize that if we step back on both the X and Y at the same time ((10,10) to (9,9)) the actual screen movement is straight upwards.  Your result should have zero for the X, and a negative number for Y.&lt;br /&gt;&lt;br /&gt;And what about the isomorphic view?  Well, it's still the same formula of "half-a-tile right" and "half-a-tile down", but remember the height of the tile, in screenspace, is the only thing that changes from the rotated view to the isomorphic view, so if we figure things out based on "tiles left" and "tiles down", we can convert that using our function above.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The calculations above let us figure out the offset of the glowing X from the player, on the player's screenspace.  But the player is likely moving.  If we didn't take that into account, that red X would stay the same distance away, "moving" along the ground as the player did.  Using this approach, we'd have to re-adjust (using UiPosition()) the X as the player moved, hooking into path_begin() and/or path_end(), or on a tick-based timer.  &lt;br /&gt;&lt;br /&gt;Alternately, we could attach it to a stationary object;  the X-marks-the-spot code might find a tree, rock or seashell near the (4,6) location, and use that as the attach point.  The drawback is that the code would have to do a search for an object, instead of just lazily using the player, but the savings would be in having to redraw the X.  As long as the code can determine that an object isn't likely to move, this might be worthwhile.&lt;br /&gt;&lt;br /&gt;So where's the final code?  Err, well, I had plans on posting the two functions that I use often -- worldSpaceToScreenSpace() and screenSpaceToWorldSpace() -- but realized as I was writing this post that I don't handle all views (the "rotated" view had been removed when I wrote it, so I don't support it), nor do I handle the camera not being attached to the user (the code makes a bunch of assumptions in this regard).  Once I add this extra support, I'll post the code to the Marketplace.&lt;br /&gt;&lt;br /&gt;I didn't talk about how to handle that -- the camera being fixed on a location instead of the player.  It's really just one more offset to be calculated, though; if we already know how to figure out how to get a screenspace object to appear at a certain worldspace location, if the player is at the center, so as long as we can convert the player's location from the camera location, we can still figure out the correct offset.  I realize as I type this that diagrams might be helpful, but trust me when I tell you that drawing those two pictures above taxed my artistic skill.&lt;br /&gt;&lt;br /&gt;The last thing I glossed over is the origin of an object.  Metaplace allows the origin to be defined anywhere on the sprite that represents the object - three "fixed" modes (top-left, center and bottom-center), as well as a free-form "percentage" system.  While this doesn't matter if you're just trying to find another worldspace location -- the player is at (10,10), regardless of how the sprite is drawn) -- there are times where you might want to know the exact screenspace coordinates at which an object's sprite is drawn.  This can depend on the scale of the sprite, and the zoom as well.  One example for this might be to draw a box around a sprite; knowing the height and width isn't enough, if you don't know where to start drawing.  I have a function for this, too, which returns the top-left, center and bottom-middle screenspace coordinates of any object, which are part of my same screenspace/worldspace library. &lt;br /&gt;&lt;br /&gt;I'm surprised how often my world ideas require mapping values between the two spaces; my UO world needed it for dropping items in-hand, and that's where I first wrote it.  But things like the nametags should scale in size and location, regardless of zoom, as should speech bubbles.  And attaching UI effects to objects (damage numbers, highlighting or selector graphics) could also take advantage of this to good effect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-1174709747362417268?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/1174709747362417268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/worldspace-versus-screenspace.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/1174709747362417268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/1174709747362417268'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/worldspace-versus-screenspace.html' title='Worldspace versus Screenspace'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-3154606950119124033</id><published>2009-06-02T12:10:00.003-06:00</published><updated>2009-06-02T12:18:48.304-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modules'/><category scheme='http://www.blogger.com/atom/ns#' term='ideas'/><category scheme='http://www.blogger.com/atom/ns#' term='sample code'/><title type='text'>Blog content -&gt; Metaplace content</title><content type='html'>My first few posts have been mainly about Metaplace from a scripting point-of-view, and I can't promise than many of the future ones won't be along the same theme.&lt;br /&gt;&lt;br /&gt;That being said, I hope the non-programmers reading the blog don't stop reading;  even if you're sure you'll never decide to try your hand at scripting, reading what's possible in Metaplace can help you design your worlds and try to find folks in the Metaplace community that are willing to implement some of the ideas you might come up with after reading this blog, perhaps in exchange for some design ideas, some writing/dialogue, or some art, depending on where your talents lie.&lt;br /&gt;&lt;br /&gt;Also, if anyone finds modules or worlds that demonstrate any of the ideas that I put forth, and I fail to mention them in the blog itself, please let me know, either in a comment or in Metaplace mail, and I'll try to keep such information up-to-date for future readers.  While this might seem like the Metaplace Marketplace's job, that does require authors to properly tag their creations, and if Metaplace becomes as successful as I expect, the Marketplace is going to become very full indeed, perhaps causing some worthwhile modules to be missed.&lt;br /&gt;&lt;br /&gt;Of course, I'd love to be the one implementing all of the ideas that I come up with here, but family life comes first, which means that Metaplace, unfortunately, doesn't get the time that it used to.  Still, I do have my ever-growing list of ideas and projects, and SOME day I'll get through it... perhaps when my grandchildren start helping.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-3154606950119124033?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/3154606950119124033/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/blog-content-metaplace-content.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3154606950119124033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3154606950119124033'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/blog-content-metaplace-content.html' title='Blog content -&gt; Metaplace content'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-629506176062452834</id><published>2009-06-02T07:36:00.004-06:00</published><updated>2009-06-02T10:52:49.041-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pathfinding'/><category scheme='http://www.blogger.com/atom/ns#' term='sliding'/><category scheme='http://www.blogger.com/atom/ns#' term='keyboard'/><category scheme='http://www.blogger.com/atom/ns#' term='moving'/><category scheme='http://www.blogger.com/atom/ns#' term='animation'/><category scheme='http://www.blogger.com/atom/ns#' term='MoveObject'/><category scheme='http://www.blogger.com/atom/ns#' term='mouse'/><category scheme='http://www.blogger.com/atom/ns#' term='collision'/><category scheme='http://www.blogger.com/atom/ns#' term='triggers'/><category scheme='http://www.blogger.com/atom/ns#' term='events'/><category scheme='http://www.blogger.com/atom/ns#' term='SlideObject'/><category scheme='http://www.blogger.com/atom/ns#' term='physics'/><title type='text'>Movement</title><content type='html'>I would say that, apart from "seeing", movement in a game or virtual world is the most important feature.  While I can come up with a few games (Boggle) or worlds (chat room) that don't require the idea of player movement, they're the exception and not the rule. Given this, you would expect that movement would be a solid, ironed-out subsystem of virtual worlds in general, and Metaplace specifically.&lt;br /&gt;&lt;br /&gt;Unfortunately, as recent discussion on the forums has shown, that's not quite the case.  Sure, your starting world in Metaplace has a nice click-to pathfinding script attached, but that doesn't provide a solution for everyone.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Inputs&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Not counting voice control or some telepathic USB device I don't know about, computers have two methods of user input:  the keyboard and the mouse.  Metaplace gives us access to both, for the most part, which means we have a choice on how we get instructions to our game world, and thus have a choice on how one might move around in our world.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The mouse&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The mouse is probably the most common input method, since at a glance we can see our world, where we want to go, and can deftly move our mouse cursor there and simply click.  Getting this click event is easy enough to do in Metaplace.  But what about other mouse-based movement?  In Ultima Online (you'll eventually get used to me using it as a reference) you did not "walk to" with the mouse, but rather "walked toward", by using the right-mouse-button on the screen and holding it down; the distance that the mouse cursor was from the player also let you decide between walking and running.&lt;br /&gt;&lt;br /&gt;Unfortunately, this isn't possible in Metaplace, for two reasons:  we don't have access to a right-click event (this brings up a Flash menu in the client; there are apparently hacks out there to allow right-clicking in Flash, but they aren't used for the Metaplace client); and we don't have access to mouse-up versus mouse-down events (and mouse-location), just mouse-clicked.  This isn't a Flash limitation, from what I understand, but more a consideration of the bandwidth involved with sending the mouse location so-many times per second.&lt;br /&gt;&lt;br /&gt;Still, we've got the click event, which lets us get our intentions across pretty well; we're also able to modify that with keys, such as ctrl-click to run there, or shift-click to sneak there.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The keyboard&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The keyboard typically has two forms of movement: a "step-based" method and a "continuous-walk" method.  There are also games where you control a cursor with the keyboard, but I suggest that that's only used when a mouse isn't available; and I suppose that you could have "direct" movement to waypoints using letters or numbers, but I'm going to happily ignore that idea for now.&lt;br /&gt;&lt;br /&gt;In the step-based movement, players will press their arrow keys or WASD keys once for each "step" they wish to take; this would likely be in a tile-based game, where a discrete step is perhaps something visible in the world, and it represents the smallest unit of movement in the world.  The continuous-walk movement instead listens for key-down events and key-up events, so holding down the arrow key will keep you walking in that direction until you release it (or until the game decides you can go no further, due to movement "points" running out, obstacles, or consumption by monsters.&lt;br /&gt;&lt;br /&gt;The continuous-walk method is really just a shorthand way of doing the step-based movement, allowing the player to hold down the key to represent repetitive, consistently-spaced key presses.  This means that continuous-walk also has the idea of a "step", but it's more likely that these steps are smaller:  it's no big deal to quickly walk across a 64-"step" tile by holding down an arrow key, but you certainly wouldn't want to have to press the arrow 64 times for the same effect!  The hold-and-release of a key allows the player to stop whenever they want, but there might also be the need to support the larger steps anyway, where releasing the arrow key still has the player continue to move until they reach the next full tile.  It all comes down to the needs of the game world.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Movement&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once we know where we want to go (or at least, which direction we should start heading), how do we actually move?  We could instantly appear there, we could slide across the screen in a direct line, or we could follow some sort of path around obstacles.  Each of these methods has their place, depending on the world or game.  Metaplace provides four ways to get you around.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://metaplace.com/wiki/index.php/MoveObject"&gt;MoveObject&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The MoveObject() function is an instant teleport.  No delay, no motion, just here one instant, and there the next.  This might be used by simple board games, moving pieces around, though you may want the movement to be animated (if only for your opponent(s) to better see what your move was).  It could be used in a simple RPG, stepping from tile to tile, but again, it might look better to have them slide along instead.  Teleportation!  There's one that surely needs to use MoveObject.  The nice thing about MoveObject is that there's no issue of collision detection, because we're not moving through anything; however, it if there's something already at that location, MoveObject won't stop you from piling objects on top of each other, so if that's a problem, it needs to be handled in the code.  MoveObject doesn't allow you to move to a blocking tile, however.  And there's no issue about timing or animation, because it's an instantaneous event.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://metaplace.com/wiki/index.php/SlideObject"&gt;SlideObject&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;SlideObject gives that nice bit of animation to the game or world, allowing you to move your objects from here to there in a specified amount of time.  Of course, you might still want to animate the sprite to make it look like it's walking, rolling or otherwise ambulating, but the movement there is nicely handled for you.&lt;br /&gt;&lt;br /&gt;There are a few things to note:  as far as the server is concerned, the object is instantaneously teleported.  The client is told about the sliding, and does the job of moving the sprite along the right path at the right speed.  However, the object's location along that path is always accessible, correctly, from the server.  So what, then, does it mean that "as far as the server is concerned, the object is instantaneously teleported"?  I believe this means that, for any subsequent requests by other objects, that location is considered occupied, such as if another object wanted to MoveObject there.  I've admittedly not tested this out, yet, but that's the only meaning I can imagine.  &lt;br /&gt;&lt;br /&gt;Also, SlideObject will allow you to slide onto tiles that are blocking. This can be a good or bad thing.  In the new &lt;a href="http://metaplace.com/RockingtheMetaverse/play"&gt;Rocking the Metaverse&lt;/a&gt; world, the bouncers get you up to the VIP section by sliding you there, past all of the blocking tiles on the stairs. On the other hand, a movement system using SlideObject would have to look for blocking tiles manually to prevent the player from wandering over them.  Also, SlideObject will allow the physics system (below) to trigger hit() and hit_by() events, if a collision takes place during the slide.  If the blocking-tile behaviour of SlideObject is acceptable to you, this is a good way to go.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://metaplace.com/wiki/index.php/Physics_properties"&gt;Physics&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The Metaplace engine has a built-in physics system.  Instead of specifying specific a location to move to or slide to, instead we can define a facing (direction) and a speed, and let our player keep going until we change these values.  Physical objects are affected by quite a few things: they will fire the hit() and hit_by() Triggers when they hit objects, allowing you to decide what should happen; blocking tiles will fire a bounce_tile() Trigger, and also reverses your direction for you; and the world edges fire a bounce_border() Trigger, and again automatically applies the "bounce" off of it.  There's also a facing() Trigger fired whenever the direction changes; this is useful for changing your sprite to rotate to another direction.&lt;br /&gt;&lt;br /&gt;This automatic bounce effect can be a problem, though, if that's not the behaviour you want.  It works well for a space maze game, like &lt;a href="http://metaplace.com/Uberspace/play"&gt;Uberspace&lt;/a&gt;, or an Arkanoid/Breakout game, but for other uses we might wish that movement in the direction of the blocking object was dampened down to zero, just stopping movement in that direction.  Of course, that's what you can do with the bounce_tile() and bounce_border() Triggers, but I would almost expect the two methods of handling it to be swapped -- the bounce to be coded, and the stopping to be standard.  Or perhaps a flag?&lt;br /&gt;&lt;br /&gt;Alas, these aren't the only concerns with the physics system.  Objects tend to "drift", where the client's representation isn't quite the same as the server's, which can lead to inexplicable collisions (the server sees the collision, but the player's view in the client doesn't match or make sense) and to sudden lurching movements (when the player changes the direction or speed, and the server suddenly updates the client with some out-of-sync location information).  Additionally, detecting collisions between blocking tiles or the corners of the world boundary don't quite work, allowing objects to escape into the unknown.&lt;br /&gt;&lt;br /&gt;Still, if you have good control over your physical objects, and use the physics system for short, immediate movements, it can provide more flexibility than SlideObject.  The fine-tune control over direction, speed and even acceleration might be easier to do with physics than it is using SlideObject, where you might have to convert angles and slide times or perform multiple slides to get a nice effect.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://metaplace.com/wiki/index.php/PathToLocation"&gt;pathfinding&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The pathfinding system is what you get with most of the worlds that you create in Metaplace, generally tied to the mouse click.  This system uses an algorithm called A* (a-star) to try to find the best path from where you are now to where you want to go, avoiding blocking tiles and tiles with blocking objects on them.&lt;br /&gt;&lt;br /&gt;Whether or not a path is found, a Trigger is fired, either path_begin() or path_not_found(), so you can either start animating your walk, or give a loud BZZZ if the player cannot get somewhere.  Upon reaching the end, a path_end() Trigger is also fired, to allow the walk animation to be turned off, or to allow some NPC to pathfind to its next location (perhaps because it's walking a circuit, patrolling).&lt;br /&gt;&lt;br /&gt;The pathfinding system uses the physics system, which means that on the way, you can receive hit() and hit_by() Triggers, as well as get facing() calls.  In theory, you shouldn't receive any bounce_border() or bounce_tile() Triggers, since the path has walked around those for you.  Finally, you can also specify a "mode", which tells the pathfinding system that it can or cannot walk across diagonals; this is useful if you want to avoid the appearance of a collision when there isn't really one (a future post) -- you can see some good diagrams on the PathToLocation() wiki page.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The best solution?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;From the four, it sounds like the pathfinding is the best way to go for most uses, especially if you need to avoid blocking tiles, if you care about collisions, and if you want the player to be able to single-click their way around.  It can also be tied to an key-press movement system, where with each press, the system attempts to pathfind one tile left/right/up/down.  A key-holding continuous system is a little trickier; based on the speed at which the pathfinding gets the player from tile to tile, you could have your movement code continuously calling the pathfinding system again, from within path_end(), if the key is still being held down.  This would mean that the animation should not be stopped, if it exists, which might cause some collisions between multiple modules trying to handle the path_end() Trigger.  Attention would have to be paid to the smoothness of the motion, to see if multiple pathfind calls, chained together like this, would appear as a jerky, stop-and-start motion.&lt;br /&gt;&lt;br /&gt;Pathfinding, because of its adherence to blocking objects, isn't useful in worlds where you can ignore these features, such as when you can fly over terrain.  As mentioned before, the bouncer in the Rocking the Metaverse world has to SlideObject you into the VIP room, because pathfinding will NOT get you there (as you can see for yourself if you click up top).  And, if you're looking for the instant-move effect, pathfinding might be able to fake it with a high enough speed, perhaps... I haven't tried!&lt;br /&gt;&lt;br /&gt;I think the marketplace needs a handful of movement systems available, so those who know what they want can piece it together.  Whether it's mouse-clicks, key-presses or key-holds, and moving, sliding or physics, there's no reason we can mix-and-match these together, or even have a single module that allows the world administrator to select their world's manner of movement.  Unfortunately, over the span of the alpha and beta testing, there have been a variety of keyboard and mouse movement systems, using move, slide, physics and pathfinding, each at one point being the "standard" movement script provided with your new world.  It would a shame if the click/pathfinding system being used by the avatars, quite obviously the most popular method now, becomes the only way people know how to interact with their worlds.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-629506176062452834?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/629506176062452834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/movement.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/629506176062452834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/629506176062452834'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/movement.html' title='Movement'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-4342319613423685386</id><published>2009-06-01T07:24:00.009-06:00</published><updated>2009-06-19T23:37:39.151-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='matrices'/><category scheme='http://www.blogger.com/atom/ns#' term='screen effects'/><category scheme='http://www.blogger.com/atom/ns#' term='tinting'/><category scheme='http://www.blogger.com/atom/ns#' term='color'/><category scheme='http://www.blogger.com/atom/ns#' term='colour'/><category scheme='http://www.blogger.com/atom/ns#' term='alpha'/><category scheme='http://www.blogger.com/atom/ns#' term='effects'/><title type='text'>Screen effects</title><content type='html'>While animating a sprite is surely the best way to get a visual effect, there are other methods to change the look of your world which don't require nearly as much artistic skill.&lt;br /&gt;&lt;br /&gt;Metaplace supplies a growing number of these:  tinting has been available for over a year, allowing each sprite to change its colour to great effect; decals provide a way to have a ground-based effect from a sprite, such as a shadow; the general UI system allows a mask overtop of any region of the screen, which with creative use of alpha, can generate some interesting effects; the sprite light system provides a similar effect as the UI system, but provides a simpler method of attaching the effect to an object, and also supports rotating along with the object; and the effects system provides a handful of other interesting changes.  And last week, the screen effects functions were added.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This new set of visual effects has three functions: UiScreenEffectColor(), UiScreenEffectMatrix() and UiScreenEffectClear().  I'll abbreviate these as USEColor/USEMatrix/USEClear to save wear and tear on my keyboard.&lt;br /&gt;&lt;br /&gt;These effects, as you can guess, cover the whole screen, regardless of size, unlike any of the other effects mentioned above, which were specific to a sprite or a region of the screen.  The USEClear function, obviously, removes the effect of an existing screen effect; it should be noted that, because it does not take any sort of "handle" or "reference", that this means you can only have one such effect in place at any one time.  This is different than most of the other effect methods mentioned above, and will be discussed later.&lt;br /&gt;&lt;br /&gt;I should mention that, until the introduction of these various effects systems, I knew NOTHING about colour theory.  Playing with these various systems has made me learn a little bit, so I should be able to convey some ideas here, but for all of you that know more than I do, be sure to chime in!&lt;br /&gt;&lt;br /&gt;Of the other two functions, the USEColor function is the simpler of the two.  The definition of the function is&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(user, time, r, g, b, method)&lt;br /&gt;&lt;br /&gt;The "r", "g" and "b" values are, essentially, scales that are going to be applied to the red, green and blue portions of every pixel on the user's screen.  The scales, perhaps a bit confusingly, are in the range of 0 to 255; while these values make sense if we're setting absolute colours, they don't make as much sense as scaling values.  However, as long as we think of zero as "none" and 255 as "full", we can think of the values as everything in between, such as 127 as "half".  In fact, you can just divide these values by 255 to get a percentage, if that helps (and in fact this is exactly how it's explained in the wiki, and how it's done in the USEMatrix function).  The method parameter currently only accepts "multiply".  You can get an "additive" effect with some of the other effect systems, but not as a full-screen effect with USEColor.&lt;br /&gt;&lt;br /&gt;So what do these scales allow us to do?  Well, if we set them all to 0, we're basically saying "set red to 0% of its original value, green to 0% of its original value, and blue to 0% of its original value" which is going to turn our RGB to 0, and give us a completely black screen:&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(self,1000,0,0,0,"multiply")&lt;br /&gt;&lt;br /&gt;You can try this out by &lt;a href="http://metaplace.com/crwth_ui/play"&gt;going to my crwth_ui&lt;/a&gt; test world, pressing the backtick (`) key (or, if on a non-US keyboard, try the single-quote key (')) to get a command console, and typing&lt;br /&gt;&lt;br /&gt;usec 1000 0 0 0&lt;br /&gt;&lt;br /&gt;You should see the scene fade to black in one second (the 1000 value is for time-to-fade).  Note the little sprite light beaming from your head -- I'll talk about that in another post.  You can return it to normal by typing&lt;br /&gt;&lt;br /&gt;useclear&lt;br /&gt;&lt;br /&gt;You can get a nice dusk effect by using values of 127:&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(self,1000,127,127,127,"multiply") (or "usec 1000 127 127 127")&lt;br /&gt;&lt;br /&gt;Also, I kinda lied above, when I said that the scale range was 0-255; you can actually go higher than that, to scale the original values even higher than they were (which just makes the 0-255 range even more confusing):&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(self,1000,500,500,500,"multiply") (or "usec 1000 500 500 500")&lt;br /&gt;&lt;br /&gt;Other interesting effects include removing all of the red from your view:&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(self,1000,0,255,255,"multiply") (or "usec 1000 0 255 255")&lt;br /&gt;&lt;br /&gt;or getting the red to stand out a little more than usual:&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(self,1000,400,200,200,"multiply") (or "usec 1000 400 200 200")&lt;br /&gt;&lt;br /&gt;or you can just see how much blue there was in your world:&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(self,1000,0,0,255,"multiply") (or "usec 1000 0 0 255")&lt;br /&gt;&lt;br /&gt;Remember that between each of these, you don't need to USEClear the effect, because you can only have one going at a time.  You might want to clear it if you're trying this out in crwth_ui, though, to "reset" the view to the original, to get a better impression on the change each is making.&lt;br /&gt;&lt;br /&gt;These effects aren't too bad;  they allow for simple brightening and darkening of our world, and also allow for a little bit of colour shifting.  But the real power comes from the last function in the set.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;USEMatrix is defined as the following:&lt;br /&gt;&lt;br /&gt;UiScreenEffectMatrix(user, time, matrix)&lt;br /&gt;&lt;br /&gt;where the matrix is a table of 20 values.  These 20 values are used in this way:&lt;br /&gt;&lt;br /&gt;redResult=(m[0] * srcR) + (m[1] * srcG) + (m[2] * srcB) + (m[3] * srcA) + m[4]&lt;br /&gt;greenResult=(m[5] * srcR) + (m[6] * srcG) + (m[7] * srcB) + (m[8] * srcA) + m[9]&lt;br /&gt;blueResult=(m[10] * srcR) + (m[11] * srcG) + (m[12] * srcB) + (m[13] * srcA) + m[14]&lt;br /&gt;alphaResult=(m[15] * srcR) + (m[16] * srcG) + (m[17] * srcB) + (m[18] * srcA) + m[19]&lt;br /&gt;&lt;br /&gt;This sure looks like a mess, with a lot of multiplication, a lot of addition, values from blue being added to the green, and alpha to the red... what's going on?&lt;br /&gt;&lt;br /&gt;If you learned (and remember) any matrix mathematics in high school or post-secondary school, then this might look familiar.  If not, don't worry about it:  you don't need to understand the math behind HOW this is applied, just what each number is going to contribute.  For those that do remember matrices, and are curious, it might help if I write it out this way (I have no doubt the proportional font is going to make a mess of this):&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;pre&gt;&lt;br /&gt;                | m0  m5  m10 m15 |&lt;br /&gt;                | m1  m6  m11 m16 |&lt;div&gt;| r g b a 1 | * | m2  m7  m12 m17 |  = | r' g' b' a' |&lt;br /&gt;                | m3  m8  m13 m18 |&lt;/div&gt;&lt;div&gt;                | m4  m9  m14 m19 |&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The row on the left is a 1x5 matrix of our pixel's value (the "1" at the bottom is there for a reason, I promise), and the block in the middle is our matrix of values.  The final row will be the new value of our pixel.&lt;br /&gt;&lt;br /&gt;Why is this so complicated?  First of all, using a matrix means that we can allow any of our original values -- our original red, green, blue and even alpha values -- to have an effect on the result.  With USEColor, all we could do was modify each of the colours only with respect to itself (and not alpha at all), which meant that if we increased our red's scale, our image was going to get brighter overall;  we could try to guess how much to reduce our green and blue by to keep the total brightness the same, but we'd be guessing, because each pixel is different.  Using this matrix means that, if we choose, our new red value can be affected by how much green and blue we also have in this pixel, not only the red.&lt;br /&gt;&lt;br /&gt;The second reason to use a matrix is because it can make it easier to apply multiple effects.  The screen effect system only allows one effect in place, so if we want to increase the red but decrease the green, we'd have to figure out a way to "add" these two effects together into a single call.  This is easy enough using USEColor, because each of the scaling values is separate from the other.  But when we extend our abilities with USEMatrix, it's not so simple.  Luckily, matrix multiplication of two different effects can let us do them both at once (with a challenge or two, if you know your matrix math).  Even if we could have more than one effect going at once, we'd likely want to multiply our multiple effects into a single matrix, because those formulas above, with sixteen multiplies and sixteen additions, are going to be done to every single pixel on your screen; that can approach two million pixels at full screen, on a nice monitor, and if every one of those pixels has to go through all those multiplications and additions every time the screen is redrawn (20, 30, 40 times a second?), that's a lot of processing no matter how fast your computer is, how efficient the client's renderer is, or if your video card is doing the work.&lt;br /&gt;&lt;br /&gt;The values in the matrix are numbers more in line with the idea of scaling, compared to those used in USEColor, but because of the additive nature of the formulas above, it's best to think of them more as a "ratio" of contribution to the final value. What does this mean?  Well, look above at the formulas, and find the mention of m[0]:&lt;br /&gt;&lt;br /&gt; redResult=(m[0] * srcR) + (m[1] * srcG) + (m[2] * srcB) + (m[3] * srcA) + m[4]&lt;br /&gt;&lt;br /&gt;This shows that the "redResult", the red portion of each pixel, is affected by m[0] through m[4], and each affects a different "source colour"; m[0] is multiplied by the pixel's red value, so m[0] can be thought of as "the amount that the original red affects the resultant red", and m[2] as "the amount that the original blue affects the resultant red."&lt;br /&gt;&lt;br /&gt;Specifically, look at m[0], m[6] and m[12].  These three are the amount that red, green and blue affect red, green and blue in the result.  Sound familiar?  This are the positions in the matrix that simulate the effect of the r,g,b values in USEColor.  In fact, if we scale the USEColor values down from the 0-255 range to a 0.0-1.0 range (by dividing them by 255, as mentioned before), and turn all of the other matrix values to zero (except for the alpha one, which we'll discuss shortly), we discover that we can make a USEMatrix do the same thing as a USEColor, such that&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(user, time, r, g, b, method)&lt;br /&gt;&lt;br /&gt;is equivalent to&lt;br /&gt;&lt;br /&gt;UiScreenEffectMatrix(user, time, {&lt;br /&gt;  r/255 , 0     , 0     , 0 , 0 ,&lt;br /&gt;  0     , g/255 , 0     , 0 , 0 ,&lt;br /&gt;  0     , 0     , b/255 , 0 , 0 ,&lt;br /&gt;  0     , 0     , 0     , 1 , 0&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;and that&lt;br /&gt;&lt;br /&gt;UiScreenEffectColor(self,1000,127,127,127,"multiply")&lt;br /&gt;&lt;br /&gt;is the same as&lt;br /&gt;&lt;br /&gt;UiScreenEffectMatrix(self,1000,{0.5,0,0,0,0,0,0.5,0,0,0,0,0,0.5,0,0,0,0,0,1,0})&lt;br /&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;UiScreenEffectMatrix(self,1000,{&lt;br /&gt;  0.5,   0,   0,   0,  0,&lt;br /&gt;    0, 0.5,   0,   0,  0,&lt;br /&gt;    0,   0, 0.5,   0,  0,&lt;br /&gt;    0,   0,   0,   1,  0})&lt;br /&gt;&lt;br /&gt;for legibility.  (Yes, 127/255 isn't the same as 0.5 ...)&lt;br /&gt;&lt;br /&gt;You can try this in the crwth_ui world with the following:&lt;br /&gt;&lt;br /&gt;usem 1000 0.5 0 0 0 0 0 0.5 0 0 0 0 0 0.5 0 0 0 0 0 1 0&lt;br /&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;usem 1000 0.5,0,0,0,0,0,0.5,0,0,0,0,0,0.5,0,0,0,0,0,1,0&lt;br /&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;usem 1000 {0.5,0,0,0,0,0,0.5,0,0,0,0,0,0.5,0,0,0,0,0,1,0}&lt;br /&gt;&lt;br /&gt;Each of our USEColor examples can be turned into a USEMatrix call in the same way, by only considering m[0], m[6] and m[12], and leaving the other colours' contributions to the result as zero.  This was what we noted above as a limitation of USEColor.&lt;br /&gt;&lt;br /&gt;So why would we want the other colours to contribute?  Why would I want the amount of green in my original pixel to affect how much red I have in my new one?&lt;br /&gt;&lt;br /&gt;One reason was hinted at above, with USEColor:  the fact that the total brightness of the image is going to be changed, if we just start increasing or decreasing each of the colours.  However, if we can "know" the brightness of each of the pixels, somehow represented in our matrix, then we can maintain that brightness even while changing the colours!  But how do we do that?&lt;br /&gt;&lt;br /&gt;Leave it to the colour scientists to tell us.  That's right, the colour scientists.  They've gone and figured out the amount that each of the red, green and blue colours contribute to the overall brighness of a pixel.  If it was completely even, then we could determine the brightness by just adding up the values of red, green and blue.  If we had those, we could then turn every one of the pixels to a grey that matches that brightness.  Because our total "brightness" can be anywhere from 0 to 765 (red/green/blue all zero, to red/green/blue all 255), we would divide the total by 3 to get the range back to our 0-255 range.  We can do all of this with our matrix -- remember that it does multiplication and addition for us, so if we just do the dividing-by-three to all values first (by multiplying by 1/3, or 0.33), and then add them together, we can get a greyscale image:&lt;br /&gt;&lt;br /&gt;  UiScreenEffectMatrix(self,1000, {0.333,0.333,0.333,0,0,0.333,0.333,0.333,0,0,0.333,0.333,0.333,0,0,0,0,0,1,0} ) (or " usem 1000 {0.333,0.333,0.333,0,0,0.333,0.333,0.333,0,0,0.333,0.333,0.333,0,0,0,0,0,1,0} ")&lt;br /&gt;&lt;br /&gt;Not bad, right?  But the colour scientists claim that red, green and blue don't actually contribute evenly to the brightness;  try this:&lt;br /&gt;&lt;br /&gt;UiScreenEffectMatrix(self,1000, {0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0,0,0,1,0} ) (or " usem 1000 {0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0,0,0,1,0} ")&lt;br /&gt;&lt;br /&gt;&lt;div&gt;These values -- 0.3086, 0.6094, 0.0820 -- are apparently the ratio of brightness for each of red, green and blue; you'll notice that they add up nicely to 1.0000 , to get the total brightness.   (I got these numbers &lt;a href="http://www.graficaobscura.com/matrix/index.html"&gt;here&lt;/a&gt;, where they mention another set of numbers used for NTSC, but that depends on the gamma of the image, which is beyond the scope of this post (and beyond my knowledge)).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Having this matrix is useful, because it allows us to get an screen that has the same brightness as before, but with no colour, leaving it open to colouring in any way we choose.  Of course, to colour it afterwards isn't possible, since we can't apply a second filter, so we'd need to combine the two effects together, which would require some matrix math.  I'm currently finishing up a Lua matrix math module, which I'll post and allow people to blend various screen effects together.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So what about that alpha stuff?  This is the transparency of the pixels, going from zero for fully-transparent, to 255 for fully opaque.  For instance, you could use&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;  UiScreenEffectMatrix(self,1000, {1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0.5,0} ) (or " usem 1000 {1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0.5,0} ")&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;to turn the whole world half as transparent as it used to be; in the case of the crwth_ui world, you should now see the background "metaplace" logo through the cacti and such.  The matrix we used said to leave red, green and blue alone, but to change the alpha of every pixel to half of its former value.  If you combine that with the grey-scaling matrix (easy to do by-hand, without complex matrix math),&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;UiScreenEffectMatrix(self,1000, {0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0,0,0,0.5,0} ) (or " usem 1000 {0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0.3086,0.6094,0.0820,0,0, 0,0,0,0.5,0} ")&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;you get what might be a ghost state of the world, where the background coming fully through might be a useful effect for a world builder.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The original matrix formulas above had things like m[3], which set "how much the pixel's new red is affected by the original pixel's alpha".  I'm really not sure if there's a good use case for this, but perhaps the more creative types can prove me wrong; the only reason that that exists is to "fill in" the matrix.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And what about the last set of values, m[4],m[9],m[14],m[19]?  These don't take any of the previous values from the pixel, but just simply add in a value.  Again, I can't really think of a good example for this one, but I welcome anyone who can!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I hope to add something to the crwth_effects world to demo these new features, in a nicer interface, but for now, I hope that anyone interested in it can try out the crwth_ui world and the "usec" and "usem" commands for now.  As for my plans for screen effects, I want to make a module that provides sun(s) and moon(s), on user-settable revolutions; they could each cast their own colours, would vary their light based on angle, and could introduce colours (such as a red sunrise or sunset) based on angle as well.  Two moons would cast more than one, and the sun (or two!) might drown out any effect the moons have.  All of this will have to be compacted into a single matrix, of course, which is why I'm writing the matrix library.  To see something along these lines, check out Brooke's (chooseareality) world &lt;a href="http://metaplace.com/model_testing/play"&gt;model_testing world&lt;/a&gt;, where she has a simple day and night cycle with the USEColor function (and her really cool fog!)&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-4342319613423685386?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/4342319613423685386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/06/screen-effects.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/4342319613423685386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/4342319613423685386'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/06/screen-effects.html' title='Screen effects'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-2581642050477902072</id><published>2009-05-29T07:56:00.001-06:00</published><updated>2009-05-29T09:05:40.271-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='avatars'/><category scheme='http://www.blogger.com/atom/ns#' term='DoCommand'/><category scheme='http://www.blogger.com/atom/ns#' term='triggers'/><category scheme='http://www.blogger.com/atom/ns#' term='commands'/><category scheme='http://www.blogger.com/atom/ns#' term='malicious code'/><category scheme='http://www.blogger.com/atom/ns#' term='scripting'/><title type='text'>Commands</title><content type='html'>A "Command" in Metaplace is like an instruction or action from the user.  Clicking on the ground in a Metaplace world sends a Command ("walk_to"), pressing a key sends a Command ("inventory open") and clicking a button in a pop-up window sends a Command ("purchase confirm").  These are all examples of "user-defined" Commands, where each of these, "walk_to", "inventory", "purchase" would have to be defined in a script attached to the user's "object" in the world.&lt;br /&gt;&lt;br /&gt;There are also "system" Commands, which for the most part, can be thought of as "meta" commands -- "meta" means "above" or "beyond", and is typically used in the sense of something thinking or acting "outside of the box" or interacting with its own reality (in Dungeons &amp; Dragons, the term "meta-knowledge" is often used to refer to knowledge the player's character has that it shouldn't, but does because the player has it, such as the fact that that dragon is immune to fire, even though the character should know nothing about dragons).  The system Commands, then, usually don't deal with the world content itself - walking around, opening inventory or confirming events - but modify things external to the gameplay, such as add new graphics to the world or restarting the server.  These commands are built-in, not written by users, and are distinguished by their leading slash, such as "/create_sprite" or "/restart_server", and are generally not (knowingly) sent by the user, but rather are sent, for the most part, by the world-building tools.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The idea that Commands are instructions from the user, then, means that, in theory, they're not something that non-users should need to send.  This is emphasized by the fact that Commands are defined in scripts attached to the user template, and can't exist in the script attached to this monster or that rock.  Monsters and rocks don't give Commands, only users!  &lt;br /&gt;&lt;br /&gt;Since the Metaplace client is the user's interface into a Metaplace world, it seems to make sense that the client is the only thing that would need to send a Command (on behalf of the user's actions of clicking and typing.  However, during alpha and beta testing, some scripters found that there were some cases where calling these same Commands from withing a script seemed reasonable:  if the player walks up to an NPC shopkeeper and says that he or she would like to buy or sell some goods, it was nice for the "shopkeeper" script to automatically pop up the user's inventory for him or her.  Since we already had a Command set up for this (for when the user pressed "i" on their client), we could use a built-in function called DoCommand(), which would tell the system to execute the Command just as if the user had asked to.&lt;br /&gt;&lt;br /&gt;The DoCommand() was also able to issue system commands.  This meant that we could have code that could automatically create sprites, or remove them; that could re-configure object templates; or that could enhance the build environment with extra tools.&lt;br /&gt;&lt;br /&gt;Unfortunately, we lost access to DoCommand().  The reasoning makes sense:  with more modules available on the Marketplace and more users arriving all the time, it's just a matter of time before malicious code comes along.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Malicious modules are going to happen.  I think it must fall under some sort of law of averages or something, but eventually there will be someone that comes along and writes a Metaplace script that does something different (or in addition) to what it claims.  I, myself, think along these lines, perhaps because my line of work involves security, or perhaps because I have a "hacker" mindset at times, and admittedly like to "cheat" at times.  But the majority of malicious code that can exist can, at most, just affect the world's operation, removing monsters, clearing highscores, or providing superuser rights.  I say "just", even though these can be devastating to a world, because I don't think these compare to the damage that could be caused if someone had access to system Commands in a world.  Even with peer review, a rating system, and distrust of closed-source modules, there are still ways to sneak in some nasty code (a future post).&lt;br /&gt;&lt;br /&gt;While script access to the slash commands through DoCommand() provided the ability to make all sorts of interesting modules, I can definitely understand why it had to be removed.  It's one thing to delete all of the trees you painstaking placed in your forest realm, but it's another if I remove the template completely, or export your code without your permission, or include even more modules... it doesn't take long to come up with some evil.&lt;br /&gt;&lt;br /&gt;And access to the user Commands from script has also been removed.  I believe the motivation behind this is because there now exist modules that interact with the user in a "meta" fashion -- things such as in-game purchases using the metacoins, or accepting friend requests, or meeping another Metaplace user -- are now possible.  These aren't actions that are world-specific, such as popping up the inventory or buying a sword, but affect your global Metaplace self.  And if a malicious script could automatically buy 1000 balloons for someone, or friend every user, or flood-meep everyone, without the user's permission... well, again, it'd be one thing if it was something just in-world (it made you automatically sell all of your equipment to the shopkeeper), but it's something completely different to affect the user at that extra-world level.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So I understand why DoCommand() was removed.  But are there legitimate uses for it?  &lt;br /&gt;&lt;br /&gt;Luckily, there's an easy solution for the user Commands:  turn them all into Triggers (so scripts can easily call them), and have the Command that is called by the user (via the client) also call the Trigger.  The numerous DoCommand()s I had in my UO scripts were all changed over to this method in less than five minutes.&lt;br /&gt;&lt;br /&gt;And what about system Commands?  Well, there's now a DoSlashCommand() function, but it's restricted to "privileged" scripts, which means specially-marked Metaplace-written scripts.  What about the rest of us?  I have three main reasons to have scriptable access to system Commands -- slash-only Commands, batch Commands, and in-world tools.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Slash-only Commands means Commands that don't have a world-builder equivalent.  While many/most of the system commands can be activated by some part of the build tools, there are some that cannot (and worse, some that used to be under the old, beloved, "JS tools" from the alpha days).  Things such as parallax and data templates (future blog posts) can only be done using slash commands (not precisely true -- some of the parallax and data templates stuff can be done from scripting as well, but not from the build tools).  The command-line interface to Metaplace isn't friendly, so being able to script these operations instead of typing them by hand would be handy, especially if you need to do large batches...&lt;br /&gt;&lt;br /&gt;Entering batches of Commands isn't really possible with the command-line, because it only takes one line at a time, and means that you'd have to cut-and-paste each line in.  Slow, error-prone... not fun.  The creation of my UO test worlds is done through a lot of automation, and involves a lot of system commands that upload sprites, configure them, set up tiles, place objects... this is hundreds of commands that can no longer be done from a script.  Another example is the avatar system...&lt;br /&gt;&lt;br /&gt;The character customization system (future blog post) is not very user-friendly at all.  Not only does it require a lot of slash-commands to be entered manually through the command-line, but even the scripting portion, and the data template portion, can be error-prone and difficult.  Having an in-world tool, an avatar wizard, could help people create their avatar module by letting them provide the easy stuff (images and animations), and having the wizard automate all of the error-prone work.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So how do we trade off script security and scripting flexibility?  Is there a solution?  I think there is, and I think it's relatively easy:  allow local scripts to use DoSlashCommand().  This means that imported modules don't have access to it, so the only malicious code is something the world owner adds.  It allows users to write a script to do slash-only and batch Commands.  The only thing that it wouldn't fix is buying an in-world wizard for things like the avatar builder.  Something like that would have to be cut-and-pasted into a local script and run from there, which wouldn't be a good practice to get into, but at least it's a workaround.&lt;br /&gt;&lt;br /&gt;I don't know if this is being considered as a solution, or if another one is coming.  We're told that DoCommand() was "deprecated", but that usually implies that one method is being phased out because a new method is being brought in.  I'm still waiting for the new method!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-2581642050477902072?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/2581642050477902072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/05/commands.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/2581642050477902072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/2581642050477902072'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/05/commands.html' title='Commands'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-5861681538261254185</id><published>2009-05-29T07:17:00.001-06:00</published><updated>2009-05-29T09:04:08.848-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ultima Online'/><category scheme='http://www.blogger.com/atom/ns#' term='teaching'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='motivation'/><category scheme='http://www.blogger.com/atom/ns#' term='MMORF'/><title type='text'>What motivates me?</title><content type='html'>I've been programming computers for almost thirty years.  After doing so for so long, I felt that I could pretty much program anything.  Sure, domain-specific things like medical imaging or physics simulation might require me to go learn more than my high-school knowledge of sciences, but other than that (and NP-complete problems), I can code pretty much anything.&lt;br /&gt;&lt;br /&gt;This makes me curious when I see an impressive piece of software, either something that's running very efficiently, or something large scale.  As I use such software, my mind tends to drift to thoughts of how I would write it.&lt;br /&gt;&lt;br /&gt;The most prevalent case of this was while playing Ultima Online.  It would usually start with a bug in the game -- some item not working correctly, or even a loophole or exploit -- and I would think about how that portion of the game must have been scripted to allow the bug; I would be debugging the problem without the code itself.&lt;br /&gt;&lt;br /&gt;This thinking leads to thoughts about the general algorithms behind the game, about how combat must work, or how crafting works, or how it makes no sense at all that the &lt;a href="http://www.raphkoster.com/2006/06/04/uos-resource-system-part-2/"&gt;8x8 macroing method&lt;/a&gt; should work.  And eventually, while thinking about how all of these subsystems of such a large-scale system must work, I got thinking about the underlying structure of the game engine.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This led me to think, "could I write a general-purpose massively-multiplayer engine"?  Of course, I expected the answer to be "yes", since I can program anything, remember?  Thus the &lt;a href="mmorf.crwth.org"&gt;MMORF&lt;/a&gt; project was born.  The primary goal was to see if I could write all of the subsystems of a generalized game engine, and the secondary goal was actually using that engine to make a game, the test-case being whether I could implement Ultima Online in what I wrote.&lt;br /&gt;&lt;br /&gt;I wrote a dozen or so posts on the blog, started working out some design, but over a year and a half, it was progressing slowly, and eventually, I discovered Metaplace.  Interest in the platform took my available time for such pursuits, especially once I got into the alpha-test program.&lt;br /&gt;&lt;br /&gt;It was a nice surprise, then, that once I got into alpha, I discovered that the backend of Metaplace was VERY similar to how I would have written MMORF; I'd say that my vision for MMORF and the Metaplace design are about 90% related.  That other 10% would make for an interesting future post...&lt;br /&gt;&lt;br /&gt;With an engine already made, very close to what I had envisioned, my goal has now changed to seeing whether or not my vision would have worked:  could I have written an Ultima Online clone with what I had thought up?  And that still remains my primary goal with Metaplace, answering the question of whether UO could be implemented on the Metaplace platform.  My notes so far on this can be found on my &lt;a href="http://beta.metaplace.com/wiki/index.php/User:Crwth"&gt;user page on the Metaplace wiki&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, this is why I'm part of Metaplace.  Of course, in the last year-and-a-half that I've been an alpha- and beta-tester, I've done a lot more than work on a UO clone (which should be obvious, considering how far I haven't gotten).  I sit in the global chat as often as I can, helping out old and new testers alike, as I like to think that I'm one of the most knowledgeable users out there, and have always enjoyed teaching and sharing knowledge.  I try to make demo worlds to show off new features, such as the &lt;a href="http://www.metaplace.com/crwth_effects"&gt;effects system&lt;/a&gt;.  I get convinced to write little code snippets for people, when it's either quick-and-simple, or a problem that challenges me.  I get side-tracked with thoughts of "I wonder if I can get Metaplace to do this", and then attempt to prove that it can, whether or not it was ever designed to.  And every so often, I even publish a module for others to use, actually &lt;span style="font-weight:bold;"&gt;finishing &lt;/span&gt; something!  Oh, and I suppose I've "finished" two worlds: a game, &lt;a href="http://www.metaplace.com/Sniper"&gt;Sniper&lt;/a&gt;, and a chatroom, &lt;a href="http://www.metaplace.com/educhat"&gt;educhat&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I still think, every so often, about the MMORF project, and how successful I would have been.  While I'm testing the design of it by testing Metaplace, it still doesn't answer the question of whether I could have done it.  And some day, after Metaplace has released, the global chat has become too unwieldy to reside in, and the Metaplace Marketplace becomes so large that every possible module is available, I shall go back and see.&lt;br /&gt;&lt;br /&gt;But until then, I'll continue working away at Ultima Online...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-5861681538261254185?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/5861681538261254185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/05/what-motivates-me.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/5861681538261254185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/5861681538261254185'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/05/what-motivates-me.html' title='What motivates me?'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-176007605098319842</id><published>2009-05-28T08:42:00.000-06:00</published><updated>2009-05-28T08:49:23.447-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ideas'/><category scheme='http://www.blogger.com/atom/ns#' term='content'/><title type='text'>Content</title><content type='html'>So what will this blog contain?  A range of things:  ideas for Metaplace worlds that I may or may not ever have time to try; ideas for plugins (modules) to write for Metaplace users; limitations and bugs that I find, including workarounds; critical commentary (read: complaints) about the functionality or direction of the service; and even some code snippets for cool things that can be done in Metascript.&lt;br /&gt;&lt;br /&gt;I hope this last bit doesn't scare anyone off; ideally, non-programmers can gain some benefit from posts that talk about scripting, to give them an idea of what's possible in Metaplace (and drive them to ask someone to script it for them), or to perhaps take on the challenge of learning how to do a little scripting themselves.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-176007605098319842?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/176007605098319842/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/05/content.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/176007605098319842'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/176007605098319842'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/05/content.html' title='Content'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5937183399046864464.post-3276961753639094044</id><published>2009-05-28T08:25:00.000-06:00</published><updated>2009-05-28T08:35:45.828-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='introduction'/><title type='text'>Introduction</title><content type='html'>Now that the NDA for beta-testing Metaplace has been lifted, a bunch of users have started blogs about it, and I figured I might as well jump on the bandwagon.&lt;br /&gt;&lt;br /&gt;I started alpha testing Metaplace back in December 2007, and I think the only day that I haven't logged into a Metaplace world is the day I was in San Diego at the Metaplace office, meeting the team.&lt;br /&gt;&lt;br /&gt;If you don't know what Metaplace is, go &lt;a href="metaplace.com"&gt;check it out&lt;/a&gt;.  As of this post, it's in Open Beta, so anyone can give it a try - signup is quick and painless.  Metaplace, as a whole, is geared to pretty much anyone, whether you're just looking for some games to play (though it's still Beta, so the selection isn't huge, compared to sites like Kongregate and the like), looking to build your small corner of the internet, or you're a game or content developer looking for a flexible platform to work on.&lt;br /&gt;&lt;br /&gt;If you do decide to give it a try, I can be found in the "metaplace" global chat channel during the (North American) day every weekday (work time), and off-and-on during evenings and weekends (family time).  I'm happy to try to answer questions and to help with problems, or just to chat.  We're friendly in that channel, so come say hi!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5937183399046864464-3276961753639094044?l=camelpate.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://camelpate.blogspot.com/feeds/3276961753639094044/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://camelpate.blogspot.com/2009/05/introduction.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3276961753639094044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5937183399046864464/posts/default/3276961753639094044'/><link rel='alternate' type='text/html' href='http://camelpate.blogspot.com/2009/05/introduction.html' title='Introduction'/><author><name>Crwth</name><uri>http://www.blogger.com/profile/00040674620903529496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_SVDGxm3sfOE/SsVPRjMiYNI/AAAAAAAAALc/4G1RflRAv9k/s1600-R/glyph2.png'/></author><thr:total>0</thr:total></entry></feed>
