Angband Forums

Angband Forums (http://angband.oook.cz/forum/index.php)
-   Idle chatter (http://angband.oook.cz/forum/forumdisplay.php?f=6)
-   -   Developing a RL (http://angband.oook.cz/forum/showthread.php?t=1372)

pndrev January 25, 2009 15:24

Developing a RL
 
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
Code:

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.
Code:

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.
Code:

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.roguelikedevelopme...eon_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

Quote:

Originally Posted by Nolendil (Post 21779)
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!


All times are GMT +1. The time now is 02:35.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.