//* Application: Blitter Boy game //* Description: main.c - core control module //* Platform: Sony Net Yaroze PlayStation //* Coded by: Chris Chadwick //* System library headers #include #include #include //* Application headers #include "main.h" #include "halloc.h" #include "pad.h" #include "collide.h" #include "icsal.h" #include "procs.h" #include "vizfx.h" #include "levman.h" #include "firing.h" #include "preset.h" #include "screens.h" #define VAB_VH (0x80120000) #define VAB_VB (0x80123e20) short vabstat; //* //* Global variables and macros //* u_long pStat; //* Holds latest pad status read by PadActions() u_long lastFireFrame=0;//* Frame count when last bullet was fired int fireDelay; //* Frame delay between each fired bullet int bullRange; //* Amount of 'distance units' a bullet can travel (1 unit = 32 pixels) int bullSpeed; //* Distance (in pixels) a bullet moves per update int bullLoop; //* Holds RANGE_LOOP iteration counter for bullets int tvStat; //* Holds TeleVator status (see enum in main.h for values) int followDist=30; //* Distance (in pixels) kept between following babies struct SCTRL *lastInLine; //* Pointer to sprite at end of chain (initially Blitter Boy) int babiesSaved; //* Number of babies successfully saved int currLevel; //* Current game level number int gameStat; //* Game status control flag int gameType; //* Game type flag: TYPE_MISSION or TYPE_SURVIVE struct SCTRL *dummyScp; //* Pointer to 1st OSD dummy sprite struct SCTRL *messHudScp; //* Pointer to the 2 linked OSD message HUD sprites //* (NULL when not active) 1st=text sprite 2nd=HUD box sprite int energyAdd; //* (Fixed point) Amount to add to energy bar length (can be + or -) int energyHit; //* (Fixed point) Amount to decrease energy bar length by each time BB is hit int energyLeft; //* Current amount of energy left int ammoAdd; //* (Fixed point) Amount to add to ammo bar length (can be + or -) int ammoUse; //* (Fixed point) Amount to decrease ammo bar length by for every bullet fired int ammoLeft; //* Current amount of PolyGun ammo left GsGLINE *ammoBarp; //* Pointer to array of ammo bar lines int flashCnt; //* Countdown until BB stops flashing after a hit struct SCTRL *scoreScp; //* Pointer to 1st OSD score digit sprite int currScore; //* Holds players current score int BBtrueSpeed; //* BB's normal, non-spurt speed int strafe; //* Strafe bullet dir offset: 0 1 2 3 int bonusMulty; //* Holds bonus multiplier minus 1 (0=X1, 1=X2 etc.) struct SCTRL *bonusMultyScp; //* Pointer to OSD bonus multiplier sprite struct SCTRL *moneybagScp; //* Pointer to 1st OSD money bag sprite int coinsInBag; //* Number of coins currently in money bag struct SCTRL *moonScp[8]; //* Holds pointers to all 8 moons when a moonring is collected struct SCTRL *chaseTarget; //* Pointer to sprite that monsters chase (usually BB) int stopRange; //* Flags to REP_update_range_HUD when fire is pressed struct SCTRL *rangeScp; //* Points to active range HUD box if one exists; NULL otherwise struct SCTRL *decoyScp; //* Points to active decoy BB if one exists; NULL otherwise int babyStrong; //* Flag set to 1 when a milk bottle is collected int polyStat; //* Flag set to 1 when a PolyCart weapon is loaded int tvVoice; //* Holds TeleVator SFX voice channel struct HISCORE hiScore[5]; //* Holds the 5 high score entries int lastFruit; //* Holds code of most recently collected fruit u_int surviveFrames; int flagGone; int flagCnt; struct SCTRL *flagScp; static int flagRow; static int flagCol; static int flagPass; static int flagIdx; static struct GRID_PAIR flagOff[] = { //* Flag position offset pairs (row,col) 6,0, 3,-2, 2,-3, 0,-4, -2,-4, -4,0, -3,3, -2,4, -3,3, -3,4, -3,3, -3,4, -1,3, 3,-1, 3,-2, 2,2, 2,-2, 2,2, 2,-2, 2,2, 2,-2, 2,2, 2,-2, 3,4, -1,-7, -4,-4, -4,-4, -3,-1, -3,1, 0,5, 3,2, 3,-2, 3,-5, -1,-6, -7,-1, -6,-1, -5,-2, 2,6, 6,0, 0,-6, -6,0, 0,6, 3,6, 3,5, -4,2, -3,-6, 2,-1, 2,1, -2,1, -2,-1, 6,0, 6,0, 0,0 //* Indicates end of array data! }; u_int bonusVal[10][6] = { //* Bonus score multiplication LUT { 100, 200, 300, 400, 500, 600}, //* X1 { 200, 400, 600, 800, 1000, 1200}, //* X2 { 300, 600, 900, 1200, 1500, 1800}, //* X3 { 400, 800, 1200, 1600, 2000, 2400}, //* X4 { 500, 1000, 1500, 2000, 2500, 3000}, //* X5 { 600, 1200, 1800, 2400, 3000, 3600}, //* X6 { 700, 1400, 2100, 2800, 3500, 4200}, //* X7 { 800, 1600, 2400, 3200, 4000, 4800}, //* X8 { 900, 1800, 2700, 3600, 4500, 5400}, //* X9 {1000, 2000, 3000, 4000, 5000, 6000} //* X10 }; void (*FireFunc) (void); //* Pointer to bullet firing function for current weapon static struct GRID_PAIR grid[28][28]; //* Randomly holds all object grid coords static struct GRID_PAIR *gridp[28][28]; //* Pointers to all coord pairs in grid[][] static int freeOff; //* Offset in grid[] that holds next free grid coord struct SGROUP preset[NUM_PRESETS]; //* Holds common sprite group settings for faster/easier //* dynamic assignment to game sprites etc. //* Level management variables //int currLevel; int enemyAlive[ENEMY_TYPE_MAX][2]; //* Order: [][0=enemy alive count; 1=enemy min] int mineMin; //* Minimum amount of mines to have on level int mineCount; //* Number of mines currently on level int mineLaying; //* Number of mines being laid by UFO's int emergeDelay; int killCount; int killsPerPickup; int ok2beam; //* struct SCTRL linked list heads struct SCTRL *aliveSprites; //* Points to 1st alive sprite SCTRL in linked list struct SCTRL *freeSprites; //* Points to 1st free sprite in main pool //* Macro function to convert sprite base into an OT priority (0 to 255) #define GetPri(base) (255 - ((base) < 0 ? 0 : ((base) > 255 ? 255 : (base)))) //* Global pointers to sprite & sprite control arrays GsSPRITE *sprt; //* Points to main game sprites struct SCTRL *sCtrl; //* Associated sprite control structures GsSPRITE *shads; //* Points to shadow sprites associated to sprt sprites GsSPRITE *bgs; //* Points to the 4 quadrant backround sprites GsSPRITE *sfront; //* Points to the 2 shop front sprites GsSPRITE *ammoText; //* Points to the ammo bar text sprite //* Pointers to the 4 BG sprites - for easier (quicker?) access (initialized in MainProper) GsSPRITE *bg1, *bg2, *bg3, *bg4; //* Single global used to pass required info to InitSprite() struct { short startX, startY, scaleX, scaleY, mx, my; int sCount, w, h; long rotate; u_char u, v, r, g, b; GsIMAGE *timInfo, *clutInfo; u_long attr; } sSpec; struct HUDS HUDbox[14]; //* Array needed to control the 14 HUD message box sprites struct HUDS *freeHUD; //* Linked list head pointing to 1st free HUD box //* Main memory addresses of TIM's to be copied to Tpages static const u_long timAddress[] = { 0x80090000, //* BB_BG.TIM 0 0x800d0220, //* BLITBOY.TIM 1 0x800d4a60, //* EN_CCLUT.TIM 2 0x800d4aa4, //* EN_PCLUT.TIM 3 0x800d4ae8, //* EN_OCLUT.TIM 4 0x800d4b2c, //* ENEMY1.TIM 5 0x800d8b6c, //* SHADZ.TIM 6 0x800da18c, //* BODYBANG.TIM 7 0x800db1cc, //* XPLODE.TIM 8 0x800db28c, //* BULLBANG.TIM 0x800db6cc, //* GUNSMOKE.TIM 0x800dbb0c, //* BULLET3.TIM 0x800dbbac, //* WNOISE.TIM 0x800dc3ec, //* BABY.TIM 0x800e142c, //* HEART.TIM 0x800e16ec, //* BAB2CLUT.TIM 0x800e1730, //* BAB3CLUT.TIM 0x800e1774, //* UFO_CLUT.TIM 0x800e17b8, //* GS_CLUT3.TIM 0x800e17fc, //* GS_CLUT4.TIM 0x800e1840, //* COIN.TIM 0x800e1d00, //* COINPAL2.TIM 0x800e1d44, //* COINPAL3.TIM 0x800e1d88, //* BB_CHARS.TIM 0x800e6dc8, //* HELMET.TIM 0x800ea7f8, //* BB_SET.TIM 0x800eea18, //* VC_LOGO1.TIM 0x800f1668, //* VC_LOGO2.TIM 0x800f42b8, //* FRONT1.TIM 0x800f49f8, //* FRONT2.TIM 0x800f5138, //* TVBEAM.TIM 0x800f9ab8, //* DUMMY.TIM 0x800f9b78, //* MASKCLUT.TIM 0x800f9bc0, //* SCORESET.TIM 0x800fa1c0, //* POLYCART.TIM 0x800fb9a0, //* EN_WCLUT.TIM 0x800fb9e4, //* RBOWFIRE.TIM 0x800fbaa4, //* F_SPLAT.TIM 0x800fc2e4, //* F_PEAR.TIM 0x800fc524, //* DROPFIRE.TIM 0x800fcb64, //* F_APPLE.TIM 0x800fcda4, //* F_CHERRY.TIM 0x800fcfe4, //* F_LEMON.TIM 0x800fd224, //* F_MELON.TIM 0x800fd464, //* F_ORANGE.TIM 0x800fd6a4, //* F_GRAPES.TIM 0x800fd8e4, //* F_SBERRY.TIM 0x800fdb24, //* DF_CLUT2.TIM 0x800fdb68, //* DF_CLUT3.TIM 0x800fdbac, //* DF_CLUT4.TIM 0x800fdbf0, //* DF_CLUT5.TIM 0x800fdc34, //* SPLASH.TIM 0x800fe474, //* MINE.TIM 0x800fe634, //* MILKBOT.TIM 0x800fea74, //* BURGER.TIM 0x800feeb4, //* PIGGY.TIM 0x800ff2f4, //* POWFLASH.TIM 0x800ff734, //* MOONRING.TIM 0x800ff8f4, //* MOONBALL.TIM 0x800ff954, //* BEANS.TIM 0x800ffd94, //* BONUS.TIM 0x801015d4, //* MULTY.TIM 0x80103414, //* MINECOMP.TIM 0x801034d4, //* MONEYBAG.TIM 0x80103658, //* HOLOBALL.TIM 0x80103898, //* MULTYOSD.TIM 0x80104418, //* RANGEHUD.TIM 0x80104998, //* BATTERY.TIM 0x80104fd8, //* BAK_PAL1.TIM 0x801051fc, //* BAK_PAL2.TIM 0x80105420, //* BAK_PAL3.TIM 0x80105644, //* BAK_PAL4.TIM 0x80105868, //* BAK_PAL5.TIM 0x80105a8c, //* MESS_HUD.TIM 0x8010642c, //* MASK256.TIM 0x80106650, //* HAND.TIM 0x80106810, //* PSX_OPTS.TIM 0x80106a8c, //* BB_ROLL.TIM //0x80106e50, //* NYTITLE.TIM 0x80119c70, //* FLAG.TIM 0x8011a110, //* HALO.TIM 0x8011b4d8, //* UFO.TIM 0x8011bd18, //* PAUSE.TIM 0x8011c038, //* FR_BONUS.TIM 0x8011c878 //* FR_MULTY.TIM }; #define NY_ADDR (0x80106e50) //* NYTITLE.TIM #define TIM_COUNT (sizeof(timAddress) / sizeof(u_long)) int numOfRegTims = TIM_COUNT; //* Number of loaded TIMs //* debug stuff int DEBUG_ON = 0; GsBOXF bbBox[MAXOBJ]; GsLINE topLine[MAXOBJ]; GsIMAGE timData[TIM_COUNT]; //* Maintains info on all loaded TIMs //* Make sprite sorting functions index accessible for rotation on/off sprites void (*primSort[]) (GsSPRITE *, GsOT *, u_short) = { GsSortFastSprite, GsSortSprite }; //* Initialize pointers to LIBPS globals (needed to allow compiler optimizations) short *psdidx = &PSDIDX; u_long *psdcnt = &PSDCNT; short *psdofsx = PSDOFSX; //* is array short *psdofsy = PSDOFSY; //* is array //* Control variables for CLUT rotations static RECT tvl, tvb; static int tvLightsX, tvLightsY, baseLight; static int tvBeamX, tvBeamY, beamBase; RECT plasRect; int plasmaX, plasmaY, plasmaBase; u_int tvLightLock, tvLightCLock; int beamSpeed; //* //* Static function prototypes //* static void GameLoop(void); //* MISSION game loop static void GameLoop2(void); //* SURVIVE game loop static void PadActions(void); //* Controller state analysis and actions static void LoadAllTims(void); //* Move TIMs into frame buffer Tpages static void InitSprite(int firstSprite); //* Uses global 'sSpec' to initialize sprites static void InitBGS(void); //* Initializes 4 quadrant background sprites static void InitNewGame(void); static void InitializeEnv(void); //* General environment initialization static void InitializeLevel(void); static void InitializeSurvive(void); static void ServiceICSALspritesF(void); void StoreScreen (u_long *destination, int x, int y, int w, int h); //*------------------------------------------------------------------------------------------ //* main //*------------------------------------------------------------------------------------- int main() { //* Define heap memory area for allocations //InitHeap(); InitializeEnv(); gameStat = GAME_TITLE_SCREEN; //* Main control loop while (gameStat != GAME_END) { switch (gameStat) { case GAME_LOGO_SCREEN: LogoScreen(); gameStat = GAME_TITLE_SCREEN; break; case GAME_TITLE_SCREEN: TitleScreen(); break; case GAME_MENU_SCREEN: MenuScreen(); break; case GAME_LEVEL_UP: if (currLevel<20) ++currLevel; FX_Peel_Screen(); LevelEndScreen(); break; case GAME_OVER: FX_Meltdown(); if (gameType == MODE_MISSION) { if (currScore > hiScore[4].score) gameStat = GAME_ENTER_SCORE_SCREEN; else gameStat = GAME_TITLE_SCREEN; } else { gameStat = GAME_DEBRIEF_SCREEN; DebriefScreen(); } break; case GAME_QUIT: FX_Meltdown(); gameStat = GAME_TITLE_SCREEN; break; case GAME_START: InitNewGame(); FX_Spin_Smudge(); gameStat = GAME_ON; break; case GAME_ON: if (gameType == MODE_MISSION) GameLoop(); else GameLoop2(); break; case GAME_MOVE_SCREEN: FX_Pixel_Out(100, 0, 200); MoveScreen(); break; case GAME_CONTROL_SCREEN: FX_Splice_Screen(FX_RIP2_SPLICE); ControlScreen(); break; case GAME_HISCORE_SCREEN: FX_Pixel_Out(0, 0, 0); HiscoreScreen(); break; case GAME_ENTER_SCORE_SCREEN: SsUtKeyOn(0, SFX_WOOHOO, 0, 60, 0, 100, 100); EnterScoreScreen(); gameStat = GAME_HISCORE_SCREEN; HiscoreScreen(); gameStat = GAME_TITLE_SCREEN; break; } } return 0; } //* 20x LUT for shadow sprite .v indexing int mult20[] = {0,20,40,60,80,100,120,140,160,180,200}; int sSync=0; //* Debug mode: holds slowest execution speed recorded so far //*------------------------------------------------------------------------------------------ //* GameLoop - for MODE: MISSION //*------------------------------------------------------------------------------------- static void GameLoop(void) { register int i, x; //* Working variables register GsSPRITE *sp; //* Working pointer to a GsSPRITE object register struct SCTRL *scp; //* Working pointer to a SCTRL structure register GsOT *currOT; //* Pointer to the current OT header int activeBuff; //* Current buffer index (toggles between 0 and 1) int hSync=0; //* Debug mode: holds execution speed in hsyncs //ssync goes here int cSync=0; //* Debug mode: holds CPU execution speed in hsyncs int pSync=0; //* Debug mode: holds peak CPU execution speed in hsyncs PACKET *buffs[2]; //* Pointers to the 2 packet buffers - initialized by OpenOT() GsOT *OTs[2]; //* Pointers to the 2 OT headers - initialized by OpenOT() GsGLINE energyBar[8]; GsLINE barBox[4]; GsGLINE ammoBar[8]; GsLINE ammoBox[4]; GsLINE staminaBar[2]; char scrs[] = "1234567"; //* Holds sprintf() converted score chars char *strp; //* Destructible copy of scrs char levStr[] = "LEVEL "; int firstGo,firstCnt; //* Flag to trigger fade-in FX int flashBox; //* Make level number string sprintf(levStr+6, "%d", currLevel); //* Set global pointer to ammo bar lines ammoBarp = ammoBar; //* Initialize energy bar's surrounding box for (i=0; i<4; i++) { barBox[i].attribute = (A_TRANS_ON + A_TRATE_1 + A_DISPLAY_ON); barBox[i].r = 0; barBox[i].g = 0; barBox[i].b = 0; } barBox[0].x0 = ENERGY_BAR_X-1; barBox[1].x0 = ENERGY_BAR_X-2; barBox[2].x0 = ENERGY_BAR_X+BAR_MAX+1; barBox[3].x0 = ENERGY_BAR_X+BAR_MAX; barBox[0].y0 = ENERGY_BAR_Y-2; barBox[1].y0 = ENERGY_BAR_Y-1; barBox[2].y0 = ENERGY_BAR_Y+8; barBox[3].y0 = ENERGY_BAR_Y+9; barBox[0].x1 = ENERGY_BAR_X+BAR_MAX; barBox[2].x1 = ENERGY_BAR_X+BAR_MAX+1; barBox[1].x1 = ENERGY_BAR_X-2; barBox[3].x1 = ENERGY_BAR_X-1; barBox[0].y1 = ENERGY_BAR_Y-2; barBox[2].y1 = ENERGY_BAR_Y-1; barBox[1].y1 = ENERGY_BAR_Y+8; barBox[3].y1 = ENERGY_BAR_Y+9; //* Initialize energy bar for (i=0; i<8; i++) { energyBar[i].attribute = (A_TRANS_ON + A_TRATE_1 + A_DISPLAY_ON); energyBar[i].x0 = ENERGY_BAR_X; energyBar[i].x1 = ENERGY_BAR_X+energyLeft-1; energyBar[i].y0 = ENERGY_BAR_Y+i; energyBar[i].y1 = ENERGY_BAR_Y+i; energyBar[i].r0 = 255; energyBar[i].g0 = 0; energyBar[i].b0 = 0; energyBar[i].r1 = 0; energyBar[i].g1 = 255; energyBar[i].b1 = 0; } //* Initialize stamina bar staminaBar[0].attribute = (A_TRANS_ON + A_TRATE_1 + A_DISPLAY_ON); staminaBar[0].x0 = ENERGY_BAR_X; staminaBar[0].x1 = ENERGY_BAR_X+(energyHit>>12)-1; staminaBar[0].y0 = staminaBar[0].y1 = ENERGY_BAR_Y+3; staminaBar[0].r = 0; staminaBar[0].g = 0; staminaBar[0].b = 0; staminaBar[1] = staminaBar[0]; staminaBar[1].y0++; staminaBar[1].y1++; //* Initialize ammo bar's surrounding box for (i=0; i<4; i++) { ammoBox[i].attribute = (A_TRANS_ON + A_TRATE_1 + A_DISPLAY_ON); ammoBox[i].r = 0; ammoBox[i].g = 0; ammoBox[i].b = 0; } ammoBox[0].x0 = AMMO_BAR_X-1; ammoBox[1].x0 = AMMO_BAR_X-2; ammoBox[2].x0 = AMMO_BAR_X+BAR_MAX+1; ammoBox[3].x0 = AMMO_BAR_X+BAR_MAX; ammoBox[0].y0 = AMMO_BAR_Y-2; ammoBox[1].y0 = AMMO_BAR_Y-1; ammoBox[2].y0 = AMMO_BAR_Y+8; ammoBox[3].y0 = AMMO_BAR_Y+9; ammoBox[0].x1 = AMMO_BAR_X+BAR_MAX; ammoBox[2].x1 = AMMO_BAR_X+BAR_MAX+1; ammoBox[1].x1 = AMMO_BAR_X-2; ammoBox[3].x1 = AMMO_BAR_X-1; ammoBox[0].y1 = AMMO_BAR_Y-2; ammoBox[2].y1 = AMMO_BAR_Y-1; ammoBox[1].y1 = AMMO_BAR_Y+8; ammoBox[3].y1 = AMMO_BAR_Y+9; //* Initialize ammo bar for (i=0; i<8; i++) { ammoBar[i].attribute = (A_TRANS_ON + A_TRATE_1 + A_DISPLAY_ON); ammoBar[i].x0 = AMMO_BAR_X; ammoBar[i].x1 = AMMO_BAR_X+BAR_MAX-1; ammoBar[i].y0 = AMMO_BAR_Y+i; ammoBar[i].y1 = AMMO_BAR_Y+i; ammoBar[i].r0 = 0; ammoBar[i].g0 = 128; ammoBar[i].b0 = 0; ammoBar[i].r1 = 0; ammoBar[i].g1 = 128; ammoBar[i].b1 = 0; } //* Create main game sprite & sprite control arrays OpenSpriteArray(&sprt, MAXOBJ, &sCtrl); //* Create a shadow/reflection sprite for every sprite in sprt OpenSpriteArray(&shads, MAXOBJ, NULL); //* Create the 4 quadrant background sprites OpenSpriteArray(&bgs, 4, NULL); bg4 = (bg3 = (bg2 = (bg1 = bgs) + 1) + 1) + 1; //* Initialize pointers to the 4 sprites //* Create the 2 shop front sprites OpenSpriteArray(&sfront, 2, NULL); //* Create the ammo bar text sprite OpenSpriteArray(&ammoText, 1, NULL); //* Create an OT environment: 2 OT headers, 2 OTs, 2 packet buffers OpenOT(OTs, buffs, 8, MAXOBJ*5); //* MAXOBJ*5 for sprite/shadow/lines/boxes/centre dot InitializeLevel(); SetLevel(currLevel); //* Initial calculation of sprite's bounding-box centre world coords for (scp=aliveSprites; scp; scp=scp->aliveLinkF) { scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); } firstCnt=(firstGo=3*60)-1; ManageLevel(); //* For initial monster placement while (gameStat==GAME_ON) { //********** Main loop start ********** //* Do game level control management if (tvStat==TV_OFF) ManageLevel(); //* Deal with controller input if (firstGo) { if (!--firstGo) sCtrl[SPRT_TVBEAM].ICSALop = turnOffTV; } else { PadActions(); } //* Set pointer to current ordering table header currOT = OTs[activeBuff = GsGetActiveBuff()]; //* Set primitive creation area GsSetWorkBase(buffs[activeBuff]); //* Clear ordering table GsClearOt(0, 0, currOT); //* Service active, ICSAL-controlled sprites ServiceICSALspritesF(); //* BB management when hit and flashing if (flashCnt) { if (--flashCnt & 0x01) { //* Odd count SsUtKeyOn(0, SFX_GUN_RAINBOW, 0, 84, 0, 64, 64); sprt->cx = 16; sprt->r = sprt->g = sprt->b = 128; sCtrl->isLight = 1; shads->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_2 + A_TRANS_ON + A_DISPLAY_ON); } else { //* Even count sprt->cx = 0; shads->r = shads->g = shads->b = 128; sCtrl->isLight = 0; shads->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_3 + A_TRANS_ON + A_DISPLAY_ON); } } //* Energy bar management flashBox = 0; if (energyAdd <= -ONE) { //* Energy level needs reducing... energyAdd += ONE; flashBox = 1; if (--energyLeft) for (i=0; i<8; i++) //* Reduce energy bar by 1 pixel length energyBar[i].x1 -= 1; else { //* No energy left - GAME OVER! if (sCtrl->ICSALop) { //* In middle of a jump! sCtrl->frameCount = 1; //* For immediate movement next time sCtrl->currFrame = 0; //* Reset to 'standing still' frame sprt->u = (sprt->u < 128 ? 0 : 128); } else { //* Not in middle of a jump! sCtrl->ICSALop = boyGameOver; sCtrl->target = &sCtrl[SPRT_TVBEAM]; } sCtrl->subID = BOY_GAME_OVER; //* Also tells check_landing_action what to do! energyAdd = 0; flashCnt = 10000; sCtrl->speed = BBtrueSpeed; sCtrl->repsPerFrame = 4; sCtrl->unintICSAL = 1; //* BB is not controllable anymore! //* Release any following babies while (sCtrl!=lastInLine) { scp = lastInLine; lastInLine = lastInLine->target; //* Will be BB at end scp->target = NULL; scp->ICSALop = babyWander; //* Make them wander again scp->subID = BABY_WANDER; scp->frameLock = LOCK_3FRAMES; scp->currLock = RunNextTime(scp->frameLock); } //* Turn off bar display for (i=0; i<8; i++) energyBar[i].attribute = (A_TRANS_ON + A_TRATE_1 + A_DISPLAY_OFF); } } else if (energyAdd >= ONE) { //* Energy level needs increasing... energyAdd -= ONE; flashBox = 1; if (energyLeft= ONE) { //* Ammo level needs increasing... ammoAdd -= ONE; flashBox = 1; if (ammoLeftfreeLink) scp->spriteLink->u = (*strp++ - '0')<<4; //* //* First add objects to OT that need to be rendered last //* Note: first in at priority 0 ensures they will be rendered last //* //* Add ammo bar text sprite to OT GsSortFastSprite(ammoText, currOT, 0); //* Add stamina bar to OT staminaBar[0].x1 = staminaBar[1].x1 = ENERGY_BAR_X+(energyHit>>12)-1; GsSortLine(&staminaBar[0], currOT, 0); GsSortLine(&staminaBar[1], currOT, 0); //* Add energy bar & ammo bar to OT for (i=0; i<8; i++) { //* Actual bars GsSortGLine(energyBar+i, currOT, 0); GsSortGLine(ammoBar+i, currOT, 0); } for (i=0; i<4; i++) { //* Surrounding boxes GsSortLine(barBox+i, currOT, 0); GsSortLine(ammoBox+i, currOT, 0); } //* Add dummy OSD sprites to OT for (scp=dummyScp; scp; scp=scp->freeLink) { //* Service any active ICSAL procs - can't use ServiceICSALsprites() //* because dummy sprites aren't kept in aliveSprites linked list if (scp->ICSALop) RunICSAL(scp); GsSortSprite(scp->spriteLink, currOT, 0); } //* Add message HUD sprites to OT (if one is active) for (scp=messHudScp; scp; scp=scp->freeLink) { if (scp->ICSALop) RunICSAL(scp); GsSortFastSprite(scp->spriteLink, currOT, 0); } //* Add OSD score digit sprites to OT for (scp=scoreScp; scp; scp=scp->freeLink) GsSortFastSprite(scp->spriteLink, currOT, 0); //* Add OSD money bag sprites to OT if ((scp=moneybagScp->freeLink)->ICSALop) RunICSAL(scp); //* Run ICSAL proc //* Display full level sprite over money bag silhouette sprite GsSortFastSprite(moneybagScp->freeLink->spriteLink, currOT, 0); GsSortFastSprite(moneybagScp->spriteLink, currOT, 0); //* Add OSD bonus multiplier sprite to OT GsSortFastSprite(bonusMultyScp->spriteLink, currOT, 0); //* Add 2 shop front sprites (if visible) to OT if (bg1->y < (-272+16)) { (sfront+1)->x = (sfront->x = bg1->x) + 288; (sfront+1)->y = sfront->y = bg1->y + 496; GsSortFastSprite(sfront, currOT, 0); GsSortFastSprite(sfront+1, currOT, 0); } //* //* Add only alive (in aliveSprites linked list), displayable sprites to OT //* for (scp=aliveSprites; scp; scp=scp->aliveLinkF) { //* Calculate sprite's bounding-box centre world coords scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); //* Add visible sprites to OT... if (SpriteIsVis(scp)) { sp = scp->spriteLink; if (!scp->isLight) //* Sprite is not a light-source so set brightness //* according to its current world Y coord sp->r = sp->g = sp->b = (65 + (scp->wy>>3)); //* Set screen coords for this sprite sp->x = bg1->x + scp->wx + sp->mx; sp->y = (scp->wy - scp->high - sp->h) + bg1->y + 1 + sp->my; //* Call GsSortSprite if sprite has rotation/scaling else call GsSortFastSprite (*primSort[!(sp->attribute & A_ROTATE_OFF)]) (sp, currOT, GetPri((scp->wy-scp->bbYOff+scp->bbH)+bg1->y+7)); } //* Add visible sprite shadows/light reflections to OT... if (scp->shadStat && ShadowIsVis(scp)) { sp = scp->shadLink; if (scp->isLight) { //* Show reflected ground-light for this sprite //* Note: light stays at ShadBase size but brightness //* is set according to sprite rgb and its off-ground height if ((x = (scp->spriteLink->r>>1) - scp->high + 25) < 0) { x = 0; } sp->r = sp->g = sp->b = x; sp->v = mult20[scp->shadBase]; } else //* Show a regular shadow for this sprite //* Note: size alters according to sprite off-ground height sp->v = (x = (scp->shadBase+(scp->high>>3))) > 9 ? 180 : mult20[x]; sp->x = (bgs->x + scp->wxMid) - scp->shadHW; sp->y = (bgs->y + scp->wyMid) - 10; GsSortFastSprite(sp, currOT, 255); } } //* Add the 4 quadrant background sprites to OT GsSortFastSprite(bg1, currOT, 255); GsSortFastSprite(bg2, currOT, 255); GsSortFastSprite(bg3, currOT, 255); GsSortFastSprite(bg4, currOT, 255); //* DEBUG CODE! - show bounding-box collisions and top line for (i=0, scp=aliveSprites; scp; i++, scp=scp->aliveLinkF) { if (DEBUG_ON && pStat&PAD1_R2 && SpriteIsVis(scp) && scp->bbYOff) { topLine[i].x0 = (scp->wx + bg1->x) + scp->bbXOff; topLine[i].y0 = topLine[i].y1 = (scp->wy + bg1->y) - scp->high - scp->topOff + 1; topLine[i].x1 = topLine[i].x0 + scp->bbW - 1; topLine[i].g = 255;//topLine[i].r = topLine[i].b = 50; GsSortLine(&topLine[i], currOT, 0); topLine[i].x0 = topLine[i].x1 = (scp->wx+bg1->x) + scp->bbXOff + (scp->bbW>>1); topLine[i].y0 = topLine[i].y1 = (scp->wy+bg1->y) - scp->bbYOff + (scp->bbH>>1); GsSortLine(&topLine[i], currOT, 0); bbBox[i].attribute = A_TRATE_1+A_TRANS_ON; bbBox[i].x = (scp->wx + bg1->x) + scp->bbXOff; bbBox[i].y = (scp->wy + bg1->y) - scp->bbYOff; bbBox[i].w = scp->bbW; bbBox[i].h = scp->bbH; if (scp->imHit) bbBox[i].r = bbBox[i].g = bbBox[i].b = 255; else bbBox[i].r = bbBox[i].g = bbBox[i].b = 50; GsSortBoxFill(&bbBox[i], currOT, 0); } scp->imHit = 0; //* Reset all hit flags ready for collision detection } if (DEBUG_ON) FntFlush(-1); //* Rotate TV beam colours every frame when beam is visible if (tvStat!=TV_OFF) { if ((tvb.x+=beamSpeed)>192) //* 192 is .cx of second CLUT copy tvb.x = beamBase + (tvb.x-193); //* wrap-around MoveImage(&tvb, tvBeamX, tvBeamY); } //* Rotate TV lights according to current frequency if (tvLightCLock & 0x00000001U) { if (++tvl.x==(baseLight+32)) tvl.x = baseLight; //* wrap-around MoveImage(&tvl, tvLightsX, tvLightsY); //DrawSync(0); } if (!(tvLightCLock>>=1)) tvLightCLock = tvLightLock; //* Reset current TV lights lock //* Rotate plasma clut entries if (++plasRect.x==(plasmaBase+224)) plasRect.x = plasmaBase; //* wrap-around MoveImage(&plasRect, plasmaX, plasmaY); cSync = VSync(1); //* Get CPU execution speed DrawSync(0); //* Wait for GPU to finish drawing hSync = VSync(0); //* Wait for CRT to finish scan GsSwapDispBuff(); //* Switch double buffers //* Set GPU to start drawing primitives registered in OT GsDrawOt(currOT); //* Call fade-in effect if this is 1st scene being drawn if (firstGo==firstCnt) { FX_Spin_Fade_In(); //* Uses image currently being drawn //firstGo = 0; tvVoice = SsUtKeyOn(0, SFX_TELEVATOR, 0, 60, 0, 100, 100); //* Create text sprites scp = CentreText("GET READY_", 0, 4); for (; scp; scp=scp->freeLink) { scp->wy = 350; scp->wx = (-bg1->x + scp->spriteLink->x)+320; //* Off screen for now scp->bbW = 16; scp->bbH = 1; scp->shadBase = 3; scp->ICSALop = textGetReady; } scp = CentreText(levStr, 0, 4); for (; scp; scp=scp->freeLink) { scp->wy = 215; scp->wx = (-bg1->x + scp->spriteLink->x)+320; //* Off screen for now scp->bbW = 16; scp->bbH = 1; scp->shadBase = 3; scp->ICSALop = textLevel; } scp = CentreText("GO_", 0, 4); for (; scp; scp=scp->freeLink) { scp->wy = 350; scp->wx = (-bg1->x + scp->spriteLink->x); scp->bbW = 16; scp->bbH = 1; scp->high = 200; scp->shadBase = 3; scp->shadStat = 0; scp->ICSALop = textGo; } } //* Do collision detection for this as yet unseen frame ActOnCollisions(); if (DEBUG_ON) { FntPrint("HSYNC=%d\n SLOW=%d\n CPU=%d\n PEAK=%d\nENEMY=%d", hSync, sSync, cSync, pSync, enemyAlive[TOTAL_ALIVE][0]); if (hSync>sSync) sSync=hSync; if (cSync>pSync) pSync=cSync; } } //********** Main loop terminal *********** SsUtKeyOff(tvVoice, 0, SFX_TELEVATOR, 0, 60); //* Housekeeping: DrawSync(0); //* Ensure GPU has finished using mem before returning it to heap CloseSpriteArray(sprt, sCtrl); //* Free memory used by sprite & sprite control arrays CloseSpriteArray(shads, NULL); //* Free memory used by shadow sprites CloseSpriteArray(bgs, NULL); //* Free memory used by the 4 background sprites CloseSpriteArray(sfront, NULL); //* Free memory used by the 2 shop front sprites CloseSpriteArray(ammoText, NULL); //* Free memory used by ammo bar text sprite CloseOT(OTs[0], buffs[0]); //* Free memory used by OT environment } //*------------------------------------------------------------------------------------------ //* GameLoop2 - for MODE: SURVIVE //*------------------------------------------------------------------------------------- static void GameLoop2(void) { register int i, x; //* Working variables register GsSPRITE *sp; //* Working pointer to a GsSPRITE object register struct SCTRL *scp; //* Working pointer to a SCTRL structure register GsOT *currOT; //* Pointer to the current OT header int activeBuff; //* Current buffer index (toggles between 0 and 1) int hSync=0; //* Debug mode: holds execution speed in hsyncs //ssync goes here int cSync=0; //* Debug mode: holds CPU execution speed in hsyncs int pSync=0; //* Debug mode: holds peak CPU execution speed in hsyncs PACKET *buffs[2]; //* Pointers to the 2 packet buffers - initialized by OpenOT() GsOT *OTs[2]; //* Pointers to the 2 OT headers - initialized by OpenOT() int firstGo, firstCnt; //* Flag to trigger fade-in FX struct GRID_PAIR *gp; //* Create main game sprite & sprite control arrays OpenSpriteArray(&sprt, MAXOBJ, &sCtrl); //* Create a shadow/reflection sprite for every sprite in sprt OpenSpriteArray(&shads, MAXOBJ, NULL); //* Create the 4 quadrant background sprites OpenSpriteArray(&bgs, 4, NULL); bg4 = (bg3 = (bg2 = (bg1 = bgs) + 1) + 1) + 1; //* Initialize pointers to the 4 sprites //* Create the 2 shop front sprites OpenSpriteArray(&sfront, 2, NULL); //* Create an OT environment: 2 OT headers, 2 OTs, 2 packet buffers OpenOT(OTs, buffs, 8, MAXOBJ*5); //* MAXOBJ*5 for sprite/shadow/lines/boxes/centre dot InitializeSurvive(); SetLevel(0); //* Initial calculation of sprite's bounding-box centre world coords for (scp=aliveSprites; scp; scp=scp->aliveLinkF) { scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); } firstCnt = (firstGo=3*60)-1; ManageLevel(); //* For initial monster placement //* Place initial flag flagRow = 14; flagCol = 14; flagIdx = 0; flagPass = 0; flagCnt = 0; PlaceNextFlag(); while (gameStat==GAME_ON) { //********** Main loop start ********** //* Do game level control management if (tvStat==TV_OFF) ManageLevel(); //* Deal with controller input if (firstGo) { if (!--firstGo) { sCtrl[SPRT_TVBEAM].ICSALop = turnOffTV; flagScp->lifeCount = PICKUP_FRAME_LIFE; surviveFrames = *psdcnt; //* Start counting frames survived } } else { PadActions(); } //* Set pointer to current ordering table header currOT = OTs[activeBuff = GsGetActiveBuff()]; //* Set primitive creation area GsSetWorkBase(buffs[activeBuff]); //* Clear ordering table GsClearOt(0, 0, currOT); //* Service active, ICSAL-controlled sprites ServiceICSALspritesF(); //* Add new flag as necessary... if (flagGone) PlaceNextFlag(); //* BB management when hit and flashing if (flashCnt) { if (sCtrl->subID != BOY_GAME_OVER) { if (sCtrl->ICSALop) { //* In middle of a jump! sCtrl->frameCount = 1; //* For immediate movement next time sCtrl->currFrame = 0; //* Reset to 'standing still' frame sprt->u = (sprt->u < 128 ? 0 : 128); sCtrl->subID = BOY_GAME_OVER; //* Tells check_landing_action what to do! } else { //* Not in middle of a jump! sCtrl->ICSALop = boyGameOver; sCtrl->target = &sCtrl[SPRT_TVBEAM]; } AliveLinkOut(flagScp); //* Kill flag sprite sCtrl->subID = BOY_GAME_OVER; //* Also tells check_landing_action what to do! energyAdd = 0; flashCnt = 10000; sCtrl->speed = BBtrueSpeed; sCtrl->repsPerFrame = 4; sCtrl->unintICSAL = 1; //* BB is not controllable anymore! surviveFrames = (*psdcnt-surviveFrames); //* Record number of frames survived } if (--flashCnt & 0x01) { //* Odd count SsUtKeyOn(0, SFX_GUN_RAINBOW, 0, 84, 0, 64, 64); sprt->cx = 16; sprt->r = sprt->g = sprt->b = 128; sCtrl->isLight = 1; shads->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_2 + A_TRANS_ON + A_DISPLAY_ON); } else { //* Even count sprt->cx = 0; shads->r = shads->g = shads->b = 128; sCtrl->isLight = 0; shads->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_3 + A_TRANS_ON + A_DISPLAY_ON); } } //* //* First add objects to OT that need to be rendered last //* Note: first in at priority 0 ensures they will be rendered last //* //* Add 2 shop front sprites (if visible) to OT if (bg1->y < (-272+16)) { (sfront+1)->x = (sfront->x = bg1->x) + 288; (sfront+1)->y = sfront->y = bg1->y + 496; GsSortFastSprite(sfront, currOT, 0); GsSortFastSprite(sfront+1, currOT, 0); } //* //* Add only alive (in aliveSprites linked list), displayable sprites to OT //* for (scp=aliveSprites; scp; scp=scp->aliveLinkF) { //* Calculate sprite's bounding-box centre world coords scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); //* Add visible sprites to OT... if (SpriteIsVis(scp)) { sp = scp->spriteLink; if (!scp->isLight) //* Sprite is not a light-source so set brightness //* according to its current world Y coord sp->r = sp->g = sp->b = (65 + (scp->wy>>3)); //* Set screen coords for this sprite sp->x = bg1->x + scp->wx + sp->mx; sp->y = (scp->wy - scp->high - sp->h) + bg1->y + 1 + sp->my; //* Call GsSortSprite if sprite has rotation/scaling else call GsSortFastSprite (*primSort[!(sp->attribute & A_ROTATE_OFF)]) (sp, currOT, GetPri((scp->wy-scp->bbYOff+scp->bbH)+bg1->y+7)); } //* Add visible sprite shadows/light reflections to OT... if (scp->shadStat && ShadowIsVis(scp)) { sp = scp->shadLink; if (scp->isLight) { //* Show reflected ground-light for this sprite //* Note: light stays at ShadBase size but brightness //* is set according to sprite rgb and its off-ground height if ((x = (scp->spriteLink->r>>1) - scp->high + 25) < 0) { x = 0; } sp->r = sp->g = sp->b = x; sp->v = mult20[scp->shadBase]; } else //* Show a regular shadow for this sprite //* Note: size alters according to sprite off-ground height sp->v = (x = (scp->shadBase+(scp->high>>3))) > 9 ? 180 : mult20[x]; sp->x = (bgs->x + scp->wxMid) - scp->shadHW; sp->y = (bgs->y + scp->wyMid) - 10; GsSortFastSprite(sp, currOT, 255); } } //* Add the 4 quadrant background sprites to OT GsSortFastSprite(bg1, currOT, 255); GsSortFastSprite(bg2, currOT, 255); GsSortFastSprite(bg3, currOT, 255); GsSortFastSprite(bg4, currOT, 255); // if (DEBUG_ON) FntFlush(-1); //* Rotate TV beam colours every frame when beam is visible if (tvStat!=TV_OFF) { if ((tvb.x+=beamSpeed)>192) //* 192 is .cx of second CLUT copy tvb.x = beamBase + (tvb.x-193); //* wrap-around MoveImage(&tvb, tvBeamX, tvBeamY); } //* Rotate TV lights according to current frequency if (tvLightCLock & 0x00000001U) { if (++tvl.x==(baseLight+32)) tvl.x = baseLight; //* wrap-around MoveImage(&tvl, tvLightsX, tvLightsY); //DrawSync(0); } if (!(tvLightCLock>>=1)) tvLightCLock = tvLightLock; //* Reset current TV lights lock //* Rotate plasma clut entries if (++plasRect.x==(plasmaBase+224)) plasRect.x = plasmaBase; //* wrap-around MoveImage(&plasRect, plasmaX, plasmaY); cSync = VSync(1); //* Get CPU execution speed DrawSync(0); //* Wait for GPU to finish drawing hSync = VSync(0); //* Wait for CRT to finish scan GsSwapDispBuff(); //* Switch double buffers //* Set GPU to start drawing primitives registered in OT GsDrawOt(currOT); //* Call fade-in effect if this is 1st scene being drawn if (firstGo==firstCnt) { FX_Spin_Fade_In(); //* Uses image currently being drawn //firstGo = 0; tvVoice = SsUtKeyOn(0, SFX_TELEVATOR, 0, 60, 0, 100, 100); //* Create text sprites scp = CentreText("GET READY_", 0, 4); for (; scp; scp=scp->freeLink) { scp->wy = 350; scp->wx = (-bg1->x + scp->spriteLink->x)+320; //* Off screen for now scp->bbW = 16; scp->bbH = 1; scp->shadBase = 3; scp->ICSALop = textGetReady; } scp = CentreText("GET ALL FLAGS", 0, 4); for (; scp; scp=scp->freeLink) { scp->wy = 215; scp->wx = (-bg1->x + scp->spriteLink->x)+320; //* Off screen for now scp->bbW = 16; scp->bbH = 1; scp->shadBase = 3; scp->ICSALop = textLevel; } scp = CentreText("GO_", 0, 4); for (; scp; scp=scp->freeLink) { scp->wy = 350; scp->wx = (-bg1->x + scp->spriteLink->x); scp->bbW = 16; scp->bbH = 1; scp->high = 200; scp->shadBase = 3; scp->shadStat = 0; scp->ICSALop = textGo; } } //* Do collision detection for this as yet unseen frame ActOnCollisions(); /* if (DEBUG_ON) { FntPrint("HSYNC=%d\n SLOW=%d\n CPU=%d\n PEAK=%d\nENEMY=%d", hSync, sSync, cSync, pSync, enemyAlive[TOTAL_ALIVE][0]); if (hSync>sSync) sSync=hSync; if (cSync>pSync) pSync=cSync; } */ } //********** Main loop terminal *********** SsUtKeyOff(tvVoice, 0, SFX_TELEVATOR, 0, 60); //* Housekeeping: DrawSync(0); //* Ensure GPU has finished using mem before returning it to heap CloseSpriteArray(sprt, sCtrl); //* Free memory used by sprite & sprite control arrays CloseSpriteArray(shads, NULL); //* Free memory used by shadow sprites CloseSpriteArray(bgs, NULL); //* Free memory used by the 4 background sprites CloseSpriteArray(sfront, NULL); //* Free memory used by the 2 shop front sprites CloseOT(OTs[0], buffs[0]); //* Free memory used by OT environment } //* //* Service functions start here //* //*------------------------------------------------------------------------------------------ //* LoadAllTims - Copy TIM data (registered in timAddress[]) from //* main memory into frame buffer Tpages //*------------------------------------------------------------------------------------- static void LoadAllTims(void) { RECT rect; GsIMAGE timInfo; int i; for(i=0; i>3) & 0x01) { //* Define CLUT destination rectangle rect.x=timInfo.cx; rect.y=timInfo.cy; rect.w=timInfo.cw; rect.h=timInfo.ch; LoadImage(&rect,timInfo.clut); DrawSync(0); //* Wait for completion of data transmission } } } //*------------------------------------------------------------------------------------------ //* InitSprite - Initialize sprite(s) in sprt[] according to global sSpec //* Note: some sSpec members will cause initialization to a default if not specified //*------------------------------------------------------------------------------------- static void InitSprite(int firstSprite) { int i, wMult, tRate; GsSPRITE *spn = (sprt + firstSprite); //* Pointer to first sprite in sprt[] to be set u_short tpage; //* Make sure attr holds correct bit mode for texture sSpec.attr = (sSpec.attr & 0xfcffffff) | ((sSpec.timInfo->pmode & 0x07) << 24); //* Get trans rate from 32bit attr flags tRate = (sSpec.attr & 0x30000000) >> 28; tpage = GetTPage((sSpec.timInfo->pmode & 0x07), tRate, sSpec.timInfo->px, sSpec.timInfo->py); //* Find appropriate width multiplier for texture colour depth wMult = (sSpec.timInfo->pmode & 0x08) ? (sSpec.timInfo->pmode & 0x01 ? 2 : 4 ) : 1; //***** Process 'sSpec' to set default values etc. ***** //* Width and/or height=0: signal to use entire texture if (!sSpec.w || !sSpec.h) { sSpec.w = sSpec.timInfo->pw * wMult; sSpec.h = sSpec.timInfo->ph; } //* Use texture's own CLUT unless another texture's CLUT is specified if (sSpec.clutInfo == NULL) sSpec.clutInfo = sSpec.timInfo; //* Default to normal RGB brightness if not specified if (!(sSpec.r | sSpec.g | sSpec.b)) sSpec.r = sSpec.g = sSpec.b = 0x80; //* Negative mx or my is request for central rotation coords if (sSpec.mx < 0 || sSpec.my < 0) { sSpec.mx = sSpec.timInfo->pw / 2; sSpec.my = sSpec.timInfo->ph / 2; } //* Default to 1:1 scale for x and/or y if not specified if (!sSpec.scaleX) sSpec.scaleX = 1; if (!sSpec.scaleY) sSpec.scaleY = 1; //* Initialize as many sprt[] sprites as requested for (i = sSpec.sCount; i; i--, spn++) { spn->attribute = sSpec.attr; spn->x = sSpec.startX; spn->y = sSpec.startY; spn->w = sSpec.w; spn->h = sSpec.h; spn->tpage = tpage; spn->u = sSpec.u; spn->v = sSpec.v; spn->cx = sSpec.clutInfo->cx; spn->cy = sSpec.clutInfo->cy; spn->r = sSpec.r; spn->g = sSpec.g; spn->b = sSpec.b; spn->mx = sSpec.mx; spn->my = sSpec.my; spn->scalex = sSpec.scaleX * ONE; spn->scaley = sSpec.scaleY * ONE; spn->rotate = sSpec.rotate * ONE; } //* Reset sSpec ready for initialization and next call sSpec.startX = sSpec.startY = 0; sSpec.scaleX = sSpec.scaleY = 0; sSpec.mx = sSpec.my = 0; sSpec.sCount = sSpec.w = sSpec.h = 0; sSpec.rotate = sSpec.attr = 0; sSpec.u = sSpec.v = sSpec.r = sSpec.g = sSpec.b = 0; sSpec.timInfo = sSpec.clutInfo = NULL; } //*------------------------------------------------------------------------------------------ //* InitBGS - Initialize backgound sprites (bgs[]), shop front sprites & ammo bar text sprite //*------------------------------------------------------------------------------------- static void InitBGS(void) { struct SCTRL *scp=sCtrl; //* Used to position BG objects according to BB's position //* Top-left BG tile bg1->attribute = (A_BRIGHT_OFF + A_8BIT_CLUT + A_ROTATE_OFF + A_TRANS_OFF + A_DISPLAY_ON); bg1->x = 160 - scp->wxMid; bg1->y = 120 - scp->wyMid; bg1->w = bg1->h = 256; bg1->tpage = GetTPage(1, 0, timData[TIM_BG256].px, timData[TIM_BG256].py); bg1->u = bg1->v = 0; bg1->cx = timData[TIM_BG256].cx; bg1->cy = timData[TIM_BG256].cy; bg1->r = bg1->g = bg1->b = 128; bg1->mx = bg1->my = 0; bg1->scalex = bg1->scaley = ONE; bg1->rotate = 0; //* Top-right BG tile bg2->attribute = (A_BRIGHT_OFF + A_8BIT_CLUT + A_ROTATE_OFF + A_TRANS_OFF + A_DISPLAY_ON); bg2->x = bg1->x + 256; bg2->y = 120 - scp->wyMid; bg2->w = bg2->h = 256; bg2->tpage = GetTPage(1, 0, timData[TIM_BG256].px+128, timData[TIM_BG256].py); bg2->u = bg2->v = 0; bg2->cx = timData[TIM_BG256].cx; bg2->cy = timData[TIM_BG256].cy; bg2->r = bg2->g = bg2->b = 128; bg2->mx = bg2->my = 0; bg2->scalex = bg2->scaley = ONE; bg2->rotate = 0; //* Bottom-left BG tile bg3->attribute = (A_BRIGHT_OFF + A_8BIT_CLUT + A_ROTATE_OFF + A_TRANS_OFF + A_DISPLAY_ON); bg3->x = 160 - scp->wxMid; bg3->y = bg1->y + 256; bg3->w = bg3->h = 256; bg3->tpage = GetTPage(1, 0, timData[TIM_BG256].px, timData[TIM_BG256].py+256); bg3->u = bg3->v = 0; bg3->cx = timData[TIM_BG256].cx; bg3->cy = timData[TIM_BG256].cy; bg3->r = bg3->g = bg3->b = 128; bg3->mx = bg3->my = 0; bg3->scalex = bg3->scaley = ONE; bg3->rotate = 0; //* Bottom-right BG tile bg4->attribute = (A_BRIGHT_OFF + A_8BIT_CLUT + A_ROTATE_OFF + A_TRANS_OFF + A_DISPLAY_ON); bg4->x = bg1->x + 256; bg4->y = bg1->y + 256; bg4->w = bg4->h = 256; bg4->tpage = GetTPage(1, 0, timData[TIM_BG256].px+128, timData[TIM_BG256].py+256); bg4->u = bg4->v = 0; bg4->cx = timData[TIM_BG256].cx; bg4->cy = timData[TIM_BG256].cy; bg4->r = bg4->g = bg4->b = 128; bg4->mx = bg4->my = 0; bg4->scalex = bg4->scaley = ONE; bg4->rotate = 0; //* Shop front tiles sfront->attribute = (A_BRIGHT_OFF + A_4BIT_CLUT + A_ROTATE_OFF + A_TRANS_OFF + A_DISPLAY_ON); sfront->x = bg1->x; sfront->y = bg1->y + 496; sfront->w = 224; sfront->h = 16; sfront->tpage = GetTPage(0, 0, timData[TIM_FRONT1].px, timData[TIM_FRONT1].py); sfront->u = 0; sfront->v = timData[TIM_FRONT1].py; sfront->cx = timData[TIM_FRONT1].cx; sfront->cy = timData[TIM_FRONT1].cy; sfront->r = 128; sfront->g = 128; sfront->b = 128; sfront->mx = 0; sfront->my = 0; sfront->scalex = ONE; sfront->scaley = ONE; sfront->rotate = 0; (sfront+1)->attribute = (A_BRIGHT_OFF + A_4BIT_CLUT + A_ROTATE_OFF + A_TRANS_OFF + A_DISPLAY_ON); (sfront+1)->x = bg1->x + 288; (sfront+1)->y = bg1->y + 496; (sfront+1)->w = 224; (sfront+1)->h = 16; (sfront+1)->tpage = GetTPage(0, 0, timData[TIM_FRONT2].px, timData[TIM_FRONT2].py); (sfront+1)->u = 0; (sfront+1)->v = timData[TIM_FRONT2].py; (sfront+1)->cx = timData[TIM_FRONT2].cx; (sfront+1)->cy = timData[TIM_FRONT2].cy; (sfront+1)->r = 128; (sfront+1)->g = 128; (sfront+1)->b = 128; (sfront+1)->mx = 0; (sfront+1)->my = 0; (sfront+1)->scalex = ONE; (sfront+1)->scaley = ONE; (sfront+1)->rotate = 0; //* Ammo bar text sprite //* Note: .w and .x are set by MakeAmmoBarText() ammoText->attribute = (A_BRIGHT_OFF + A_4BIT_CLUT + A_ROTATE_OFF + A_TRANS_OFF + A_DISPLAY_ON); ammoText->y = AMMO_BAR_Y+1; ammoText->h = 5; ammoText->tpage = GetTPage(0, 0, timData[TIM_POLYCART].px, timData[TIM_POLYCART].py); ammoText->u = 124; ammoText->v = timData[TIM_POLYCART].py + 40; ammoText->cx = timData[TIM_POLYCART].cx; ammoText->cy = timData[TIM_POLYCART].cy; ammoText->r = ammoText->g = ammoText->b = 128; ammoText->mx = ammoText->my = 0; ammoText->scalex = ammoText->scaley = ONE; ammoText->rotate = 0; MakeAmmoBarText("POLYCART SLOT EMPTY"); } //*------------------------------------------------------------------------------------------ //* PadActions - Read and act on controller status //*------------------------------------------------------------------------------------- static void PadActions(void) { register struct SCTRL *scp=sCtrl; register GsSPRITE *sp=scp->spriteLink; register int boyMoved=1; register u_long lastPad; struct GRID_PAIR *gp; register int x, y; //* Get 32bit controller status from buffers lastPad = pStat; pStat = ReadPadStat(); //* Process Blitter Boy's movement... //* Note: can't control BB while TV is active or energy is out if (!scp->unintICSAL) { if (pStat & PAD1_LEFT) { //* Sprite frame control management if (!(--scp->frameCount)) { scp->frameCount = scp->repsPerFrame; scp->currFrame = ++scp->currFrame & 0x3; } if (pStat & PAD1_UP) { //* up-left diagonal BOY_MOVE_UP_LEFT BOY_BB_UP_LEFT } else if (pStat & PAD1_DOWN) { //* down-left diagonal BOY_MOVE_DOWN_LEFT BOY_BB_DOWN_LEFT } else { //* left on its own BOY_MOVE_LEFT BOY_BB_LEFT } } else if (pStat & PAD1_RIGHT) { //* Sprite frame control management if (!(--scp->frameCount)) { scp->frameCount = scp->repsPerFrame; scp->currFrame = ++scp->currFrame & 0x3; } if (pStat & PAD1_UP) { //* up-right diagonal BOY_MOVE_UP_RIGHT BOY_BB_UP_RIGHT } else if (pStat & PAD1_DOWN) { //* down-right diagonal BOY_MOVE_DOWN_RIGHT BOY_BB_DOWN_RIGHT } else { //* right on its own BOY_MOVE_RIGHT BOY_BB_RIGHT } } else if (pStat & PAD1_UP) { //* up on its own //* Sprite frame control management if (!(--scp->frameCount)) { scp->frameCount = scp->repsPerFrame; scp->currFrame = ++scp->currFrame & 0x3; } BOY_MOVE_UP BOY_BB_UP } else if (pStat & PAD1_DOWN) { //* down on its own //* Sprite frame control management if (!(--scp->frameCount)) { scp->frameCount = scp->repsPerFrame; scp->currFrame = ++scp->currFrame & 0x3; } BOY_MOVE_DOWN BOY_BB_DOWN } else { //* No direction pressed... scp->frameCount = 1; //* Triggers immediate movement next time a direction is pressed scp->currFrame = 0; //* Reset to 'standing still' frame sprt->u = (sprt->u < 128 ? 0 : 128); scp->accelX = scp->accelY = 0; boyMoved = 0; //* Flag for 'energy spurt' } //* Set BB's world X coord and BG sprites X coords scp->wx = (scp->wxMid += scp->accelX) - (scp->bbXOff + (scp->bbW >> 1)); if (scp->wxMid < 160) { bg1->x = bg3->x = 0; bg2->x = bg4->x = 256; if ((scp->wx + scp->bbXOff) < 22) { scp->wx = 22 - scp->bbXOff; scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); } } else if (scp->wxMid > 352) { bg1->x = bg3->x = SCROLLX_MAX; bg2->x = bg4->x = SCROLLX_MAX + 256; if ((scp->wx + scp->bbXOff + scp->bbW) > 490) { scp->wx = 490 - scp->bbXOff - scp->bbW; scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); } } else { bg1->x = bg3->x = 160 - scp->wxMid; bg2->x = bg4->x = bg1->x + 256; } //* Set BB's world Y coord and BG sprites Y coords scp->wy = (scp->wyMid += scp->accelY) + (scp->bbYOff - (scp->bbH >> 1)); if (scp->wyMid < 120) { bg1->y = bg2->y = 0; bg3->y = bg4->y = 256; if ((scp->wy - scp->bbYOff) < 40) { scp->wy = 40 + scp->bbYOff; scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); } } else if (scp->wyMid > 392) { bg1->y = bg2->y = SCROLLY_MAX; bg3->y = bg4->y = SCROLLY_MAX + 256; if ((scp->wy - scp->bbYOff + scp->bbH) > 512) { scp->wy = 512 - scp->bbH + scp->bbYOff; scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); } } else { bg1->y = bg2->y = 120 - scp->wyMid; bg3->y = bg4->y = bg1->y + 256; } //* Bullet firing management if ((pStat & PAD1_CROSS) && ((*psdcnt - lastFireFrame) >= fireDelay)) { if (pStat & PAD1_R2) strafe = (++strafe & 0x00000003); //* Set next strafe bullet direction else strafe = 0; //* Reset to normal forward bullet stopRange = 1; //* Flag to REP_update_range_HUD that fire has been pressed (*FireFunc) (); //* Fire appropriate bullet type ammoAdd -= ammoUse; //* Decrease PolyCart ammo level } //* BB jump if ((pStat & PAD1_CIRCLE) && !sCtrl->ICSALop) sCtrl->ICSALop = boyJump; //* BB energy spurt if (pStat & PAD1_SQUARE) { if (boyMoved) { sCtrl->speed = BBtrueSpeed<<1; //* BB now twice as fast! sCtrl->repsPerFrame = 3; energyAdd -= (ONE>>3); } else { sCtrl->speed = BBtrueSpeed; sCtrl->repsPerFrame = 4; } } else { sCtrl->speed = BBtrueSpeed; sCtrl->repsPerFrame = 4; } } //* Program termination if((pStat & PAD1_SELECT) && (pStat & PAD1_START)) { gameStat = GAME_END; return; } if ((pStat & PAD1_START) && !(lastPad & PAD1_START)) { //* Enter PAUSE menu... DEBUG_ON = !DEBUG_ON; sSync = 0; PauseScreen(); } if(pStat & PAD1_SELECT) { VSync(20); SetVideoMode(MODE_NTSC); GsInitGraph(SCREEN_X, SCREEN_Y, GsOFSGPU, 0, 0); GsDefDispBuff(0, 0, 0, SCREEN_Y); } //* add a polycart if (pStat & PAD1_R1) { //* Get the x,y coords of an 'empty' grid position gp = GetRandGrid(); //* Convert grid coords to world coords for 16x16 sprite //* Note: odd y grid coords cause x to be right-shifted by //* 8 pixels making the grid a preferable 'brickwork' style y = 55 + (gp->r<<4); x = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); //* Get 2 sprites for PolyCart + HUD box scp = GetSprite(2); //* Get a random PolyCart type //pType = (*psdcnt & 0x00000001) + 0x100; //* 0x100 is PolyCart ID bit //* Initialize PolyCart sprite PresetSprite(scp, PRESET_POLYCART); scp->subID = CART_FRUIT_DROP; scp->wx = x; scp->wy = y; AliveLinkIn(scp); //* Initialize HUD box sprite sp = (scp = scp->freeLink)->spriteLink; scp->subID = GetHUDidx(); sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_ON + A_DISPLAY_ON); sp->w = 0; sp->h = 16; sp->tpage = GetTPage(0, 0, timData[TIM_POLYCART].px, timData[TIM_POLYCART].py); sp->v = HUDbox[scp->subID].v; sp->u = HUDbox[scp->subID].u; sp->cx = timData[TIM_POLYCART].cx; sp->cy = timData[TIM_POLYCART].cy; scp->wx = x-10; scp->wy = y+600; scp->high = 620; scp->shadStat = 0; scp->isLight = 1; //* To prevent RGB alteration scp->ICSALop = fruitDropHUD; AliveLinkIn(scp); } if (pStat & PAD1_L1) { //* pause/screen grab while ((pStat=ReadPadStat()) & PAD1_L1) if(pStat & PAD1_SELECT) { StoreScreen ((u_long *)0x80090000, 0, !*psdidx*240, 320, 240); VSync(60); } } if ((pStat & PAD1_L2)) //* slo-mo VSync(20); if (pStat & PAD1_TRIANGLE) { //energyAdd += (50<<12); // VSync(10); gp=GetRandGrid(); //* Create mine sprite at required world coords scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_MILKBOT); scp->wx = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); scp->wy = 55 + (gp->r<<4); AliveLinkIn(scp); gp=GetRandGrid(); //* Create mine sprite at required world coords scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_BEANS); scp->wx = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); scp->wy = 55 + (gp->r<<4); AliveLinkIn(scp); gp=GetRandGrid(); //* Create mine sprite at required world coords scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_PIGGY); scp->wx = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); scp->wy = 55 + (gp->r<<4); AliveLinkIn(scp); gp=GetRandGrid(); //* Create mine sprite at required world coords scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_MOONRING); scp->wx = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); scp->wy = 55 + (gp->r<<4); AliveLinkIn(scp); gp=GetRandGrid(); //* Create mine sprite at required world coords scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_BURGER); scp->wx = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); scp->wy = 55 + (gp->r<<4); AliveLinkIn(scp); gp=GetRandGrid(); //* Create mine sprite at required world coords scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_HOLOBALL); scp->wx = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); scp->wy = 55 + (gp->r<<4); AliveLinkIn(scp); gp=GetRandGrid(); //* Create mine sprite at required world coords scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_BATTERY); scp->wx = 28 + (gp->c<<4) + ((gp->r & 0x00000001)<<3); scp->wy = 55 + (gp->r<<4); AliveLinkIn(scp); } } //*------------------------------------------------------------------------------------------ //* SpriteIsVis - Checks to see if a given sprite is displayable on-screen //*------------------------------------------------------------------------------------- int SpriteIsVis(register struct SCTRL *scp) { register int sx, sy; register GsSPRITE *sp; sp = scp->spriteLink; sx = bg1->x + scp->wx; sy = (scp->wy - scp->high - sp->h) + bg1->y + 1; return ((sx + sp->w > 0) && (sx < SCREEN_X)) && ((sy + sp->h > 0) && (sy < SCREEN_Y)); } //*------------------------------------------------------------------------------------------ //* ShadowIsVis - Checks to see if a given sprite's shadow is displayable on-screen //*------------------------------------------------------------------------------------- int ShadowIsVis(register struct SCTRL *scp) { register int sx, sy; sx = bg1->x + scp->wx; sy = bgs->y + scp->wy - scp->bbYOff - ((20 - scp->bbH) >> 1); return ((sx + scp->shadLink->w > 0) && (sx < SCREEN_X)) && \ ((sy + 20 > 0) && (sy < SCREEN_Y)); } //*------------------------------------------------------------------------------------------ //* InitNewGame - Initializations for start of new game //*------------------------------------------------------------------------------------- static void InitNewGame(void) { if (gameType==MODE_MISSION) { energyLeft = BAR_MAX; energyAdd = 0; energyHit = (28<<12); currScore = 0; bullRange = 2; currLevel = 1; BBtrueSpeed = 1; bonusMulty = 0; coinsInBag = 0; strafe = 0; } else { bullRange = 4; BBtrueSpeed = 1; strafe = 0; } } //*------------------------------------------------------------------------------------------ //* InitializeEnv - General environment initialization //*------------------------------------------------------------------------------------- static void InitializeEnv(void) { int i, n, x, y; int activeBuff; //* Current buffer index (toggles between 0 and 1) GsOT *currOT; //* Pointer to the current OT header PACKET *buffs[2]; //* Pointers to the 2 packet buffers - initialized by OpenOT() GsOT *OTs[2]; //* Pointers to the 2 OT headers - initialized by OpenOT() RECT r; GsSPRITE s1, s2; GsIMAGE timInfo; SetVideoMode(TV_MODE); PadInit(); //* Get controller status reception buffers //* Drawing and Display environment setting GsInitGraph(SCREEN_X, SCREEN_Y, GsOFSGPU, 0, 0); GsDefDispBuff(0, 0, 0, SCREEN_Y); /* DrawSync(0); setRECT(&r, 0, 0, 320, 480); //* CLS both buffers ClearImage(&r, 0, 0, 0); DrawSync(0); GsGetTimInfo((u_long *)(NY_ADDR+4), &timInfo); //* Load NY_TITLE tim setRECT(&r, timInfo.px, timInfo.py, timInfo.pw, timInfo.ph); LoadImage(&r, timInfo.pixel); DrawSync(0); setRECT(&r, timInfo.cx, timInfo.cy, timInfo.cw, timInfo.ch); LoadImage(&r, timInfo.clut); DrawSync(0); OpenSpriteArray(&sprt, 10, NULL); OpenOT(OTs, buffs, 1, 10); s1.attribute = s2.attribute = (A_BRIGHT_OFF + A_8BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); s1.x = 0; s2.x = 256; s1.y = s2.y = 0; s1.w = 256; s2.w = 64; s1.h = s2.h = 240; s1.tpage = GetTPage(1, 0, timInfo.px, timInfo.py); s2.tpage = s1.tpage+2; s1.u = s2.u = 0; s1.v = s2.v = 0; s1.cx = s2.cx = timInfo.cx; s1.cy = s2.cy = timInfo.cy; currOT = OTs[activeBuff = GsGetActiveBuff()]; GsSetWorkBase(buffs[activeBuff]); GsClearOt(0, 0, currOT); GsSortFastSprite(&s1, currOT, 0); GsSortFastSprite(&s2, currOT, 0); GsDrawOt(currOT); DrawSync(0); VSync(0); GsSwapDispBuff(); VSync(60*5); DrawSync(0); CloseSpriteArray(sprt, NULL); CloseOT(OTs[0], buffs[0]); */ //* Load debug font and open stream FntLoad(704, 256); FntOpen(8, 8, 304, 224, 0, 38*28); //* Copy all TIMs registered in timAddress[] to VRAM LoadAllTims(); printf("\nTIMs registered = %d\n", numOfRegTims); //* Load sfx vabstat = SsVabTransfer((u_char *)VAB_VH, (u_char *)VAB_VB, -1, 1); printf("\nVAB ID = %d\n", vabstat); SsSetMVol(127, 127); SetSpritePresets(); //* Make 2 copies (side-by-side) of TeleVator CLUT entries 96-127 //* for TV light rotation //* Note: placed directly below original clut tvLightsX = tvl.x = timData[TIM_BG256].cx + 96; tvLightsY = tvl.y = timData[TIM_BG256].cy; tvl.w = 32; tvl.h = 1; MoveImage(&tvl, tvl.x-96, tvl.y+1); //* First copy DrawSync(0); MoveImage(&tvl, tvl.x-64, tvl.y+1); //* Second copy DrawSync(0); baseLight = tvl.x -= 96; tvl.y++; tvLightLock = tvLightCLock = LOCK_3FRAMES; //* Make 2 copies (side-by-side) of TV beam clut entries 64-255 //* for beam colour rotation //* Note: placed directly below original clut (will be 6 tpages wide) tvb.x = tvBeamX = timData[TIM_TVBEAM].cx + 64; tvb.y = tvBeamY = timData[TIM_TVBEAM].cy; tvb.w = 192; tvb.h = 1; MoveImage(&tvb, tvb.x-64, tvb.y+1); DrawSync(0); MoveImage(&tvb, tvb.x+128, tvb.y+1); DrawSync(0); beamBase = tvb.x -= 64; tvb.y++; beamSpeed = 2; //* Make 2 copies (side-by-side) of plasma CLUT entries 32-255 //* Note: placed directly below original clut plasmaX = plasRect.x = timData[TIM_BB_CHARS].cx + 32; plasmaY = plasRect.y = timData[TIM_BB_CHARS].cy; plasRect.w = 224; plasRect.h = 1; MoveImage(&plasRect, plasRect.x-32, plasRect.y+1); //* First copy DrawSync(0); MoveImage(&plasRect, plasRect.x+192, plasRect.y+1); //* Second copy DrawSync(0); plasmaBase = plasRect.x -= 32; plasRect.y++; plasRect.w = 64; //* Initialize high score table sprites... strcpy(hiScore[0].name, "BEAT"); strcpy(hiScore[1].name, "THIS"); strcpy(hiScore[2].name, "A.B."); strcpy(hiScore[3].name, "C.D."); strcpy(hiScore[4].name, "E.F."); hiScore[0].score = 10000; hiScore[1].score = 9000; hiScore[2].score = 8000; hiScore[3].score = 7000; hiScore[4].score = 6000; } //*------------------------------------------------------------------------------------------ //* InitializeLevel //*------------------------------------------------------------------------------------- static void InitializeLevel(void) { int i, x, r, c, row, col, dx, dy; u_char u, v, temp; struct SCTRL *scp; GsSPRITE *sp; struct GRID_PAIR temp2; RECT src; int clutNo; //* Copy required BG colour scheme into BG TIM clut area //* Note: cycles through the 5 available colour schemes clutNo = TIM_BG_CLUT1 + ((currLevel-1)%5); src.x = timData[clutNo].cx; src.y = timData[clutNo].cy; src.w = 256; src.h = 1; MoveImage(&src, timData[TIM_BG256].cx, timData[TIM_BG256].cy); DrawSync(0); //* Reset pointers to the 8 moonball sprites for (i=0; i<8; i++) moonScp[i] = &preset[PRESET_POOL_DEFAULT].sctrl; //* Initialize HUD message box sprite controllers freeHUD = HUDbox; //* Set linked list head dx = timData[TIM_POLYCART].px+1; //* 1 is 4 4bit pixels dy = timData[TIM_POLYCART].py+2; u = 0; v = timData[TIM_POLYCART].py; for (i=0; i<14; i++) { //* Note: .src is initially set ready to copy //* a 'clean' HUD box into the actual sprite //* area in REP_update_HUD_box which then sets //* .src ready for font image copying. HUDbox[i].src.w = 9; //* 9 is 36 4bit pixels HUDbox[i].src.h = 16; HUDbox[i].src.x = timData[TIM_POLYCART].px+54; //* 54 is 216 4bit pixels HUDbox[i].src.y = timData[TIM_POLYCART].py+32; HUDbox[i].tlX = (HUDbox[i].destX = HUDbox[i].leftX = dx) - 1; HUDbox[i].tlY = (HUDbox[i].destY = dy) - 2; HUDbox[i].charsDone = 0; HUDbox[i].u = u; HUDbox[i].v = v; HUDbox[i].freeLink = (HUDbox+i+1); if (i==6) { //* Do second row of 7 boxes dy += 16; dx = timData[TIM_POLYCART].px+1; u = 0; v += 16; } else { dx += 9; //* 9 is 36 4bit pixels wide u += 36; } } HUDbox[13].freeLink = NULL; //* Terminate linked list //* Reset TeleVator // tvStat = TV_OFF; // beamSpeed = 2; // tvLightLock = tvLightCLock = LOCK_3FRAMES; FireFunc = NormalFire; fireDelay = 8; bullSpeed = 4; if (bullRange > 2) //* Reduce range if above minimum --bullRange; bullLoop = (bullRange<<3); if ((energyHit>>12) < 28) //* Hit reduction is below normal... energyHit += (ONE<<2); //* Increase hit by 4 polyStat = 0; rangeScp = decoyScp = messHudScp = NULL; babyStrong = 0; energyAdd = 0; ammoAdd = 0; ammoUse = 0; ammoLeft = BAR_MAX; flashCnt = 0; babiesSaved = 0; lastInLine = sCtrl; //* Blitter Boy is initially sprite to be follwed by babies chaseTarget = sCtrl; lastFruit = 0xFF; //* Invalid fruit code so 1st fruit collected never matches //* Initialize object position grid array... //* First, create all possible coordinate pairs for (r=0; r<28; r++) for (c=0; c<28; c++) { grid[r][c].r = r; grid[r][c].c = c; } //* Mix .c coords in each row for (r=0; r<28; r++) for (c=0; c<28; c++) { i = rand() % 28; temp = grid[r][i].c; grid[r][i].c = grid[r][c].c; grid[r][c].c = temp; } //* Mix coord pairs in each column for (c=0; c<28; c++) for (r=0; r<28; r++) { i = rand() % 28; temp2 = grid[i][c]; grid[i][c] = grid[r][c]; grid[r][c] = temp2; } freeOff = 0; //* Reset grid offset //* Initialize gridp[] array which holds pointers to all //* coord pairs in grid[] (needed by GetAbsGrid) for (r=0; r<28; r++) for (c=0; c<28; c++) { row = grid[r][c].r; col = grid[r][c].c; gridp[row][col] = &grid[r][c]; } //* Link sprites to associated shadow/reflection sprite and //* initialize/defrag free sprite linked list freeSprites = NULL; for (i=MAXOBJ-1; i>=0; i--) { sCtrl[i].shadLink = &shads[i]; PutSprite(&sCtrl[i]); } GetSprite(1); //* Remove sprt[0] (always SPRT_BLITBOY) from pool GetSprite(1); //* Remove sprt[1] (always SPRT_TVBEAM) from pool GetSprite(6); //* Remove the 6 baby sprites from pool aliveSprites = NULL; //* Initialize TV beam sprite sSpec.attr = (A_BRIGHT_ON + A_8BIT_CLUT + A_ROTATE_OFF + A_TRATE_2 + A_TRANS_ON + A_DISPLAY_ON); sSpec.sCount = 1; sSpec.w = 40, sSpec.h = 145; sSpec.b = 1; sSpec.timInfo = &timData[TIM_TVBEAM]; InitSprite(SPRT_TVBEAM); sprt[SPRT_TVBEAM].b = 0; scp = &sCtrl[SPRT_TVBEAM]; scp->ID = ID_TELEVATOR; scp->bbXOff = 1; scp->bbYOff = 21; scp->bbW = 38; scp->bbH = 21; scp->topOff = 1000; scp->isLight = 1; //* Prevent rgb alteration scp->shadStat = 0; //* Shadow off scp->wx = 236; scp->wy = 284; scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); AliveLinkIn(scp); sprt[SPRT_TVBEAM].r = sprt[SPRT_TVBEAM].g = sprt[SPRT_TVBEAM].b = 64; tvStat = TV_ON; beamSpeed = 4; tvLightLock = tvLightCLock = LOCK_NOFRAMES; sCtrl[SPRT_TVBEAM].ICSALop = turnOnTV; sCtrl[SPRT_TVBEAM].currFrame = 3; //* Initialize Blitter Boy sprite sSpec.attr = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sSpec.sCount = 1; sSpec.w = 32, sSpec.h = 36; sSpec.timInfo = &timData[TIM_BLITBOY]; InitSprite(SPRT_BLITBOY); scp = &sCtrl[SPRT_BLITBOY]; scp->ID = ID_BLITTER_BOY; scp->subID = NORMAL; scp->speed = BBtrueSpeed; scp->repsPerFrame = 4; scp->frameCount = 1; //* Triggers immediate movement next time a direction is pressed scp->currFrame = 0; scp->dir = DIR_DOWN; BOY_BB_DOWN scp->shadBase = 2; //shads[SPRT_BLITBOY].r = shads[SPRT_BLITBOY].g = shads[SPRT_BLITBOY].b = 100; scp->wxMid = sCtrl[SPRT_TVBEAM].wxMid; scp->wyMid = sCtrl[SPRT_TVBEAM].wyMid; scp->wx = (scp->wxMid - scp->bbXOff - (scp->bbW >> 1)); scp->wy = (scp->wyMid + scp->bbYOff - (scp->bbH >> 1)); AliveLinkIn(scp); //* Initialize background sprites //* Note: must be done AFTER BB's position has been set so BG can be centred on him! InitBGS(); //* Initialize all 6 baby sprites sSpec.attr = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sSpec.sCount = 6; sSpec.w = sSpec.h = 32; sSpec.timInfo = &timData[TIM_BABY]; InitSprite(SPRT_BABY1); for (scp=&sCtrl[SPRT_BABY1], i=6; i; scp++, i--) { sp = scp->spriteLink; scp->ID = ID_BABY; scp->subID = BABY_WANDER; scp->speed = 1; //scp->repsPerFrame = 4; //scp->frameCount = 4; scp->frameCount = scp->repsPerFrame = 2; scp->frameLock = LOCK_3FRAMES; scp->currLock = RunNextTime(scp->frameLock); scp->ICSALop = babyWander; //scp->shadBase = 1; //* Set direction-specific members switch (rand()%8) { case DIR_DOWN: SET_MOVE_DOWN BABY_BB_DOWN break; case DIR_DOWN_LEFT: SET_MOVE_DOWN_LEFT BABY_BB_DOWN_LEFT break; case DIR_LEFT: SET_MOVE_LEFT BABY_BB_LEFT break; case DIR_UP_LEFT: SET_MOVE_UP_LEFT BABY_BB_UP_LEFT break; case DIR_UP: SET_MOVE_UP BABY_BB_UP break; case DIR_UP_RIGHT: SET_MOVE_UP_RIGHT BABY_BB_UP_RIGHT break; case DIR_RIGHT: SET_MOVE_RIGHT BABY_BB_RIGHT break; case DIR_DOWN_RIGHT: SET_MOVE_DOWN_RIGHT BABY_BB_DOWN_RIGHT break; } //* Set different starting positions (+40 for top wall size) switch (i) { case 1: scp->wx = 180 - scp->bbXOff - (scp->bbW >> 1); scp->wy = (100+40) + scp->bbYOff - (scp->bbH >> 1); scp->moveCount = scp->moveMax = 100; break; case 2: scp->wx = 331 - scp->bbXOff - (scp->bbW >> 1); scp->wy = (100+40) + scp->bbYOff - (scp->bbH >> 1); scp->moveCount = scp->moveMax = 102; sp->cx = timData[TIM_BABY_CLUT2].cx; sp->cy = timData[TIM_BABY_CLUT2].cy; break; case 3: scp->wx = 100 - scp->bbXOff - (scp->bbW >> 1); scp->wy = (255+40) + scp->bbYOff - (scp->bbH >> 1); scp->moveCount = scp->moveMax = 104; sp->cx = timData[TIM_BABY_CLUT3].cx; sp->cy = timData[TIM_BABY_CLUT3].cy; break; case 4: scp->wx = 411 - scp->bbXOff - (scp->bbW >> 1); scp->wy = (255+40) + scp->bbYOff - (scp->bbH >> 1); scp->moveCount = scp->moveMax = 106; sp->cx = timData[TIM_BABY_CLUT2].cx; sp->cy = timData[TIM_BABY_CLUT2].cy; break; case 5: scp->wx = 180 - scp->bbXOff - (scp->bbW >> 1); scp->wy = (411+40) + scp->bbYOff - (scp->bbH >> 1); scp->moveCount = scp->moveMax = 108; sp->cx = timData[TIM_BABY_CLUT3].cx; sp->cy = timData[TIM_BABY_CLUT3].cy; break; case 6: scp->wx = 331 - scp->bbXOff - (scp->bbW >> 1); scp->wy = (411+40) + scp->bbYOff - (scp->bbH >> 1); scp->moveCount = scp->moveMax = 110; break; } AliveLinkIn(scp); } //* //* On-screen display (OSD) sprites //* //* Initialize 6 dummy sprites for OSD dummyScp = scp = GetSprite(6); for (i=319-16; scp; scp=scp->freeLink) { sp = scp->spriteLink; sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_ON + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); i = (sp->x = i) - 17; sp->y = 6+8; sp->w = sp->h = 16; sp->tpage = GetTPage(0, 0, timData[TIM_DUMMY].px, timData[TIM_DUMMY].py); sp->u = 16; sp->v = timData[TIM_DUMMY].py; sp->cx = timData[TIM_DUMMY].cx; sp->cy = timData[TIM_DUMMY].cy; sp->mx = sp->my = 8; } //* Initialize 10 sprites (7 digits + $ + 2 commas) for OSD score digits x = 8; scoreScp = scp = GetSprite(10); for (i=1; scp; scp=scp->freeLink, i++) { sp = scp->spriteLink; sp->attribute = (A_BRIGHT_OFF + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->y = 6; sp->h = 16; sp->tpage = GetTPage(0, 0, timData[TIM_SCORESET].px, timData[TIM_SCORESET].py); sp->v = timData[TIM_SCORESET].py; sp->cx = timData[TIM_SCORESET].cx; sp->cy = timData[TIM_SCORESET].cy; switch (i) { case 1: //* 'millions' digit sp->x = x+17; sp->w = 16; sp->u = 0; break; case 2: case 3: case 4: //* 'thousands' digits sp->x = x+(i*17)+4; sp->w = 16; sp->u = 0; break; case 5: case 6: case 7: //* 'hundreds' 'tens' 'units' digits sp->x = x+(i*17)+8; sp->w = 16; sp->u = 0; break; case 8: //* $ sign sp->x = x; sp->w = 16; sp->u = 160; break; case 9: //* 1st comma (5x7) sp->x = x+33; sp->y += 11; sp->w = 5; sp->h = 7; sp->v += 9; sp->u = 176; break; case 10: //* 2nd comma (5x7) sp->x = x+88; sp->y += 11; sp->w = 5; sp->h = 7; sp->v += 9; sp->u = 176; break; } } //* Initialize 2 money bag sprites for OSD moneybagScp = scp = GetSprite(2); //* 1st is transparent silhouette sp = scp->spriteLink; sp->attribute = (A_BRIGHT_OFF + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->x = 156; sp->y = 5; sp->w = sp->h = 18; sp->tpage = GetTPage(0, 0, timData[TIM_MONEYBAG].px, timData[TIM_MONEYBAG].py); sp->u = ((timData[TIM_MONEYBAG].px<<2) % 256); sp->v = timData[TIM_MONEYBAG].py; sp->cx = timData[TIM_MONEYBAG].cx; sp->cy = timData[TIM_MONEYBAG].cy; //* 2nd is full level indicator scp = scp->freeLink; sp = scp->spriteLink; sp->attribute = (A_BRIGHT_OFF + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->x = 156; sp->w = 18; sp->tpage = GetTPage(0, 0, timData[TIM_MONEYBAG].px, timData[TIM_MONEYBAG].py); sp->u = ((timData[TIM_MONEYBAG].px<<2) % 256)+18; sp->cx = timData[TIM_MONEYBAG].cx; sp->cy = timData[TIM_MONEYBAG].cy; i = (coinsInBag<<12)/FIX_COINS_PER_SCAN; //* Set sprite to show current full level... sp->v = timData[TIM_MONEYBAG].py+(17-i); sp->h = i; sp->y = 6+(16-i); //* Initialize bonus multiplier OSD sprite bonusMultyScp = scp = GetSprite(1); sp = scp->spriteLink; sp->attribute = (A_BRIGHT_OFF + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->x = 156+18; sp->y = 5; sp->w = 32; sp->h = 18; sp->tpage = GetTPage(0, 0, timData[TIM_MULTYOSD].px, timData[TIM_MULTYOSD].py); sp->u = ((timData[TIM_MULTYOSD].px<<2) % 256); sp->v = timData[TIM_MULTYOSD].py+(bonusMulty*18); sp->cx = timData[TIM_MULTYOSD].cx; sp->cy = timData[TIM_MULTYOSD].cy; } //*------------------------------------------------------------------------------------------ //* InitializeSurvive //*------------------------------------------------------------------------------------- static void InitializeSurvive(void) { int i; struct SCTRL *scp; GsSPRITE *sp; RECT src; //* Copy required BG colour scheme into BG TIM clut area src.x = timData[TIM_BG_CLUT5].cx; src.y = timData[TIM_BG_CLUT5].cy; src.w = 256; src.h = 1; MoveImage(&src, timData[TIM_BG256].cx, timData[TIM_BG256].cy); DrawSync(0); FireFunc = NormalFire; fireDelay = 8; bullSpeed = 4; bullLoop = (bullRange<<3); polyStat = 0; babiesSaved = 0; chaseTarget = sCtrl; energyAdd = 0; ammoAdd = 0; ammoUse = 0; flashCnt = 0; lastInLine = sCtrl; chaseTarget = sCtrl; flagGone = 0; //* Link sprites to associated shadow/reflection sprite and //* initialize/defrag free sprite linked list freeSprites = NULL; for (i=MAXOBJ-1; i>=0; i--) { sCtrl[i].shadLink = &shads[i]; PutSprite(&sCtrl[i]); } GetSprite(1); //* Remove sprt[0] (always SPRT_BLITBOY) from pool GetSprite(1); //* Remove sprt[1] (always SPRT_TVBEAM) from pool aliveSprites = NULL; //* Initialize TV beam sprite sSpec.attr = (A_BRIGHT_ON + A_8BIT_CLUT + A_ROTATE_OFF + A_TRATE_2 + A_TRANS_ON + A_DISPLAY_ON); sSpec.sCount = 1; sSpec.w = 40, sSpec.h = 145; sSpec.b = 1; sSpec.timInfo = &timData[TIM_TVBEAM]; InitSprite(SPRT_TVBEAM); sprt[SPRT_TVBEAM].b = 0; scp = &sCtrl[SPRT_TVBEAM]; scp->ID = ID_TELEVATOR; scp->bbXOff = 1; scp->bbYOff = 21; scp->bbW = 38; scp->bbH = 21; scp->topOff = 1000; scp->isLight = 1; //* Prevent rgb alteration scp->shadStat = 0; //* Shadow off scp->wx = 236; scp->wy = 284; scp->wxMid = scp->wx + scp->bbXOff + (scp->bbW >> 1); scp->wyMid = scp->wy - scp->bbYOff + (scp->bbH >> 1); AliveLinkIn(scp); sprt[SPRT_TVBEAM].r = sprt[SPRT_TVBEAM].g = sprt[SPRT_TVBEAM].b = 64; tvStat = TV_ON; beamSpeed = 4; tvLightLock = tvLightCLock = LOCK_NOFRAMES; sCtrl[SPRT_TVBEAM].ICSALop = turnOnTV; sCtrl[SPRT_TVBEAM].currFrame = 3; //* Initialize Blitter Boy sprite sSpec.attr = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sSpec.sCount = 1; sSpec.w = 32, sSpec.h = 36; sSpec.timInfo = &timData[TIM_BLITBOY]; InitSprite(SPRT_BLITBOY); scp = &sCtrl[SPRT_BLITBOY]; scp->ID = ID_BLITTER_BOY; scp->subID = NORMAL; scp->speed = BBtrueSpeed; scp->repsPerFrame = 4; scp->frameCount = 1; //* Triggers immediate movement next time a direction is pressed scp->currFrame = 0; scp->dir = DIR_DOWN; BOY_BB_DOWN scp->shadBase = 2; //shads[SPRT_BLITBOY].r = shads[SPRT_BLITBOY].g = shads[SPRT_BLITBOY].b = 100; scp->wxMid = sCtrl[SPRT_TVBEAM].wxMid; scp->wyMid = sCtrl[SPRT_TVBEAM].wyMid; scp->wx = (scp->wxMid - scp->bbXOff - (scp->bbW >> 1)); scp->wy = (scp->wyMid + scp->bbYOff - (scp->bbH >> 1)); AliveLinkIn(scp); //* Initialize background sprites //* Note: must be done AFTER BB's position has been set so BG can be centred on him! InitBGS(); } static struct SCTRL *KS[50]; //* Stack of pointers to pickup sprites to be killed //*------------------------------------------------------------------------------------------ //* ServiceICSALspritesF - faster DCache version with special pickup 'flash' handler //* Note: use in main game loop only. Handles max of 256 ICSAL-controlled sprites. //*------------------------------------------------------------------------------------- static void ServiceICSALspritesF(void) { register struct SCTRL **IS = (struct SCTRL **) 0x1f800000; register struct SCTRL *scp; register int i, n; //* First, build stack of active ICSAL sprites from current aliveSprites linked list. //* Note: It is not possible to simply run through the aliveSprites list dealing with //* each ICSAL in turn because some ICSAL functions themselves link sprites in/out //* of aliveSprites which is not advisible while the list itself is being used. for (i=n=0, scp=aliveSprites; scp; scp=scp->aliveLinkF) { if (scp->ICSALop) { if (scp->lifeCount) { //* Only pickups have a life counter... if (--scp->lifeCount < 180) { //* Max of 3 seconds (NTSC) of life left if (scp->lifeCount) { //* Toggle display on/off scp->spriteLink->attribute ^= 0x80000000; if (scp->subID==PICKUP_FLAG && ((scp->lifeCount&0x07)==0x07)) { SsUtKeyOn(0, SFX_LEV_SCREEN, 0, 108-(scp->lifeCount>>2), 0, 127, 127); } } else { //* Time to kill pickup KS[++n] = scp; continue; } } } if (scp->currLock & 0x00000001U) IS[i++] = scp; //* Run this sprite's ICSAL proc if (!(scp->currLock>>=1)) //* Shift currLock bit value scp->currLock = scp->frameLock; //* Is zero, so reset to frameLock } } //* Kill any expired pickups while (n) { scp = KS[n--]; if (scp->subID & 0x100) //* scp is a PolyCart so... scp->freeLink->ICSALop++; //* set it's linked HUD box sprite to close if (scp->subID > PICKUP_BRONZE_COIN) { //* Coins dont use grid positions! if (scp->subID==PICKUP_FLAG) { //* Flag: SURVIVE mode only... flagGone = 1; if (!flashCnt) { //* Only do this for 1st time! flashCnt = 1; SsUtKeyOn(0, SFX_LEV_SCREEN, 0, 72, 0, 127, 127); } } else { PutGrid(scp->wx, scp->wy); //* Make grid position available } } AliveLinkOut(scp); } //* Now run all ICSAL procedures on sprites contained in the stack array... while (i) { if ((scp=IS[--i])->ICSALop) //* In case sprite has been killed by a previous ICSAL call! RunICSAL(scp); } } static struct SCTRL *IS[2000]; //* ICSAL stack array //*------------------------------------------------------------------------------------------ //* ServiceICSALsprites //*------------------------------------------------------------------------------------- void ServiceICSALsprites(void) { register struct SCTRL *scp; register int i; //* First, build stack of active ICSAL sprites from current aliveSprites linked list. //* Note: It is not possible to simply run through the aliveSprites list dealing with //* each ICSAL in turn because some ICSAL functions themselves link sprites in/out //* of aliveSprites which is not advisible while the list itself is being used. for (i=0, scp=aliveSprites; scp; scp=scp->aliveLinkF) { if (scp->ICSALop) { if (scp->currLock & 0x00000001U) IS[i++] = scp; //* Run this sprite's ICSAL proc if (!(scp->currLock>>=1)) //* Shift currLock bit value scp->currLock = scp->frameLock; //* Is zero, so reset to frameLock } } //* Now run all ICSAL procedures on sprites contained in the stack array... while (i) { if ((scp=IS[--i])->ICSALop) //* In case sprite has been killed by a previous ICSAL call! RunICSAL(scp); } } //*------------------------------------------------------------------------------------------ //* AliveLinkIn - Adds sprite with scp to double-linked list of alive sprites //*------------------------------------------------------------------------------------- void AliveLinkIn(register struct SCTRL *scp) { if (aliveSprites) { //* There's at least 1 object in the list... aliveSprites->aliveLinkB = scp; //* link scp in as first object in the list. scp->aliveLinkF = aliveSprites; } aliveSprites = scp; //* Set list head to point at scp object. scp->alive = 1; //* Set alive flag } //*------------------------------------------------------------------------------------------ //* AliveLinkOut - Removes sprite with scp from double-linked list of alive sprites //*------------------------------------------------------------------------------------- void AliveLinkOut(register struct SCTRL *scp) { if (scp->aliveLinkF) //* A forward link exists... scp->aliveLinkF->aliveLinkB = scp->aliveLinkB; //* so adjust pointers to keep link. if (scp->aliveLinkB) //* A backward link exists... scp->aliveLinkB->aliveLinkF = scp->aliveLinkF; //* so adjust pointers to keep link. else //* No backward link exists... aliveSprites = scp->aliveLinkF; //* scp MUST=aliveSprites so adjust. PutSprite(scp); //* Add sprite to pool } //*------------------------------------------------------------------------------------------ //* GetSprite - Returns a pointer to the 1st sprite in a group of numSprites in sprite pool //* Note: all sprites in the group are linked via the .freeLink member which should be used //* to reference each sprite in the group. Pointer arithmetic should NOT be used on the //* returned pointer as the sprites aren't guaranteed to be contiguous in memory because //* of possible fragmentation, caused by random calls to GetSprite() and PutSprite(). //*------------------------------------------------------------------------------------- struct SCTRL *GetSprite(register int numSprites) { struct SCTRL *scp = freeSprites, *lp; //* Allocate required sprites from pool & update list head for (; numSprites; numSprites--) { lp = freeSprites; if ((freeSprites=freeSprites->freeLink)==NULL) printf("\nGetSprite: insufficient sprites in pool!"); } lp->freeLink = NULL; //* Break link with remaining pool sprites return scp; } //*------------------------------------------------------------------------------------------ //* PutSprite - Returns sprite scp - allocated by GetSprite() - to the sprite pool linked list //*------------------------------------------------------------------------------------- void PutSprite(register struct SCTRL *scp) { register struct SCTRL *sctrlDp=&preset[PRESET_POOL_DEFAULT].sctrl; register GsSPRITE *spriteDp=&preset[PRESET_POOL_DEFAULT].sprite; register GsSPRITE *shadDp=&preset[PRESET_POOL_DEFAULT].shad; //* Make sure spriteLink, shadLink and sectorLink are preserved sctrlDp->spriteLink = scp->spriteLink; sctrlDp->shadLink = scp->shadLink; sctrlDp->sectorLink = scp->sectorLink; //* Set pool sprite to default settings *scp = *sctrlDp; *scp->spriteLink = *spriteDp; if (scp->shadLink) *scp->shadLink = *shadDp; //* Only if there is a shadow sprite to set!!! scp->freeLink = freeSprites; //* Add sprite to start of list freeSprites = scp; //* Point list head at it } //*------------------------------------------------------------------------------------------ //* GetHUDidx //*------------------------------------------------------------------------------------- int GetHUDidx(void) { int idx; if (freeHUD) { //* There is a free HUD box available... idx = (freeHUD - HUDbox); freeHUD = freeHUD->freeLink; } else { //* None left! (for debug only) idx = 0; printf("\nGetHUDidx: no free HUD message boxes available!"); } return idx; } //*------------------------------------------------------------------------------------------ //* PutHUDidx //*------------------------------------------------------------------------------------- void PutHUDidx(int idx) { HUDbox[idx].freeLink = freeHUD; freeHUD = (struct HUDS *) &HUDbox[idx]; } //*------------------------------------------------------------------------------------------ //* CentreText //*------------------------------------------------------------------------------------- struct SCTRL *CentreText(char *text, int topLine, int spacing) { char *tp; char x=0; int numChars=0; int pw=0; int nextX=0; struct SCTRL *charp, *scp; GsSPRITE *sp; //* Count number of characters/sprites required for (tp=text; x=*tp; tp++) { if (x!=' ') //* Spaces aren't sprites numChars++; //* Total up pixel width of entire text string //* according to character's true width switch (x) { case '+': //* 15 pixel wide characters pw += (15+spacing); break; case 'P': case '7': //* 14 pixel wide characters pw += (14+spacing); break; case '\"': case '1': //* 12 pixel wide characters pw += (12+spacing); break; case 'I': case '!': //* 9 pixel wide characters case'(' : case ')': pw += (9+spacing); break; case ';': case ':': //* 5 pixel wide characters case '.': case ',': case '\'': pw += (5+spacing); break; default: //* Regular 16 pixel wide characters pw += (16+spacing); break; } } pw -= spacing; charp = scp = GetSprite(numChars); nextX = (320-pw)>>1; //* Find top-left x coord for first char sprite for (tp=text; x=*tp; tp++) { if (x!=' ') { sp = scp->spriteLink; sp->attribute = (A_BRIGHT_ON + A_8BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->w = sp->h = 16; sp->tpage = GetTPage(1, 0, timData[TIM_BB_SET].px, timData[TIM_BB_SET].py); sp->u = (((x-' ')%8)<<4); sp->v = (((x-' ')>>3)<<4) + timData[TIM_BB_SET].py; sp->cx = timData[TIM_BB_CHARS].cx; sp->cy = timData[TIM_BB_CHARS].cy; //sp->mx=sp->my=7; //sp->scaley=2*ONE; sp->x = nextX; sp->y = topLine; AliveLinkIn(scp); scp = scp->freeLink; } //* Find x coord for next character switch (x) { case '+': //* 15 pixel wide characters nextX += (15+spacing); break; case 'P': case '7': //* 14 pixel wide characters nextX += (14+spacing); break; case '\"': case '1': //* 12 pixel wide characters nextX += (12+spacing); break; case 'I': case '!': //* 9 pixel wide characters case '(': case ')': nextX += (9+spacing); break; case ';': case ':': //* 5 pixel wide characters case '.': case ',': case '\'': nextX += (5+spacing); break; default: //* Regular 16 pixel wide characters nextX += (16+spacing); break; } } return charp; } //*------------------------------------------------------------------------------------------ //* LjustText //*------------------------------------------------------------------------------------- struct SCTRL *LjustText(char *text, int startX, int y, int spacing) { char *tp; char x=0; int numChars=0; int nextX=0; struct SCTRL *charp, *scp; GsSPRITE *sp; //* Count number of characters/sprites required for (tp=text; x=*tp; tp++) { if (x!=' ') //* Spaces aren't sprites numChars++; } charp = scp = GetSprite(numChars); nextX = startX; //* Find top-left x coord for first char sprite for (tp=text; x=*tp; tp++) { if (x!=' ') { sp = scp->spriteLink; sp->attribute = (A_BRIGHT_ON + A_8BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->w = sp->h = 16; sp->tpage = GetTPage(1, 0, timData[TIM_BB_SET].px, timData[TIM_BB_SET].py); sp->u = (((x-' ')%8)<<4); sp->v = (((x-' ')>>3)<<4) + timData[TIM_BB_SET].py; sp->cx = timData[TIM_BB_CHARS].cx; sp->cy = timData[TIM_BB_CHARS].cy; sp->x = nextX; sp->y = y; AliveLinkIn(scp); scp = scp->freeLink; } //* Find x coord for next character switch (x) { case '+': //* 15 pixel wide characters nextX += (15+spacing); break; case 'P': case '7': //* 14 pixel wide characters nextX += (14+spacing); break; case '\"': case '1': //* 12 pixel wide characters nextX += (12+spacing); break; case 'I': case '!': //* 9 pixel wide characters case '(': case ')': nextX += (9+spacing); break; case ';': case ':': //* 5 pixel wide characters case '.': case ',': case '\'': nextX += (5+spacing); break; default: //* Regular 16 pixel wide characters nextX += (16+spacing); break; } } return charp; } //*------------------------------------------------------------------------------------------ //* EjectPolyCart //*------------------------------------------------------------------------------------- void EjectPolyCart(void) { register struct SCTRL *scp; register GsSPRITE *sp; register int i; FireFunc = NormalFire; fireDelay = 8; bullSpeed = 4; bullLoop = (bullRange<<3); //* Set full, green ammo bar ammoLeft = BAR_MAX; ammoAdd = ammoUse = 0; for (i=0; i<8; i++) { ammoBarp[i].x1 = AMMO_BAR_X+BAR_MAX-1; ammoBarp[i].r0 = 0; ammoBarp[i].g0 = 128; ammoBarp[i].b0 = 0; ammoBarp[i].r1 = 0; ammoBarp[i].g1 = 128; ammoBarp[i].b1 = 0; } MakeAmmoBarText("POLYCART SLOT EMPTY"); //* Eject spent cartridge sp = (scp = GetSprite(1))->spriteLink; sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_ON + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->w = sp->h = 16; sp->tpage = GetTPage(0, 0, timData[TIM_POLYCART].px, timData[TIM_POLYCART].py); sp->v = timData[TIM_POLYCART].py+32; sp->u = 0; sp->cx = timData[TIM_POLYCART].cx; sp->cy = timData[TIM_POLYCART].cy; sp->mx = sp->my = 8; scp->speed = 1; scp->bbXOff = 2; scp->bbYOff = 4; scp->bbW = 12; scp->bbH = 6; scp->topOff = 12; scp->high = sCtrl->high + 20; scp->shadBase = 2; scp->ICSALop = cartEject; //scp->frameLock = LOCK_NOFRAMES; scp->currLock = RunNextTime(scp->frameLock); //* Set eject direction to opposite of BB switch (scp->dir = (sCtrl->dir+4) & 0x07) { case DIR_DOWN_LEFT: scp->accelX = -scp->speed; scp->accelY = scp->speed; scp->wx = sCtrl->wx; scp->wy = sCtrl->wy+2; break; case DIR_UP_LEFT: scp->accelX = -scp->speed; scp->accelY = -scp->speed; scp->wx = sCtrl->wx; scp->wy = sCtrl->wy-10; break; case DIR_DOWN: scp->accelX = 0; scp->accelY = scp->speed; scp->wx = sCtrl->wx+8; scp->wy = sCtrl->wy+4; break; case DIR_DOWN_RIGHT: scp->accelX = scp->speed; scp->accelY = scp->speed; scp->wx = sCtrl->wx+16; scp->wy = sCtrl->wy+2; break; case DIR_UP_RIGHT: scp->accelX = scp->speed; scp->accelY = -scp->speed; scp->wx = sCtrl->wx+16; scp->wy = sCtrl->wy-10; break; case DIR_UP: scp->accelX = 0; scp->accelY = -scp->speed; scp->wx = sCtrl->wx+8; scp->wy = sCtrl->wy-4; break; case DIR_LEFT: scp->accelX = -scp->speed; scp->accelY = 0; scp->wx = sCtrl->wx-4; scp->wy = sCtrl->wy-2; break; case DIR_RIGHT: scp->accelX = scp->speed; scp->accelY = 0; scp->wx = sCtrl->wx+20; scp->wy = sCtrl->wy-2; break; } polyStat = 0; AliveLinkIn(scp); } //*------------------------------------------------------------------------------------------ //* MakeAmmoBarText //*------------------------------------------------------------------------------------- void MakeAmmoBarText(char *charp) { RECT src; int len=0; int destX = timData[TIM_POLYCART].px+31; //* 31 is 124>>2 because of 4bit pixels int destY = timData[TIM_POLYCART].py+40; int ascii; int fontX = timData[TIM_POLYCART].px+4; //* Set font image source rect (constant members) src.y = destY; src.h = 5; src.w = 1; //* 1 is 4 4bit pixels wide //* Copy chars to sprite image area while (ascii = *charp++) { if (ascii==' ') src.x = fontX + 26; //* Space pattern is after Z else src.x = fontX + (ascii - 'A'); MoveImage(&src, destX++, destY); len++; } //* Update .x and .w sprite settings for this text ammoText->x = AMMO_BAR_X + ((BAR_MAX - (ammoText->w=len<<2)) >> 1); } //*------------------------------------------------------------------------------------------ //* GetRandGrid //*------------------------------------------------------------------------------------- struct GRID_PAIR *GetRandGrid(void) { //printf("\nGET: r=%d c=%d", (((struct GRID_PAIR *) grid) + freeOff)->r, (((struct GRID_PAIR *) grid) + freeOff)->c); return (((struct GRID_PAIR *) grid) + freeOff++); } //*------------------------------------------------------------------------------------------ //* GetAbsGrid - Removes a specific grid coord from 'available' list (for level manager only!) //*------------------------------------------------------------------------------------- void GetAbsGrid(int r, int c) { register struct GRID_PAIR *freep=(((struct GRID_PAIR *) grid) + freeOff); register struct GRID_PAIR *absp=gridp[r][c]; //struct GRID_PAIR temp; //* Swap r,c coord pair with pair at 'free' position // temp = *freep; // *freep = *absp; // *absp = temp; *absp = *freep; //* Update pointers to both coord pairs // gridp[r][c] = freep; // gridp[temp.r][temp.c] = absp; gridp[freep->r][freep->c] = absp; ++freeOff; } //*------------------------------------------------------------------------------------------ //* PutGrid - Returns a grid coord to the 'available' list //*------------------------------------------------------------------------------------- void PutGrid(int wx, int wy) { register struct GRID_PAIR *freep; register struct GRID_PAIR *swapp; register int r, c; //* Convert world coords to grid coords r = ((wy-55)>>4); c = ((wx-(28+((r & 0x00000001)<<3)))>>4); --freeOff; swapp = ((struct GRID_PAIR *) grid) + freeOff + (rand()%(783-freeOff)) + 1; freep = ((struct GRID_PAIR *) grid) + freeOff; *freep = *swapp; swapp->r = r; swapp->c = c; //printf("\nPUT: r=%d c=%d", r, c); } //*------------------------------------------------------------------------------------------ //* PlaceNextFlag - preset flag sprite placement for SURVIVE mode game //*------------------------------------------------------------------------------------- void PlaceNextFlag(void) { register int r, c; register struct SCTRL *scp; r = flagOff[flagIdx].r; c = flagOff[flagIdx].c; if (!r && !c) { //* Data end... flagPass = (++flagPass & 0x00000003); //* Cycle pass 0 to 3 flagIdx = 0; //* Reset data index r = flagOff[flagIdx].r; //* Get row offset c = flagOff[flagIdx].c; //* Get column offset } //* Add/Subtract row & col offsets according to pass code... switch (flagPass) { case 0: //* Normal flag path flagRow += r; flagCol += c; break; case 1: //* Horizontally & vertically flipped flag path flagRow -= r; flagCol -= c; break; case 2: //* Vertically flipped flag path flagRow -= r; flagCol += c; break; case 3: //* Horizontally flipped flag path flagRow += r; flagCol -= c; break; } //* Create and position new flag sprite flagScp = scp = GetSprite(1); PresetSprite(scp, PRESET_PICKUP_FLAG); scp->wx = 28 + (flagCol<<4) + ((flagRow & 0x00000001)<<3); scp->wy = 55 + (flagRow<<4); AliveLinkIn(scp); ++flagIdx; //* Index next data pair flagGone = 0; //* Reset flag status indicator } void StoreScreen (u_long *destination, int x, int y, int w, int h) { RECT rect; *(destination+0) = 0x00000010; /* ID */ *(destination+1) = 0x00000002; /* FLAG(15bit Direct,No Clut) */ *(destination+2) = (w*h/2+3)*4; /* pixel bnum */ *(destination+3) = ((0 & 0xffff) << 16) | (640 & 0xffff); /* pixel DX,DY: at 640, 0 */ *(destination+4) = ((h & 0xffff) << 16) | (w & 0xffff); /* pixel W,H */ // NO CLUT since 16-bit mode used rect.x = x; rect.y = y; rect.w = w; rect.h = h; DrawSync(0); StoreImage(&rect, destination+5); printf("\n\nPress [F10][F4] for dsave.\n"); printf("Dsave[0]: filename %08x %x\n\n\n", destination, (w*h/2+5)*4); DrawSync(0); VSync(0); }