“Unmindful” The Movie by Natascha Randt & Karima Hoisan

I will take a LITTLE credit for parts of this, mostly my expertise at not having any particular goal in mind :), and a nice “make a ball look like it’s rolling without risking actual physics” script that I did. It’s a great movie, and a great sim; experience both!

Digital Rabbit Hole

On July 16th, I posted here in my blog, about a new world that Dale Innis and I were beginning to build on Kitely – Virtual Worlds on Demand, called, “Unmindful” See post:
As I said in the post, it was an experiment, to prove or disprove something I had come to believe: “You don’t need an idea to create…ideas come…all you need to do is start.”

So we started off  with no ideas about what the world would be, just making sort of odd stone structures, with off-sim wind turbines, and a parade of wild animals standing on rugs moving in a figure-eight. That was our first part of the build, but then unmindful to how the focus even began changing, we shifted in a very different direction. I had the concept, but I promise you, if it weren’t for Dale, we would never have pulled this off. (Have…

View original post 535 more words

A poem, and a painting, come alive

It’s been my pleasure and privilege to be part of another of Karima Hoisan’s amazing Kitely sims, based this time on one of her lovely poems.  (See previously The Hudson Line, based on the Hudson line, and In Your Head, based on your head (well…).)

The poem is here, and the artist’s own posting on the sim is here; I urge you to follow both those links, and then experience the Kitely sim for yourself, if you do the Kitely thing at all. (And if you don’t, maybe you should; it turns out you’re still allowed to use SL, too! /grin ).

And in the meantime, here are a couple (more) pictures, as further incentive:

amb_003

lighthouse1_001

Doing the scripting for this sim was great fun, and I hope there is at least one thing that makes you scratch your head and think “How did they do that?”.  Most likely it is the result of Ms. Hoisan and I racking our brains for a few days trying to figure out how to get a particular effect, and then when it seemed impossible her saying “I don’t suppose we could…?” and me smacking my forehead.  :)

Gotta ride it like you find it

(That’s actually about the Rock Island Line, and this is about a different line, but it seems apposite.)

Train crossing a bridge

That picture is from Karima Hoisan’s new Kitely sim “The Hudson Line”, which she has just announced over in her (much more frequently updated) weblog.

It’s a wild dreamlike re-interpretation of the RL commuter rail line of the same name; in one of those great SL synergies it started in casual conversations about the differences between various people’s living and working environments, and grew into this lovely impressionistic world. I helped with my usual small scripting contributions, and this time even some of the building!

It was wonderful fun to watch it emerging out of nothing (what an age we live in), and while you don’t get to have that fun, you can visit the announcement, and the sim itself (Kitely account or something required I guess?), and ride the train, and watch the movies, and find out what’s down there in the tunnel at the end (and it’s not Grand Central Station, muhahaha).

(Scripting note: llSetKeyframedMotion works very nicely in Kitely these days, and therefore I assume probably also in OpenSim; I do advise not entirely trusting that the moved object will arrive exactly where you expect it to, especially the first time after a sim reset, and setting at least a backup timer as well as the usual at_target() event. But it’s running the trains and cars and trucks and tugboats in the Hudson Line sim just fine!)

My acting career continues! :)

Marvelous talented friends Karima and Nat have another movie out, Drink Me With Your Eyes; another rendering of one of Karima’s poems (a passionate piece in mixed English and Spanish), in amazing visuals of dancers and horses and eyes and… well, go see it. :)

rearing horse

I got a little bit part in the bar scenes (expertly sitting on a poseball!), and also in the horses-running-about scenes (as the Slightly Confused Horse), and got to contribute a little miscellaneous scripting also. But mostly just watched Karima and Nat and wondered to myself how they do it.

I will stick to scripting. :) Although I understand some people find that mysterious also!

Moving Trains

That title is a pun, since these particular trains that move scriptily down the track are in a video that is itself quite moving in the emotional sort of way.

As the official announcement from Karima Hoisan says, Karima and Natascha Randt’s “Window on a Train” is now up for general viewing an’ admiration.

Here it is on the “You Tube”:

and note that per Director Randt “Watching our film without being in full-screen and HD (720p) is Prohibited!” :)

I don’t want to say anything specific about the movie itself, because I think it has the most impact when viewed with the least preconceptions. Do prepare to be touched…

Machinima in general is so cool. I am not a visual person myself, so generally I can only sort of gawk (or make pictures with math), but I am pleased to say that in this case I was able to contribute, in the form of some scripts to provide smooth and mostly consistent motion in three objects (an engine and two cars) that were far too primmy to make physical, and also far too primmy to link together.

If I can gather up the energy and time :) I will post about it (the one-word giveaway is LLSetKeyframedMotion, which having been added to LSL later than 2007, is for me a Strange New Thing in the world, which proved Just Right for this particular use-case).

But anyway! Watch the movie! :) It is good!

became a blur, because they moved so fast…

Here is a machinima (machinema?) of a poem by v talented friend Karima.
 

 
I am happy to brag :) that I contributed in a tiny way by providing scripted examples of ways to make papers sort of blow around, one of which ended up in the video. Yay me!

Nat (the videographer and cat) and Karima (the poet and voice) do all various neat things; you should “check them out”.

Happy New Year, an’ all!

2012 Seasonal Card

Continuing the occasional tradition! :) I spammed this out to all 450+ people on my Friends list earlier today. (If you don’t get one and would have expected to, let me know; I’m not sure I entirely trust SL to have done that perfectly.)

Image uses the techniques (and the little HUD device) from Dale Meets Dale. Once I had two images that I liked (which took awhile!) it was easy.

Wow, I haven’t posted here much lately, have I? I have a somewhat more developed and usable version of the script from the automatic walking post, that lets you increase and decrease (and even reverse) the force that makes you walk, and it gets along okay with jumping and flying. So you can arrange for really Superman jumps, and you can fly along at whatever speed cross-country you want without having to hold down a key, and so on.

I’m still not entirely happy with the turning behavior; or maybe I’m just too lazy to make a Wiki page so far. :) But when I do I will post here about it. Or vice-versa.

