Adeline Software was my first video game employer: I guess I could have started in a much worse place! Obviously, over time we tend to skip over the crapy times1 and only remember the rosy memories, but what's sure is that working on Time Commando and Little Big Adventure has taught me a lot.
Quick Start
I don't remember what I was expecting when I joined, but what's sure is that it was not to just be given a computer with full access to everything, a pile of documentation, and a "go ahead, play with the libs, do whatever you want". In January 1995 we were still using MSDOS as the main development platform for the entire team2, but we knew that Windows 95 was coming so there was a transition period where some of the developers were testing and updating internal systems so we could migrate to Windows. When I arrived, my main machine was a 486 DX 50 machine3 with a few megabytes of RAM, a CD ROM Drive4 and a 15 inch VGA monitor5.Network structure
When I arrived, Adeline was still using Novel NetWare with a 10BASE2/BNC based network where basically every computer is connected to every other one. If for some reason6 one of the node failed, the entire network would fail, making diagnostic a long and painful operation7.- PROJECT, with subfolders for each project (so LBA, TIME)
- TOOLS, for the published tools executables (ANIMIT, TCED, ACF tools, ...)
- COMPIL, where you would find the Watcom compiler, TASM, etc...
- PANIER, for personal storage and data exchange
:: NT Server Killer 1.0 (c) "Oop, sorry, did not know!"
:Loop
IF EXIST F:\SOMEFILE\ON.THE\NETWORK\TESTFILE.TXT GOTO FoundIt
GOTO Loop
:FoundIt
(And yes, if you were wondering: Just adding a tiny pause before the GOTO drops the CPU usage to 0%)
The Adeline libraries
I had never used libs before, so I had to ask around about how to even start, and Sébastien Viannay pointed me to a small program which had the basic initializations and displayed a text on the screen.MiniDraw 1.0
The word "minimalist" was not just used randomly: Basically all this program does is to allow you to load or save a picture (specifically 640x480 in 256 colors PCX files), zoom and scroll it, and select a color to freehand draw in full screen mode. That's really all there is, and you can see that on this video.// // // TEST.C // // 5 janvier 95 // // Juste pour tester les libs de menu, et le watcom C // // #include <stdio.h> #include "\projet\lib386\lib_sys\adeline.h" #include "\projet\lib386\lib_sys\lib_sys.h" #include "\projet\lib386\lib_svga\lib_svga.h" #include "\projet\lib386\lib_menu\lib_menu.h" #include "\projet\lib386\lib_3d\lib_3d.h" UBYTE PcxPathname[255]; UBYTE *Image; UBYTE pal[768]; UBYTE *Screen1; UBYTE *Screen2; T_MENU MenuTest; WORD couleur; #define flag_plein 1 WORD interieur; #define id_crayon 1 #define id_ligne 2 #define id_cadre 3 #define id_cercle 4 #define id_charge 5 #define id_sauve 6 #define id_plein 7 #define id_couleur 8 #define id_quitte 9 #define id_fenetre 10 #define id_xpos 15 #define id_ypos 16 #define id_zoom 17 #define id_spy 18 WORD ox,oy; WORD zoom; LONG xs1,xs2,ys1,ys2; LONG xfen1,xfen2,yfen1,yfen2; LONG largeur_fenetre; LONG hauteur_fenetre; WORD spy_var; void ChangeChangeValue(T_MENU *ptrmenu, WORD handle, WORD step, WORD minvar, WORD maxvar, WORD flagaff) { WORD n, nblcb,value; T_CLICK_BOX *ptrlcb ; nblcb = ptrmenu->NbBox ; // Nombre de boites... ptrlcb = ptrmenu->PtrMallocList ; // Début de la liste... for (n=0;n<nblcb;n++) { if ((ptrlcb->Handle==handle) AND (ptrlcb->Type==TYPE_CHANGE_VALUE)) { value=*(ptrlcb->PtrVar); if (value<minvar) value=minvar; if (value>maxvar) value=maxvar; *(ptrlcb->PtrVar)=value; ptrlcb->Mask=step; (ptrlcb+1)->Mask=minvar; (ptrlcb+2)->Mask=maxvar; if (flagaff) DrawBox(ptrmenu,n,NO_FLAG,TRUE); return; } ptrlcb++ ; } } void InitMenus() { InitPalMenu(); OpenMenu(&MenuTest,42,31); AddText(&MenuTest,0,0,7,1,FLAG_CONTOUR,"MiniDraw 1.0"); AddButton(&MenuTest,id_crayon,1,3,7,2,FLAG_CENTRE,"Crayon"); AddButton(&MenuTest,id_ligne,1,5,7,2,FLAG_CENTRE,"Ligne"); AddButton(&MenuTest,id_cadre,1,7,7,2,FLAG_CENTRE,"Cadre"); AddButton(&MenuTest,id_cercle,1,9,7,2,FLAG_CENTRE,"Cercle"); AddButton(&MenuTest,id_charge,1,12,7,2,FLAG_CENTRE,"Charge"); AddButton(&MenuTest,id_sauve,1,14,7,2,FLAG_CENTRE,"Sauve"); AddSwitch(&MenuTest,id_plein,1,17,4,1,FLAG_CENTRE,"PLEIN",&interieur,flag_plein); AddChangeValue(&MenuTest,id_couleur,1,19,7,1,NO_FLAG,"COL:",&couleur,1,0,255); AddButton(&MenuTest,id_quitte,1,28,7,2,FLAG_CENTRE+FLAG_RED,"QUITTER"); AddWindow(&MenuTest,id_fenetre,9,0,33,31,FLAG_PUSHED); GetCoorButton(&MenuTest,id_fenetre,&xfen1,&yfen1,&xfen2,&yfen2); largeur_fenetre=xfen2-xfen1; hauteur_fenetre=yfen2-yfen1; AddChangeValue(&MenuTest,id_xpos,1,25,7,1,NO_FLAG,"X:",&ox,1,-5,639-largeur_fenetre); AddChangeValue(&MenuTest,id_ypos,1,26,7,1,NO_FLAG,"Y:",&oy,1,-5,479-hauteur_fenetre); AddChangeValue(&MenuTest,id_spy,1,21,7,1,NO_FLAG,"?:",&spy_var,1,0,9999); AddChangeValue(&MenuTest,id_zoom,1,23,7,1,NO_FLAG,"Zoom:",&zoom,1,1,600); } void AfficheMenu() { DrawMenu(&MenuTest,0,0); Affiche_fenetre(); } void Affiche_fenetre() { if (zoom==0) { CopyBlock(ox,oy,ox+largeur_fenetre,oy+hauteur_fenetre,Image,xfen1,yfen1,Log); } else { ScaleBox(ox,oy,ox+largeur_fenetre/zoom,oy+hauteur_fenetre/zoom,Image, xfen1,yfen1,xfen2,yfen2,Log); } CopyBlockPhys(xfen1,yfen1,xfen2,yfen2); } void GereMainMenu() { WORD flag=FALSE; WORD choix; WORD i; LONG x,y,k; LONG oldx,oldy; UWORD touche; Cls(); Flip(); AfficheMenu(); Flip(); while (flag==FALSE) { AffMouse(); choix=GereMenu(&MenuTest); switch (choix) { case id_crayon: { CopyScreen(Image,Log); // Affiche l'image Flip(); // -> Ecran oldx=Mouse_X; oldy=Mouse_Y; do { k=Click; if (k==1) { x=Mouse_X; y=Mouse_Y; Line(x,y,oldx,oldy,couleur); CopyBlockPhys(min(x,oldx),min(y,oldy),max(x,oldx),max(y,oldy)); oldx=x; oldy=y; } } while (k!=2); CopyScreen(Log,Image); // Mémorise l'image modifiée Cls(); AfficheMenu(); } break; case id_charge: { *PcxPathname=0; if (FileSelector("Chargement PCX","*.PCX",PcxPathname,SELECT_TEST_EXIST)) { Load_Pcx(PcxPathname,Image,pal); Palette(pal); AfficheMenu(); } } break; case id_sauve: { Save_Pcx("test.pcx",Image,pal); } break; case id_xpos: case id_ypos: { AfficheMenu(); } break; case id_zoom: { ChangeChangeValue(&MenuTest,id_xpos,16,0,639-largeur_fenetre/zoom,1); ChangeChangeValue(&MenuTest,id_ypos,16,0,479-hauteur_fenetre/zoom,1); Affiche_fenetre(); } break; case id_quitte: { CopyScreen(Log,Screen1); if (Confirm("Etes vous sur de vouloir quitter ???","OUI","non")==1) flag=TRUE; CopyScreen(Screen1,Log); Flip(); } break; } } } void Quit(char *mess) { ClearKeyboard(); ClearMouse(); ClearAdelineSystem(); if (Screen1) Free(Screen1); if (Screen2) Free(Screen2); if (Image) Free(Image); printf("%s\n",mess); exit(0); } void main() { int choix; InitAdelineSystem("LBA.CFG",INIT_SVGA); InitMouse(); InitKeyboard(); Screen1=Malloc(640*480+500); RazMem(Screen1,640*480+500); if (!Screen1) Quit("Pas asser de mémoire"); Screen2=Malloc(640*480+500); RazMem(Screen2,640*480+500); if (!Screen2) Quit("Pas asser de mémoire"); Image=Malloc(640*480+500); RazMem(Image,640*480+500); if (!Image) Quit("Pas asser de mémoire"); InitMenus(); GereMainMenu(); Quit("Test OK !!!"); }Just by looking at the code today I can see there's a clear number of defects:
- It's normally not necessary to test if a pointer is not null before freeing it.
- The RazMem11 calls should happen after the zero test
- Comparing to FALSE is generally frowned upon
- No matter the reason for quitting, the program always returns 0 to the caller
- The indentation is not the same everywhere
That's all for today
I'm not sure about what I will be able/allow to share, but the idea is to give you a clear idea of how these games were made. I hope that was interesting so far, I have more material to share, but in the mean time, here is a photo of the Adeline Team relaxing at the terrace in a ski resort, probably in 1995 or 1996.1. Insane crunch, financial difficulties, ...↩
2. Except Frédéric Taquet who used a Silicon Graphics workstation for Softimage 3D work on cut scenes↩
3. The other mostly had 486 DX2-66, so I was a bit annoyed, but in practice this machine running internally at 50mhz was quite up to the task. It was originally used as a server, and was rock solid. I got a Pentium 60 upgrade when we switched to Windows 95↩
4. Laurent Salmeron who was working on a CD driver for the new games had a very fancy drive - possibly a NEC MultiSpin - where you could store multiple discs at the same time. He managed to trigger the CD ejection while it was still spinning, breaking in splinters against the wall behind him!↩
5. Not a very big screen by today's standards, I was lucky that my graphic card supported the 132x50 text mode (possibly even more, but I remember the display to be to fuzzy to be usable)↩
6. Like for example cleaning personnel deciding to make their work easier by nicely rolling together a mess a cable in one of the rooms, resulting in a nice induction coil blocking the data transfer.↩
7. Basically divide and conquer: Split the network in two, if one the two is working then you know the issue is in the other half. Rinse and repeat until you found the problem.↩
8. 320x200 pixels in 256 colors, sometimes referred to as MCGA↩
9. 640x480 pixels instead of 320x200↩
10. Z-Soft PC Paintbrush↩
11. RAZ=Remize A Zero, so RazMem is literally ZeroMemory↩