Sunday 30 November 2008

The Bridge Class - Part 1

May I apologise in advance to Java experts for (at least) the following irritations they may experience.
  • Due to a lifetime of chasing brace-matches through C code, I cannot seem to take to the Java habit of failing to line up braces vertically;
  • Further, because I've been programming in many languages since 1964 (yes, 1964), I have a habit of calling methods "functions".


Now, let's start to work through the Applet. First, the introductory bit, which specifies the standard packages we will use, and defines the Applet:

// Bridge Applet (c)Amazon Systems

import java.awt.*;
import java.applet.*;
import java.net.URL;
import java.util.Vector;
import java.awt.event.*;
import java.net.MalformedURLException;
public class Bridge extends Applet
implements MouseMotionListener, MouseListener
{

Next, a pile of variables are defined. Many of these are class variables.
Ticktimer is a Thread class handling the timing of the main loop.
BridgeData is where all the Scene data are defined.
DisplayManager and DirtRectSet handle the Graphics.
The variables for communication with Javascript (data and flags) are also defined here.

// Timers
private boolean timerRunning;
public Ticktimer timer = null;
private long timewas, timenow;
public long ticks;
// 1000/n where ticks are 1/n seconds
private static int FRACTION = 1000/12;

// Scene control
public BridgeData bridgedata;
public String currscene;
public String popscene;
public Scene thisscene;
public Image backcloth;
public DirtyRectSet dirtyrectset;
public DisplayManager displaymanager;
public int mousex, mousey;
private Sprite isHolding;
public String codebasestr;

// Communication Area
private boolean invFlag;
private boolean songFlag;
private String songName;
private boolean saveFlag;
private String saveString;
private boolean restoreFlag;
private String restoreData;
private int currCursor;

Saturday 29 November 2008

The Applet Call

And now, the tiny scrap of HTML that calls the Bridge Applet and handles the buttons.

The first thing the code does is to start the timeout for checking the applet. Once called, this function continuously calls itself again. I fancy that we are sometimes calling an applet that isn't loaded yet. This causes a confusing message from time to time and I should catch and suppress it (using the self.onerror function that is the first line of Javascript) but while in development, I just let the error show. I hoped there ought to be a cleaner method of starting it off, though it's hard for Java to report to its calling function.

Next, we set up a table with the applet load instruction on the left and the buttons on the right.

All the buttons call the applet, either via the javascript functions we talked about yesterday, or directly.

<BODY onLoad = "setTimeout('checkApplet()', 5000);">
<TABLE>
<TR>
<TD>
<APPLET code="Bridge" archive = "com/bridge/bridge11.jar"
width = "640" height="480" id="Bridge"
name="Bridge"> </APPLET></TD>
<TD>
<FORM name = form1>
<input type="button" value="Inventory"
onClick = "document.Bridge.goInv()"><BR>
<input type="button" value="Save"
onClick = "saveGame()"><BR>
<input type="button" value="Restore"
onClick = "restoreGame()"><BR>
<input type="button" value="Switch Music Off"
onClick = "togglesound()" name="togglebutton">
<BR>
</FORM>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>

Friday 28 November 2008

The Javascript functions in Page3js

(Boy, did I have a job rendering the code in an acceptable way. Finally got it the way it is, and I'm happy with it but I've modified the template for pre tags.)

The page page3js.htm is the page that arrives in the "mainframe" frame.

The first section of this is a couple of functions to deal with the midi songs.

There may be an issue with starting and stopping the music when the applet is "frozen", as the music continues in Firefox if it is on a hidden tab.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Bridge - Page3js</TITLE>

<script language="Javascript" type="text/javascript">
//self.onerror = function(){return (true);}


var songname="hush"; //current song
var soundtoggle = "on" //music on


// change the contents of the sound frame
// unless it's already playing
function newsong(name)
{
if (name == songname) return;
if (name=="c12") parent.soundframe.location="c12.htm";
if (name=="ara") parent.soundframe.location="ara.htm";
if (name=="") parent.soundframe.location="hush.htm";

songname = name;
}

// switch music off or on
function togglesound()
{
var temp = songname; //remenber the current song
if (soundtoggle == "on")
{
newsong(""); //silence
songname = temp;
soundtoggle = "off";
document.form1.togglebutton.value="Switch Music On";
}
else
{
songname = "hush";
newsong(temp);
soundtoggle = "on";
document.form1.togglebutton.value="Switch Music Off";
}
}


The following functions deal with communication to and from the applet, which, for historical reasons that I forget, is called Bridge.

The first function calls two more functions that poll the applet for new Save data and for a request to change the song currently being played. Then it arranges to be called again.

The communication always takes the form of a function being called, a function of the form: document.Bridge.function-name(...).

Note that save data is stored in a cookie. On a request from the Save button press, the game is requested to start collecting save data. When a poll from checkSave() results in some data being there, the javascript stuffs it away. When a Restore is requested, the applet is called with the cookie data.

function checkApplet()
{
checkSave();
changeSong();
setTimeout('checkApplet()', 2000);
}
function checkSave()
{
// the term: + "" added to the result to ensure
// against a null return
var savestring = document.Bridge.getSave() + "";
if (savestring != "")
{
var nextyear = new Date();
nextyear.setFullYear(nextyear.getFullYear()+1);
document.cookie = "saved=" + escape(savestring)
+ "; expires=" + nextyear.toGMTString();
}
}
function changeSong()
{
var appsong = document.Bridge.checkSong() + "";
if (appsong != "none")
{
if (soundtoggle == "on")
{
newsong(appsong);
}
else
{
songname = appsong;
}
}
}
function saveGame()
{
document.Bridge.doSave();
}
function restoreGame()
{
var allcookies = document.cookie;
var pos = allcookies.indexOf("saved=");
var start = pos+6;
var end = allcookies.indexOf(";", start);
if (end == -1) end = allcookies.length;
var value = allcookies.substring(start, end);
value = unescape(value);
document.Bridge.doRestore(value);
}
</script>
</HEAD>

Tomorrow I'll continue with the BODY of page3js.htm

Thursday 27 November 2008

HTML and Javascript Framework


When the user starts up the game, he is calling up an HTML page like the one here. It consists of two frames, one of which you can see, the other is "hidden". We'll come back to the hidden frame in a moment.

The frame you can see has a rectangle where the Java applet resides. The applet is doing most of the work here, but we need to initiate it and communicate with it.


There are also four buttons on a Javascript form which communicate with the Java applet.

Here is the HTML for the outer framework.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>China 11</TITLE>
</HEAD>
<FRAMESET rows="90%, 1%">
<FRAME SRC="./page3js.htm" name="mainframe">
<FRAME SRC="./hush.htm" name="soundframe">
</FRAMESET>
</HTML>

The almost invisible frame "soundframe" enables the game to play midi tunes. The initial page that goes in that frame is hush.htm, which does nothing:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>hush</TITLE>
</HEAD>
<BODY>
Bridge hush
</BODY>
</HTML>

For debugging purposes, I can open up the frame and see what page is "playing".

A different page that has a tune on it can be loaded by Javascript at run-time. This is the page for one of the songs.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>ara</TITLE>
<BGSOUND src="./sounds/arabian.mid" loop = "-1">
</HEAD>
<BODY>
<BGSOUND src="./sounds/arabian.mid" loop = "-1">
<EMBED name = "ara" SRC = "./sounds/arabian.mid" autostart="true"
loop="true" hidden="true" mastersound></embed>
Bridge ara
</BODY>
</HTML>

Note that it uses BGSOUND as well as EMBED. This was to cover old Internet Explorer versions. The idea is that it plays in a loop until the game ends or a new song is put in place. There are a number of problems with this system - not every browser gets the right sound or any sound at all. There's a horrible crunch when the song is changed on Firefox, whereas it all works rather well on IE.

I don't want to dwell too much on this, because I intend to change the mechanism in due course.

Tomorrow I'll start on the main page that sits in the "mainframe" frame and communicates with the applet.

Wednesday 26 November 2008

Plan of Action

By the way, I'm not going to mess about with extended navel-gazing here. It's my plan to do a top-down analysis of the product to date (bearing in mind its age) and remind myself of how it works as I go.

At the time it was written, typical users didn't have broadband, and some compromises were made to save transfer volumes and improve speeds. This restriction no longer holds.

A link to a zip file containing all the sources can be found in the Links section. Just click the link and my phpzilla download page will come up

Finally, I'll improve it and turn it into the adventure it ought to be, hopefully with some help from my readers.

Introduction


Some five years ago, maybe longer, I started writing an adventure system which could deliver a Myst-like adventure over the web (i.e. through a browser).

I did a proof of concept small adventure which you can see by clicking the image to the right here:

It's a java applet operating with some assistance from a javascript framework.

I caution you... it is pretty lame. There are only 3 or 4 scenes, the click detection is not correct and the music is delivered via a javascript framework.

But it contains the basis for an adventure - the circling birds on the front screen, the ability to pick up and drop the key, the inventory, save and restore features, the ability to add scenes and sprites at will via simple java functions. The scenes are generated using POVRAY.

It began with Mark Tacchi's Gamelet Toolkit, and I'd hoped to use it almost unchanged, but although there are still chunks of Mark's code in the graphics area, my engine is largely unrecognisable.

So this is the starting point. Here we go. Comments and offers of assistance are welcome!