Otherwise doing pretty much the usual stuff. Good SL friend and prominent photographer and DJ Calli had a Seventh Rezday party last night (with fireworks!). That was a lot of fun. Next year I’ll be turning seven myself; pretty neat! :)

Automatic walking (and the invisible stranger)

So very poetic friend Karima flatteringly chose my avatar following script as some context for a new poem of hers lauding scripters (she even used some of the code in the poem, and although this sounds like it couldn’t possibly work it certainly does, and you should go read it).

This reminded me of a related script that I have been too lazy to work on forever and ever, and I now have a rough draft. It may stay a rough draft for some time :) since LSL makes it hard to do in a really polished way, but in any meantime here it is. When you’re using it, once you start walking you keep walking, more or less forward unless you use the side-arrow keys to turn, until you tap backward or etc to stop. (Works best on or over flat ground; see the page for details!)

If I ever like it enough I’ll put it into the Library.

Here is a picture of me walking with the automatic walker. Note that this is a hard sort of picture to get in general, because you have to walk and cam around to looking at yourself from the front at the same time, but the walker makes it relatively simple!

(Well, okay, and without any special scripts you can also cheat by just playing a walking anim and not actually walking. :) But this would let you make a video of yourself walking! A bit, anyway…)

In other news, I noticed that for a long time (like, weeks) there had been a green dot over in an adjacent parcel on the Rise, and whenever I went over to look it was the same AV name in the title floating there, but with no visible person, and they never answered chat or IMs.

Feeling whimsical the other day, I’d nudged the invisible person (who turned out to be nudgeable) over into my land, and built a little protective envelope of colorful devices around them. I figured I’d keep them around as a decoration until my conscience got the better of me, and then send them home (since they were on my land, and I could now do Teleport Agent Home).

But then not long after that I logged into the Rise, and there they were again, standing invisible and unresponsive at the same place in the empty field next door. (And yeah I’ve checked high and low and Highlight Transparent; I suppose they are probably wearing a full-transparent body alpha, or something subtler.)

Most puzzling! What process moved them back there again, to stand again invisible and unresponsive?

But that’s why we love the mainland. :)

Update: the llSetForce() version of this seems to work just awesomely! I am now playing with a version where you stop by pressing Back, and each time you press Forward it speeds you up a bit. Enormous fun so far!

Dale meets Dale

An age and an age ago, I made a picture with both Dales in it (or, strictly speaking, both Dale-bottoms), for some pictures (of bottoms) event that has since slipped my mind.

(Oh, right, looking at the text, it was one of them there weblog memes, notable for involving bottoms.)

It was comparatively easy, since I had the two of me almost entirely not touching, so there was just one tiny piece of foot to fiddle with carefully in Photoshop, and otherwise the only hard part was holding the camera absolutely still while I took one picture, stood up, changed shape and skin and hair, sat down again on the other sit-place, and took another picture.

For some reason the other day I wanted to take some more-overlapping Dale and Dale pictures, and eventually I did:

Narcissus 1

Nice but her face doesn’t show at all. (And there’s that little oops just under his near hand, oops.)

Narcissus 2

Quite nice, really. :)

This time I was working in gimp rather than Photoshop (for financial and sundry reasons), and it turns out that you can do this sort of thing pretty easily. I’m not going to do a detailed tutorial unless someone wants that, but basically you take two pictures from exactly the same place, layer one over the other, add a layer mask to the top one, and then draw on that layer mask in black or white, depending on which layer you want to show at any given point. It’s a nice forgiving process, doesn’t require an exceptionally steady hand, and so on.

This time I didn’t want to have to hold the camera absolutely still to take the pictures, so I wrote a script. Here it is!

integer SAVE_FACE = 0;
integer RESTORE_FACE = 1;
vector saved_camera_pos = ZERO_VECTOR;
rotation saved_camera_rot = ZERO_ROTATION;
integer have_saved = FALSE;

default {
    state_entry() {
        have_saved = FALSE;
    }
    attach(key id) {
        if (id!=NULL_KEY)
            llRequestPermissions(id,PERMISSION_CONTROL_CAMERA | 
                                    PERMISSION_TRACK_CAMERA);
    }
    run_time_permissions(integer p) {
        llOwnerSay("Touch that one face to save camera position,"+
                   " that other face to put it back.");
    }
    touch_start(integer total_number) {
        if (llDetectedTouchFace(0)==SAVE_FACE) {
            have_saved = TRUE;
            saved_camera_pos = llGetCameraPos();
            saved_camera_rot = llGetCameraRot();
            llOwnerSay("Camera position saved.");
        } else if (llDetectedTouchFace(0)==RESTORE_FACE) {
            if (have_saved) {
                llSetCameraParams([
                    CAMERA_ACTIVE,TRUE,
                    CAMERA_POSITION,saved_camera_pos,
                    CAMERA_POSITION_LOCKED,TRUE,
                    CAMERA_FOCUS,saved_camera_pos+*saved_camera_rot,
                    CAMERA_FOCUS_LOCKED,TRUE
                    ]);            
            } else {
                llOwnerSay("No position is saved yet.");
            }
        }
    }
}

So basically you put that into some prim, you color face 0 some color, color face 1 some other color, attach it to your HUD somewhere, fiddle it around so that you can see both of those faces, put your camera where you want it, touch face 0, take a picture, do whatever you want to, touch face 1, possibly press ESC to make sure you’re on the default camera, and now your camera is back where it was before for taking your second picture.

I could have added a third face case with like llReleaseCamera() or whatever it is on it, but it’s easiest to just take the HUD off when you’re done to free up your default camera. :)

Isn’t nature wonderful! I may get up the energy to put up a real page about it like my famous ExplodingObjects and AvatarFollower pages, but for now I am impressed with myself just for having slapped it up here.

Now I will go back to admiring my pictures; aren’t I a cute couple? :)

Combat System Scripting V: our first combat meter

I have suddenly remembered this here series of combat scripting posts that I was doing the other Geological Epoch!

So I will do another one. A pretty simple, but important one; our first Combat Meter.

