walking dood

Redoing an half baked side project where a character token (created with the charas project generator) gets to go for a walk using javascript.

1) 7-24-08 - dood can walk, and turn.
2) 7-26-08 - Contact object rules over walls and pushables
3) 8-02-08 - Class inheritence overhaul
4) 8-03-08 - Contact overhaul
5) 8-17-08 - 8 direction walk

TODO

DONE! So next are Contact, and blocks (dimensions), pushables, and walls (nonpushable)
DONE! Remake Contact to be faster and more accurate. Have moveables send a standard move-data packet (possibly in Element Tangible (renamed))
DONE! Let char push two blocks (not char push block that pushes block) - Contact fail
Tweak Contact to allow diagonal moves
Beasts

walking dood : session notes

(8-17) The main "walking dood" image has changed to a little knight. I've decided that in order to give this project everything that I wanted, I'll need to have to move and shoot in 8 directions. Borrowing from what I believe is Battle Soccer, I took an isometric UltraMan(?) and made him the hero. Looking only in cornerly directions, the movement in all eight directions is pretty smooth, provided that I specifically state that he should face South when only moving West or East. In any other cases, he'll face with ever was the last to be used (so when going South, it may be SouthWest or SouthEast facing, very smooth). The dude's "step increment" is also set back to zero when movement stops, leaving him in a stading position. Other frames in the set include what is doing to be a decent attack motion, and also some frames of falling and dying. A set of Godzilla monsters are in here as well, which is great because now I can starting thinking about Beasts (I guess lizards and ogres), and each one also has the same frames (including death!). The downside of this is now I have to redo Contact again to handle motion in two directions.

Two new objects have entered the scene that I think will be a good thing, Key and Timer. Key is going to be the object that keeps track of the keys being pressed. There has been a lot of thought and testing into which methods to capture (keydown vs keypress) mostly, and it turns out that this is a lose-lose situation any way you look at it. The final straw was when it was realized that Firefox can't do multiple keydown's (except for when shift, control, or alt are involved) -- but I found a way, using Key and keypress, to get the perfect sort of behavior that you'd expect in an eight-directional walking dood test. Safari's last overhaul on their Event Handling pretty much crippled keypress for non-character keys, but I don't really care to worry about cross-browser any more. I'm aiming for this to be a greasemonkey script -- startable on any web page, but if that doesn't seem like fun (I really need dungeons and that sort of thing), then I can try to revisit that.
Timer is a way for me to manage the step-repeats that you get from holding a key down by limiting motion based on time. This is also thinking ahead to Beasts that will move on their own and at their own rate. Timer also has the global "framerate" of the game, so that it can be tweaked to the user's taste or the browser's capability. Like with contact, objects that are interested in knowing the time register themselves with it, and then receive updates. The object itself can then determine if it "is time" to do something, or wait. I want the Hero to be able to out run most Beasts, so now speed is just a member of the object and the rest handles itself.

(8-3) I was able to pull off the Contact rewriting that I wanted to do. Obviously, I am no mathematician, else I would have found a better way to get "contacts" earlier in the week. A 32x32 square has 1024 coordinates, and diffing that against another object was way to heavy of an operation to keep carrying out on every object. But then it hit me, I'm dealing exclusively with rectangles, and I could determine a contact with one very simple rule: If the two shapes share any x and any y. then we have a contact. So the loops justs runs along (at most) half the perimeter of shape, once for x stopping if it gets a hit, and once for y if it got a hit for x, stopping where it hits. This drove the number of operations needed way down — but still runs on all objects to compare against the object in motion. I still want to try and find a way to skip objects that are obviously nowhere near the moving object. In the picture, I'm showing the checks for the red object moving into space owned by the blue one -- it makes me happy to know that if it were the blue one moving into the red's space, that contact would be found on the first full operation.

