@ Zeno Rogue Games @ Vapors of Insanity @ [ Necklace of the Eye ] @ Hydra Slayer @ HyperRogue @ Untahris @
@ About @ Games and Screenshots @ Brogue @ Downloads @ More information @ History & Future @ [ Dev Guide ] @
 

@ Necklace of the Eye: development guide






Disclaimer

NotEye is still under active development. I try to make the changes backward compatible, but it is likely that changes to NotEye make older NotEye games stop working with new versions of NotEye. For this reason, NotEye is not documented very well. If you want to use some of the more sophisticated features of NotEye, you might run into lack of documentation. Do not let this detract you, just send me an e-mail to zeno@attnam.com -- I will try to answer your specific question, as well as you will push me to improve the online documentation. Note that the NotEye port of PRIME has been made almost without my help, so it is possible to do with the current docs, at least if you are an experienced programmer.

Licensing

NotEye is released under GPL (GNU General Public License) version 3, with the following motivations:

I believe that, ideally, art should be for everyone to experience and share freely (once it is created), but still, authors should be credited for their work. For this reason, you can use NotEye for free in your game/library, as long as the whole game/library is free and open source.

If you want to use NotEye in a closed source game, just contact me for a permission (a licensing exception) -- that's how it was done for ADOM (I think Thomas Biskup has good reasons to make ADOM closed source). For example, you probably cannot use Oryx's roguelike tileset in a GPL game, as its license is not compatible. So, if you want to use Oryx's tileset together with NotEye in a closed source game, pay me for a licensing exception of NotEye, as you have paid for using Oryx's tileset.

It would be wrong for someone to use NotEye in a paid game without giving me a share, so NotEye's license forbids it. If you want to give me a share, though, please contact me -- I am open for negotiations (depending on how "evil" your idea actually is, for example a free (as in free beer) closed-source game and a paid deluxe version which does not change the gameplay is probably OK with me, but I would need to analyze this in case-by-case basis).