Now as I didn’t explain in the first post but probably should have, when it comes to people (AVs) taking “damage” in Second Life, there are two distinctly different ways that can work. There is a notion of “health” and “damage” that are built directly into the world (but as far as I can tell very seldom used, because they’re inflexible and kind of silly), and then there’s “health” and “damage” as enforced by scripts in things that you wear.

(In a way this is like the difference between vendors that let you buy using “buy”, and those that let you buy using “pay”, as explained in my extremely thorough post on the subject. “Buy” is built into the world whereas “Pay” enables scripting, just like Linden Damage is build into the world whereas Scripted Damage is scripted. Not that there is anything all that significant about the analogy. :) )

Many, even most, Residents probably don’t even know that every AV is always at some state of Health, and that if that Health goes to zero, we “die”, in the sense that we are suddenly teleported Home, just as though we’d hit control-shift-h or whatever.

The reason that people don’t know this is that the vast majority of SL land has the “damage enabled” flag turned off, so you’re always at 100% health, and the viewer doesn’t normally even bother displaying the health meter in that case.

But if you stop by my Park in the center of Hughes Rise, or various other places, and then look carefully, you’ll be able to find a little heart on the screen, probably with a “100%” next to it. It you fly or teleport a few dozen meters into the sky and let yourself fall, or fly at high speed into a tree, or other amusing things, the number may go down from 100% (and then rather quickly go back up again). If you lose enough health fast enough, you may even hear your AV make a little supposedly gender-appropriate pained-grunt sort of noise.

And if you get the little number next to the heart down to 0%, you will suddenly be teleported home.

Now that sounds like a useful basis for a combat system, but it really isn’t, because it’s relatively easy to script up an object that instantly kills anyone it’s pointed at, and being teleported home isn’t a very flexible way to implement defeat (for instance it’s tough to script a scoreboard of “kills” or anything, just for one example), and healing is always exactly the same over time (no way to make “medikits” or “health potions” or anything), and it doesn’t apply to non-AVs (targets, monsters, robots, etc) and so on and so on.

So basically no one uses the built-in health and damage system for actual combat. What they use instead is Scripted Damage, which works by having the combatants wear some sort of attachment or other, that somehow finds out when they are “damaged” by “weapons”, or “healed” by whatever, and does whatever the system designer thought appropriate when they “die”.

The things that you wear that keep track of your damage generally have some sort of indicator of how healthy you are right now, in Scripted Damage terms, and are therefore generically called Damage Meters.

In today’s posting here, we will make a Damage Meter. Or, actually, we will discover that we’ve Already Got One!

(“Oh, yes, it’s very nice.“)

Already got one with a few modifications, that is; because it turns out that our self-healing target, from the self-healing target post, makes a very nice basis for an AV damage meter.

This is because of this Very Important Fact: when something hits your AV, all of your attachments see the collision event.

(I understand that this may not be true in OpenSim, or in some OpenSims, in which case damage meters there will have to work in some entirely other way of which I amn’t aware; I may explore that at some point too.)

So for instance if you were to take our self-healing target into inventory, and attach it to the center of your HUD say, and stand in front of the auto-popgun, you would see the health value above it go gradually down; and then if you step aside out of the stream of pellets, it will go slowly up again.

The only problem with it is that when it gets down to zero nothing useful happens. The script does an “llDie()” at that point, which you might think would cause the attachment to Cease To Be, or to detach or something, but in fact (another Interesting Fact) llDie() does nothing at all in attachments, so nothing actually happens.

This is easy to change, though. For now let’s just have the meter say something amusing when you “die”, to demonstrate that it is working. We will just change the process_collision routine so that it does something slightly different when health is zero, as in:

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        health = health - 1;
        show_health(health,MAX_HEALTH);
        if (health<=0) {
            llSay(0,"Arg! "+llKey2Name(llGetOwner())+
                    " has been defeated!");
        }
        llSetTimerEvent(HEAL_SECONDS);
    }
}

(We also took out the “pow” and “clunk”, because we’re confident enough that things are generally working that we don’t need them anymore.)

So modify the target script as above, put it into a prim, wear that prim somewhere appropriate on your HUD, and stand in front of the auto-popgun or have a friend shoot at you, and you will find yourself being “damaged” and eventually being “defeated”.

Combat! Shazam!

Some other stuff one might want to do:

  • Support “medikits” or “health potions” or whatever, that heal you when, say, you walk (run) across them (I have code for that!),
  • Cause you to, say, fall down and stop running around when you “die” (I have code for that too!),
  • Remove your ability to use your own weapons while you are “dead” (I don’t have code for that yet, but I might talk about various approaches).

(The other thing I’m currently playing with, which I haven’t quite scripted up yet, is to have the targets, the “monsters”, be “lootable”, so that once you’ve defeated one you can touch it or something and get I dunno some kind of reward. If anyone knows of a sim or a system of scripts that does this sort of WoW-like lootable-monsters thing, let me know; I’d like to take a look!)

So that’s all for this time. Pew pew! :)

Randomly generating terrains

… aka “When the Creator misplaces a parenthesis”. :)

So my first attempt to write a program to generate random but pleasing landforms for my Sim on a Stick did not go quite as expected.

When the Creator God misplaces a paren...

Bit spikey, for instance.

But then I found a misplaced parenthesis! And things got better.

Ah, much better

I think I am liking the algorithm…

Landforms from the RNG

For the curious, here is the basic (recursive, self-similar) routine, in everyone’s favorite language: Python. (Squished down to fit in the weblog here, not guaranteed to compile as-is.)

