Big Book of Elma Facts

From Elma Wiki
Jump to: navigation, search

The Big Book of Elma Facts was released as part of the Elma Ultimate DVD and written by Domi. It covers technical aspects of the program. The contents of the book are below, or a zip copy of the book is available on kopasite.

Contents

Menu animation

There’re 9 fancy buttons flying in the background of menu. This animation begins from top-left button, that receives initial speed of value 1 and random angle between 0.028648 and 89.848719 degrees. The actual angle formula is: 0.999 * pi/2 * s_random(1000) / 1000 + 0.0005 Buttons layout

The initial position with some additional info is shown on the picture. There’re only 1000 initial states so one can suggest there’re only 1000 different states in each subsequent moment of time, which is not the case — the state of this button system is recalculated each frame, and thus depends on framerate. By the way, the vertical synchronization in menu is off and there’s no way to switch it on other than in videocard settings.

The mass and inertia moment of buttons is calculated as for solid disks of uniform density but there’s a deprecated (no way to change it without hexediting) option in elma that allows to calculate it as for solid spheres.

The interaction between buttons generally follows real-world physics of solid body collisions, but it’s still unclear how and why the buttons receive angular speed. Probably this happens because of uneven mass distribution (due to holes), but I haven’t deduced which physical law Balazs relies on in this case. Whatsoever, the mass distribution is taken into account only when collision happens between two buttons, the collision of button and wall is calculated as for uniform density.

The total energy of system equals to 576 (that is, 24 × 24), but the system can lose or gather up to 0.001 of energy on each collision (because the energy transfer is calculated approximately), so it is theoretically possible that buttons either speed up to infinity or slow down to full stop after a certain amount of time.

Menu helmet

Helmet

Menu helmet is 1 pixel higher than the actual picture sisak.pcx stored in the elma.res. During menu graphics initialization stage this missing line of pixels is added to each helmet frame by propagating the color of the fourth filled pixel from the left in the topmost line, as shown on the picture. This process continues until fourth filled pixel to the right is reached. The algorithm doesn’t take into account any gaps or color variations in the topmost line, so the results for custom helmets might be weird.

Wheel size

Visual wheel size differs from physical wheel size. Physical size of both wheels equals to 0.8 elmameters. Visual size is kinda tricky: when the bike is not changing its direction, visual size of both wheels equals to 0.79 elmameters; when the bike is changing its direction, the visual size of the leading wheel equals its physical size (i.e. 0.8) while visual size of slave wheel is still 0.79 elmameters. As elmameter normally equals to 48 pixels, this difference is clearly visible on zoom of 2 and higher.

April joke

Moposite released elma20.exe as April Fool’s joke in 2003. That file is really an executable file written in german version of Borland Delphi 6. The source code of this program looked like this:

program Project1;
uses Dialogs;
{$R *.res}
begin
  ShowMessage('You got April fooled! Haha!')
end.

The program was compiled on 30.03.2003 in 20:44:16. The compiled version itself occupied 376kb, which probably was considered too small for elma 2.0, so another 1600kb of junk (0xFF actually) were added in the end of the file.

Birthdays

Version	Compiled	Released
Action SuperCross 1.0 registered	11.02.1997	 
Action SuperCross 1.0 shareware	11.02.1997	06.03.1997
Action SuperCross 1.1 registered	 	 
Action SuperCross 1.1 shareware	24.03.1997	24.03.1997
Action SuperCross 1.2 registered	24.03.1998	 
Action SuperCross 1.2 shareware	23.03.1998	24.03.1998
Action SuperCross 1.3 registered	 	 
Action SuperCross 1.3 shareware	06.01.1999	06.01.1999
ElastoMania 1.0 registered	27.01.2000 10:38:50	 
ElastoMania 1.0 shareware	27.01.2000 10:43:55	31.01.2000 10:00:10
ElastoMania 1.1 registered	25.08.2000 13:38:16	 
ElastoMania 1.1 shareware	 	 
ElastoMania 1.11 registered	25.09.2000 11:10:10	 
ElastoMania 1.11 shareware	 	 
ElastoMania 1.11a registered	19.10.2000 15:50:19	 
ElastoMania 1.11a shareware	19.10.2000 15:11:08	 
ElastoMania 1.1 registered (BeOS)	 	 
ElastoMania 1.1 shareware (BeOS)	12.04.2001 19:11:14	12.04.2001 21:02:04