If you prefer your work to be licensed permissively (without GPL's restrictions), that is OK with me, and GPL allows it too -- just put your own work under a permissive license, and distribute it together with NotEye. The whole package will be licensed under GPL, but if somebody wants to use your code for something that GPL does not allow, they can take your code without NotEye and do what they want. We have been discussing this here.

Introduction

NotEye is a frontend for roguelikes. Its capabilities are listed on NotEye's main website. This page is for developers and modders, and documents how to use NotEye with new roguelikes.

Typically, if you want to use NotEye with a game, you need two things:

Game script

The game script has to be written in Lua. NotEye has some sensible defaults, so a very simple game script, which only specifies the map region (e.g. it tells NotEye that the map resides in the rectangle from (0,2) to (80,22)), already works -- it draws '#' characters as walls, '.' characters as floors, etc. And right away you get most of features of NotEye, like using Shift/Ctrl for diagonals, isometric and FPP modes, screenshots, beautiful console with a selection of fonts and palettes, and so on. More complicated game scripts tell NotEye how to display the '+' character in the map, how to animate the characters, how to display menus, how to display several items on the same tile, how to animate missiles, etc. Game scripts appear in the "games" subdirectory, for example "games/hydra.noe" is the (very complicated) game script for Hydra Slayer. Look at the examples to get the idea.

Roguelike

A roguelike can be made NotEye-compatible in one of the following ways:

Starting to work with NotEye

Note: you can run NotEye with a parameter (e.g. noteye -N -L noteye.log), this way any problems will be sent to a diagnostic log file.

NotEye menu has some hidden options which help with development. Press F5 to reload the scripts (so you can immediately see the effects of your changes). Press F6 to see the reference about icons available in RLTiles (F1), Vapors tiles (F2) and the font (F3). Press F7 to display some helpful data about the game's display (coordinates, symbols, and color of the character under the mouse cursor).

You should probably start from one of the configurations provided as examples (frozen.noe, nlarn.noe, rogue.noe, sample.noe, crawl.noe, doomrl.noe, adom.noehydra.noe contains lots of hacks to make NotEye display hex and is more difficult). Copy it to gamename.noe and add it to the menu in the noteye.noe file.

The amount of work depends on how original the game's display is, and how complete do you want your tileset to be. In some cases it should be enough to simply copy one of the provided configuration files, and modify some numbers. Usually you will need to modify the mapregion variable. It says which part of the screen contains the map in the given roguelike. If there is something original, or you want more complicated features, you will probably need to learn some Lua and understand how NotEye works.

'caller' is a script that either runs the game or tells the users to care for themselves. The first parameter of caller3 is the full game of the name, and the second one is used as both the name of the executable and the name of the directory where the game is expected to be found.

Lua is designed so that you can replace functions by your own implementations (which can call the original ones), and NotEye uses this. While the difficult tasks like drawing the sprites are done natively in C++, most of the library is written in Lua. If NotEye's implementation of something does not fit your needs, you can replace NotEye's implementation with one of your own.

If you want to provide tiles for more things, it is probably the easiest to use "generic-tiles.noe" and modify the xtileaux2 function (copy the implementation from the NLarn config, for example, and modify it). If the game incorrectly guesses the position of the player character, you can try changing the ispc function (like hydra.noe which makes sure that the @ sign is white, so that we don't take an e-mail address for a map), or the copymap function if the first method does not work. If you don't like how some things are colored on the minimap, change the xminimap function. And so on.

NotEye library, accessed from the game script

NotEye library handles images, tiles, screens (rectangular blocks of tiles), fonts, and game processes. Typically, the game script accesses functions provided by the NotEye library; these are written natively in C++, so they work fast, but you cannot change the implementation. Typically they are not used by the game itself, but you can do this if you want (in C++ games, with Python or Java it is probably harder). For example, Hydra Slayer directly manipulates Images, so that it can procedurally generate hydra pictures.

Here is a short description of concepts available in NotEye:

Object

Objects include images, fonts, tiles, screens, and game processes. You create objects by calling functions implemented by NotEye, and use them by calling other functions. These functions return numbers which act as identifiers for these objects. You can use delete(x) to delete objects, and objcount() to know how many objects have been allocated so far.

Image

Images are just that, images, that is, rectangular blocks of pixels. The following Lua functions work on images:

Graphics to display

Gfx is an Image, so saveimage, setpixel, getpixel, and imgcopy functions work on it — you need to give Gfx (which equals 1) as the image. Also the following are required:

Screen

A screen is a rectangular block of Tiles. Screens can be drawn on an Image, and also can be connected to a Process, so that the Screen contains the console output from a roguelike.

Tiles

Screens are made of Tiles. This is a general concept that encompasses both console characters that roguelikes output and the tiles shown on screen, and even is allowed to include 3D information about how a tile will look in the FPP view. You can create new tiles with the following: These functions return the tile index that you can use on screens.

Warning: tile indices are not recycled yet. This means that every combination will use up some memory. In typical 15-color ASCII games which do not use background colors there is no problem (15 colors times 100 ASCII characters = 1500), but with true color and animated tiles, the siutation becomes more complicated. Will probably fix it someday.

Fonts

Fonts are created with F = newfont(I, x, y, trans). I is an image which contains the characters. There are x characters in each row, and y columns (there should be x*y == 256). The value 'trans' contains the color treated as transparent. You can get the tile for the given character with fget(F, ch), e.g. fget(F, "@").

Process

Processes are games, command shells, and other things that NotEye can run and process its screens. The screen associated to a process is filled with tiles of form:
tilemerge(tilefill(vgaget(BACKGROUND)), tilerecolor(fget(Font, CHAR), vgaget(FOREGROUND)))
which can be correctly displayed using drawscreen. You can also process this form using functions gch (get character), gco (get foreground color (as a full color, not palette index), gba (get background color), gp2 (get just the character, without the background).

(to be continued)

NotEye common script

Most of the NotEye logic (like graphical modes, menus, etc.) are implemented in Lua. This means that if something does not fit your needs, you can replace NotEye's implementation with one of your own.

All this is placed in the common directory. Just look there. Hopefully, all the functions and variables are commented. Of course, if you have seen a feature in some NotEye game, it might be possible that this feature is implemented only in this game's game script -- you can read the game script and learn from it, or copy the feature from it. For example, hex display is implemented in the Hydra Slayer's game script. Typically, such stuff is moved to common when I feel that it is generic enough to be used in more than one roguelike.

Here are some highlights:

(none yet)

NotEye library, accessed from the game itself

If you want to access the NotEye library directly from your game, read one of the samples. Simple Samples in C, Pascal, Python, and Java are included with NotEye. Hydra Slayer (C++) is also included, although it is also much more complicated. You can also download the source of PRIME, which also uses NotEye.

We use C/C++ in the following. The names of functions might be a bit different in other languages. Note that in C++ you need to use the namespace to access the functions: using namespace noteye;.

Initialization

The following functions are typically called during the initialization:

Output

Output is based on the Curses functions.

Use noteye_getinternalx() and noteye_getinternaly() to get the console size.

Use noteye_move(int y, int x) to move the cursor to the given location (y and x are 0-based). Use noteye_addch(char ch) to write a character at this location, or noteye_addstr(const char *s) to write a string. You can also use noteye_mvaddch or noteye_mvaddstr which moves and writes in one call. Change the color with setTextAttr(int fore, int back), where fore and back are foreground and background color indices, using the palette (as shown by NotEye), or with setTextAttr32(int fore, int back), which accepts fore and back arguments in true color -- use the 0xFRRGGBB format, where F is the index of color in the palette (NotEye has a 16-color terminal output, and this is useful if you want to make your game look decently here). You can also use int getVGAcolor(int c) to get the true color value of the palette color number c.

The screen is automatically refreshed if the UI thread is resumed, which happens when you wait for input (see the next section) or you call the noteye_refresh() function.

Input

Normally, use noteye_getch() to wait for a keypress and return. Note that keypresses are not read directly from the keyboard -- the gamescript may perform some preprocessing (usually, direction keys are preprocessed in isometric/FPP modes).

Normal characters are returned as their ASCII values. Function keys are returned as KEY_F0 + number (thus, F1 is KEY_F0 + 1). Directions are DBASE + i, where i is from 0 (east) counterclokwise to 7 (southeast). (Use the dx/dy tables from the samples in order to translate these directions to vectors which can be added to positions.) Keypad center is DBASE + 8.

For more complicated keyboard handling, you can use noteye_getchev(). This returns on all keypresses (including the non-printable ones such as Shift) and key releases. You can call noteye_getlastkeyevent(), which returns the last key event in the SDL key event format (noteye_getlastkeyevent()->type tells whether it is a keypress or release, noteye_getlastkeyevent()->key.keysym.sym gives the SDL symbol of the key, noteye_getlastkeyevent()->key.keysym.unicode tells the Unicode value of the character, noteye_getlastkeyevent()->key.keysym.mod gives information about the modifier keys used, etc.)

Use noteye_halfdelay(int i) to wait at most 100i ms for the next keypress. Use i=0 for a quick return (after resuming the Lua thread for one iteration of the cycle). Use noteye_cbreak() to wait forever.

Error handling

Use the noteye_handleerror to add your own error handler, which will be executed in case of an error in the Lua script. If errors in the Lua script have caused the UI thread to crash, noteye_getch() will return NOTEYEERR. This usually can be fixed by reloading the NotEye scripts -- halt NotEye (noteye_halt()), initialize it (noteye_init), and run the gamescript (tt>noteye_run) again (no need to reset the globals).

How to call functions from Lua

(Note: In C++ you could also use the Lua functions directly. NotEye simply provides wrappers so that you do not have to have Lua libs installed to compile your game.) See the samples (this is shown in all of them). During the initialization, do noteye_globalfun to add a global function accessible from Lua. Your function gets one argument, struct lua_State *L.

Access the arguments with noteye_argcount(L) (the number of arguments), and noteye_argInt(L, i) (i-th argument as an integer), noteye_argNum(L, i) (i-th argument as a floating point number), noteye_argBool, noteye_argStr (likewise). (NotEye does not include wrappers for table arguments yet.)

Your function returns an int, which is the number of the arguments returned. Use noteye_retInt(L, i) to return the integer i, similarly there ar functions noteye_retBool and noteye_retStr. If you want to return a table, call the noteye_table_new function, then other noteye_table_* functions to set its fields (see the samples).

@ Donate @ Project Blog @ Twitter @ Discuss @


Tweet                  

Thanks to Slashie for hosting this at RogueTemple!
@ Zeno Rogue Games @ Vapors of Insanity @ [ Necklace of the Eye ] @ Hydra Slayer @ HyperRogue @ Untahris @
@ About @ Games and Screenshots @ Brogue @ Downloads @ More information @ History & Future @ [ Dev Guide ] @