def fill_in_terrain(t,lowx,lowy,maxx,maxy):
   ''' Takes the input terrain, where the low-by-max '''
   ''' corners are already filled in, and fills in '''
   ''' everything inside those corners, by the '''
   ''' Magic of Recursion. '''
   if maxx-lowx<2: return t
   if maxy-lowy<2: return t
   xmid = int((lowx+maxx)/2)    
   ymid = int((lowy+maxy)/2)
   hscale = (maxx - lowx) * HSCALE
   # first the corners
   if t[lowx][ymid] is None:
     t[lowx][ymid] = (t[lowx][lowy] + t[lowx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[maxx][ymid] is None:
     t[maxx][ymid] = (t[maxx][lowy] + t[maxx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[xmid][lowy] is None:
     t[xmid][lowy] = (t[lowx][lowy] + t[maxx][lowy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[xmid][maxy] is None:
     t[xmid][maxy] = (t[lowx][maxy] + t[maxx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   # then the center
   if t[xmid][ymid] is None:
     t[xmid][ymid] = (t[lowx][ymid]+t[maxx][ymid]
                     + t[xmid][lowy]+[xmid][maxy])/4 
                     + hscale - math.floor(2*hscale*random.random())
   # and recurse on the four quadrants
   t = fill_in_terrain(t,lowx,lowy,xmid,ymid)
   t = fill_in_terrain(t,lowx,ymid,xmid,maxy)
   t = fill_in_terrain(t,xmid,lowy,maxx,ymid)
   t = fill_in_terrain(t,xmid,ymid,maxx,maxy)
   return t

def get_terrain(x,y):
   ''' returns an [x][y] array of floats representing a terrain; '''
   ''' range unpredictable '''
   ''' x and y ought to be powers of two, for best results.  '''
   ''' oh, and equal I suppose '''
   answer = [ [None for a in range(y+1)] for b in range(x+1) ]
   answer[0][0] = 0
   answer[x][0] = 0
   answer[0][y] = 0
   answer[x][y] = 0
   answer = fill_in_terrain(answer,0,0,x,y)
   return answer

Fun that all these pretty hills and things come out of that little bit of (also pretty) code…

Breeding like prims

So yet another thing about having one’s own private virtual world is that one can do all sorts of fun but dangerous stuff that one wouldn’t do in anyone else’s.

The main fun-but-dangerous thing, naturally, being the creation of uncontrolled replicators.

(Carefully controlled replicators are safe even for, say, Second Life; and of course SL has the Grey Goo Fence, which is good for the Grid but can be annoying for Residents.)

The other day in my local OpenSim-on-a-stick I made a little physical sphere that randomly moves around. It promptly took off into the open water ‘way off the side of the sim (into space that, strictly speaking, doesn’t exist).

So then I made a tall green wall, and put the randomly-moving ball inside the wall, and that was better.

Then I taught the ball to make copies of itself, and also to randomly delete itself. I set the probability of replicating just barely above the probability of self-deletion, knowing from my days as an Anti-Virus Guru that this should lead to an (initially) slow population increase.

Then I got bored, watching the number of balls inside the wall go from one to two to three, back to two, back to three, to four, back to one…

So I cranked up the probability of replication a bit. And then of course the phone rang and I had to open a different window to do something, and when I went back to the viewer:

Runaway 1

Oh, dear.

Well, no problem, I thought; the sim was running kind of slow, but I could just cam up, select everything nearby, deselect the wall, and hit Delete.

And that worked great, except that in the few seconds between my selecting everything and hitting Delete, another few dozen balls had spawned, and they weren’t selected or deleted, and then a couple seconds after that the sim was running so slowly that deleting stuff wasn’t really working anymore.

And then I noticed…

Runaway 2

a potential crisis! If the balls were to start spawning outside the wall, it seemed not unlikely I would have to restore the sim from the last OAR file I took or something.

On closer investigation there were at least three balls that had apparently escaped the wall:

Runaway 3

but when I went to examine (and hopefully quickly Delete them), it turned out none of them actually existed as far as the sim was concerned, the viewer was just confused as a result of all the physics and lag going on. So that was good.

I thought I would log out and then log back in as the AV with Estate powers (Simona Stick, rather than Test User). I noticed that over on the Opensim console, things were not entirely happy:

Runaway 4

It look a long time to settle down again when I tried to log out, and at this point I started to panic a bit. In the time it would take me to log in as the estate owner and try to turn off scripts or whatever, would the sim have become utterly unusable? So instead I did a shutdown on the Opensim console. This resulted in lots more red messages for quite awhile…

Runaway 5

but eventually it did shut down.

Okay, smartypants, so now what? Well, it turns out that Opensim in Sim on a Stick happens to use a MySQL database to store everything in, and that due to the Day Job I have some knowledge of MySQL, and the database tables that Opensim uses are at least somewhat documented.

So once I’d figured out the MySQL username and password that SOAS had installed, I logged into the MySQL console, and viola:

mysql> select count(*) from prims;
+----------+
| count(*) |
+----------+
| 1482 |
+----------+
1 row in set (0.00 sec)

So we have fourteen hundred and eighty-two prims. Fortunately all of the self-reproducing ones have the same name, and each rezzed prim (as far as I can tell) corresponds to one or more records in just three different tables.

First we delete the shape-records for the prims (Opensim could save considerable space in the database by compressing out duplicates here, one would think):

mysql> delete from primshapes where UUID in (select UUID from prims where name="moving and reproducing ball");
Query OK, 1294 rows affected (0.05 sec)

So it looks like we have about 1294 replicators

Then, we destroy the contents (prim inventories) of all of them:

mysql> delete from primitems where primID in (select UUID from prims where name="moving and reproducing ball");
Query OK, 2385 rows affected (0.14 sec)

And that more or less makes sense; each replicator typically contains a script and a copy of itself, so we’d expect to have roughly twice as many contents as we have replicating prims. 2385 is somewhat less then twice 1294, but that’s because of that “roughly” in the last sentence, which I will not go into in detail.

And finally, we remove the little beggars themselves:

mysql> delete from prims where name="moving and reproducing ball";
Query OK, 1294 rows affected (0.05 sec)

Another reassuring 1294.

Now we would expect to have 1482-1294 prims left, and:

mysql> select count(*) from prims;
+----------+
| count(*) |
+----------+
| 188 |
+----------+

(counting on fingers) that adds up!

Crossing our fingers and venturing back into the world as Test User, we find:

Runaway 6

The wall (and everything else) undamaged, but the nasty replicators gone! And sim performance back to normal. So huzzah!

Disclaimer: I have no idea whether the steps above are actually the right way to remove a bunch of nasty things from an OpenSim instance, and for all I know they have left the database in some incoherent state that will cause it to die horribly tomorrow. If your Opensim world is valuable to you, take frequent backups and don’t go messing with the database directly in MySQL because of something you saw in a weblog once. Also, in retrospect, it probably would have been cleverer to use MySQL just to turn off scripting for the estate, and then gone in and cleaned up normally inworld. But hindsight is a penny earned!

Wind and Rotations

So I must apologize for (A) having gotten distracted from the combat-system scripting by thinking about the details of sailboat physics, and (B) posting some stuff here that is really just for me, but I thought it might someday be useful to someone else to, and besides this was a handy place to jot it down.

There are an annoying number of ways to write down rotations and directions and things in LSL, and different parts of the word want different ones. (And this is just, for the moment, talking about rotations in two dimensions!)

For instance, llWind() returns a vector representing the wind direction. Wind is (I believe!) always flat (parallel to the ground) in SL, so we can ignore Z. But what exactly are X and Y?

Well, Rule 1 (which I always forget and have to try out again) is that X is East and Y is North. So if for instance I am standing at 117, 119, 117 in Hughes Rise, on the walkway in the Park, and I walk one meter due North, I’ll be at 117, 120, 117. See how Y got bigger? And if I walk one meter due West from there, toward the stairs, I’ll be at 116, 120, 117; X got smaller.

So if llWind returns < 5, 0, 0 > for instance, that means the wind is blowing at five meters per second toward the East (and therefore is called a West wind, ’cause winds are called by where they are coming from, just to be confusing).

Now say you have a sail, and you want to know the angle between the sail and the wind. Well, the sail’s trim is represented as a rotation, which can be represented in LSL either as an actual rotation object (which has four components, corresponding to the four components of the corresponding quaternion, where a quaternion is of course an element of a four-dimensional associative normed division algebra over the real numbers), which is mathematically wonderful but not too useful for actually thinking about, or as a three-element vector, each element representing a rotation about the corresponding axis.

So for instance if your sail is flat in the X dimension, it will be facing head-on to winds coming from the east or west. If you then rotate it ninety degrees (which is a rotation of pi/2, or PI_BY_TWO, in radians) about the Z axis (the up-and-down one), it will be facing head-on to winds coming from the north or south.

To represent a rotation of pi/2 radians as a vector in LSL, we write < 0, 0, PI_BY_TWO >. Now the fun part! If I know the X and Y parts of the wind speed from llWind, and I have the rotation of my sail as a rotation about the Z axis, how do I figure out the angle at which the wind is hitting my sail?

Now I pause to think and experiment a little, so as to have an answer to write down in the next paragraphs. :)

Well, first of all notice that by using a symmetrical sail and a rotation of pi/2, I’ve sort of cheated; in particular, I didn’t have to figure out whether rotations are clockwise or counterclockwise (anticlockwise). This is probably obvious to smart people of various kinds, but I can never remember it (some Right Hand Rule or something is probably involved) so I have done the experiment and will write down Rule 2: Rotations are counterclockwise, or, to be precise and therefore meaningful, if you’re looking down at something that is not rotated, and you rotate it positively about the Z axis, it moves around anticlockwise.

This surprised me. :)

It makes sense, though, if we think back to like Trig 101 or whatever that was; X is East (i.e. to the right on the map), and Y is North (upward on the map), which is just how the graphs were all done on the board, and if you start out with a line running from the origin to < 1,0 >, say, and “rotate it” by some small angle theta, it goes up a bit into th’ ol’ First Quadrant there, which means it’s moved anticlockwise.

(Whoever designed clocks must have skipped that class or something.)

So we can represent wind direction as a rotation-about-Z by drawing it on the board there using the X and Y that we got from llWind(), and straightforwardly figure out what angle we would have to rotate that origin-to-1,0 line through to get it there, and it turns out to be (unless I’m wrong) llAtan2(Y,X); that is, the angle whose tangent is y over x.

So we have two rotation amounts, the amount that our sail’s been rotated and the amount that the wind’s been “rotated”, and we can just subtract them from each other to get the angle between them. If they’ve been rotated the same, the difference is zero, and our sail is getting (basically) all of the benefit of the wind. If they are ninety degrees apart, the sail is edge-on to the wind, and getting no benefit at all. Because we suspect there is probably something trigonometric going on here, we will note that llCos(0) is 1.0, and llCos(PI/2) is 0.0, and decide that we can do, say:


vector wind = llWind(ZERO_VECTOR); // The wind right here
float windrotz = llAtan2(wind.y,wind.x);
vector sailrot = llRot2Euler(llGetLocalRot());
float sailrotz = sailrot.z;
float rot_difference = windrotz - sailrotz;
float wind_effect_on_sail = llCos(rot_difference);

and so wind_effect_on_sail will be 1 if the wind’s blowing right onto the sale, will be 0 if the wind is blowing edge-on onto the sail (luffing!), and will be inbetween in between. (It will also be negative, I think, if the wind is blowing on the wrong side of the sail, but I haven’t thought about that yet.)

And some similar calculation can be used to get the angle between the sail and the hull (or, really, the keel), to figure out how much of the wind’s effect on the sail then contributes to the forward motion of the boat. But that’s a little later. :)

Combat System Scripting, interlude: OpenSim

So on our last post in this thread someone asked if this stuff would work in OpenSim.

I did a little playing around, and the answer seems to be “it depends”.

Unlike Second Life, which is both (arguably) a bunch of software, and a particular virtual world that uses that software, OpenSim is just a bunch of software (and an Open Source one at that, which means that there are all various forks and versions and levels and patches around out there). So what you get when you log into one virtual world that’s running (some level of) OpenSim may be different from what you get in another.

What I found when I tried out the Scripts So Far in the first OpenSim-based grid that came to hand (running perhaps “OpenSim 0.7.2 ReactionGrid”, although it wasn’t ReactionGrid) was:

There’s one simple thing in the self-healing target script that this OpenSim didn’t like; this line gets a syntax error:

integer health = MAX_HEALTH;

because apparently this OpenSim is even more silly than Second Life is, about global initializers not having any hard stuff like math or variables in them. So I changed that to just:

integer health;

and added a

    health = MAX_HEALTH;

down in the state_entry() handler, and that got rid of the compilation error.

The autopopgun script worked fine except that (as one of the commentors anticipated) I had to create my own bullet for it because there was no popgun in the library to cheat with. :) I won’t post the bullet script and other bullet details here right now ’cause it would be a distraction, but it worked.

Then I pointed the autoshooter at the target, and the target said “Clunk”.

Repeatedly.

As you may recall, this means that it thinks it’s being hit by something that’s not moving fast enough to cause damage.

A little poking around revealed that in this particular OpenSim, llDetectedVel() was always returning 0.000, making it not very useful for our purposes!

As a temporary test, I changed the

    if (llVecMag(llDetectedVel(index))>15) {

in the target script to the obvious

    if (llVecMag(llDetectedVel(index))>=0) {

so that instead of “is the thing that hit us moving at least fifteen meters per second?”, it was instead asking “is the thing that hit us moving at all, or for that matter not moving at all?”. To which the answer must always be Yes! :)

With that change the target worked fine; its health count went down while the shooter was shooting at it, went back up again when I moved the shooter away, and then went down and turned yellow and then red and then the target Ceased to Be when I left the shooter pointed at it long enough.

There may be OpenSim-based grids where llDetectedVel() works (this list of LSL functions that work in OpenSim seems to claim that it is implemented), but it didn’t seem to in mine. But other than that, and the one tiny syntax change mentioned above, everything we’ve done so far seems to work in OpenSim!

In case anyone else was wondering. :)

Combat System Scripting IV: the auto-popgun

Before we continue in our series from targets to personal combat-meters, it will be convenient to have something that shoots.

So far we’ve been using whatever projectile weapons happen to be in inventory to shoot at targets, but as soon as we want something to shoot back, we’re going to need either a patient and/or interested friend (but those aren’t always available at short notice), or something that will shoot on its own.

And to avoid the complexities of bullets and all, we’re going to cheat. :)

Here’s what you do to get our project for today, the auto-popgun:

  1. Search inventory for “popgun”,
  2. Find the last one that shows up; it should be in the library rather than your personal inventory, and be called something like “Popgun (drag onto yourself)”,
  3. Don’t drag it onto yourself, but just onto the ground.
  4. Edit it, rename it to say “autopopgun”, set the rotations to 354, 0, and 0 (so it’s pointing down a little), and raise it up a bit off the ground.
  5. Go into Contents, open the Popgun script in there, and replace all the text with the script below.

It should then sit there shooting a little ball every second or so, and the little balls should vanish on their own, just as if you were shooting it yourself.

You can rez one of the targets we made earlier in its path, or rez a target and then rotate and move the gun so it’s pointing at it, and observe how the target satisfyingly gets damaged and vanishes, just like you’d expect.

Here’s the autopopgun script:

float SPEED         = 18.0;   //  Speed of bullet in meters/sec
integer LIFETIME    = 7;      //  How many seconds will bullets live 
float DELAY         = 1.0;    //  Delay between shots

default
{
    state_entry() {
        llSetTimerEvent(DELAY); 
    }
    
    on_rez(integer param) {
        llSetTimerEvent(DELAY);
    }
    
    timer() {
        string bullet_name = llGetInventoryName(INVENTORY_OBJECT,0);
        vector vel = <0,SPEED,0>*llGetRootRotation();
        vector pos = llGetPos()+vel/SPEED;      
        llRezObject(bullet_name, pos, vel, ZERO_ROTATION, LIFETIME); 
    }
  
}

Pretty simple script, boiled down from the original popgun script by taking out all of the user-interaction stuff. Basically, once a second, in the timer() event, it figures out the name of the first object in inventory, figures out which way it (the gun) is pointing, and rezzes one of those objects moving in that direction at the given speed SPEED.

It passes the LIFETIME number along to the objects that it rezzes, and as it happens the bullets in the popgun read that number and delete themselves after that many seconds. But we don’t have to worry about any of that, because we cheated and didn’t have to do the bullet scripting ourselves! We are so lazy… :)

Next time, or some time after, or something: our first personal combat meter, so we can stand in the way of the popgun and be shot and take damage!

Combat System Scripting III: target, heal theyself

Okay, short one! No picture! :)

Starting with the final script from last time, as usual, the simplest way to allow our targets to gradually heal themselves over time, is to just stick in a little timer that will increase health back up to maximum:

  timer() {
    if (health<MAX_HEALTH) {
      health += 1;
      show_health(health,MAX_HEALTH);
    }
  }

and add something to, say, state_entry(), that starts the timer, so that state_entry() would now look like this:

    state_entry() {
        show_health(health,MAX_HEALTH);
        llSetTimerEvent(HEAL_SECONDS);
    }

with HEAL_SECONDS set to like 2.0 or something at the top.

And if you do that and play with it, you’ll find that it works. You may also notice that once in awhile when you shoot the target, it will instantly heal again, which is annoying.

To fix that, we can make sure that the timer is running only when there is actually damage to heal, by moving the “start the timer” line to the place where the target notices damage, and adding a “stop the timer” line to the timer itself, whenever it’s at full health.

And so tonight’s final result looks like this:

integer MAX_HEALTH = 20;
integer health = MAX_HEALTH;
float HEAL_SECONDS = 2.0;

vector color_for(integer h,integer max) {
    float ratio = (float)h/(float)max;
    if (ratio>0.666) return <0,1,0>;   // Green
    if (ratio>0.333) return <1,1,0>;   // Yellow
    return <1,0,0>;  // Red
}

show_health(integer h,integer max) {
    llSetText((string)h+"/"+(string)max,color_for(h,max),1);
}

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        health = health - 1;
        show_health(health,MAX_HEALTH);
        if (health<=0) {
            llSay(0,"boom");
            llDie();
        } else {
            llSay(0,"pow");
        }
        llSetTimerEvent(HEAL_SECONDS);
    } else {
        llSay(0,"clunk");
    }
}
default {
    state_entry() {
        show_health(health,MAX_HEALTH);
    }
    collision_start(integer n) {
        integer i;
        for (i=0;i<n;i++) process_collision(i);
    }
    timer() {
        if (health<MAX_HEALTH) {
            health += 1;
            show_health(health,MAX_HEALTH);
        } 
        if (health>=MAX_HEALTH) llSetTimerEvent(0.0);
    }
}