Field is empty if I have no installation package or executable file for that version. Any help with that would be appreciated.

DOS elma

DOS elma

Elma was initially developed for DOS, but Balazs decided to release elma for Windows as it was clearly much more popular than DOS. Luckily the fork of DOS version was still used for resource development (fonts, elma.res, LGR) and was later released as LGR DK. You can download hexedited version of make_lgr.exe and check the DOS elma yourself (you will need registered elma.res as well). Most noticeable differences from later elma versions are: biker sits lower in his saddle, wheels are not rotating, volt keys are sticky — if you press it once, it will very well autovolt until level finishes. The picture shows delma running on DOSBox.

Palette issues

Gradient

It is well known that elma uses 8-bit graphics: it can display not more than 256 colors simultaneously, and this set of colors is named palette. Every color in palette has 3 color components: red, green and blue; and each component is unsigned 8-bit integer (i.e. it can hold any integer from 0 to 255). It would seem that the set of displayable colors consists of 256³ = 16M possible colors (24-bit palette, also named Truecolor), but here comes the glitch — if you would try to create LGR-file with grayscale palette from 0 to 255 and then display gradient in elma, you won’t get what you expect, as is shown on the picture.

That happens because of DOS origins of elma. Elma shares codebase with delma, and delma in turn heavily reuses across code, which is known to support VGA video mode. DAC resolution in VGA equals to 6 bit (i.e. each color component has the maximal value of 63), so every program that works in this mode must make sure every color component doesn’t exceed 6 bits. And so does across — after any PCX-file is loaded into memory, its palette is cut from 24-bit to 18-bit dividing each color component by 4.

Palette

