Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Bear

Pages: [1] 2
1
Hi.

I know I've been gone for a while, but I wanted to drop by and ask:  Have the authors of any published roguelike games found my 'Roguelike AI' series of articles useful?  And if so, would you mind if I put the names of your games somewhere on the articles, or on my webpages?





2
Programming / Neohack progress report - I'm being chased by wolves!
« on: September 29, 2014, 05:37:02 PM »
Module interface is now debugged and working.  New creatures (and by extension items, tools, supplies, treasure, etc) can now be added without touching any of the 'engine' code.

Full blog post at:
http://dillingers.com/blog/2014/09/29/i-have-never-been-so-happy-to-be-chased-by-wolves/

Excerpt:
Quote
So Action_Wolf_INIT does the following things;  First, it defines an attribute stack for wolves.  Neohack has generic actors, whose actions, interactions, and capabilities are all defined by attributes.  An actor can have any number of attributes.  The attributes that define wolves give them things like a speed, a reference to the AI routine they use, a map representation (the brown 'w'), a size and mass for modeling in-game interactions, and so on.  Then it adds the attribute stack, with the name 'wolf' and the additional information that it is a 'species' kind, to the kinds table, so from now on that 'kind' will refer to a species having that attribute stack.   Then it creates an event (using the predefined event routine 'Action_Make_Critter') which will make two wolves, and puts that event into the 'Natural Animals' generation table, with an oddment, in this case, of 1000, which corresponds to a probility of 1000 out of however many oddments the Natural Animals table has when everything has eventually been added to it.  And that is absolutely everything that Action_Wolf_INIT needs to do.

The map generator gets called after all the INIT events have run.  It calls the 'Natural Animals' table to get an event, makes a copy of the event, adds a 'to location' argument to that copy so that the created critters will appear in a definite location on the map, and then puts the copy into the schedule as a new event.

3
So:  I put up a basic homepage/announcement page for neohack.

http://dillingers.com/blog/neohack/

It basically talks about the design goals and does some simple intro material. 

In the future, all updates about Neohack will go there. When there's a release, it'll be available from there. 

And yes, I'm now mentioning an actual timeframe for release.  For, approximately, the first time.

4
Design / Copied content?
« on: September 12, 2014, 08:32:55 PM »

Do people have opinions about reusing content from other games?

In Nethack, for example, there is the "Rogue level", which copies many features from the earlier game Rogue, as a sort of tribute/acknowledgement. 

But what if it goes much further than that?  What if an implementation provided its own controls and interface, but then just flatly read in the content from the source files of different games, such as the monster/item definitions from bands, the monster.h file from Crawl, and so on?  Most of these are written in very regular languages that would be easy to make parsers for, and implement a translator that constructed monsters with similar names, console letter+color representation, abilities, etc. 

At what point does it cease to be tribute and start being outright theft?

Or is it a neat idea that a player could get into the content of different games without actually climbing the learning curve of a different U once he's invested the effort of learning a complicated keyboard-driven UI?

5
Programming / Slow progress is slow.
« on: August 28, 2014, 12:26:04 AM »

I've been working on and off (mostly off) on the roguelike game for years. 

First attempt:  Made an architectural mistake because I was playing and experimenting instead of properly thinking about how to organize a major project.  It got harder and harder to make progress, and I worked on it more and more seldom because it was frustrating. Finally I realized that I'd made a fundamental error of design that made reliable memory management impossible (yes, I'm using a non-GC language - on purpose).  Threw it out.  Over  a year later, I started again.

Second attempt: Excessively simplified design didn't allow me to do things much more interesting than early angband variants were already doing them.  I worked on it less and less because It got boring.  Eventually decided that if completed it wouldn't represent an improvement over existing games and set it aside.   Then I got sucked into a full-time job for several years, did nothing on roguelike front.

Third attempt:  Excessively baroque design made content too hard to manage.  Got the whole thing written, but realized at about the 10th complete monster definition that I had created a system in which I'd just spent six weeks on Goblin Villages, mostly creating things and interactions that players would never see or care about.  And that the baroque features allowed monster types individually valid, to be incompatible with each other.  Thought about that for a while.  Vaguely intended to improve it but spent a very long time for the right idea about how to fix it to strike, and it never struck.   Eventually I guess I  stopped thinking about it.  A year or two later the hard drive it was on crashed, and I realized I didn't have a backup of the code.  But considering everything, maybe that was for the best.  Started over completely from scratch (well, not completely; I did reuse my line-of-sight code, map representation, and a few other things).

