Monday, August 25, 2008

Second Life Physics Scripting #28 - disconnecting users

I arrived at the game to find that it would not let me start because it thought someone else was still playing.

This is the code that wasn't allowing me to join.


touch_start(integer total_number)
{
if (avatar_attached)
{
llSay(0, "Currently allows only one player at a time. Sorry.");
return;
}

But the real problem was in the avatar_attached flag not being reset. The user must have logged out without hitting the release keys button so I never go the disconnect event. There doesn't seem to be anyway to drop those connections so I need to at least pay attention to their distance from the table.

This is the new timer code.

timer()
{
....

// see if we still have control
if (avatar_attached)
{
if( !(llGetPermissions() & PERMISSION_TAKE_CONTROLS) )
{
auto_tick_count = 0;
avatar_attached = FALSE;
}
else
{
// check the distance to the avatar...
key curkey = llGetPermissionsKey();

list temp;
vector pos;
vector pos2;

temp = llGetObjectDetails(curkey,[OBJECT_POS]);
pos = llList2Vector(temp,0);

pos2 = llGetRootPosition();

float dist = llVecDist(pos,pos2);
llOwnerSay("dist = "+(string)dist);
if (dist>15)
{
llSay(0,"Player too far away - resetting");
auto_tick_count = 0;
avatar_attached = FALSE;
llRequestPermissions(avatar_key, 0);
avatar_key = NULL_KEY;
}
}
}
}

I get the key of the avatar that is currently attached. Then I check that avatar's distance. If they are too far away, I reset the avatar attached key. There was still one more problem if they walked back into the area then they could still send control events so I added some extra code to runtime permissions to save the last player that attached.


run_time_permissions(integer perm)
{
// permissions dialog answered
if (perm & PERMISSION_TAKE_CONTROLS)
{
// we got a yes
// take up and down controls
llTakeControls(CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);
// remove any balls on the table
llSay(1296,"byebye");
in_play = FALSE;

avatar_attached = TRUE;
avatar_key = llGetPermissionsKey();
RestartGame();
}
}


And then to the control code to double check the current key with the key sending the control event.


control(key id, integer held, integer change)
{
if (id!=avatar_key)
{
list temp = llGetObjectDetails(id,[OBJECT_NAME]);
string nm = llList2String(temp,0);

llSay(0,nm+", you have been disconected, press the Release Keys button and touch the pinball machine again.");
return;
}

I wanted to send them the message directly and probably can with an IM, but for now it was just easier to send it with their name attached to identify that the message was to them.

Friday, August 22, 2008

Second Life Physics Scripting #27 - New Artwork

I finally found a screen shot I liked for the base table, then used my own avatar for the upper scoreboard. Not that my avatar is anything special, it's about as plain as you can get on the day you are SL born except for the customer t-shirt. My thought is that the simple avatar looks even more second life so will hopefully add to the effect? Probably just looks cheesy.

I also finally got really tired of the default plywood texture and made a red to yellow gradient for the bumpers and used a neon blue for the side walls. It may be overkill and too simplified looking, but I think it is better than it was. I need to do a better layout with the bumpers, get some sound and particle effects on the bounces and add some bigger scoring elements. It will probably never be 'done'.

Thursday, August 21, 2008

Second Life Physics Scripting - Pinball #26 - The First Player

I put in some logging to see if anyone was playing the actual game. I had my first player (Grimley Graves) and asked him if the game was actually working. He owns a haunted house down the street which is how he found the game. Sure enough, the game didn't work at all. He offered to help me test it and I found the problem.

The problem was in the keyboard code from post #12. This is what I had.


llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);


It turns out that the first parameter is the person who's keys you want to take over. Sure enough since I'm the owner and was neaby when he touched the table, I received the dialog asking if I wanted to play. Oops.

This is the correct code.


llRequestPermissions(llDetectedKey(0), PERMISSION_TAKE_CONTROLS);


The llDetectedKey(0) is the key for the first avatar that touched the table. Once I made this change everything worked great. I went back and changed post #12 to reflect this difference so someone doesn't have this same problem later.

Wednesday, August 20, 2008

Second Life Physics Scripting - Pinball #25 - A Bit Of Art

I played around with the art a little today, just using the standard sunset images that come with every second life inventory. I'm planning on making some images of a bunch of people and trying to use those.

I added some code to see if anyone actually plays the game. This just saves each name to a list and I listen for a specific chat and print out the list if the machine hears me.


list player_list;

integer isNameOnList( string name )
{
integer len = llGetListLength( player_list );
integer i;
for( i = 0; i < len; i++ )
{
if( llList2String(player_list, i) == name )
{
return TRUE;
}
}
return FALSE;
}


state_entry()
{
...
listen_handle = llListen( 0, "", llGetOwner(), "" );
...
}
touch_start(integer total_number)
{
...
string detected_name = llDetectedName( 0 );
//if( isNameOnList( detected_name ) == FALSE )
if( detected_name != "Wood Wheels" )
{
player_list += detected_name;
}
}
listen(integer channel, string name, key id, string message)
{
...
else if ((channel==0) && (llToLower(message)=="players"))
{
llSay( 0, "Player List:" );
integer len = llGetListLength( player_list );
integer i;
for( i = 0; i < len; i++ )
{
llSay( 0, llList2String(player_list, i) );
}
llSay( 0, "Total = " + (string)len );

llSay(0," High Score - "+highscore_name+" - "+(string) highscore);
}
}

It has been a few days since that time and no one has played it. I did attract some sort of interest tough because the previously empty land above me has added an advertising post. I wouldn't normally mind, but since it had some sort of porn ad I placed a brick wall in front of it.



I need to do some work on the art and layout and then add some more game play elements, like scoring better than 1 point per bumper bounce.

Tuesday, August 19, 2008

Second Life Game Scripting - Pinball #24 - Game Play

I finally made it to the point where I could add what I call "Full Circle Game Play". This is where the player is given a limited opportunity, score is kept and the game restarts when they are out of resources.

In this case I decided to place this code in the PayAndKeyboard script I use to keep track of the keyboard and will add the pay to later if people start to play.


integer balls_remain;

string curscore_name;
integer curscore;
integer scoreboard = -1;

string highscore_name = "Wood Wheels";
integer highscore = 27;

First the variables that are used and a startup high score. Since this score will be reset each time I recompile the script I've decided to edit this entry by hand when I make changes so then I can at least keep a longer term high score. I wonder who will be the first to beat my high score? I'll post their name here.

Then I added a restart game method in the PayAndKeyboard script because this will be called from two places. When they run out of balls and when they first start the game.

RestartGame()
{
llSay(0,"Game Restarting");

llSay(0,"Current High Score - "+highscore_name+" - "+(string)highscore);

balls_remain = 5;
llSay(0, "You have "+(string)balls_remain+" balls remaining");

curscore = 0;
llMessageLinked(scoreboard,MSG_SET_NUM,(string)curscore,NULL_KEY);
}

That link message was part of the original scoreboard and I'm just resetting the score to 0 for the game.

From the keyboard code, the run_time_permissions is called after we are given permission to listen to the keys.

run_time_permissions(integer perm)
{
// permissions dialog answered
if (perm & PERMISSION_TAKE_CONTROLS)
{
// we got a yes
// take up and down controls
llTakeControls(CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);

// remove any balls on the table
llSay(1296,"byebye");
in_play = FALSE;

avatar_attached = TRUE;

RestartGame();
}
}

In this code we first tell any ball currently on the table to go away. This is the same message sent by the back wall and the ball is listening for this.

We also call RestartGame to kick off the actual game play. We set the avatar_attached flag after the "byebye" because this has logic issues when this script is actually listening for this exact message.

There is one other place where we can restart a new game. This is where the

if ((channel== 1296) && (llToLower(message) == "byebye"))
{
if (avatar_attached)
{
balls_remain= balls_remain - 1;
llSay(0,"Oops, drain. "+(string)balls_remain+" balls remain");
if (balls_remain<=0)
{
if (curscore>highscore)
{
llSay(0,"HIGH SCORE!!!!!");
highscore = curscore;
highscore_name = curscore_name;
}
RestartGame();
}

llSay(0, "Press 'Page Up' to start another ball.");
}
else
{
llSay(0, "Press 'Page Up' to start another ball.");
}

in_play = FALSE;
}

This is the code that decrements the number of balls remaining, checks for high score and restarts the game if the number of balls is 0. This is why I reset the avatar_attached flag after sending "byebye". Note there I saved the player name curscore_name when the user first touched the table.


touch_start(integer total_number)
{
if (avatar_attached)
{
llSay(0, "Currently allows only one player at a time. Sorry.");
return;
}

....

string detected_name = llDetectedName( 0 );
curscore_name = detected_name;
}

Most touch_start samples iterate through the total number of touches. In this case it is only a one player game so I only use 0 as the player name since I'm going to ignore all others onece the avater is attached to the table and playing.

Oh, I almost forgot. I had to add one more message to the score board so it sends the current score to the root prim whenever the score changes. I then save that in the curscore. I thought about sending a message to the scoreboard to have it send the score back, but I was worried about the asynchronous nature of the communication and thought the control code would be much more complicated than simply sending it on each change.

This is the listener in the PayAndKeyboard script.

integer MSG_TOTAL = 4116; // current score from the scoreboard

link_message(integer from, integer msg_id, string str, key id)
{
if (msg_id == MSG_TOTAL)
{
//llOwnerSay("scoreboard says: "+str);
curscore = (integer) str;
}
}


This is the full link_message function in the scoreboard. The scoreboard is listening for message from the bumpers when they tell it to increment the score. And the root script sends the 0 score when the game starts (see above). Yes, that means that the PayAndKeyboard will set the curscore to 0, then call the scoreboard which will send that same score back to the root again. It's just one extra message and probably not a big deal.

Notice that this function now sends MSG_TOTAL to LINK_ROOT whenever it receives a message to change the score. I could probably have had this as a single call outside the if/else statement, but I'm always worried about some rogue message setting off the message or some future change not needing it and leaving it in that I just added the same line to both sections of the if/else.

link_message(integer from, integer msg_id, string str, key id)
{
//llOwnerSay("scoreboard received "+(string)msg_id);
if (msg_id == MSG_SET_NUM)
{
curval = (integer)str;
//llOwnerSay("setting num to "+(string)curval);
doSendNumbers();

// send the score to the root
llMessageLinked(LINK_ROOT,MSG_TOTAL,(string)curval,NULL_KEY);
}
else if (msg_id == MSG_INC_NUM)
{
curval = curval + 1;
//llOwnerSay("inc num to "+(string)curval);
doSendNumbers();

// send the score to the root
llMessageLinked(LINK_ROOT,MSG_TOTAL,(string)curval,NULL_KEY);
}
}

Monday, August 18, 2008

Back Home - Africa

We returned yesterday afternoon and it's back to work today.

It was another amazing experience. A ton of work and not much sleep, but it's always a great experience to serve others. We had the best team I have been on so far. I was on the team that was focused on the Academy. We taught classes during the day and ran an After School Program. There were a lot of teachers on the trip since it is summer here. I knew I would be teaching one computer class to each of the grades and didn't prepare beyond a few ideas and having downloaded some software I might need. I was just going to offer a few different topics and let the students decide which one they wanted the most. I could do any of the office tools, image editing (using Gimp), html and website creation or programming. Most of their current learning has been with Word so the office tools were out. They chose image editing so I helped them load Gimp.

All of the kids at the academy are orphans from the townships where Bridges Of Hope is working. The Bridges Of Hope model is away from state run orphanages (even the state sees orphanages as an unacceptable model) into a boarding school (the elite schools are boarding schools) where the students return to their communities when school is out. The townships are very dangerous and this may not be the best place for these students when school is out, but they still have the connection to their home and become role models for positive development. Turning what was a desperate situation for them into an opportunity to be a community leader.

They were pretty far behind in their computer skills so part of my goal was just to get them to click around on menus and try stuff. The first class was all about using the brush tools and learning to zoom in and out of a picture.

It looked like I was going to have a light load on Friday and I had fully planned to do some manual labor around the school. As I tried gather a few people the head of the school said the Computer teacher had failed to show and asked me to teach the classes. I spent another class on Gimp and showed them how to use selections to remove someone (me) from a picture and place me in another picture. This was a lot of fun and once they figured it out they were moving each other from picture to picture. I also showed them a few of the effects tools to change image colors and such. All and all it was a lot to pack in in an hour, but the students said they loved it and I did see them using Gimp outside of class which was nice.

The second week I had a few math classes with my wife. We taught Money math which included, saving, tithing, investing, and credit cards. We handed out monopoly money and asked them how they would each spend their first pay check. There were not many "savers", but after our class I'm sure most of them would have put away as much as possible and always fully pay a credit card bill. Some of the best lessons my father ever taught me. The only (nearly) guarnteed way to get rich is over a long time with a constant savings and a good interest rate.

I learned that the computer teacher had actuall quit and I got to teach four more classes on the second Friday. This time I knew the kids a lot better and had watched them all playing music from their thumb drives. I decided to teach them Audacity to make music. They really went crazy with this. It was hard because I tried to cram three weeks worth of classes into an hour, but by the end they were recording their own singing and creating rudimentary mixed tracks. They now have the software and enough knowledge to take it to the next level themselves.

For the afterschool program we helped put on a school play (High School Musical) and worked on it over the two weeks. They showed the play on our last night along with a talent show and it was a lot of fun. We also ran our "Choose To Wait" classes the second week. These are basically sex ed type classes where we talk about God's plan for sex and marriage. In an area that has 20-30% HIV/AIDS infection rates the best thing is to get young people to abstain from sex. Wear a condom if you are going to have sex, but don't expect that to fully protect you from HIV/AIDS or pregnancy. Even the condom box says that they are not 100% effective. I've taught this three times now in Africa and I went from being a partial skeptic to a full believer in the curriculum. It does an amazing job and I think that committing to abstain before my wife and I were married really helped us create a much deeper relationship. It is much different when you explain to kids why God wants us to only have sex inside of Marriage instead of just telling them to not have sex and giving no real reasons besides HIV/AIDS and pregnancy. Or just giving them a condom and falsely telling them they'll be safe as long as they use it.

The other teams spent a lot of time in the township called Sweet Home training leaders there on how to teach an Alcohol Abuse prevention and recovery program. They also taught first aid and classes on emotional wounds. The township we have adopedted is one of the poorest places I have ever been and it was exciting to see the foundation laid for a more positive future. It will take years and years, but I can see that there is potentional to change generations of neglect. The Life Wind model is all about community development with a focus away from relief. I have seen communities transformed by this process and I'm still excited to see more positive changes than what normally happens when we in the west simply throw money at problems and think our ideas will solve the needs that communities don't even recognize as needs. If you want to stop throwing money and time down the drain and truely make a change, Life Wind is an organization with a revolutionary model of community development.

Another team ran a VBS camp for the kids in the same township. I was there for part of two of the days and I would have to say that VBS is really just a bit of semi-controlled chaos.

Everyday was pretty much the same. Up at dawn, work all day, homework until 9pm, team meeting at 9pm then crash at midnight to do it all over again. We did get two days off. One was a trip to Robben Island to get acquinted with the CHE leaders from Sweet Home and the other was to a game drive where we saw the Big 5 animals from a jeep. It wasn't "in the wild", but it was a good time and a fun experience.

All in all it was a great first trip and the start of what looks like a long and fruitfull partnership. We relieved some of the staff at the academy for two weeks and help sow seeds of change in a very poor township.