We make such progress! :)

Combat System Scripting II: slightly buffer target

Ah, now I remember! Before going on to other things besides the basic target, I was first going to make the basic target a little less basic.

In this posting, we’ll make the targets a little more robust, so it takes more than one collision to make them Cease To Be (or, in more violent language, more than one shot to kill them).

We’ll also, by the end, stick some nice color-changing floaty text over them, so you can tell how many collisions away from Ceasing to Be they are. (I’m sort of swerving between showing how a combat system might work, and giving little lectures on programming style, here, but it’s my weblog and I can do what I want! Feedback welcome if you’d like to see it go faster or slower or whatever, although I don’t promise to necessarily do what any feedback might suggest.)

The nice simple target script from last time was this:

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        llSay(0,"boom");
        llDie();
    } else {
        llSay(0,"clunk");
    }
}
default {
    collision_start(integer n) {
        integer i;
        for (i=0;i<n;i++) process_collision(i);
    }
}

To make the target a little harder to kill, we’ll add a “health” variable to keep track of just how many more hits it has left, and update process_collision to count that variable down, and do the Cease to Be thing only when it reaches zero. Other hits will just make some I dunno “pow” noise. So:

integer health = 20;

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        health = health - 1;
        if (health<=0) {
            llSay(0,"boom");
            llDie();
        } else {
            llSay(0,"pow");
        }
    } else {
        llSay(0,"clunk");
    }
}