Fourth attempt: Now happy with my architecture.  Working very carefully.  Design draws heavily on the "learning experiences" of the past.  Everything I want to do can be done.  Everything that can be done can be done without introducing conflicts.  I built a good really good plugin code interface; just drop the source file into the plugin directory and recompile; you never need to touch any existing code, worry about conflicts, or manage diffs or patches.  Built a sort of "engine without content", then took a couple years off after growing completely disgusted with someone in the community and what I saw as a general breakdown in civility and discussion.  But eventually I dusted it off and continued because, well, becoming disgusted with one antisocial idiot and the deaths of a few forums shouldn't be something that stops me from doing something that is fun in itself. 

Now finally I'm back at the point of starting to add content, a lot of it is 'category' content (poison, f'rex) that is as much an addition to the engine as to the game, and will be eventually depended on by other content (venomous snakes, poison arrows, potions etc).  Other content is 'ubiquitous' content like, say, doors.  They have to be done, and they have their own complexities, and they're their own category, and they're basic enough to be considered part of the engine - but they're not 'category' content that other things are going to depend on.

Today I added the first (to this architecture) normal monster.  It's a little tricky, because this monster (which happens to be a wolf) is defined in a plugin.  Plugins get called at game startup and essentially work by adding entries to tables and registering callbacks.  So, a landmark of sorts.

There is a little bit of 'baroque' going on with the architecture - in a way that results from making things very general.  The whole architecture comes down to just a few kinds of thing, Events, Actors, Properties, and Triggers.  First there are 'events.'  Any thing which causes a change in the dungeon is an 'event'.  And an 'event' can add more events to the schedule.  The entire game consists of loading the Game-Start event into the schedule, then repeating get an event, do the event, until the schedule is empty. 

All creatures, items, terrain features other than floor and walls, and even the player character, are 'Actors'.  Most events affect Actors.  'Actors' have an unbounded table of 'Properties' that describe them.  Among these 'Properties' are a few special ones called 'Triggers' which are checked before and after the actor is affected by an event.  If there's a trigger that responds to that event (and that actor's role in the event), it results in code being executed. 'Trigger' code can schedule new events, modify scheduled events, or set properties, etc.

Each event can set off triggers, and triggers can schedule further events. These triggers can be set arbitrarily on the actors that an event affects.  For example, when someone puts on blink boots, that's an event.  The blink boots have a trigger that schedules an immediate action when they are the object of a 'wear' action.  The action they schedule sets a trigger on the wearer which will go off anytime the wearer is the subject of a 'walk' action.  Now whenever the wearer walks, another action which teleports him a short distance in the same direction is scheduled immediately after. 

Adding content given this setup is -- tedious, but not because of irreducable non-automatable complexity.   Much nicer than last time, anyway.  A few convenience functions and so forth will streamline it a lot; right now I'm still at the stage of figuring out what the repetitive tasks I need to automate are. 

So anyway; progress is being made.  It's just slow.  It seems hard to me to understand how quickly others are working; I feel like I spend a week or two fairly frequently just waiting for inspiration about what the RIGHT way to do something is. 

6
Programming / The Random Number God: With Code in C.
« on: August 18, 2014, 05:08:41 PM »

New Blog post at http://dillingers.com/blog/2014/08/17/the-random-number-god/

It is:

A brief discussion of the role of PRNG's in game systems and why you'd want to code one instead of just assuming that your dev environment has one that's good enough (or that all your different dev environments are consistent enough in their choices).

A review of a bunch of system PRNG's (mostly bad ones)

A bunch of other PRNG's (mostly better ones that are just as easy to code)

And one fully coded library for random numbers in C, using a lagged-fibonacci generator with 99 32-bit words of state and maximal period.  All of these routines except the first four are easy to adapt to some other kind of PRNG. 

RNG_new  allocates and initializes a new pseudorandom generator. 

RNG_randomize  drinks randomness from /dev/urandom to initialize the generator.

RNG_Tweak  takes an integer argument and uses it to transform the state of an RNG so that the RNG produces a different sequence from that point onward.

RNG_Rand  is the fundamental building block; it uses the PRNG to produce a random number between zero (inclusive) and RNG_MAX (exclusive).

RNG_roll produces a roll uniformly distributed between 0 inclusive and some integer argument exclusive.

