I've been spending a lot of time thinking about Quest's design lately, and in order to actually get something
done, I've decided to simply dump my thoughts here and get to work implementing them. Inspired by QuakeForge,
I've decided to call this thing 'state of the code'. Hopefully it'll be semi-periodic (weekly?) (although that
depends on me having enough time to write it, and there being something worth writing about).


1.
The current plugin system should be split into two parts: 'plugins' that contain code, and 'configurations'
(better name?) that handle the plugins, set variables, define the UI, and stuff like that. This should make a lot
of things a lot cleaner, although it means a 'downloadable plugin' will need at least two files.

One of the main reasons for this is good support for different games and mods. Since mods (and some games, eg. Quake
and Hexen) need the exact same code for texture loading and map loading/saving, but have settings that need to
be stored separately (default palette, entity sets, texture path (as defined by user)), there needs to be some way
to handle this. In the new system, there would be a configuration for each game/mod, and it would set the necessary
parameters and load the necessary plugins. Settings would be saved inside each configuration.

Configurations will be stored in separate files, in a text based format, and a Lisp-like language. Saved settings would
be stored in a similar way, though in different files (so on Unix, global configuration files could go in /.../share/quest,
users' own configurations (if any) and saved settings go in ~/.quest/ (or something)). (A way to specify global default
saved values would be nice for multi-user systems where many persons use Quest, but I'm guessing nobody uses it that way).

Examples (at least it would be something similar to this):

/*
The configuration 'Quest' is automatically loaded when Quest starts.

The syntax would be (config name (attributes or something) code...)
*/
(config Quest
	(
		(description "Default main configuration")
	)

	/* by default just load the classic UI */
	(define configurations (ui.quest-classic))
	(config.load configurations)
)

/*
'classic' UI
*/
(config ui.quest-classic
	(
		(description "Classic Quest UI (v1.*, v2.*)")
		(provides ui)
	)

	(plugin.load basic camera)

	(define menu_file (menu.add nil "_File"))
	(define menu_edit (menu.add nil "_Edit"))
	(define menu_camera (menu.add nil "_Camera"))
	(define menu_brush (menu.add nil "_Brush"))
	(define menu_csg (menu.add menu_brush "_CSG"))
	(define menu_face (menu.add nil "_Face"))
	(define menu_entity (menu.add nil "E_ntity"))
	(define menu_group (menu.add nil "_Group"))
	(define menu_misc (menu.add nil "_Misc"))
	(define menu_misc (menu.add nil "_View"))
	(define menu_plugins (menu.add nil "_Plugins"))
	(define menu_help (menu.add nil "_Help"))

	(menu.set menu_file)
	(bind (menu "_New") map.new)
	(bind (menu "_Load...") (map.load))
	(bind ("control+s" (menu "_Save")) (map.save)) /* no parameter to map.save, use default name or ask */
	(bind (menu "S_ave as...") (map.save nil)) /* nil, always ask */
	(menu nil)
	(bind	("escape" (menu "E_xit")) (exit))

	(bind "left" (camera.move DIR_LEFT))
	(bind "right" (camera.move DIR_RIGHT))
	(bind "up" (camera.move DIR_UP))
	(bind "down" (camera.move DIR_DOWN))
	(bind "a" (camera.move DIR_FORWARD))
	(bind "z" (camera.move DIR_BACKWARD))
	/* etc */
)

/*
Some game configurations
*/
(config game.quake
	(
		(description "Configuration for Quake I")
		(provides game) /* something like this is necessary so Quest can
								 make sure only one game is loaded */
	)

	(define game.quake1.paths.pal "game/quake1/quake.pal")
	(plugin.load game.quake1)
	
	(entity.load_entity_set "game/quake1/*.es")
	(texture.load_categories "game/quake1/quake1.cat")
)

(config game.hexen
	(
		(description "Configuration for Hexen")
		(provides game)
	)

	(define game.quake1.paths.pal "game/hexen/hexen.pal")
	(plugin.load game.quake1)

	(entity.load_entity_set "game/hexen/*.es")
	(texture.load_categories "game/hexen/hexen.cat")
)

/*
A downloadable plugin would need to include some plugin.ql file that
contained stuff like
*/
(config csg.intersect
	(
		(description "CSG intersection command")
		(requires ui)
	)
	(plugin.load csg.intersect)
	(bind ("alt+i" (menu menu_csg "_Intersect"))	(csg.intersect))
)


There'd be a configuration manager where users could load and unload configurations. I'm thinking that a plugin
that creates simple game configurations might be nice, i.e. you select a texture system from a list and fill in
some information on directories and a configuration for such a game would be created for you. The save files
would be something like (automatically created by Quest):

(config-save game.quake
	(set
		(game.quake1.paths.wad "/mnt/msdos_d/quake/id1/tex.wad:/mnt/msdos_d/quake/id1/hintskip.wad")
		(texture.default "some_quake1_texture")
	)
)

(config-save game.hexen
	(set
		(game.quake1.paths.wad "/mnt/msdos_c/spel/hexen/all.wad")
		(texture.default "some_hexen_texture")
	)
)

(config-save csg.intersect)

(config-save ui.quest-classic)

(config-save Quest
	(set
		(configurations (ui.quest-classic game.quake csg.intersect))
		(grid_size 16)
	)
)

The empty config-save:s mark that the configuration has been loaded at some time. Each configuration definition should
probably be split into several functions:

'load': Called everytime it's loaded (does what all the above code does).
'unload': Called when a configuration is unloaded. Shouldn't be necessary most of the time.
'init': Called the first time a configuration is loaded. Could be used to make Quest seem newbie-friendly
by automatically searching for game directories, generating .wad files, telling you what you should download
to build maps for this game, etc. Could also have some sort of version checking, so the first time you start
a new version of Quest, a window could pop up with readme.txt or something (I don't really like the idea of
bringing things like that up without the user saying it should, but OTOH, some users would benifit from being
forced to read readme.txt).

The approach used above to bind stuff to keys/menus has problems with automatically generating manuals. Only being
able to bind functions (and no expressions) would solve that problem, but makes it harder to bind stuff that's only
slightly more complicated than a pure function call. (Maybe all that lambda stuff would help.)


2.
The above system should make it easy (or easier) to support several UIs (a UI would be some specific way
of keyboard and menu layout, and maybe some specific commands (mouse actions would probably need them)).
For Quest v3.0, I plan on including:

quest-classic: Like Quest v2.*. This is the one I'm working on now, since it saves me from making UI design decisions
while testing internal stuff.

quest-default:	A default, somewhat user-friendly UI. It would probably be a good idea to start with v2.4 and improving it,
adding new stuff, and trying to make it at least a little logical.

quest-hardcore (better name?): Optimized for fast, effective editing for people who know what they're doing
and have the time to learn the interface properly. Designing this will be interesting (early testing makes me think
it would be best to optimize for keeping the right hand on a mouse and the left hand at the keyboard's left edge,
and then figuring out a way to bind all commands that are often used to keys nearby. At least the arrow keys
should be duplicated there so you can do navigation and keyboard vertex movement without moving your right hand).

maybe radient: (qe|gtk)radient-clone? At least it should be possible.


3.
The Lisp-like language used for configurations could also be used for scripting, either for simple commands, or (probably
more useful) for automatic testing of Quest. I guess it'd like something like:

(script foo
	(brush.create.cube (-128 -128 -128) (128 128 128))
	/* functions should (when reasonable) either run interactivly if called
	with no parameters, or accept parameters that tell them what to do */
	(debug.dump "t1") /* Writes some 't1' header and a dump of map data */
	(brush.clip_plane (0 0 1 64))
	(debug.dump "t2")
)

And then some shell script compares the output to what it should be, and tells you in which section(s) it failed. I'm also
planning on adding some debug functions that run consistency checks on map data. For automatic testing, I'm also going to
write a simple text-based frontend (a CLI 3d-editor :).

Being able to type commands while in Quest would also be nice, but requires extending the message area to a
proper console.


4.
It would be a lot cleaner to allocate enough space for any renderer fields in brushes, entities, faces, and groups while
allocating the object itself than trying to allocate it inside the renderer (avoids complexity, and solves the problem
with recovering from out-of-memory errors in there). The renderer would define a structure like:

typedef struct
{
	face_t f;
   gl_texture_t *t;
   float light;
} S_r_face_t;

and tell the core stuff what size this structure is. For brushes and entities this is easy (I've already done it), but
faces are currently allocated in an array, so a lot of code will have to be changed. Should be worth it, though.

Also, for memory efficiency (at least on DJGPP, which allocates memory in blocks with power of two sizes,
which wastes a lot of memory when allocating lots of small structures (i.e. brushes and faces)), I'm writing a
system for heaps of fixed size objects.


5.
Coding style. I'm satisfied with the way it is now, except for handling indention. Currently, some files use tabs
and some use spaces (and some are mixed), so I'm deciding that there should be one tab for each level of indention,
and spaces should be used to line stuff up.


6.
Directory structure. Probably something like this:

quest/
 |- core/  everything that's currently in the main directory
 |- (include/?)  .h files used in many places?
 |- cmds/? plugin/? code for all the plugins, probably some internal structure
 |- cfgs/?  all the configurations
 |- fe/  frontends
 |  |- gl/  common OpenGL stuff
 |  |- gtk/  gtk/OpenGL frontend
 |  |- sw/  software 3d-renderer
 |  |- fb/  framebuffer based GUI, using software renderer, like v2.*
 |  |  |- djgpp/
 |  |  |- x11/
 |  |  |- svgalib/
 |  |  \- amiga/  would be neat if someone could make this one work again
 |  |- win32/  Win32 frontend?
 |  \- text/  Simple text based frontend, used for testing
 |- graphics/  button images, fonts
 \- games/ entity sets, texture category files,	palettes for different games


Being able to build core/ as a shared library (libquestcore.so?) would be nice, especially when I start doing
automatic testing using the text/ frontend and real use testing using the gtk/ frontend. Anyone know of a nice way of
handling that? (Is there any portable way at all except automake/libtool?) Dynamically loading plugins would also be nice.


7.
Naming stuff. Should	plugins be allowed to link directly to other plugins? If not, everything in the plugins should be static,
except for one symbol (either a structure with information about the plugin or a function that returns a pointer
to the structure) that tells the plugin system what the file contains.

Names for functions and plugins should also follow some system, eg.:

camera.*: commands for manipulating the camera
edit.*: editing commands that work on several kinds of map objects
brush.*: commands that work on brushes in general, and on normal brushes
brush.curve.*: commands that work on curves
csg.*: CSG operations (maybe brush.csg.*?)
entity.*: entity commands
group.*: group commands
face.*: face commands
texture.*: texture commands
render.*: renderer specific stuff (e.g. switch 2d/3d, textured/wireframe, etc.)
misc.*: everything else?

Name conflicts could be a problem when trying to understand the code. In the above csg.intersect example, there's a
configuration called csg.intersect that loads a plugin called csg.intersect containing a function called csg.intersect.
A bit messy, but shouldn't be a big problem in practice. And probably better than enforcing that a plugin called
foo can only have functions called foo.*.



OK, that's all I could think of right now. Comments, ideas, suggestions, pointing out problems, etc. would be
appreciated.

- Alexander Malmberg

