Sunday, September 29, 2013

Vision spells



I know we haven't even dived (dove? doven? diven?) into line of sight yet,  but affecting vision could be a very useful things for mages to do that's not combat or healing/buffing.

-Increase range of vision
-Reveal stealthed enemies
-See through an obstacle? Eh, that one sounds complicated.

The Gorgon Menace is serious business, guys.


Different style & a bit darker than our established tone, but having some fun. Also, totally picking up some "Left 4 Dead witch" vibes.

I should like do some UI or something.

Working through some thoughts

Fair warning: this post is boring.

I feel like legacy is four or five projects at once, which is good and bad. Good because they are the four or five things I really like, and bad because it's hard to focus and it can be overwhelming.

1. 2.5D GPU graphics
2. Rapid iteration game development tools
3. Story driven algorithmic gameplay generation
4. Client/Server game architecture
5. Career development, learning, growth beyond flash ui work
6. Fun gameplay and an engaging experience

Each of those categories represents a deep well, and the problem is that working on one delays the others in some ways. I'm building the tools, engine, and game all at once, while simultaneously trying to pick a technology to invest in for my own career, while also learning about the current state of 3d graphics. The combination paralyzes my ability to make decisions and drive forward, but it's hard to let go of because all of these things are so appealing to me right now.

I've made a few choices with Legacy specifically, in order to minimize this pain. I've decided to stick to flash for this project, at least until I have something that I want to share/publish. That takes a lot of the tech angst off the table, at least, as long as I can hold to that decision.

Next I've worked to strictly prioritize the work for legacy in order to make the game itself fun. So, I dip into each of the other wells only when they're next in line. Separating the prioritization step from the 'do work' step is a really powerful cognitive tool, I guess. I see this professionally as well. If I don't have to worry about the decision while I'm doing the work, the work goes MUCH faster and better.

Now, to the specific situation at hand. My next goal is to get puppetLab animations working inside Legacy, so that I can animate attacking and taking damage, so that when I add AI and people will actually know what's happening, so that the game will be fun. This work (getting the animations in) involves breaking apart a lot of my early work on drawing figures in the game, and replacing that work with my newer work that more accurately reflects where I want to go with my 2.5D graphics engine and tools.

That means rebuilding the data for how heroes and monsters are rendered, and that poses a couple of interesting problems. The way Legacy currently handles humans is pretty complicated and very closely associated with the idea of humans wearing gear. Those concepts don't fit into a generic graphics toolkit. So I can try to generalize and abstract them into something I can bring into the toolkit, or, I could push them into the game layer and have to toolkit expose hooks that the game uses to define that stuff.

In order to make a good decision here, I need to wrap my head around the future of the game and the future of the toolkit. For example if I only ever want to do this with humans and gear, then it's fine for that capability to live in the game. But if this composition capability is something that could be useful in all sorts of other places (drauven, your base, richer sites and monsters), or if the toolkit finds this capability interesting (layering, photoshop importing, direct support for building composite textures either in the gpu or as a preprocess step..), then I should think more about how to make it generic.

I've decided not to hurry. I have my next priority, but I want to get there in a way that I find interesting and satisfying. So I'll spend a little time researching the toolkit angle and thinking about what a data-driven layer composition system looks like. There are a bunch of choices there too.. if you don't constrain yourself to a single texture for the figure, the possibility space blows up very quickly. In some really interesting ways.

Saturday, September 28, 2013

PuppetLab is fun

PuppetLab 2013 just showed up in the Legacy dropbox.

It can do a few cool things, like animation.
Above is monster3.plc, which you'll find in the "demo" folder. It has a few different poses and can animate between them. Try dragging the vertices around. You can always reload if you get it in a bad place.

For now you can't do much from the UI, I do about half the editing with a text editor on the file itself. So it's really ready for prime time yet. But as an experiment I think it's pretty interesting.

I've been thinking a lot lately about how 2D animation wants to work. There are some really cool things you can do with vertex and pixel shaders, that traditional animation tools don't give you access to. On the other hand you still want your animation tool to support grouping and rigid-body transforms (which mine does not yet.)

Somewhere in our collective heads there's a tool that gives you the power you want over the gpu without making you understand it, and that allows you to do the basics really easily too.

Next up for legacy though is just getting these animations into the game, and then marching forward on the fun combat backlog.

Site Generation

I did a bunch of work on site generation that I wanted to share.

My thesis here is that a small set of random-number based heuristic techniques is actually a much shorter and more sustainable path to good site maps, than fine-tuning a more general purpose algorithm (e.g. advanced perlin noise, geologic simulation) would be.

Put another way, rather than try to solve board generation from first principles or in frequency space or in any other way that I find hard to think about, instead I will focus on building a set of operations that I understand in the problem domain. Things like, roll every square on or off.
Erode terrain.
 Grow terrain (dilate).
 Draw random lines that represent tunnels.