RNG_dieroll produces a roll uniformly distributed between some minimum inclusive and some maximum exclusive.

RNG_diesum produces a sum of dierolls, of a sort familiar and intuitive to lots of RPG gamers.

RNG_Uniform produces a double uniformly distributed in the range between a minimum argument and a maximum argument.

RNG_Gaussian takes a mean and standard deviation as a range and produces a double having a normal distribution (aka, a Gaussian distribution).

RNG_LimGaussian takes a mean, standard deviation, and a limit, and produces a double with a gaussian distribution but excludes results more than <limit> standard deviations away from the mean.

RNG_Contest takes two integer values and a dominance factor, then makes a random determination which of them should be regarded as “winning” in a given instance of a contest.  The bigger one is more likely to win the higher the dominance factor is.  It’s for things like hit rolls, strength contests, etc.

As usual for blog-published code, it's free for anybody to use.

7
Today's blog post at http://dillingers.com/blog/ is actually a cleaned-up and extended version of an old article I wrote on RGRD, which has also been picked up on roguebasin.  In hopes that it is helpful, I'm going to paste/post it here as well.
Quote
The version of the popular ncurses library that handles wide characters, or Unicode, is surprisingly hard to get working correctly with C programs. This article is intended to be a checklist for developers so that they can effectively use the library. This is material I learned in programming a roguelike game, but it’s useful to everybody who wants to use ncurses with a full unicode repertoire.

As with most development articles, this will be a bit too specific in terms of platform. This article was written with respect to a Linux development platform running Debian Linux. To the extent that your platform is different, there are likely to be important things I don’t know about getting development on your platform working with this library.

First, you have to be using a UTF-8 locale (Mine is en_US.UTF-8; I imagine others will have different choices). Type ‘locale’ at a shell prompt to be sure.

Second, you have to have a term program that can display non-ASCII characters. Most of them can handle that these days, but there are still a few holdouts. rxvt-unicode and konsole, popular term programs on Linux, are both good.

Third, you have to use a console font which contains glyphs for the non-ASCII characters that you use. Again, most default console fonts can handle that these days, but it’s still another gotcha, and if you routinely pick some random blambot font to use on the console you’re likely to miss out.

Try typing a non-ASCII character at the console prompt just to make sure you see it. If you don’t know how to type non-ASCII characters from the keyboard, that’s beyond the scope of what’s covered here and you’ll need to go and read some documentation and possibly set some keyboard preferences. Anyway, if you see it, then you’ve got the first, second, and third things covered.

Fourth, you have to have ncurses configured to deal with wide characters. For most linux distributions, that means: Your ncurses distribution is based on version 5.4 or later (mine is 5.9) but NOT on version 11. I have no idea where version 11 came from, but it’s definitely a fork based on a pre-5.4 ncurses version, and hasn’t got the Unicode extensions. Also, you must have the ‘ncursesw’ versions, which are configured and compiled for wide characters.

How this works depends on your distribution, but for Debian, you have to get both the ‘ncursesw’ package to run ncurses programs that use wide characters and the ‘ncursesw-dev’ package to compile them. The current versions are ncursesw5 and ncursesw5-dev.

But there’s an apparent packaging mistake where the wide-character dev package, ncursesw-dev, does not contain any documentation for the wide-character functions. If you want the man pages for the wide-character curses functions, you must also install ncurses-dev, which comes with a “wrong” version of ncurses that doesn’t have the wide-character functions. Don’t think too much about why anyone would do this; you’ll only break your head. The short version of the story is that you pretty much have to install ncurses, ncurses-dev, ncursesw, and ncursesw-dev, all at the same time, and then just be very very careful about not ever using the library versions that don’t actually have the wide character functions in them.

Fifth, your program has to call “setlocale” immediately after it starts up, before it starts curses or does any I/O. If it doesn’t call setlocale, your program will remain in the ‘C’ locale, which assumes that the terminal cannot display any characters outside the ASCII set. If you do any input or output, or start curses before calling setlocale, you will force your runtime to commit to some settings before it knows the locale, and then setlocale when you do call it won’t have all of the desired effects. Your program is likely to print ASCII transliterations for characters outside the ASCII range if this happens.

