PDA

View Full Version : Developing a RL


pndrev
January 25, 2009, 15:24
Ok, I suppose technically it would fit better on r.g.a.*, but I don't feel comfortable with such a large audience yet. ;)

All the threads here about variant development have me inspired to start work on my own roguelike!
Nothing too detailed is fixed yet, as I want the basic engine up and running before really presenting anything.


I'm roughly following the 15 steps from the roguebasin wiki, as a guide for milestones. So far I got a scrolling map, the base classes for actors, items, terrain and effects (already used for testing purposes).

Time is implemented through firing heartbeat events in the main loop, received by all objects on the map. Actions cost energy and receiving a heartbeat restores energy or calls the action routines. The only difference so far between PC and NPCs is that if a PC has its 'currently active' flag set, the heartbeat stops until the PC is no longer active (energy below the action threshold) - effectively waiting for keyboard input.

The message system is event-driven as well, which is especially nice for debugging, as I can just listen to all messages directly in the game window.

Display is traditional ASCII (tiles shouldn't be a problem, as the representation is the responsibility of the object, the display class just draws what it is told), although I had to implement my own double-buffering to avoid flicker.

Not too bad for a couple of evenings, although I expect that progress will get slower once I get to data files, inventory management and effects. Not to mention map generation beyond the current completely random fill. Truth be told, I dread map generation. I'm tending towards a tree based approach - dividing the map into parts, dividing the parts again... then placing a room in each part and traversing the tree, connecting the parts. So far, that is the only approach that I could wrap my head around when it comes to thinking about the actual implementation.


But next up is the menu system - I'm still not decided if I should display it in a separate area or in the message panel. Right now, I just want the basic 'Really quit? y/n' message to work. Any further menus should then be a matter of inheritance and responding to inputs.

Wish me luck. ;)

Elsairon
January 25, 2009, 15:34
In my experience, r.g.r.d will welcome you with open arms, generally after you have some semi-working something released. I'm currently in the item/effect stage myself, in the middle of a long few month hiatus from coding.

Best Wishes and Luck (aka perseverance) ;)

pndrev
January 29, 2009, 00:13
Got menus displaying and properly catching input. I also found the reason for the map scrolling not working 100% as expected, but it was more the fault of 'just put a hack in so at least something gets drawn'.

Next up is correcting that mistake by implementing movement routines where they belong, the map / actor objects instead of the game loop. That will probably optimise the drawing and refreshing routines of the display as well, eliminating that last annoying flicker that just occasionally happens.

FWIW, up to now, I had the display store an array of Glyph (which store a character and it's colour). To determine which elements need drawing, it went through the objects in the map and recorded a second 'delta' array. That was the one being drawn, then the full map gets updated from the delta (only really needed for fast, full refresh).

That works, but if I put the 'move' routines with the actors (and items, effects), I can fire a simple 'has moved' event that causes the delta to be written for exactly that object, instead of polling all available objects for new positions whenever the player presses a key.

Side-effect: Depending on the actual implementation (exactly when to fire that event and if updating the delta automatically draws it as well), I might get animation for free. And since I only have to update two tiles (the old position and the new one), it should be reasonably fast as well.


So, next up is:
- Implement real movement and event-based screen updates.
- Start working on an explorable map as opposed to everything being visible.

After that, I'll see. Probably basic items and inventory. And working out a couple of magic numbers found purely by guesswork and approximation that are currently used in the screen drawing routine. :D

pndrev
January 29, 2009, 21:30
Fixed the remaining flicker of the screen, because a) it was annoying as hell when debugging and b) if I couldn't even get a simple '@' running around without intermittent flickering, I might as well give up and stick to the apps I'm coding at work.

Of course, this also meant implementing the events for screen updates from actors instead of a general 'lets see if we can spot the difference'. (which, btw. wasn't the cause of the flicker...). Which fortunately was one of the rather high-prio tasks in my todo list. :)

I'll sideline the explorable map for a while longer - theoretically it would use the same event system to toggle 'explored' and 'seen' flags on the terrain. But knowing myself, once I start, I'll head down the lane towards LOS, and THAT I'm not ready to tackle yet. Working on structuring the player-input handling routines so they can bump into things, that seems currently much more tempting.

Although... A basic floodfill with a range-from-origin limit might work as a first approximation? I guess that's how lanterns or light are done, anyway. Might also come in handy when checking for connectedness of rooms etc.


OT: I hope it's okay that I abuse this thread as some sort of running commentary? I could move it to a website or blog, it's just that I sometimes start seeing solutions once I type out my thoughts about the problem. And of course, I count on someone yelling 'stop!' when I'm about to do something extraordinarily stupid.
Other than trying to write a RL while already developing software as a dayjob. ;)