The rest of the script (the collision_start() handler in the default state there) stays the same.

So play with that a little. It’s very nice, but it’s lacking some indication of how damaged the target is. We’ll add the floaty-text for that. It’ll require a new event handler in the default state to initialize the floaty text in the first place, and then some new code in process_collision to update the text when it’s hit.

That gets us to something like this:

integer health = 20;

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        health = health - 1;
        llSetText((string)health+"/20",<1,1,1>,1);
        if (health<=0) {
            llSay(0,"boom");
            llDie();
        } else {
            llSay(0,"pow");
        }
    } else {
        llSay(0,"clunk");
    }
}
default {
    state_entry() {
        llSetText("20/20",<1,1,1>,1);
    }
    collision_start(integer n) {
        integer i;
        for (i=0;i<n;i++) process_collision(i);
    }
}

This works nicely, but it is Evil because very similar llSetText() calls appear in two different places, and the “20” appears in four different places, which means that if I decided to change it to only take 10 hits, say, I would be sure to miss at least one of them.

With the maintainability improved, it does exactly the same thing, but looks nicer:

integer MAX_HEALTH = 20;
integer health = MAX_HEALTH;

show_health(integer h,integer max) {
    llSetText((string)h+"/"+(string)max,<1,1,1>,1);
}

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        health = health - 1;
        show_health(health,MAX_HEALTH);
        if (health<=0) {
            llSay(0,"boom");
            llDie();
        } else {
            llSay(0,"pow");
        }
    } else {
        llSay(0,"clunk");
    }
}
default {
    state_entry() {
        show_health(health,MAX_HEALTH);
    }
    collision_start(integer n) {
        integer i;
        for (i=0;i<n;i++) process_collision(i);
    }
}