Sixth, you have to #define _XOPEN_SOURCE_EXTENDED in your source before any library #include statements. The wide character curses functions are part of a standard called the XOPEN standard, and preprocessing conditionals check this symbol to see whether your program expects to use that standard. If this symbol is found, and you’ve included the right headers (see item Seven) then macroexpansion will configure the headers you include to actually contain definitions for the documented wide-character functions. But it’s not just the ‘curses’ headers that depend on it; you will get bugs and linking problems with other libraries if you have this symbol defined for some includes but not others, so put it before all include statements.

Unfortunately, the XOPEN_SOURCE_EXTENDED macro is not mentioned in the man pages of many of the functions that won’t link if you don’t do it. You’d have to hunt through a bunch of not-very-obviously related ‘see also’ pages before you find one that mentions it, and then it might not be clear that it relates to the function you were interested in. Trust me, it does. Without this macro, you can use the right headers and still find that there are no wide-curses definitions in them to link to.

Seventh, you have to include the right header file rather than the one the documentation tells you to include. This isn’t a joke. The man page tells you that you have to include “curses.h” to get any of the wide-character functions working, but the header that actually contains the wide-character function definitions is “ncursesw/curses.h“. I hope this gets fixed soon but it’s been this way for several years so some idiot may think this isn’t a bug.

Eighth, you have to use the -lncursesw compiler option (as opposed to the -lncurses option) when you’re linking your executable.  Earlier versions of gcc contained a bug that -WError and -WAll would cause linking to fail on the ncursesw library, but this appears to have been fixed.

Ninth, use the wide-character versions of everything, not just a few things. This is harder than it ought to be, because the library doesn’t issue link warnings warn you about mixing functionality, and the documentation doesn’t specifically say which of the things it recommends won’t work correctly with wide characters. That means cchar_t rather than chtype, wide video attributes rather than standard video attributes, and setcchar rather than OR to combine attributes with character information.

Use cchar_t rather than chtype. cchar_t is a record type that contains colorpair information, video attributes, and a short unicode string. The only thing about this unicode string that affects your display is the first spacing character, which must also be the first character. So the rest of the string is pretty useless until someone implements a term program that handles unicode combining characters, but you still have to build a null-terminated unicode string to make a cchar_t.

Use the new WA_* video attributes rather than the older A_* video attributes. That is, WA_STANDOUT rather than A_STANDOUT, WA_UNDERLINE rather than A_UNDERLINE, and so on. The WA_* attributes are of the newly defined attr_t type and have their bits aligned correctly for using in cchar_t rather than chtype. On my platform, attr_t is an unsigned long int. If you have code that casts video attributes to or from int or short int, it will fail with wide video attributes.

Use get_wch rather than getch to get input from the keyboard. If the keyboard driver delivers unicode characters, you want the whole character rather than just the last 8 bits of it, right?

Use setcchar to combine character, wide video attributes and colorpair number together into a cchar_t. Your existing curses code probably uses logical OR. The documentation says you can use OR, but the documentation is talking about single ASCII characters, chtype and narrow attributes rather than unicode strings, cchar_t and wide attributes, and it will definitely do the wrong thing if you try to use it here. You can still use logical OR to combine wide video attributes, but don’t attempt to combine them with the character values or with narrow attributes. Note that the color pair number can be converted into a video attribute using the COLOR_PAIR(n) macro provided by ncurses, and can then be correctly combined with wide or narrow video attributes.

Now, if you jumped through all the hoops, you can compile and use an ncursesw application with support for Unicode characters.
 

8
Design / Pathfinding and Navigation.
« on: July 26, 2014, 04:15:35 PM »

In a game with large(ish) levels, autoexplore and pathfinding are very handy features.  But how ought they work?

Autoexplore is implementable as: First, find the nearest walkable known square that has a walkable unknown neighbor.  Second, plan the shortest walkable path to it and take a step on that path.  Rinse, repeat. 

That's not too much trouble, but while autoexploring by that algorithm, your guy will be making choices about which of several unknown areas to explore first, and is likely to do them in an inefficient order - leaving bits and pieces that he'll have to cross the whole level later to take a look at.   The 'obvious' fix, planning out an optimal route, uses map information that isn't revealed at the time the route is being planned.  The 'correct' fix, trying to optimize the route planned at each step based on only the information revealed by the time the step is to be taken, is Hard.

Here's another quandary.  In an autopath menu, one of your choices is 'downstairs', meaning find your way to a downstair.  If you don't know about any downstair, this would mean autoexplore until you find one, then go to it.  If you know the map of the whole level, this would mean going as directly as possible to a downstair.  Simple enough.