Make sure the spawn area is filled in.
 Trim corners.
 Flood fill to eliminate islands.
So, you get there through a series of simple steps, that you can understand and tweak individually in order to make really different maps. Here's what the script looks like in code:
var actions:Vector. = new Vector.();
var noise:NoiseAction = new NoiseAction(_map);
noise.minX = 1;
noise.minY = 1;
noise.maxX = _map.width - 2;
noise.maxY = _map.height - 2;
noise.chanceToFill = 0.5;
actions.push(noise);

var erode:GlobAction = new GlobAction(_map);
erode.filter = [true, false];
erode.fractionOfTotalSquaresToGlob = 1.0;
actions.push(erode);

var dilate:GlobAction = new GlobAction(_map);
dilate.filter = [false, true];
dilate.fractionOfTotalSquaresToGlob = 2.0;
actions.push(dilate);

var tunnel:Tunnel = new Tunnel(_map);
actions.push(tunnel);

var spawnRect:Rectangle = new Rectangle(1, 1, 4, 3);

var spawnArea:SetRectangleAction = new SetRectangleAction(_map);
spawnArea.minX = spawnRect.x;
spawnArea.maxX = spawnRect.right;
spawnArea.minY = spawnRect.y;
spawnArea.maxY = spawnRect.bottom;
actions.push(spawnArea);

var trim:TrimCornerGaps = new TrimCornerGaps(_map);
actions.push(trim);

var floodFill:FloodFillMap= new FloodFillMap(_map, 3, 3);
actions.push(floodFill);

var floorFixer:FloorTileRectifier = new FloorTileRectifier(_map);
actions.push(floorFixer);

var lights:PlaceLights = new PlaceLights(_map);
actions.push(lights);

var sequence:AQueueAction = new AQueueAction(actions);
sequence.getCompleted().add(onTilesRendered);
sequence.invoke();
So if you take a look you can see each step is just an action added to a sequence. The individual actions make sense and are individually configurable. Here's one that makes forest maps:

var actions:Vector. = new Vector.();
var noise:NoiseAction = new NoiseAction(_map);
noise.minX = 1;
noise.minY = 1;
noise.maxX = _map.width - 2;
noise.maxY = _map.height - 2;
noise.chanceToFill = 0.98;
actions.push(noise);


var erode:GlobAction = new GlobAction(_map);
erode.filter = [true, false];
erode.fractionOfTotalSquaresToGlob = 1.5;
actions.push(erode);

var dilate:GlobAction = new GlobAction(_map);
dilate.filter = [false, true];
dilate.fractionOfTotalSquaresToGlob = 1.0;
actions.push(dilate);

var spawnRect:Rectangle = new Rectangle(1, 1, 4, 3);

var spawnArea:SetRectangleAction = new SetRectangleAction(_map);
spawnArea.minX = spawnRect.x;
spawnArea.maxX = spawnRect.right;
spawnArea.minY = spawnRect.y;
spawnArea.maxY = spawnRect.bottom;
actions.push(spawnArea);

var bigTree:TreesAction = new TreesAction(_map);
bigTree.safeAreas.push(spawnRect);
bigTree.minX = 5;
bigTree.width = _map.width - 10;
bigTree.minY = 5;
bigTree.height = _map.height - 10;
bigTree.minTreeSize = 6;
bigTree.maxTreeSize = 9;
bigTree.density = 1.0;
bigTree.maxTreesOverride = 1;
actions.push(bigTree);

var trees:TreesAction = new TreesAction(_map);
trees.minTreeSize = 1.5;
trees.maxTreeSize = 3.0;
trees.density = 0.55;
trees.safeAreas.push(spawnRect);
actions.push(trees);

var trim:TrimCornerGaps = new TrimCornerGaps(_map);
actions.push(trim);

var floodFill:FloodFillMap= new FloodFillMap(_map, 3, 3);
actions.push(floodFill);

var floorFixer:FloorTileRectifier = new FloorTileRectifier(_map);
actions.push(floorFixer);

var lights:PlaceLights = new PlaceLights(_map);
actions.push(lights);

var sequence:AQueueAction = new AQueueAction(actions);
sequence.getCompleted().add(onTilesRendered);
sequence.invoke();
And it's pretty!
Well, the tree scenery needs some work. But as a map this will be fun to play on, and now we have infinite variety.

There are some holes to fill here. For one thing we should be using a repeatable pseudo-random number generator, so that we can iterate better, and so that we can regenerate the same map given a seed. That could be really valuable. For another thing we need to do some validation to make sure that the map we generate is actually playable.

But I think this is a good proof of concept and it's already fun to play with and on.