//*********************************************************** // // TANX2.0a by Stuart Macdonald // Codewarrior Version // main.c 1.7 op // //*********************************************************** /* changes to the code: 14/12/99: ANDY:included commondef.h, included joypad.h added joypad.c added new global variable: int gJoypads[2] for joypad use changed input to work with this library (ProcessUserInput) changed alive attribute of the players to unsigned char changed collision attribute of the players to unsigned char changed 1 = collision / 0 = no collision to true / false changed 1 = alive / 0 = dead to true / false had a look at the sprite routine... seems like the coords of the sprites are set to display out of screen -> therefore resetted them in the ResetPlayer function for testing reasons 23/01/00: STUART:Added weapon icon code to Objects.c Altered DrawAllSprites to include display variable to determine which screens to draw to Fixed firing pad bug 25/01/00: STUART:Added teleport, shield, emp-shock & invisibility icons Added models for above. Added AddPowerup, UsePowerup & UdatePowerup functions to Objects.c Added powerups to DropItemOnMap (Object.c) and InitiliseBldgs (world.c) Added healthbar code to UpdatePlayer and RestartPlayer Added DrawAllBoxes & DrawBox to Display.c Added BoxesStructType0 structure to header.h Added SetHealthBar function to Objects.c 26/01/00: STUART:Fixed queue system for special powerups Added specials effects to UpdatePlayer and UsePowerup functs in Objects.c Added static1 & static2 tims Added static code to CreateObject and UpdateObject in Objects.c Altered DrawTanks in Display.c to allow invisibility on opponent's screen using display int Added Circle pad press to activate specials in ProcessUserInput in main.c 27/01/00: STUART:Added random map routine to world.c Added Minimap routine (world.c) and minimap.tim, pixel1-5.tim 29/01/00: STUART:Finished random map generator, designed 10 sub-maps Added shock effect for emp-shock of tank Added shock 1 & shock2 tims Added beam effect to teleport special, modified beam effect in CreateObject and UpdateObject 31/01/00: STUART:Added tazer gun turret model Added tazer gun electro shot tims Added tazer icon Added tazer code to objects.c 01/02/00: STUART:Modified Flamethrower to fire faster and further 03/02/00: STUART: Added missile launcher turret Added missile launcher icon Added missile launcher smoke trail 03-05/02/00: STUART: Can't remember! Can't find my notes... Think I added victory and perfect text for a kill during game 06/02/00: STUART:Added number sprites 0-9.tim Added UpdateScore routine to display.c Added Speed-up special item Added speed-up icon Added speed-up special code to Objects.c (UsePowerup, UpdatePlayer, DropItemOnMap, etc.) 07/02/00: STUART:Added GreyScreen function to display.c, dims play view for options text Added intro and option screen text sprites Added InitialiseIntroScreen, SetupIntro, SetupOptions and ReadOptionsPad to display.c Altered UpdateScore, removing number sprite routine to below Added LoadNumber to allow number sprites to be used by intro/options screen and scores 08/02/00: STUART:Fixed intro and option screen bug Altered Objects structure, removing powerups arrays, inefficient use of memory since only 2 out of 100 objects use these. Added PowerupHandlerType0 struct to header.h Altered code in objects.c to utilise PowerupHandlerType0 struct Worked on Spike & Blade character art ( nothing to do with tanx2 though! ). */ // changes on the code: // // all source files that contain version 1.0b were changed for restructering the layout // structure changed (if, for, calls, brackets (no brackets needed for case: ... break;) ) // all source code files with a "op" following the 1.0b were changed for optimising // the speed of the code // // some recommendations for the future: // could we use macros instead of all that constants in the code? // i think it would be much more easier to understand, if // we use for example alive = true|false, // switch (SUBTYPE) ... case SUBTYPE_BULLET ... // would be fine, if it would be easier to see what variables are global // you could reach that goal by adding an identifyer to all global // variables (for example: all global variables could start with a "g" ot "the", then // following the variable name // can we convert your building type data to another format: at the moment there are for example // 'P' etc... i would like numbers from 0 to X or chars from 'A' to 'Z' instead (number even better) // so we could speed up your collision detection with buildings a little bit /* FEATURE LIST: feel free to alter that, perhaps we could use the dual shock's analogue sticks as well? better lighting draw only things, that will be on screen The things to be done after the current code is improved are probably: 1. Special weapons & explosions ( 2-3 types ) 2. Updating the tanks armour and dealing with killed tank ( ie. restarting and crediting opponent with a kill to his/her score member ). Can be done in UpdatePlayer. 3. put the weapon icons and a healthbar onscreen. 4. the minimap functionality 5. new weapons. 6. destructable buildings. 7. the Interface (front end, options, etc) 8. Level design ( random, or designed?) 9. Optimisation 10. sounds 11. Alpha release!?! 12. Hope OPM are still looking for decent stuff for their disks... */ /* Well, here goes on the questions: In OBJECT.C : UpdateAllObjects: The case counts down to 1 in each case as that is what the animation subtypes of each object does, ie. counting from a large subtype number to 1 then 0, with frame being used as a counter and lifespan deciding the length of each animation stage. You could reverse this if you want to or think it will improve speed. In the case(EMPTY) slot I had the line: theObjects[n].gsObjectHandler.attribute ^ (1 << 30); it was to reset the semi-transparency to solid again, but I don't think it's necessary, when creating a bullet or missile you could ensure the object isn't semi-transparent instead. I've also commented out CreateObject(EXPLOSION) until we're ready to implement them, although I'm thinking of having 2-3 types of explosion for variety. CreateObject: There should be an error check to return out of the function if FindFreeObject returns -1, to avoid num reaching out of the array of theObjects, something using a linked list for theObjects would avoid altogether! I've been reding c++ mostly recently, so I think it's affecting my c coding! ResetMatrix I've been reading a little of matrices, so I think I understand them a little better ( but not much ), I know an identity matrix is the basic matrix you need to set up for an object. AdvanceModel If we're not having a hovercraft type of tank the code for the hovercraft thrust can be cut out. UpdatePlayer There's a check for damage here which will also use the DamagedBy structure member to give points to the player damaging. CheckCollision I've used a simple method for collision detection here. First, check map tile you're moving into is unoccupied, if it is, get radius of object in map tile ( building/tank/object ) and check object's radius dosn't over lap. Since the models in the game are all roughly square/octagonal, I don't bother to go onto a third bounding box check, therefore I keep the speed up. Careful design of objects to fill a circle reasonably well should give decent collision detection without needing bounding boxes. CheckBuildingCollision I commented out the trees as i didn't like them, and put so many on the map the game slowed down!! SetChassisType I've included this in case we decide to have different chassis in the future. All that changes is the model, armour level and speed, with the exception of the hovercraft which has a different handling system ( see original Tanx for that ). In MAIN.C: I agree with using true|false to simplify the reading of the code. I was also thinking of bit fields if we want to reduce the memory size of the objects array ie: unsigned alive :1; // 1=alive, 0=dead unsigned display :2; // 0=no display,1=left side only, 2=right side only, 3=both sides unsigned depth :4; // 0-15 depth levels unsigned collision :1; // 1=collision, 0=no collision I'm sure this is how the pros use bit fields as flags for various object traits and save memory. Using SUBTYPE_ before types is a good idea, but I'm not sure about global identification - I don't want to end up with too many single letter prefixes - variable names could end up confusing. Do convert the building types to numerical rather than char values - it should make help to make the types tie in with the map values. Feel free to change the pad reading code ( I think I said that already! ). Your changes to the counters are obvious now I see them! thanks. In WORLD.C Feel free to cut the useless loop in LoadLevel. I was thinking we could change this later. Surely there is a way of loading in data files and reading the memory rather than defining large arrays? If there is just going to be snow on the ground ( I have grass as well, then we could lose those blank arrays for the world and simplify InitialiseWorld as well so it just reads the luminosity value. Or we could use some sort of random map generation with a few miniature maps ( say 4x4 tiles ) combined in random order to make up slightly different maps each time ( say 24 x 24 large ). i think Diablo uses this sub-map method to create it's random dungeons. I favour this because the trouble with deathmatch style games is that one player always knows the map better than another which gives him an unfair advantage, random maps would help. I hope this is enough info for you to be able to change whatever code you were wondering about. I've got a pile of graphical stuff to make and think about how the new weapons are going to work as well as the minimap! Are you still thinking about a different graphical look ( ie. Gourard? ). I think what we should do is for you to assume the 'Head Programmer' role and handle the builds for the code as my compiler is playing up and I'm going to try and sort it over the next few days, and you're the better programmer between the two of us so you should be able to grasp the 'big picture' better than I can, I've laid the groundwork, but I think you can make the code better. This would free time for me to work on the models, textures and graphics sound effects later ), and I'll code where you need me to add anything features/functionality ). I guess we'll need some sort of alphabet/number graphics as well. I still haven't had time to dissect the particle code I was given, but when I do I'll let you know ( I've a feeling it might not work with split screens unless we make double the particles and display a seperate set in each screen ). Did I send the code to you with the 'Memtool' I was using for calculating the memory locations of tims and tmds for the batch file? I nabbed it from Rob Swan's Adventure Game code. It works well. Anyway, let me know what you think should be our next step with the code.. Cheers. Stuart. */ // *********************************************************** // library includes // *********************************************************** //#include //#include #include //#include //#include // *********************************************************** // own includes // *********************************************************** #include "header.h" //#include "objects.h" //#include "world.h" //#include "display.h" //#include "sound.h" // *********************************************************** // global variables // *********************************************************** int PLAYING; // game is running? int INGAME; // we're ingame? int gJoypads[2]; // our two joypads int startP1; int startP2; int RotCounter; int level; int counter; int setVictory;// victory condition stuff; int whoWon; // Create and instance of the structure to hold our tank object (tmd) ObjectStructType0 theTank[2]; // tank body and turret ObjectStructType0 theTurret[2]; ObjectStructType0 theObjects[MAX_NO_OF_OBJECTS]; // all our 3D objects SpriteStructType0 theSprites[MAX_NO_OF_SPRITES]; // sprite array for all our sprites LineStructType0 theLines[MAX_NO_OF_LINES]; // line array for all our lines BoxStructType0 theBoxes[MAX_NO_OF_BOXES]; // box array for health bars WorldStructType0 theWorld[2]; // world (map) array WorldStructType0 theBldgs[2]; // and buildings on the map u_long PADstatus = 0; voice_data bang,pickup,missile,missile2,shell2, shell, bang2, ping, alarm; // sound effects // *********************************************************** // extern global variables // *********************************************************** // (mostly stuff from our own libraries, we need direct access to) extern short vab_id; extern GsRVIEW2 view[2]; // We need a viewing system for 3D graphics extern GsOT othWorld[2]; // We need two Ordering Table Headers, one for each buffer // And we need Two Ordering Tables, one for each buffer extern GsOT_TAG otWorld[2][1< theTurret[i].reloadTime) { FireBullet(i); theTurret[i].frame = 0; } } if (gJoypads[i] & SPADci) { if((TankPowerups[i].powerupcounter > 0) && (TankPowerups[i].powerupdelay <= 0))// check if powerup available { UsePowerup(&theTank[i]); TankPowerups[i].powerupdelay = 20; PlaySFX(&ping); PlaySFX(&shell2); } } theTurret[i].frame++; TankPowerups[i].powerupdelay--; } } } //********************************************************************* // fSnap1_0 snapshot code. (make some test screens) //********************************************************************* // Snapshot the current display. u_long FSnapshotDISPENVp(u_long *address) { extern DISPENV GsDISPENV; return ( FSnapshotFRAMEBUFFERp(address, GsDISPENV.disp.x, GsDISPENV.disp.y, GsDISPENV.disp.w, GsDISPENV.disp.h) ); } // Snapshot a specified drawing environment. u_long FSnapshotDRAWENVp(u_long *address, DRAWENV *drawenv) { return ( FSnapshotFRAMEBUFFERp(address, drawenv->clip.x, drawenv->clip.y, drawenv->clip.w, drawenv->clip.h) ); } // Store a 16bit snapshot as a TIM file. u_long FSnapshotFRAMEBUFFERp(u_long *address, u_short x, u_short y, u_short w, u_short h) { RECT rect; u_long *memaddr = (u_long *) (((u_long) address) + ((4 - ((u_long) address) % 4) % 4)); // align with 32bit boundary u_long pixeldatalength = (w*h*2) + 12; // pixel and header size (bytes) u_long timdatalength = pixeldatalength + 8; rect.x = x; rect.y = y; rect.w = w; rect.h = h; *memaddr = (0x00<<8) + 0x10; // version, ID. *(memaddr + 1) = (0<<4) + 2; // no CLUT, 15-bit *(memaddr + 2) = pixeldatalength; // pixel bnum *(memaddr + 3) = (y<<16) + x; *(memaddr + 4) = (h<<16) + w; StoreImage (&rect, memaddr + 5); DrawSync(0); printf("Exit then DSave filename.tim %8x %x\n", memaddr, timdatalength); return timdatalength; } u_long FSnapshotDISPENV(u_long address) { return ( FSnapshotDISPENVp( (u_long *) address) ); } u_long FSnapshotDRAWENV(u_long address, DRAWENV *drawenv) { return ( FSnapshotDRAWENVp( (u_long *) address, drawenv) ); } u_long FSnapshotFRAMEBUFFER(u_long address, u_short x, u_short y, u_short w, u_short h) { return ( FSnapshotFRAMEBUFFERp( (u_long *) address, x,y, w,h) ); } //*********************************************************** // Main function // setting up all important stuff and running the main // loop of the game // exit (clearing before) //*********************************************************** int main (void) { // set up print-to-screen font FntLoad(960, 256); FntOpen(-90, 0, 240, 320, 0, 512); PLAYING = 1; //PadInit(); InitPads(); InitialiseGraphics(); InitialiseAllLights(); InitialiseAllTextures(); InitSound(); InitialiseBldgs(); InitialiseWorld(); //vab_id, program, tone, note, fine(pitch), voll, volr InitSFX(&bang,vab_id, 0, 0, 64, 127, 127); InitSFX(&pickup,vab_id, 0, 1, 64, 127, 127); InitSFX(&missile,vab_id, 0, 2, 64, 127, 127); InitSFX(&missile2,vab_id, 0, 3, 64, 127, 127); InitSFX(&shell2,vab_id, 0, 4, 64, 127, 127); InitSFX(&shell,vab_id, 0, 5, 64, 127, 127); InitSFX(&bang2,vab_id, 0, 6, 64, 127, 127); InitSFX(&ping,vab_id, 0, 7, 64, 127, 127); InitSFX(&alarm,vab_id, 0, 8, 64, 70, 70); // set-up for CD-DA tracks SsSetSerialAttr (SS_CD,SS_MIX,SS_SON); SsSetSerialVol (SS_CD, 127, 127); INGAME = 1; while(INGAME) { whoWon = 0; // victory condition stuff. setVictory = 0; level = 0; RotCounter = 0; LoadLevel(level); //load the level InitialiseAllObjects(); //Initialise the player's tanks theTank[0].allegance = 0; RestartPlayer(&theTank[0],&theTurret[0]); theTank[0].score = 0; InitialiseTurretView(&view[0], 0); theTank[1].allegance = 1; RestartPlayer(&theTank[1],&theTurret[1]); theTank[1].score = 0; InitialiseTurretView(&view[1], 1); theTank[2].alive = false; theTank[3].alive = false; ResetLuminance(); SetupScreenLuminance(); counter = 0; InitialiseIntroScreen(); //GreyScreen (&theBoxes[4], 0, 1); while (PLAYING) { if(setVictory == 0) { UpdatePads(); ProcessUserInput(); // UpdatePlayer then UpdateAllObjects function call // which will advance explosion animations, ai routines, etc. UpdatePlayer (&theTank[0], &theTurret[0]); UpdatePlayer (&theTank[1], &theTurret[1]); UpdateAllObjects (); RotCounter += 32; RotCounter %= 4096; RenderWorld (); ResetLuminance (); //SetupScreenLuminance(); } else { setVictory++; InitialiseSprite(VICTORY_TIM, &theSprites[20].sprite, -48, 20); theSprites[20].sprite.v = 32; theSprites[20].display = whoWon; theSprites[20].depth = 0; theSprites[20].id = 1; //view[whoWon].vpy += setVictory; //view[whoWon].vpz += setVictory; //view[whoWon].vrz -= setVictory; //view[whoWon].vpx += setVictory; if (setVictory > 100) { PLAYING = 0; theSprites[20].id = 0; } RenderWorld (); } counter++; counter %= 101; }// end of PLAYING while }// end of INGAME while StopSound (); ResetGraph (0); return (0); } /* end of code */