But there's that damnable inbetween state.  You know about a downstair (or whatever other navigation goal) that's on the far side of some unknown territory.  Should your autopath then assume that it can traverse that unknown territory, allow itself to be proven wrong, and keep rerouting as it discovers new squares it cannot traverse?  Or should it assume that it cannot traverse that unknown territory, plan a path through known walkable squares, and allow itself to be proven wrong and keep rerouting as it discovers new squares that it can traverse?  Should it warn 'cannot guarantee optimal path based on current knowledge of map' and allow the user to opt out?   

The first way could reveal more unexplored territory and wind up being a lot faster ... but then again, it could expose your character to more unknown risks and wind up being a "longer shortcut."  The second way minimizes unknown risks and puts an upperbound on route time, but might take a lot longer than necessary.  Either way might incidentally reveal a new downstair not previously known and if so, should divert pathfinding to the new target. 

Ultimately, these are all character decisions, and should be options the player can set in some kind of autoexplore/autopath submenu.  Do they matter enough for players to care about?  Would they matter enough if there were a 'speedrunner' subgame with a competition among players to produce runthroughs of given scenarios minimizing turncount?


9
Design / Identification subgame
« on: July 15, 2014, 08:41:07 PM »
Which of the following, in your collective opinions, are good features for an identification subgame?  I'm trying to make something that's interesting and has moderate depth, but not punishing nor too grindy. 

I plan having no "identify" effect as such in the game at all, replacing it with means of fairly quickly learning which things are (reasonably) safe to try out and experiment with, combined with various channels that can give more specific information if you bother to use them but usually won't provide full certain identification. 

Looking through rings.  If you have a ring whose magic is of a general type, then when looking through it, you can "see the aura of a similar enchantment" and tell whether another item has the same general type of magic. For example, looking through your levitation ring should give you a positive reading for the flying broom and looking through your stealth ring should give you  positive readings on the shadow cloak and the silent boots.  This effect can be used to link ring enchantments with other item enchantments in general families, giving partial-identify information both for rings (where other items are known) and for items (where rings are known). 

Rarities  Rarities are item types or varieties that only exist in a fraction of all games, making the number of "unidentified categories" uncertain.  For example, in a game with a dozen standard types of rings, one game might have fifteen ring types (because three rarities were included) and a different game might have eighteen ring types (because six rarities were included). The point is that the player does not start the game knowing how many kinds of rings there are, nor knowing exactly what kinds of rings (beyond the basic dozen) are there, so certain kinds of "process of elimination" subgames for item ident become more interesting.

Combos One kind of rarity is the combo, an item which has essentially both the effects of two randomly-selected "standard" items.  And when the combo is included, both the standard types are eliminated.  For example, a rarity boot type combining stealth and feather-fall may be a "combo" in a given game, and standard stealthy boots and featherfalling boots would not appear in games where that combo item appeared.  So instead of a dozen standard types of boots there are only 10 standard types plus the combo and any other rarities.  The point is that in the presence of combos, the player doesn't start the game knowing for sure even which standard types of boots exist in that game.  Selecting effects to combine should be weighted against the most powerful effects, to avoid creating combos of unbalanced power too frequently.