pndrev
February 8, 2009, 15:31
Finally got around to continue working on this.

Implemented a basic inventory - picking up, dropping and using items. In the course of that I refined the menu system (for updating menu text while the menu is displayed) and tweaked the basic message system (so everything gets send through the same channels instead of having a few special cases).

A very pleasant surprise was how much of picking up and dropping already was supported in my map code. I just needed to plug in a new list for items, and everything from there went automatically. Three cheers for OOD!


I also decided on a theme for the game. So I'm currently having two different brainstorming lists - one with everything that somehow might fit the theme and one with what I think might get implemented in this first version. When I get the next step done, basic stats, I'll take a break and talk about the theme and scope. From what I have so far it will definitely be a coffee-break RL, but I try to design everything with a possible expansion in mind.

For stats, so far I'm toying with the idea of borrowing Fallouts SPECIAL system. Which would fit in nicely with the theme idea. :D

pndrev
July 17, 2009, 15:44
Good god. I knew I would have a shortage of time and motivation, but seriously...

Anyway, I started tackling the map generation, not because it is the most pressing issue, but because I was interested in finding a simple, easy algorithm. That's probably the best way to keep making progress - find interesting things to do.

So here is the basic skeleton of the code. I'd appreciate some input if it really might work. A few 'magic' bits are in the comments, but what concerns me is the recursion for the division of rooms into smaller ones.

A Map class that starts it all

public class Map
{
private ArrayList _world;
public ArrayList World
{
get { return _world; }
}

public Map()
{
Room main = new Room(0, generateDepth, 0, 0, screenWidth, screenHeight);
_world = main.Build();
}
}


The Room class with the recursive algorithm.

public class Room
{
// some collections to store information
private ArrayList _rooms;
private ArrayList _corridors;
private ArrayList _floor;

public Room(int iteration, int maxiteration, int x, int y, int w, int h)
{
_rooms = new ArrayList();
_corridors = new ArrayList();

// only generate to a certain depth
if (iteration < maxiteration)
{
// recurse into more rooms
Divide(iteration, maxiteration);
// generate main corridor between subrooms
Connect(_rooms);
// connect main corridor to subrooms proper
foreach(Room r in _rooms)
{
r.ConnectWorld(_corridors[0]);
}
}
else
{
// flood fill _floor from x, y, width, height
_floor = BuildRoom(x, y, w, h);
}
}

private ArrayList BuildRoom(int x, int y, int w, int h)
{
// ...magic happens...
return room;
}

public ArrayList Build()
{
if (_rooms.Count == 0)
{
// end of recursion, no subrooms and corridors exist
return _floor;
}
else
{
// recurse through subrooms
ArrayList subfloors = new ArrayList();
foreach(Room r in _rooms)
{
subfloors.AddRange(r.Build());
}

// add corridors
foreach(Corridor c in _corridors)
{
subfloors.AddRange(c.Floor);
}

return subfloors;
}
}

private void Divide(int iteration, int maxiteration)
{
iteration++;

// randomly choose new x, y, width, height (fitting within current room, not overlapping)
Room sub1 = new Room(iteration, maxiteration, x1, y1, w1, h1);
Room sub2 = new Room(iteration, maxiteration, x2, y2, w2, h2);

// store subrooms
_rooms.Add(sub1);
_rooms.Add(sub2);
}

private void Connect(ArrayList roomlist)
{
// connect each rooom to the next one, without looping back
foreach (Room r in roomlist)
{
if (roomlist.IndexOf(r) < (roomlist.Count - 1))
{
Connect(r, roomlist[roomlist.IndexOf(r) + 1]);
}
}
}

private void Connect(Room r1, Room r2)
{
// connect subrooms
Corridor cor = new Corridor(r1, r2);
_corridors.Add(cor);
}

public void ConnectWorld(Corridor corx)
{
// connect incoming corridor from parent room to each subroom
foreach(Room r in _rooms)
{
Corridor cor = new Corridor(corx, r);
_corridors.Add(cor);
}
}
}


The Corridor for connection rooms, with some magic bits for finding the path.

