From bc4c10ecc12662bd35b7e474e0f0e2a912aa32fa Mon Sep 17 00:00:00 2001 From: mariogazziro <84649066+mariogazziro@users.noreply.github.com> Date: Sat, 9 Sep 2023 14:46:23 -0300 Subject: [PATCH] Add files via upload --- famitone2.s | 1322 ++++++++++++++++++++++++++++++++++++++++++++++++ game.c | 865 +++++++++++++++++++++++++++++++ gameover_nam.h | 9 + level1_nam.h | 23 + level2_nam.h | 32 ++ level3_nam.h | 37 ++ level4_nam.h | 43 ++ level5_nam.h | 57 +++ level_nam.h | 7 + music.s | 439 ++++++++++++++++ neslib.h | 337 ++++++++++++ sounds.s | 58 +++ tileset.s | 2 + title_nam.h | 27 + welldone_nam.h | 10 + 15 files changed, 3268 insertions(+) create mode 100644 famitone2.s create mode 100644 game.c create mode 100644 gameover_nam.h create mode 100644 level1_nam.h create mode 100644 level2_nam.h create mode 100644 level3_nam.h create mode 100644 level4_nam.h create mode 100644 level5_nam.h create mode 100644 level_nam.h create mode 100644 music.s create mode 100644 neslib.h create mode 100644 sounds.s create mode 100644 tileset.s create mode 100644 title_nam.h create mode 100644 welldone_nam.h diff --git a/famitone2.s b/famitone2.s new file mode 100644 index 0000000..f80e9f4 --- /dev/null +++ b/famitone2.s @@ -0,0 +1,1322 @@ + +;settings, uncomment or put them into your main program; the latter makes possible updates easier + +; FT_BASE_ADR = $0300 ;page in the RAM used for FT2 variables, should be $xx00 +; FT_TEMP = $00 ;3 bytes in zeropage used by the library as a scratchpad +; FT_DPCM_OFF = $c000 ;$c000..$ffc0, 64-byte steps +; FT_SFX_STREAMS = 4 ;number of sound effects played at once, 1..4 + +; FT_DPCM_ENABLE ;undefine to exclude all DMC code +; FT_SFX_ENABLE ;undefine to exclude all sound effects code +; FT_THREAD ;undefine if you are calling sound effects from the same thread as the sound update call + +; FT_PAL_SUPPORT ;undefine to exclude PAL support +; FT_NTSC_SUPPORT ;undefine to exclude NTSC support + + .import popa + +FT_BASE_ADR =$0500 ;page in RAM, should be $xx00 +FT_DPCM_OFF =$c000 ;sample data start address + +.define FT_THREAD 1 ;undefine if you call sound effects in the same thread as sound update +.define FT_PAL_SUPPORT 1 ;undefine to exclude PAL support +.define FT_NTSC_SUPPORT 1 ;undefine to exclude NTSC support +.define FT_SFX_ENABLE 1 +.define FT_DPCM_ENABLE 0 +.define FT_SFX_STREAMS 4 + +.segment "ZEROPAGE" + +FT_TEMP: .res 3 + +.segment "CODE" + + .export _famitone_init,_famitone_update + .export _music_play,_music_stop,_music_pause +.if(FT_SFX_ENABLE) + .export _sfx_init + .export _sfx_play +.endif +.if(FT_DPCM_ENABLE) + .export _sample_play +.endif + +;void __fastcall__ famitone_init(void* music_data); + +_famitone_init=FamiToneInit + + + +;void __fastcall__ famitone_update(void); + +_famitone_update=FamiToneUpdate + + + +;void __fastcall__ music_play(unsigned char song); + +_music_play=FamiToneMusicPlay + + + +;void __fastcall__ music_stop(void); + +_music_stop=FamiToneMusicStop + + + +;void __fastcall__ music_pause(unsigned char pause); + +_music_pause=FamiToneMusicPause + + +.if(FT_SFX_ENABLE) +;void __fastcall__ sfx_init(void* sounds_data); +_sfx_init=FamiToneSfxInit + +;void __fastcall__ sfx_play(unsigned char sound,unsigned char channel); +_sfx_play: + + and #$03 + tax + lda @sfxPriority,x + tax + jsr popa + jmp FamiToneSfxPlay + +@sfxPriority: + + .byte FT_SFX_CH0,FT_SFX_CH1,FT_SFX_CH2,FT_SFX_CH3 +.endif + +.if(FT_DPCM_ENABLE) +;void __fastcall__ sample_play(unsigned char sample); +_sample_play=FamiToneSamplePlay +.endif + +;FamiTone2 v1.12 + + + +;internal defines +;FT_PITCH_FIX if either NTSC or PAL defined + .if(FT_PAL_SUPPORT && FT_NTSC_SUPPORT) +FT_PITCH_FIX = 1 + .else +FT_PITCH_FIX = 0 + .endif + +FT_DPCM_PTR = (FT_DPCM_OFF&$3fff)>>6 + + +;zero page variables + +FT_TEMP_PTR = FT_TEMP ;word +FT_TEMP_PTR_L = FT_TEMP_PTR+0 +FT_TEMP_PTR_H = FT_TEMP_PTR+1 +FT_TEMP_VAR1 = FT_TEMP+2 + + +;envelope structure offsets, 5 bytes per envelope, grouped by variable type + +FT_ENVELOPES_ALL = 3+3+3+2 ;3 for the pulse and triangle channels, 2 for the noise channel +FT_ENV_STRUCT_SIZE = 5 + +FT_ENV_VALUE = FT_BASE_ADR+0*FT_ENVELOPES_ALL +FT_ENV_REPEAT = FT_BASE_ADR+1*FT_ENVELOPES_ALL +FT_ENV_ADR_L = FT_BASE_ADR+2*FT_ENVELOPES_ALL +FT_ENV_ADR_H = FT_BASE_ADR+3*FT_ENVELOPES_ALL +FT_ENV_PTR = FT_BASE_ADR+4*FT_ENVELOPES_ALL + + +;channel structure offsets, 7 bytes per channel + +FT_CHANNELS_ALL = 5 +FT_CHN_STRUCT_SIZE = 9 + +FT_CHN_PTR_L = FT_BASE_ADR+0*FT_CHANNELS_ALL +FT_CHN_PTR_H = FT_BASE_ADR+1*FT_CHANNELS_ALL +FT_CHN_NOTE = FT_BASE_ADR+2*FT_CHANNELS_ALL +FT_CHN_INSTRUMENT = FT_BASE_ADR+3*FT_CHANNELS_ALL +FT_CHN_REPEAT = FT_BASE_ADR+4*FT_CHANNELS_ALL +FT_CHN_RETURN_L = FT_BASE_ADR+5*FT_CHANNELS_ALL +FT_CHN_RETURN_H = FT_BASE_ADR+6*FT_CHANNELS_ALL +FT_CHN_REF_LEN = FT_BASE_ADR+7*FT_CHANNELS_ALL +FT_CHN_DUTY = FT_BASE_ADR+8*FT_CHANNELS_ALL + + +;variables and aliases + +FT_ENVELOPES = FT_BASE_ADR +FT_CH1_ENVS = FT_ENVELOPES+0 +FT_CH2_ENVS = FT_ENVELOPES+3 +FT_CH3_ENVS = FT_ENVELOPES+6 +FT_CH4_ENVS = FT_ENVELOPES+9 + +FT_CHANNELS = FT_ENVELOPES+FT_ENVELOPES_ALL*FT_ENV_STRUCT_SIZE +FT_CH1_VARS = FT_CHANNELS+0 +FT_CH2_VARS = FT_CHANNELS+1 +FT_CH3_VARS = FT_CHANNELS+2 +FT_CH4_VARS = FT_CHANNELS+3 +FT_CH5_VARS = FT_CHANNELS+4 + + +FT_CH1_NOTE = FT_CH1_VARS+.lobyte(FT_CHN_NOTE) +FT_CH2_NOTE = FT_CH2_VARS+.lobyte(FT_CHN_NOTE) +FT_CH3_NOTE = FT_CH3_VARS+.lobyte(FT_CHN_NOTE) +FT_CH4_NOTE = FT_CH4_VARS+.lobyte(FT_CHN_NOTE) +FT_CH5_NOTE = FT_CH5_VARS+.lobyte(FT_CHN_NOTE) + +FT_CH1_INSTRUMENT = FT_CH1_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH2_INSTRUMENT = FT_CH2_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH3_INSTRUMENT = FT_CH3_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH4_INSTRUMENT = FT_CH4_VARS+.lobyte(FT_CHN_INSTRUMENT) +FT_CH5_INSTRUMENT = FT_CH5_VARS+.lobyte(FT_CHN_INSTRUMENT) + +FT_CH1_DUTY = FT_CH1_VARS+.lobyte(FT_CHN_DUTY) +FT_CH2_DUTY = FT_CH2_VARS+.lobyte(FT_CHN_DUTY) +FT_CH3_DUTY = FT_CH3_VARS+.lobyte(FT_CHN_DUTY) +FT_CH4_DUTY = FT_CH4_VARS+.lobyte(FT_CHN_DUTY) +FT_CH5_DUTY = FT_CH5_VARS+.lobyte(FT_CHN_DUTY) + +FT_CH1_VOLUME = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+0 +FT_CH2_VOLUME = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+0 +FT_CH3_VOLUME = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+0 +FT_CH4_VOLUME = FT_CH4_ENVS+.lobyte(FT_ENV_VALUE)+0 + +FT_CH1_NOTE_OFF = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+1 +FT_CH2_NOTE_OFF = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+1 +FT_CH3_NOTE_OFF = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+1 +FT_CH4_NOTE_OFF = FT_CH4_ENVS+.lobyte(FT_ENV_VALUE)+1 + +FT_CH1_PITCH_OFF = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+2 +FT_CH2_PITCH_OFF = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+2 +FT_CH3_PITCH_OFF = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+2 + + +FT_VARS = FT_CHANNELS+FT_CHANNELS_ALL*FT_CHN_STRUCT_SIZE + +FT_PAL_ADJUST = FT_VARS+0 +FT_SONG_LIST_L = FT_VARS+1 +FT_SONG_LIST_H = FT_VARS+2 +FT_INSTRUMENT_L = FT_VARS+3 +FT_INSTRUMENT_H = FT_VARS+4 +FT_TEMPO_STEP_L = FT_VARS+5 +FT_TEMPO_STEP_H = FT_VARS+6 +FT_TEMPO_ACC_L = FT_VARS+7 +FT_TEMPO_ACC_H = FT_VARS+8 +FT_SONG_SPEED = FT_CH5_INSTRUMENT +FT_PULSE1_PREV = FT_CH3_DUTY +FT_PULSE2_PREV = FT_CH5_DUTY +FT_DPCM_LIST_L = FT_VARS+9 +FT_DPCM_LIST_H = FT_VARS+10 +FT_DPCM_EFFECT = FT_VARS+11 +FT_OUT_BUF = FT_VARS+12 ;11 bytes + + +;sound effect stream variables, 2 bytes and 15 bytes per stream +;when sound effects are disabled, this memory is not used + +FT_SFX_ADR_L = FT_VARS+23 +FT_SFX_ADR_H = FT_VARS+24 +FT_SFX_BASE_ADR = FT_VARS+25 + +FT_SFX_STRUCT_SIZE = 15 +FT_SFX_REPEAT = FT_SFX_BASE_ADR+0 +FT_SFX_PTR_L = FT_SFX_BASE_ADR+1 +FT_SFX_PTR_H = FT_SFX_BASE_ADR+2 +FT_SFX_OFF = FT_SFX_BASE_ADR+3 +FT_SFX_BUF = FT_SFX_BASE_ADR+4 ;11 bytes + + +;aliases for sound effect channels to use in user calls + +FT_SFX_CH0 = FT_SFX_STRUCT_SIZE*0 +FT_SFX_CH1 = FT_SFX_STRUCT_SIZE*1 +FT_SFX_CH2 = FT_SFX_STRUCT_SIZE*2 +FT_SFX_CH3 = FT_SFX_STRUCT_SIZE*3 + + +;aliases for the APU registers + +APU_PL1_VOL = $4000 +APU_PL1_SWEEP = $4001 +APU_PL1_LO = $4002 +APU_PL1_HI = $4003 +APU_PL2_VOL = $4004 +APU_PL2_SWEEP = $4005 +APU_PL2_LO = $4006 +APU_PL2_HI = $4007 +APU_TRI_LINEAR = $4008 +APU_TRI_LO = $400a +APU_TRI_HI = $400b +APU_NOISE_VOL = $400c +APU_NOISE_LO = $400e +APU_NOISE_HI = $400f +APU_DMC_FREQ = $4010 +APU_DMC_RAW = $4011 +APU_DMC_START = $4012 +APU_DMC_LEN = $4013 +APU_SND_CHN = $4015 + + +;aliases for the APU registers in the output buffer + + .if(!FT_SFX_ENABLE) ;if sound effects are disabled, write to the APU directly +FT_MR_PULSE1_V = APU_PL1_VOL +FT_MR_PULSE1_L = APU_PL1_LO +FT_MR_PULSE1_H = APU_PL1_HI +FT_MR_PULSE2_V = APU_PL2_VOL +FT_MR_PULSE2_L = APU_PL2_LO +FT_MR_PULSE2_H = APU_PL2_HI +FT_MR_TRI_V = APU_TRI_LINEAR +FT_MR_TRI_L = APU_TRI_LO +FT_MR_TRI_H = APU_TRI_HI +FT_MR_NOISE_V = APU_NOISE_VOL +FT_MR_NOISE_F = APU_NOISE_LO + .else ;otherwise write to the output buffer +FT_MR_PULSE1_V = FT_OUT_BUF +FT_MR_PULSE1_L = FT_OUT_BUF+1 +FT_MR_PULSE1_H = FT_OUT_BUF+2 +FT_MR_PULSE2_V = FT_OUT_BUF+3 +FT_MR_PULSE2_L = FT_OUT_BUF+4 +FT_MR_PULSE2_H = FT_OUT_BUF+5 +FT_MR_TRI_V = FT_OUT_BUF+6 +FT_MR_TRI_L = FT_OUT_BUF+7 +FT_MR_TRI_H = FT_OUT_BUF+8 +FT_MR_NOISE_V = FT_OUT_BUF+9 +FT_MR_NOISE_F = FT_OUT_BUF+10 + .endif + + + +;------------------------------------------------------------------------------ +; reset APU, initialize FamiTone +;------------------------------------------------------------------------------ + +FamiToneInit: + + sta FT_SONG_LIST_L ;store music data pointer for further use + stx FT_SONG_LIST_H + sta 0 + ldx #FT_SFX_CH0 + jsr _FT2SfxUpdate + .endif + .if FT_SFX_STREAMS>1 + ldx #FT_SFX_CH1 + jsr _FT2SfxUpdate + .endif + .if FT_SFX_STREAMS>2 + ldx #FT_SFX_CH2 + jsr _FT2SfxUpdate + .endif + .if FT_SFX_STREAMS>3 + ldx #FT_SFX_CH3 + jsr _FT2SfxUpdate + .endif + + + ;send data from the output buffer to the APU + + lda FT_OUT_BUF ;pulse 1 volume + sta APU_PL1_VOL + lda FT_OUT_BUF+1 ;pulse 1 period LSB + sta APU_PL1_LO + lda FT_OUT_BUF+2 ;pulse 1 period MSB, only applied when changed + cmp FT_PULSE1_PREV + beq @no_pulse1_upd + sta FT_PULSE1_PREV + sta APU_PL1_HI +@no_pulse1_upd: + + lda FT_OUT_BUF+3 ;pulse 2 volume + sta APU_PL2_VOL + lda FT_OUT_BUF+4 ;pulse 2 period LSB + sta APU_PL2_LO + lda FT_OUT_BUF+5 ;pulse 2 period MSB, only applied when changed + cmp FT_PULSE2_PREV + beq @no_pulse2_upd + sta FT_PULSE2_PREV + sta APU_PL2_HI +@no_pulse2_upd: + + lda FT_OUT_BUF+6 ;triangle volume (plays or not) + sta APU_TRI_LINEAR + lda FT_OUT_BUF+7 ;triangle period LSB + sta APU_TRI_LO + lda FT_OUT_BUF+8 ;triangle period MSB + sta APU_TRI_HI + + lda FT_OUT_BUF+9 ;noise volume + sta APU_NOISE_VOL + lda FT_OUT_BUF+10 ;noise period + sta APU_NOISE_LO + + .endif + + .if(FT_THREAD) + pla + sta FT_TEMP_PTR_H + pla + sta FT_TEMP_PTR_L + .endif + + rts + + +;internal routine, sets up envelopes of a channel according to current instrument +;in X envelope group offset, A instrument number + +_FT2SetInstrument: + asl a ;instrument number is pre multiplied by 4 + tay + lda FT_INSTRUMENT_H + adc #0 ;use carry to extend range for 64 instruments + sta + +//#link "famitone2.s" +//#link "music.s" +//#link "sounds.s" + +// CHR data +//#resource "tileset.chr" +//#link "tileset.s" + +//include nametables for all the screens such as title or game over + +#include "title_nam.h" +#include "level_nam.h" +#include "gameover_nam.h" +#include "welldone_nam.h" + +//include nametables for levels + +#include "level1_nam.h" +#include "level2_nam.h" +#include "level3_nam.h" +#include "level4_nam.h" +#include "level5_nam.h" + +//game uses 12:4 fixed point calculations for enemy movements + +#define FP_BITS 4 + +//max size of the game map + +#define MAP_WDT 16 +#define MAP_WDT_BIT 4 +#define MAP_HGT 13 + +//macro for calculating map offset from screen space, as +//the map size is smaller than screen to save some memory + +#define MAP_ADR(x,y) ((((y)-2)<>FP_BITS); + + if(pad_trigger(0)&PAD_START) break; + + iy+=dy; + + if(iy<0) + { + iy=0; + dy=-dy>>1; + } + + if(dy>(-8<>8); + + if(pad_trigger(0)&PAD_START) break; + + ++frame_cnt; + } + } + + pal_fade_to(0); +} + + + +//set up a move in the specified direction if there is no wall + +void player_move(unsigned char id,unsigned char dir) +{ + px=player_x[id]>>(TILE_SIZE_BIT+FP_BITS); + py=player_y[id]>>(TILE_SIZE_BIT+FP_BITS); + + switch(dir) + { + case DIR_LEFT: --px; break; + case DIR_RIGHT: ++px; break; + case DIR_UP: --py; break; + case DIR_DOWN: ++py; break; + } + + if(map[MAP_ADR(px,py)]==TILE_WALL) return; + + player_cnt[id]=TILE_SIZE<2) vram_put(0x10+num/100); + if(len>1) vram_put(0x10+num/10%10); + vram_put(0x10+num%10); +} + + + +//the main gameplay code + +void game_loop(void) +{ + oam_clear(); + + i=game_level<<1; + + vram_adr(NAMETABLE_A); + vram_unrle(levelList[i]); //unpack level nametable + + vram_adr(NAMETABLE_A+0x0042); + vram_write((unsigned char*)statsStr,27); //add game stats string + + pal_bg(levelList[i+1]); //set up background palette + pal_spr(palGameSpr); //set up sprites palette + + player_all=0; + items_count=0; + items_collected=0; + + //this loop reads the level nametable back from VRAM, row by row, + //constructs game map, removes spawn points from the nametable, + //and writes back to the VRAM + + i16=NAMETABLE_A+0x0080; + ptr=0; + wait=0; + + for(i=2;i>FP_BITS; + + if(player_wait[i]) + { + if(player_wait[i]>=16||player_wait[i]&2) py=240; + } + + oam_meta_spr(player_x[i]>>FP_BITS,py,spr,sprListPlayer[i]); + spr-=16; + } + + //wait for next frame + //it is here and not at beginning of the loop because you need + //to update OAM for the very first frame, and you also need to do that + //right after object parameters were changed, so either OAM update should + //be in a function that called before the loop and at the end of the loop, + //or wait for NMI should be placed there + //otherwise you would have situation update-wait-display, i.e. + //one frame delay between action and display of its result + + ppu_wait_frame(); + + ++frame_cnt; + + //slowly fade virtual brightness to needed value, + //which is max for gameplay or half for pause + + if(!(frame_cnt&3)) + { + if(!game_paused&&bright<4) ++bright; + if( game_paused&&bright>2) --bright; + + pal_bright(bright); + } + + //poll the gamepad in the trigger mode + + i=pad_trigger(0); + + //it start was released and then pressed, toggle pause mode + + if(i&PAD_START) + { + game_paused^=TRUE; + music_pause(game_paused); + } + + //don't process anything in pause mode, just display latest game state + + if(game_paused) continue; + + //CHR bank switching animation with different speed for background and sprites + + bank_bg((frame_cnt>>4)&1); + bank_spr((frame_cnt>>3)&1); + + //a counter that does not allow objects to move while spawn animation plays + + if(wait) + { + --wait; + + if(!wait) music_play(MUSIC_GAME);//start the music when all the objects spawned + } + + //check for level completion condition + + if(items_collected==items_count) + { + music_play(MUSIC_CLEAR); + game_done=TRUE; + game_clear=TRUE; + } + + //process all the objects + //player and enemies are the same type of object in this game, + //to make code simpler and shorter, but generally they need to be + //different kind of objects + + for(i=0;i=(player_x[0]+(12<=(player_y[0]+(12<>(TILE_SIZE_BIT+FP_BITS)), + (player_y[i]>>(TILE_SIZE_BIT+FP_BITS))); + + if(map[i16]==TILE_ITEM) + { + map[i16]=TILE_EMPTY; //mark as collected in the game map + + sfx_play(SFX_ITEM,2); + ++items_collected; + + //get address of the tile in the nametable + + i16=NAMETABLE_A+0x0080+(((player_y[i]>>(TILE_SIZE_BIT+FP_BITS))-2)<<6)| + ((player_x[i]>>(TILE_SIZE_BIT+FP_BITS))<<1); + + //replace it with empty tile through the update list + + update_list[0]=i16>>8; + update_list[1]=i16&255; + update_list[3]=update_list[0]; + update_list[4]=update_list[1]+1; + i16+=32; + update_list[6]=i16>>8; + update_list[7]=i16&255; + update_list[9]=update_list[6]; + update_list[10]=update_list[7]+1; + + //update number of collected items in the game stats + + update_list[14]=0x10+items_collected/100; + update_list[17]=0x10+items_collected/10%10; + update_list[20]=0x10+items_collected%10; + } + } + } + } + + if(!player_cnt[i]) //movement to the next tile is done, set up new movement + { + if(!i) //this is the player, process controls + { + //get gamepad state, it was previously polled with pad_trigger + + j=pad_state(0); + + //this is a tricky part to make controls more predictable + //when you press two directions at once, sliding by a wall + //to take turn into a passage on the side + //this piece of code gives current direction lower priority + //through testing it first + //bits in player_dir var are matching to the buttons bits + + if(j&player_dir[0]) + { + j&=~player_dir[0]; //remove the direction from further check + player_move(i,player_dir[0]); //change the direction + } + + //now continue control processing as usual + + if(j&PAD_LEFT) player_move(i,DIR_LEFT); + if(j&PAD_RIGHT) player_move(i,DIR_RIGHT); + if(j&PAD_UP) player_move(i,DIR_UP); + if(j&PAD_DOWN) player_move(i,DIR_DOWN); + } + else //this is an enemy, run AI + { + //the AI is very simple + //first we create list of all directions that are possible to take + //excluding the direction that is opposite to previous one + + i16=MAP_ADR((player_x[i]>>8),(player_y[i]>>8)); + ptr=player_dir[i]; + j=0; + + if(ptr!=DIR_RIGHT&&map[i16-1]!=TILE_WALL) dir[j++]=DIR_LEFT; + if(ptr!=DIR_LEFT &&map[i16+1]!=TILE_WALL) dir[j++]=DIR_RIGHT; + if(ptr!=DIR_DOWN &&map[i16-MAP_WDT]!=TILE_WALL) dir[j++]=DIR_UP; + if(ptr!=DIR_UP &&map[i16+MAP_WDT]!=TILE_WALL) dir[j++]=DIR_DOWN; + + //randomly select a possible direction + + player_move(i,dir[rand8()%j]); + + //if there was more than one possible direction, + //i.e. it is a branch and not a corridor, + //attempt to move towards the player + + if(j>1) + { + if(ptr!=DIR_DOWN &&player_y[0]player_y[i]) player_move(i,DIR_DOWN); + if(ptr!=DIR_RIGHT&&player_x[0]player_x[i]) player_move(i,DIR_RIGHT); + } + } + } + } + } + + delay(100); + pal_fade_to(0); +} + + + +//this is where the program starts +extern const void sound_data[]; +extern const void music_data[]; + +void main(void) +{ + famitone_init(&music_data); + sfx_init(&sound_data); + nmi_set_callback(famitone_update); + + while(1)//infinite loop, title-gameplay + { + title_screen(); + + game_level=0; + game_lives=4; + + while(game_lives&&game_level