Detect effects A number of detects can give information about items without giving full identify information.  For example a relatively easy "detect magic" effect (a "magic ring" with no particular effect, if you're allowing look through rings) can be used to tell which items are magical.  A "detect curse" effect can be used to tell which items are cursed.  Other "detect" spells might reveal which things use elemental magic, which use fire magic, and so on. 

ID by Use.  When a character puts on both an unidentified ring and some unidentified boots, and then later sets off a pit trap but floats gently to the bottom instead of falling, should the character automatically know which of the unidentified items caused floating rather than falling? When a combat-knowledgeable character like a fighter picks up an unidentified sword and uses it through a few fights, he should know very quickly what general combat bonuses it has been giving him.  But should he notice the 'dragon-slaying' magic on it if none of the things he fought with it were dragons?  A less combat-knowledgeable character like a wizard, picking up an unidentified blade and using it for a while, might take a lot longer to figure out what combat bonuses it's been giving, but would eventually figure it out because he'll notice the magic working.  So should the wizard, who is a more magic-sensitive character, notice the dragon-slaying magic lying latent in the blade after fighting a bunch of non-dragons with it? 

Item Variability  Items of the same type may differ significantly in their effect;  We're already used to seeing swords that vary in hit and damage bonuses.  What if wands also varied in their range, area of effect, number of charges possible, and damage?  What if some have a cooldown period and others don't?  What if occasionally a wand has a weird inexplicable trait like having doubled range when fired northward and half range when fired southward, or the last charge always comes out at double strength? 

Labels. Some item types might be clearly and correctly labeled.  Common things like healing potions that we can imagine being used for trade, especially.  This could provide a major advantage to characters with the literacy skill.  Of course some labels might be cryptic or miss important information.  The clearly labeled "fireball wand" might neglect to mention that it has five charges and creates a fireball sixty meters wide, within which volume the user will almost certainly be if he is so misfortunate as to use it indoors.  If I introduce mislabeling (the poison that's labeled as healing potion) I should probably introduce an effect to magically detect mislabeled items.  But if the player creates a label for something, it should call it mislabeling unless the character actually knew what it was *and* labeled it correctly. 





10
Design / Are MMORPG's games?
« on: July 05, 2014, 11:56:56 PM »

I sat down to write an article about the advancement of player power in games. 

And about halfway through, I used a recent example from a comment on this very board from a player who hated grinding for experience but did it anyway because it was "the optimal strategy" for winning a game - the point being that the game designer failed him (or her) because the way to have fun playing is not also the way to win. 

But then I realized something.  The genre where level-grinding has become most widespread and cancerous - MMORPG's (as far as I know) has no win or loss conditions.  There absolutely is no "Game over, you won," nor even a "Game over, you lost" - there is no winning or losing no matter what you do, in fact.  Nor is there even a score you can compare with other characters, unless you count experience or in-game money - which is exactly the kind of level-grinding that I had been prepared to say shouldn't be allowed to distract players from having fun or winning.

So are they even games?  Are they even a proper subject for an article about game design?  I know what a win in chess or checkers or even solitaire looks like, and I can talk about those games in terms of how a mechanic interacts with their endgame conditions - but not an MMO.


11
Off-topic (Locked) / Ergh. Medical problems.
« on: December 23, 2011, 09:32:59 AM »

Without going into too much grody detail, I have a medical problem that gets worse when I don't take care of it.  And, with the holiday season on us and my day job getting crazy, I've been not taking care of it.

One of the main symptoms is insomnia .  If some of you look at the times of my posts, you may have been wondering when the hell I sleep.  Well, the sad truth is that when it's bad, there are days, and sometimes entire weeks, when I don't.  Srsly.

The consequence of not sleeping, is that my available energy and ability to concentrate (and my ability/motivation to take care of my medical problem properly) are severely diminished.  And that means, among other things, that I can't code very well.

I've been opening the files to work on, looking at the code, messing with it, looking at my dev plan, messing with it, and not getting anything done. Then I get frustrated and go read webcomics instead.

I had intended to get Neohack into a complete-and-playable state by the end of the year.  But realistically, on December 23rd, that's not going to happen.

Now I'm just hoping to be able to sleep again by the end of the year, and I'm going to devote all the energy and concentration I can muster, to getting back into good enough shape to sleep.

Bear

It's 1:30 AM here.  I have a headache most of you wouldn't believe.  And I haven't slept in four days.

On the bright side?  There's a cat purring in my lap.



12
Temple of the Roguelike / 404 on front gate.
« on: November 11, 2011, 03:23:52 AM »
I'm seeing a great big 404 error on the front gate of roguetemple.com right now.

13
Programming / neohack -- code introspection in C
« on: October 27, 2011, 12:50:38 AM »

On reflection, I probably shouldn't have used the word "Hack" in the name of this game, considering what I am doing.  It's just a little bit too appropriate. 

So, I'm writing in C because I like the old-fashioned hardcore-ness of C.  But C has no introspective capabilities at all and, after due consideration, I used a data-directed style with reified events and a  component architecture.  So, naturally, I had to fake some code introspection, which I've done by using sed scripts in the makefile. 

There are (so far) two classes of modular data-functions in the game, which are "action" functions and "trigger" functions.  I have a closure-type based on action functions, which is the reified event type I was talking about.  The events go on the schedule, and the game proceeds by just getting the next event, executing it, and disposing of it, repeat until done.  Executing any event may cause more events to be allocated and placed on the schedule.  The "trigger" functions are also data-functions, and reified trigger calls become attributes which can be attached to an actor.  When a particular thing happens to an actor (ie, it has a particular part in some event that gets executed) any matching triggers get called.  So, for example, when a mummy takes fire damage, a trigger can cause it to burst into flame, or when the player character processes an AI event, a trigger can cause the game to display the map and prompt for input. 

In order to facilitate this, I decided I needed function tables for events and triggers; that is, arrays that have function pointers in them.  In order to refer to the function, I refer to its location in the function table.  I do this using a named constant whose name is derived from the name of the function.  And the names of the constants are also available to the program as strings, so that it can read or write the names (which will stay the same no matter how other functions are added or removed) rather than the index values or worse yet the bare function pointers, in savefiles when serializing the schedule or the actors' attributes. 

Today, I made a subclass of events even more special than they were previously.  Now at a crucial point at the beginning of a game, the program directly calls all events whose names end in 'INIT'.  At the same time, I added some more makefile hackiness, so that all the c files in a subdirectory named 'content' get built and linked to the project. 

The idea is that game content can be added by adding a file of code to the 'content' subdirectory.  You include one "action" function named 'action_something_init' in the file, the game calls it during startup, and you get your opportunity to add content to the game using the interfaces in the main code, that let you do things like add a creature or an item to the random-generation tables, allocate prototypes and add attributes to them, add map generation methods, etc. 

If correctly done (and the rules for doing it correctly are simple) you can add arbitrary amounts of content, just by dropping the files into the content directory.  No need to change the makefile, no need to set flags, no need to add a call to the new code; just drop the file in, rebuild, and it's there.

doing this in C is a huge hack.  But I like the design.
Bear


14
Programming / FastLOS (used to be FastFOV) for those who are interested.
« on: October 22, 2011, 08:00:13 PM »
I've renamed the FastFOV algorithm to FastLOS, improved it a bit, and streamlined/clarified its roguebasin page a lot.

http://roguebasin.roguelikedevelopment.org/index.php/FastLOS

FastLOS is the only algorithm that can check Line-of-sight between any two tiles in constant time (with no need to touch any of the tiles between them).  It uses precalculation to generate a bitmask for each tile; in order to check line of sight, you just take the AND of the two bitmasks and check to make sure that the two tiles are in sight radius of each other.  If the AND result is nonzero and the tiles are within sight radius of each other, then there is Line of Sight between the two tiles.

Because it can check line-of-sight in constant time, it is the most efficient line-of-sight algorithm known.

It can be used for FOV, but being constant-time per square found, has the same efficiency as spiral-path or recursive shadowcasting, and is inferior to those algorithms if the map is one where FastLOS has any approximations.

It's always been approximate, in the sense that sometimes squares that ought to show in a perfect line-of-sight algorithm will not show if the bitmasks are too small or the map is too complicated. 

The recent improvements that I've put up in the FastLOS article at RogueBasin allow these imperfections to be detected.  The new bitmask generation heuristic (which is simpler than the old one anyway) automatically marks tiles whose field of view isn't perfectly represented by FastLOS. 

If FastLOS shows that line of sight exists, then it definitely exists.  If it shows that no line of sight exists, then up to now it was never really possible to know for sure.  With the new generation heuristic, you can tell more exactly; if either of the squares is NOT marked imperfect, then there is definitely no line of sight.  If BOTH are marked imperfect, and they're within sight radius of each other, then it's worth your time to check with a conventional LOS algorithm if you need perfection.

Other improvements in the precalculation method allow single tiles to be used as View Area generators, with both a much simpler, more elegant algorithm AND better results.  Also, single tiles may now be used to generate several View Areas rather than just one.


15
Temple of the Roguelike / How do roguebasin categories work?
« on: October 03, 2011, 06:16:05 PM »
A few days ago I put up a roguebasin page for my game-in-progress, Neohack. 

As I went back to it today, I noticed that it had acquired a category, "hacklike game." 

This is accurate, if a bit premature; I wouldn't have called it a game yet, it's more a tech demo at the moment.  Anyway, "hacklike" is an accurate description of where I'm going, and I'm glad it happened.  But I'm trying to understand how and why it happened.

The page for "hacklike games" doesn't show any recent edits, and the categorization isn't part of the page source, and the Neohack page doesn't show any edits besides mine (and one guy correcting the capitalization of the name of another game I mentioned).  And yet, when I go into the diff histories, the categorization shows up with the second edit - which is probably before anyone else noticed the page, and I think, is before I even had put up the project home page that it links to on my server. 

How'd that happen?


Pages: [1] 2