easy does it: a first look

My latest game Easy Does It is a dungeon crawl. Sort of. With a twist. Here’s the blurb:

“Easy, the fabled adventurer, leaves a path of devastation through a cavern as he crushes his enemies and steals their treasures. He’s got no time to make amends; that’s your job. Calm the ghosts of his victims, dispose of their bodies, appease the gods—and make a little money. Very little money.”

I came up with the idea after a Skyrim play-through. I spent a lot of time charging into caves, killing anyone who happened to live there, and taking their stuff. Skyrim justifies this by having nearly every cave-dwelling NPC attack you on sight, but it still feels wrong. Where’s the button for oh hi, I just wandered in, nice décor, I’ll see myself out? Mm, can’t find it. Guess I have to kill again.

After a while, I felt like some kind of medieval sociopath. It’s not hard to start seeing every protagonist in every RPG in that light.

In Easy Does It, you follow one such remorseless killer, like a pilot fish hanging around a shark hoping to catch a few bits of food. Easy tends to drop a few moldy coins here and there, possibly by mistake.

He also leaves the bodies of those he’s slaughtered laying around.

He drapes a cloth over them because it’s easier than modeling a full human form. (Okay, it’s a bit of a cheat.) Your job is to cremate the bodies, which hides the evidence of his crimes.

However, before you can deal with the bodies, you’ve got to deal with the ghosts.

You have no weapons beyond words. Make excuses, flatter their misty forms, do a little victim-blaming, whatever. Get them to stop whispering damnation to you before you lose your nerve and flee the cave. Once a ghost has broken off, you can incinerate the body, which also releases the ghost into the afterlife. Job well done, and on to the next bend of the cave.

There’s a map key in case you get lost, though each section of the cave is pretty small. You have to collect wood (from smashed treasure chests) and spilled lamp oil to cremate bodies, and if you don’t have enough, you’ll have to leave the ghost wandering the cave forever. The gods don’t look kindly on this. A “grace bar” tells you how you fare in the divine balance. Go high enough and you’re free of Easy forever. Go low enough—and you enter a different sort of eternity.

The game is browser-based, free to play, and should be available in early April. Stay tuned.

I love my brick

Had a bit of an oh, shit moment late last night. I’ve been having problems with my PC not coming out of sleep mode properly. My motherboard, an Asus M2A-VM, seemed to be the culprit, so I decided to upgrade its BIOS.

Yeah, that’s usually the beginning of a good oh, shit PC story.

The upgrade process bricked my PC. No display, no drive activity, not one single, solitary beep from the POST. I had to have done something wrong. More to the point, I had to undo it.

Now, modern Asus motherboards have this “crash-free” BIOS thing where you’re allowed a do-over after a failed upgrade. Put a known good BIOS image on a drive and boot the PC, and some lingering ghost of sanity on the motherboard will pull it down and re-flash the BIOS. I found a new image on the Internet, as it wasn’t on the recovery disk where it was supposed to be.

I haven’t had a floppy drive in almost a decade, but I tried putting the image on a USB drive, which didn’t do anything, then burning it to a DVD, which also did nothing. A light came on the DVD, but that was all.

Frantic, I scoured the Internet for answers, and came across a HardOCP thread I can’t link to because it was on another PC and it was late and I’ve forgotten how the hell I found the thread in the first place. But the gist of the thread was, if you’re upgrading an Asus motherboard and it fails, try pulling out the memory and replacing it with one single stick.

What the hell? I had nothing to lose except more sleep. I yanked out all the RAM, put in a single 512MB stick, and it fucking started right up.

I’m fuzzy on the details, but it has to do with memory voltages, which are settable on Asus motherboards. Most BIOSes default this to Auto, which is fine until an upgrade changes what the default voltage actually is. To support four sticks of RAM, I had to lock it to 2.1V instead of whatever low-ass default the upgraded BIOS thought it should be. What I found funny was that the upgrade didn’t actually fail, it succeeded, and that was the problem. If it had failed, it probably would have been easier to diagnose and fix.

So, that’s what I did last night instead of sleeping. Still, it could have been worse.

Did I ever mention how much I love the Internet?

the good, the bad, and the tragic

I’ve spent the last few days reworking how I generate caves. That canvas-based map object I mentioned in the last entry really helped me focus on the algorithms, rather than the mechanics of getting data into the map. I’m able to create distinct structures by drunk-walking around a set of randomly-chosen points.

I create each path by making incremental steps and doing a fillRect at each point to plot a small square with a miniscule alpha.

// closure function for drawing a meandering path
function drawPath(x, y, tx, ty) {
	var dx, dy, d;
	do {

		dx = rng.get() - rng.get();
		dy = rng.get() - rng.get();
		d = Math.sqrt(dx * dx + dy * dy);
		x += 0.5 * dx / d;
		y += 0.5 * dy / d;

		dx = tx - x;
		dy = ty - y;
		d = Math.sqrt(dx * dx + dy * dy);
		x += 0.05 * dx / d;
		y += 0.05 * dy / d;

		x = SOAR.clamp(x, 0, l);
		y = SOAR.clamp(y, 0, l);

		map.context.fillRect(x - 2, y - 2, 4, 4);
	} while (Math.abs(dx) > 1 || Math.abs(dy) > 1);
}

...

// generate paths
map.context.fillStyle = "rgba(255, 0, 0, 0.05)";
for	(i = 1, il = this.area.length; i < il; i++) {
	drawPath(this.area[i - 1].x, this.area[i - 1].y,
		this.area[i].x, this.area[i].y);
}

Premultiplied alpha insures that I’ll get a nice cumulative effect as the rectangles overlap each other. Instead of the sheer walls I often get with noise algorithms, I can get tunnels, and other natural-looking structures.

The canvas performs alpha blending and color accumulation much faster than if I tried to do it in raw Javascript. It’s a win-win, except when something like this happens.

Oh, dear. Good luck getting through that.

I’m still trying to figure out how to prevent this. A rare occurance, but it only has to happen once to bring a game to a halt. If I make the alpha too large, I wind up with dull flat cave floors instead of exciting curvy tunnels. If I make the rectangle size too big, I’ll get vast open spaces with little suspense about what’s around the next corner.

And this in fact leads to the sole issue I have with using the canvas to generate maps. I have to call getImageData to see the actual pixel data, and the object it returns doesn’t track canvas changes, so if I wanted to use it to fix paths I’d have to generate additional data objects. Wasteful!

Naturally, I could operate on the data object itself, checking and correcting each path by flipping its bits, though that makes me wonder why I’m using the canvas in the first place. I will exhaust other options first.

UPDATE: Worked out a nice solution. In the path drawing loop, I draw a filled circle with a heavier alpha (0.25) and a radius of 2 every time the distance between the current (x, y) and the last time I drew my circle exceeds 1. It looks like this.

dx = lx - x;
dy = ly - y;
d = Math.sqrt(dx * dx + dy * dy);
if (d > 1) {
	f = map.context.fillStyle;
	map.context.fillStyle = "rgba(255, 0, 0, 0.25)";
	map.context.beginPath();
	map.context.arc(x, y, 2, 0, SOAR.PIMUL2, false);
	map.context.fill();
	map.context.fillStyle = f;
	lx = x;
	ly = y;
}

Stick that in just after the start of the do..while loop. It insures that the path between points is always open just enough to squeeze through.