Much better.

And for one final touch, we can make the floaty text be green or yellow or red depending on how damaged the thing is, by just changing the <1,1,1> in show_health(), which causes it to always appear white, to a call to yet another function that will calculate the right color for us.

The final form for today, then:

integer MAX_HEALTH = 20;
integer health = MAX_HEALTH;

vector color_for(integer h,integer max) {
    float ratio = (float)h/(float)max;
    if (ratio>0.666) return <0,1,0>;   // Green
    if (ratio>0.333) return <1,1,0>;   // Yellow
    return <1,0,0>;  // Red
}

show_health(integer h,integer max) {
    llSetText((string)h+"/"+(string)max,color_for(h,max),1);
}

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        health = health - 1;
        show_health(health,MAX_HEALTH);
        if (health<=0) {
            llSay(0,"boom");
            llDie();
        } else {
            llSay(0,"pow");
        }
    } else {
        llSay(0,"clunk");
    }
}
default {
    state_entry() {
        show_health(health,MAX_HEALTH);
    }
    collision_start(integer n) {
        integer i;
        for (i=0;i<n;i++) process_collision(i);
    }
}

Isn’t Nature Wonderful?

Next time, maybe: target, heal thyself!

Combat System Scripting I: intro and our first target

As foreshadowed previously, we will now begin a series of posts about the Combat System Scripting we have been doing, because (a) it will be fun, and (b) it might be interesting.

What I’ve done so far, and hope to get to in this series, includes:

  • Stationary targets to shoot at,
  • Moving targets that shoot back,
  • A combat HUD that keeps track of your “damage” and makes you fall down when you take too much,
  • A “medikit” that makes you be less damaged when you walk through it.

Which is enough to have fun battles with your friends and/or Daleks. :) We’ll talk about just that first one in this episode.

A couple of notes:

  • If you just want to have a working combat/RP system to play with, and don’t want to learn scripting of it from first principles, take a look at Myriad Lite (Preview 4) on the Second Life wiki; lots of neat features (and a certain amount of complexity) there.
  • I’m going to assume here that you know how to rez a cube, and how to get a script into one. Not much more than that, though!
  • Loose design goals of the project will include compatibility with various existing things (e.g. it would be good to be able to shoot at stuff with any normal sort of SL weapon), and a preference for less inter-object communication over more, where that’s possible (to reduce lag and complexity).
  • I’m not going to worry too much about preventing ‘cheating’ of various kinds; this is mostly to play with, not to organize serious combat RP with untrusted others around.

Okay, off we go!

The first thing we’ll make is a simple target. Here’s probably the simplest possible target script:

default {
    collision_start(integer n) {
        llSay(0,"clunk");
    }
}

This says, basically, “whenever something collides with you, say ‘clunk'”. (You can change that line to ‘llOwnerSay(“clunk”);’ if you don’t want anyone else to hear the object talking for whatever reason.) Technically the stuff inside “collision_start” is an event-handler, and in this case the event is something hitting the prim that the script is in.

So make a cube, put that script into it, and walk into the cube. It will say “clunk”. Amazing!

Of course walking into things is sort of boring, so let’s get out a weapon and shoot at the cube bang bang. Any weapon that fires actual projectiles will do; if you don’t have one, or aren’t sure of what you have, you can use the simple popgun that is in the standard SL library (the lower part of your inventory, where many people never go; there’s some pretty good stuff in there).

Find the popgun, wear it, go into Mouselook (via the Mouselook button, or pressing M, or whatever), mouse so the little crosshair is on the box (the popgun has weightless projectiles, so you don’t need to compensate for any drop), and click. A thing will emerge and hit your cube (with some amusing colors and sounds and bubbles), and the cube will say “clunk”; huzzah!

Just saying “clunk” is good to be able to tell that you’ve actually hit it, but if we’re going to do Combat we want the target to explode into a flaming ball of gas. Well, or at least to Cease To Be, since I’m not going to figure out a particle-system for a Flaming Ball of Gas today.

So here’s another script:

default {
    collision_start(integer n) {
        llSay(0,"boom");
        llDie();
    }
}

Replace the original one with this one, make a copy of the prim, and then walk into it or shoot at it, and it will say “boom” (which sounds more final than “clunk”), and Cease To Be (quite thoroughly Cease To Be, not in your trash or Lost and Found or anything, which is why I suggested you make a copy of the prim).

You can use shift-drag (or just rez a bunch of copies) to make a whole row of these, and either run along it or go into mouselook and pop them one at a time, boom boom boom boom.

Not bad for a six-line script!

Now it’d be better if the target didn’t actually Cease To Be if someone just casually bumped into it. There are various ways to try to tell casual bumps from official Weapon Projectiles; we’ll use one of the simplest: speed. If somehthing is moving more than, say, fifteen meters per second (which is about as fast as it’s possible for a lone AV to move in normal circumstances), we’ll assume it’s a Weapon Projectile (or maybe a car; getting hit by a speeding car counts); but if it’s moving slower than that, it isn’t. So:

default {
    collision_start(integer n) {
        if (llVecMag(llDetectedVel(0))>15) {
            llSay(0,"boom");
            llDie();
        } else {
            llSay(0,"clunk");
        }
    }
}

The only new complexity here is the “llVecMag(llDetectedVel(0))” bit, which just means “the scalar magnitude of the velocity vector of the thing that hit us” which is to say, how fast it was going.

The (0) means “the first of the N things that hit us”, where N is almost always 1 (so it looks at the first and only thing that hit it), but in some situations, especially when there are lots of bullets flying about, two or more things can collide with the cube so quickly that they’ll both get reported in the same collision event.

If we were just saying “clunk” that wouldn’t matter too much, but if (say) a slow bump happened at the same time as a fast projectile impact, we wouldn’t want to look at just the slow bump. So we’ll add a loop that looks at each of the things that hit us, in case there’s more than one.

default {
    collision_start(integer n) {
        integer i;
        for (i=0;i<n;i++) {
            if (llVecMag(llDetectedVel(i))>15) {
                llSay(0,"boom");
                llDie();
            } else {
                llSay(0,"clunk");
            }    
        }
    }
}

Relatively straightforward.

And then, and finally for this episode, just because I like to keep blocks of code simple wherever possible, we’ll pull out the bit of code that gets done for each colliding thing in to a subroutine of its own, and call it from the event handler:

process_collision(integer index) {
    if (llVecMag(llDetectedVel(index))>15) {
        llSay(0,"boom");
        llDie();
    } else {
        llSay(0,"clunk");
    }    
}
    
default {
    collision_start(integer n) {
        integer i;
        for (i=0;i<n;i++) process_collision(i);
    }
}

And there you have it! A nice target that can be bumped into with impunity, but that goes away with a boom when shot (or when run into at high speed in a Chevy, I expect, although I haven’t tested that… yet).

Next time, hm, I dunno… maybe the Combat HUD or something…

Battling Bots

The Sinister Inventor contemplates his latest Evil Creation.

Contemplating improvements

“I believe the lower edge is dragging on the ground; perhaps if I boosted the hoverheight a bit…”

Well, that worked

“Well, that worked.”

Removing the combat HUD leads to instant resurrection.

Dale's Killbots

“Ah, my faithful lethal servants! What shall I call you? Dale’s Killbots, perhaps. ‘Daleks’ for short!

“Nah, that’s a silly name…”

(The combat scripting proceeds apace; the shooting robots there can be damaged and ultimately destroyed using anything that shoots projectiles, pretty much, although new ones get rezzed as long as the system is on. The combat HUD tosses one rudely to the ground after a certain number of hits by any projectile. I also made a full-auto infinite-ammo version of the standard Linden popgun, which is pretty awesome, but probably banned in most RP sims. I am vaguely thinking of doing a series of weblog posts, tutorial-style, on how it all works. Unless I do something else instead…)

The secret is out! “Placemaker: Plazas” released to worldwide acclaim!

Well, I’m sure there will be worldwide acclaim soon, anyway. Probably!

Placemaker: Plazas / publicity shot

There is the publicity shot for Placemaker: Plazas (Basic Edition), just released onto the SL Marketplace.

For a mere 400 Linden-dollars, you can own a device capable of producing literally a whole lot of different and sometimes even interesting (and in any case full-perm) plaza-like structures!

I have no doubt if that price-point makes any sense :) but I thought I’d give it a try.

So buy it if it sounds interesting! Let me know if it works for you, if the instructions were confusing, or anything else. If I continue to find the general idea interesting, maybe someday there will be a “Placemaker: Houses”. That was actually my original thought, but that turned out to be a little grandiose to code from scratch, so I started smaller…