When Balazs switched to DirectDraw, which supports 24-bit palette, the PCX-palette got required to be 24-bit, not 18-bit. For some unknown reasons Balazs decided not to remove the division, but to compensate it by multiplying every color component by 4. Though the result color is close to original one, it still slightly differs from it and that is why you can’t losslessly transfer arbitraty palette to elma. By the way, you can’t get exact white (#FFFFFF) in elma: (255 / 4) × 4 = 63 × 4 = 252.

Timer

Color distribution

The color of each pixel of ingame timer is calculated as follows: if every color component of underlying pixel is less than 80 then the underlying pixel is considered dark and the timer pixel is drawn with the lightest color in the palette; else the underlying pixel is considered light and the timer pixel is drawn with the darkest color in the palette. The brightness of the color in the palette is calculated as the sum of color components of this color. If there are several lightest/darkest colors in the palette, the first (by palette index) of them would be selected. The picture shows the distribution of dark and light colors on default LGR.

Implications:

  • Timer is not necessarily black-and-white. It can be black-and-red or brown-and-lime, or whatever depending on the LGR-file in use.
  • The brightness estimate used in elma poorly matches with how human eye senses the light. For example, elma considers #FF0000 and #00FF00 are equally bright, while eye perceives green almost twice as bright as red.
  • Only 3.05% (i.e. (80/256)³) of all possible colors are considered dark, thus on arbitrary LGR-file timer would be completely dark for 96.95% of time. The default LGR has 48 dark colors of 256, in this case timer would be completely dark for 81.25% of time (not taking into account level topology or textures preferences).

LGR basics

Elma uses special files with extension “LGR” to store ingame graphics. The idea is to separate level topology (LEV files) and level visual properties (LGR files), which allows several levels to share same visual properties and thus drastically reduces the average amount of data required to store playable levels. LGR files first appeared in elma, but the idea was used in across as well — there were LEI files, each defined its own game appearance by specifying map colors, textures, objects, bike parts, etc, much like LGR files do. LEI files contained no image data though, only picture names that were used to retrieve appropriate images from across.res, where all the game graphics was stored along with other game data (including LEI files themselves). LEI files were in turn associated with levels via special reference table hardcoded in across.exe.

LGR may stand for Level Graphics Resource.

Each LGR file consists of two blocks: picture list block and image data block. Picture list defines optional pictures provided by this LGR — grass, additional food animations, textures, masks and all the scenery. Each record in picture list contains name and some properties describing how named picture should be used in game: type (picture, texture, mask), distance from the surface of display (1-999), clipping (ground, sky, none), and transparency.

Image data block is basically a bunch of image files sticked together. It includes all the optional images mentioned in picture list above along with special images required to be in each LGR: bikers’ parts (q1*.pcx and q2*.pcx), exit and killer animations (qexit.pcx and qkiller.pcx), colors of ingame map window (qcolors.pcx) and the underlying texture for the game screen (qframe.pcx, could be seen in multiplayer and if you decrease your game screen size).

The palette of LGR is not strictly predefined — pictures in LGR may have palettes different from standard one, and even different from each other (it would look weird in game though). The palette of LGR is read from the file q1bike.pcx and is applied to all pictures in LGR no matter what palettes they have.

Sprite

There is a certain number of well known file formats used by elma. These include lev, rec, dat, and less but still wide known lgr and res. Besides, there is a whole bunch of formats supported and used by elma for its internal reasons, yet absolutely unknown to public.

One of such formats is SPR (which is shorthand for “sprite”), a custom graphics file format, supposedly developed by Balazs. SPR is used as graphics container in across and elma font files and that is its only real application. However both elma and across are capable of reading and storing standalone SPR files as well as the other supported graphics files — PCX.

The main differences between SPR and PCX are listed below:

  • SPR natively supports the transparency via transparency mask, while PCX has no transparency at all and is forced to have special transparency color if transparency is needed. In particular that means that SPR could use all 256 palette colors, while transparent PCX could use only 255 colors;
  • PCX includes palette along with bitmap data, while SPR has no own palette, and is intended to be used in some palette context;
  • PCX compresses bitmap data using RLE, while SPR stores it uncompressed.

Speaking of size, PCX is bigger on small pictures because of wasteful header and palette, while SPR is bigger on big pictures because of transparency mask and uncompressed bitmap data. It is unknown why Balazs didn’t implement RLE compression in SPR and why he didn’t use it as an internal graphics format instead of PCX.

LGR tricks, Part 1

Head proportions

No surprise that optional pictures (see “LGR basics”) in custom LGR files may have dimensions different from default.lgr counterparts. The dimensions of biker parts are not strictly predefined either — except for bike itself they could have any size. E.g. default head size is 189 × 189 and one can make it, say, 20 × 200 and it would look just fine in game, as shown on the picture. The only restriction is that neither height nor width of such picture can exceed 255 pixels. This is just another legacy issue — for the sake of performance these pictures are stored in memory in raw format (inside the continuous array of pixels), and in DOS there were problems with allocation of memory blocks bigger than 64KB, so both w and h are limited to square root of 65536 less 1, i.e. sqrt(65536) − 1 = 255.

Bike dissection

The dimensions of bike picture are way more restricted because elma dissects bike into 4 parts using the hardcoded coordinates, and these coordinates have to lie within picture bounds. Just like other biker parts, bike ought to be stored in raw mode, but both its dimensions exceed 255 pixels, so it can’t be stored in one part and thus is required to be dissected. It is unclear though, why Balazs didn't scale the bike image so it would fit in 255 × 255 square and why the bike isn’t cut in 3 parts (which is possible). The picture illustrates bike cutting scheme. One may notice that parts are 2 pixels overlapping, the reason for that is unknown.

LGR could contain not only PCX files. The required pictures are referenced by full name (e.g. q1bike.pcx) thereby forced to be PCX, while optional pictures are referenced by name without extension and thus can be SPR files as well. LGRDK however provides no means of loading SPR files into LGR.

File qcolors.pcx is used for coloring the ingame map window. By default it contains 9 filled squares 11 × 11 pixels in the top-left corner with some explanations to the right. However, the actual color is taken from the central pixel of each square (x = 6 and y = 6 + 12 × i). It should be noted that timer color from this file is not really used anywhere, actual timer color is calculated as it was said in “Timer”.

Typefaces

There is not much to say:

  • Typeface used in Action SuperCross title screen and menu is VAG Rounded. Ironically, this typeface was designed for automotive company.
  • Typeface used in ElastoMania title screen and menu is Arial Rounded.
  • Typeface used in ElastoMania menu background is Dom Casual designed in 1951, which makes it the oldest typeface of all used in across and elma.
  • Typeface used in “Secret area” sign is likely Arial, though it can be any similar typeface.
  • Font used in qcolors.pcx is Small Fonts 7px.

Codebase

Codebase timeline

Both across and elma are written in C++, five different compilers were used over the time. Borland C++ 3.1 was used for pre-alpha versions of across and auxiliary across utilities like sound setup and level editor. Later Balazs switched to Watcom C/C++ 10.0 (10.5 shortly after), and used it for all further versions of across, alpha versions of elma and auxiliary elma utilities, namely LGR DK. After changing game platform from DOS to Windows, Balazs switched to Microsoft Visual C++ 6.0 Professional and used it for all Windows versions of elma. It is unclear why he didn't use Watcom, which is capable of producing Windows executables as well. BeOS port of elma was compiled using gcc 2.9.

Despite the fact elma is compiled as C++ program, Balazs used few of language features, namely classes and operator overloading; encapsulation is barely used, most of class fields are very well accessible out of class methods. Other object-oriented concepts like inheritance, polymorphism, and templates are not used in elma, neither are standard C++ classes.

Elma code is very modular, mostly due to its object-oriented nature. It is impossible to tell the exact amount of modules in compiled binary, but it seems to be around 60. Taking into account the fact that Microsoft Visual C++ doesn’t link unused functions and modules, we can suggest there were about 70 source code files occupying 600-700KB in total.

Most of error messages are Hungarian, others contain just random text. And yes, most variable, function and class names are Hungarian.

Code statistics

Information in this section refers to registered ElastoMania 1.11a as the most popular version around.

Elma code section includes 233512 bytes of user code and 51745 bytes of library code and auxiliary data (e.g. jumptables or alignment data). User code consists of 72821 machine instructions which form 610 user functions of average size about 119.4 machine instructions / 382.8 bytes. Median user function size equals to 54 machine instructions / 164.5 bytes. As median size is less than average size, we can conclude that there are relatively few huge functions and a lot of small ones. This fact in turn indicates that elma is well-written.

There are 5 functions consisting of “return;” statement only, likely because of conditional compiling.

Five biggest functions in elma are responsible for biker drawing (6587 bytes), LGR loading (5955 bytes), physics calculations (4723 bytes), image mapping (4176 bytes) and picture list in editor (4049 bytes). They occupy 25490 bytes in total, which is about 11% of user code size. Biker drawing, physics calculations and image mapping are that big because of extensive use of vector operations, which are quite heavy. LGR loading and picture list processing are just big.

At least one function in elma was not written by Balazs. It is the function GetDXVersion, responsible for DirectX version detecting, and it was taken from DirectX 5 SDK.

Balazs is overscrupulous in relation to runtime errors. Most of potentially erroneous actions (like I/O operations and memory allocation) are checked, and program terminates in case of any failure. There are 637 calls to program terminating due to errors, which means that every 110 machine instructions are checked in average. By the way, to get actual error messages instead of “Sorry, internal error”, you should create file message.inf of arbitrary content (empty will do as well) in elma directory. This works for all across and elma versions.

Menu background

Menu background is basically a splat of two screens (for the sake of simplicity we will reference them as light and dark) with fancy buttons serving as alphamap — dark screen is shown inside and light screen is shown outside the buttons. If menu animation is off, there are no buttons, and only light screen is used. Both screens are filled in the same pattern with textures stored in elma.res: light screen is filled with szoveg1.pcx and dark screen is filled with szoveg2.pcx.

Menu filling pattern

It is handy to imagine pattern as a collection of rows defined by base points. Each row consists of infinite number of adjoining textures, one of them located at the base point. There are two different filling patterns in elma — for error screen base points have the coordinates of (w/2 × i; h × i) and for menu screen base points have the coordinates of (110 × i; h × i − 47), where w and h are the dimensions of filling texture, and i is the row iterator. The picture could help to understand the concept.

Menus pattern algorithm borrows some needless actions from error screens pattern algorithm, so we can suggest that initially menu background was filled in the same way as error background is filled nowadays, but was later changed to look more attractive.

Elma.res features two secondary textures related to menu background: ures1b.pcx (light) and ures2b.pcx (dark). They are used in shareware version of elma for the background of screen where you are offered to register the game. To be more precise, elma fills primary textures (szoveg*.pcx) with these textures when it is time to show exit screen, and then primary textures are used just as was described earlier. Secondary textures are not used in registered version, but they could not be deleted from elma.res, as they are first loaded into memory and only then registration status is checked. They are useless but required.

Elma assumes that dark texture (szoveg2.pcx, ures2b.pcx) has the same size as corresponding light texture (szoveg1.pcx, ures1b.pcx), so if dark texture is actually bigger than light, only top-left part of it would be used, and if dark texture is smaller than light, there would be undefined (filled with junk) areas on the dark screen. © 2010 domi