Double push also works now (or more accurately pushing any number of objects from the same depth). Before, I was determining the success of a move by way of an attempt. If Char wanted to move, and a Pushable Block were in the way, the result move() would be that of the Pushable's move(). Because it returned right away, the second block that the Char might be trying to push as well wouldn't get evaluated, and freakiness would ensue. As before with Contact, a moveable object's move() method calculates for itself the space that it wants to occupy, then asks if(Contact.mayMove(here)){ move(here); }. Contact works the rest out with logic like, "Well, something's there, can it move? Oh man, something's in its way, can it move?". It's a recursive function, so if we were tring to push 20 things in a line, the last one to get checked has the last say in whether the whole push can even be done. (I'd like to make a little fun test page for this).

extend and extends as part of a base Object prototype is working out nice, but I'm starting to see how they might be the devil. There are a few places in my functions where I just have a data object or an array, and want to go through its contents, but have to dogde these methods or face errors. Not so fun, but I'm working with it.

callMove:function(moves)   //  Contact.callMove
{
	for(id in moves)
	{
		if(is(moves[id],'function'))
			continue;  //  bah, is extend or extends
		
		moves[id].extend({ok:true});
		this.getObject(id).move(moves[id]);
	}
}

(8-2) Yay, the weekend is here and it's time to start pushing around some more blocks. I think that I finally solved the riddle of class inheritence in a way that I'll probably use for other projects to come. The breakthrough was when I stopped trying to modify the prototype itself (extending the class), but rather make it a factory for extended objects. Most of the pushlished examples of extending teach this, but not mostly for a single object instance and not this reusable factory way. All it takes is are a couple of handy methods extend and extends (of which there are examples on the interet from every javascript developer ever). This is included in an all new global.js (the stuff I'll reuse). The code I have uses the function is, but you can use typeof in your own scripts if you want to reuse this without my is(). ("is") btw is a reserved word for javascript2 I hear, so one day I may want to change that to isa() or is_a() ).

/*
@param object to add to this one
@param bool (=false) overwrite if exists?
*/
Object.prototype.extend = function(o,over)
{
	if(!is(o,'object'))
		return this;
	for(i in o)
		if(is(over) || (!is(over) && !is(this[i])))
			this[i]=o[i];
	return this;
}

/*
@param object to add to this one
@param bool (=false) overwrite if exists?
*/
Object.prototype.extend({
	extends:function(cls,over)
	{
		if(is(cls,'function'))
		{
			try
			{
				this.extend(new cls,over);
			}
			catch(e)
			{
				return e;
			}
		}		
		else if(is(cls,'object'))
			this.extend(cls,over);
		return this;
	}
});

Creating new objects now has the interface I was after, new Pushable({*params*}), and the resulting object will extend Block, which extends Tangible, and keeps also the params I'd given it.

var Foo = function()
{
	this.extend({ foo:'foo!' });
		// could have also been
		// this.foo = 'foo!';
};

var Bar = function()
{
	this.extends(Foo)
	.extend({ bar:'bar!' });
};

var Baz = function(params) // gave "params" only to final objects
{
	this.extends(Bar)
	.extend({ baz:'BAZ!' })
	.extend( params );
};

var foobarbaz = new Baz( {wark:'KWEE!'} );
// Object foo="foo!" bar="bar!" baz="BAZ!" wark="KWEE!"

(7-27 thoughts). The Contact object in yesterday's version (2/) is definitely flawed. It considers "encroachment" only by judging the corners of the incoming element. so in the picture below, the red moveable does not register a contact because its corners never enter the blue square. One could argue that the evaluations could should also happen the other way, but the system in general not ideal; so I don't want to do a double-bad to try and fix it. So I'm going to attempt an entire point map of coordinates and the objects contained within. Hopefully, this will not be too heavy on the system;; opting to use Objects (as they pass by reference) instead of Arrays (which I'm pretty sure copy). I'll need to test this seperately though.

(7-26) Several new classes, Block and it's children Wall and Pushable, Contact to manage object collisons, and a base object Element from which all "physical" things are derived. Nothing really planned for the todo after pushables, so I guess I'll have a beer and think about it some more. Perhaps it's time to start a 360° "gauntlet" test char.
Contact.mayMove() is a little buggy sometimes (char walks though the pushable), so I can't forget to go back and try to figure out why that happens. Solved I think. The initial Pushable was a little smaller than the moveable character, and Contact determines contact by comparing the four corners of the encroaching element against the static one - so it was possible that the four corners of the character could be completely outside the pushable, making him walk over it. I just made the Pushable bigger, and haven't seen it happen again.
I should seriously reconsider how Contact.getObjectsWithin(area) does it's checking. While not an issue yet, this could get very bad if on every move, every object was evaluated. I should be indexing and trimming on directional change. For instace, if the chatracter is at position 0,0 and moves, what is the sense of checking a object positioned at 128,128 ? So on a move East, I should look ahead at the objects mapped and determine what objects, if any, are going to impede this movement should it continue. That way, I could likely continue "easting" while keep tabs on the distance between at most one other object in a quick and painless fashion. This was also discussed with Tristan over beers last night, but I failed to execute it by the end of my session today.

(7-25 thoughts) "Blocks" which could be extended to Walls and Pushables. Discussed with Tristian the pro/cons of "Contact" , methods by which an element asks "am I being touched?" -- and we think that it might be best to group this is into one object, that with the all seeing eye. This way, the character or other moveable could be denied or chain it's action. Right now, telling dood to move forward() does it. But up against a wall, dood would have to first seek permission from the Contact object. If Contact says, "sorry dood, you're in contact with a non-moveable in the direction" then forward() is false. Contact would have polled the whole map up front, so it only ever evaluating the coords associated with the moving object.

For pushables, additional contacts is pretty much automatic (but needs to trickle back to the initial moveable). If dood trys to make through a pushable, Contact says "you're allowed to encroach on the space taken up by this block because it's a pushable, but it too needs to move in the direction". If all is clear, it moves() -- but the same check with Contact needs to be made, "am I allowed to move? Am I up against a wall on the other side?". Char would have to be denied it's move if the pushable is denied. I'm really liking the idea of Contact now, because a "move" is not just a method of an element, but a chain of events upon several objects that could either happen or not. An example used during the discussion was a Pushable box of TNT, and a roaring fire. dood pushes the box into the fire, and it explodes. Regardless of map size, Contact is really only tracking the two moving contact points and referencing them with what it knows is there.