public class Corridor
{
private ArrayList _floor;

// complex constructors omitted, basically all should lead to the same result
// connect two rooms using the shortest direct path
public Corridor(Room r1, Room r2)
// connect a room to a corridor using the shortest direct path
public Corridor(Corridor c1, Room r1)
// connect two coordinate using the shortest direct path
public Corridor(int x1, int y1, int x2, int y2)
{
// find most direct way between the starting points and fill _floor
_floor = FindPath(x1, y1, x2, y2);
}

public ArrayList Floor
{
get { return _floor; }
}

private ArrayList FindPath(int x1, int y1, int x2, int y2)
{
// ...magic happens...
return path;
}
}


Working through the code on paper seems like it actually results in a useable map. But if anybody has experience with a similar "divide rooms into smaller ones and connect" generation, I'd really appreciate comments. ;)

Nolendil
July 17, 2009, 18:01
I haven't read your code but your description ( "divide rooms into smaller ones and connect" ) makes me think about that method :
http://roguebasin.roguelikedevelopment.org/index.php?title=Basic_BSP_Dungeon_generation

I'm thinking about coding a RL with a friend but we're not satisfied with any algorithm so far. We're still searching.

pndrev
July 17, 2009, 21:20
Yep, that was basically the idea. It's the simplest algorithm I can think of, short of randomly placing stuff.

Nolendil
July 18, 2009, 08:21
The problem I have with this algorithm is that I feel the variety of dungeons produced is not wide enough for my taste.
However, it seems perfectly functional and produces levels where all rooms are guaranteed to be connected.
You can start with that anyway, it's not hard to code and you can change it later if you're not satisfied.

Sirridan
July 18, 2009, 10:06
The problem I have with this algorithm is that I feel the variety of dungeons produced is not wide enough for my taste.
However, it seems perfectly functional and produces levels where all rooms are guaranteed to be connected.
You can start with that anyway, it's not hard to code and you can change it later if you're not satisfied.

That's the one I'm going to use myself. Right now the dungeon I use is pre-generated. Once I have item code, and monster code and combat done, then I can move on to dungeons.

I'll probably beg people to test mine... and I'll gladly test anyone elses!

RogerN
July 20, 2009, 16:34
Looks like you're using C#? You might be interested in some of my own musings on dungeon generation here (http://angband.oook.cz/forum/showthread.php?t=927). There's a screenshot of the output in the first post, and the C# source code is available near the bottom of the thread.

pndrev
July 20, 2009, 17:32
Yes, C#, as it's the language I work with every day.

I read your posts back when the thread was started, but it's always worth it going over old material again.

To counter the similarity a pure BSP approach would bring, I'll introduce a random element (current recursion depth / maximum recursion depth) to stop room generation at varying points.
The rooms themselves will (later) also be replaced by other algorithms, i.e. cave, vault, mine, graveyard... - probably via some Factory / Interface pattern, so the basic code remains the same.

Cipher
November 20, 2009, 21:45
I'm also comtemplating writing a C# version of Rogue or Angband but thought it would be better to work on a joint venture - therefore I'm asking if anyone out there fancies having an experienced developer working on a project of this nature. I've worked with 1.1, 2.0 and 3.5 and have a number of ideas for the creative design.

How about a futuristic setting for the rogue venture?
Spaceships / mines / cities instead of dungeons and alien races instead of mythical creatures. Multiplayer element like MAngband would be good too.

Drop me an email if you are interested.

Magnate
November 21, 2009, 14:00
How about a futuristic setting for the rogue venture?
Spaceships / mines / cities instead of dungeons and alien races instead of mythical creatures. Have a look at Prospector if you're going down this route ...

Bodkin
November 21, 2009, 17:33
Heh. When I saw the thread title, I assumed we were talking about "Developing a Real Life." Maybe someone can explain how to code that. . .

Cipher
November 21, 2009, 19:08
Thanks for the advice. I will check out Prospector but a link would be appreciated? I just really want to get involved in a C# game - there seems to be the general attitude that C# is not good enough for games because C++ is better for managed I/O ports and other processes that are used in game development. However the fact that processors are getting faster all the time means that we don't need to solely stick with C++ if we think long term.

Magnate
November 21, 2009, 19:43
Thanks for the advice. I will check out Prospector but a link would be appreciated? I just really want to get involved in a C# game - there seems to be the general attitude that C# is not good enough for games because C++ is better for managed I/O ports and other processes that are used in game development. However the fact that processors are getting faster all the time means that we don't need to solely stick with C++ if we think long term.http://code.google.com/p/rlprospector/

I mentioned it because it's a roguelike set in space - I don't think it's in C#.

Cipher
November 21, 2009, 21:03
Many thanks for the post and the help!