diff --git a/presets/nes/chase/game.c b/presets/nes/chase/game.c index fae09315..4cdff5d6 100644 --- a/presets/nes/chase/game.c +++ b/presets/nes/chase/game.c @@ -33,74 +33,74 @@ //game uses 12:4 fixed point calculations for enemy movements -#define FP_BITS 4 +#define FP_BITS 4 //max size of the game map -#define MAP_WDT 16 -#define MAP_WDT_BIT 4 -#define MAP_HGT 13 +#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); + scroll(-8,iy>>FP_BITS); - if(pad_trigger(0)&PAD_START) break; + if(pad_trigger(0)&PAD_START) break; - iy+=dy; + iy+=dy; - if(iy<0) - { - iy=0; - dy=-dy>>1; - } + if(iy<0) + { + iy=0; + dy=-dy>>1; + } - if(dy>(-8<(-8<>8); + pal_col(2,frame_cnt&2?i16&0xff:i16>>8); - if(pad_trigger(0)&PAD_START) break; + if(pad_trigger(0)&PAD_START) break; - ++frame_cnt; - } - } + ++frame_cnt; + } + } - pal_fade_to(0); + pal_fade_to(0); } @@ -445,21 +445,21 @@ void show_screen(unsigned char num) 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); + 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; - } + 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; + 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); + if(len>2) vram_put(0x10+num/100); + if(len>1) vram_put(0x10+num/10%10); + vram_put(0x10+num%10); } @@ -481,355 +481,355 @@ void put_num(unsigned int adr,unsigned int num,unsigned char len) void game_loop(void) { - oam_clear(); + oam_clear(); - i=game_level<<1; + i=game_level<<1; - vram_adr(NAMETABLE_A); - vram_unrle(levelList[i]); //unpack level nametable + 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 + 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 + 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; + 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 + //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; + i16=NAMETABLE_A+0x0080; + ptr=0; + wait=0; - for(i=2;i>FP_BITS; + for(i=0;i>FP_BITS; - if(player_wait[i]) - { - if(player_wait[i]>=16||player_wait[i]&2) py=240; - } + 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; - } + 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 + //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(); + ppu_wait_frame(); - ++frame_cnt; + ++frame_cnt; - //slowly fade virtual brightness to needed value, - //which is max for gameplay or half for pause + //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; + if(!(frame_cnt&3)) + { + if(!game_paused&&bright<4) ++bright; + if( game_paused&&bright>2) --bright; - pal_bright(bright); - } + pal_bright(bright); + } - //poll the gamepad in the trigger mode + //poll the gamepad in the trigger mode - i=pad_trigger(0); + i=pad_trigger(0); - //it start was released and then pressed, toggle pause mode + //it start was released and then pressed, toggle pause mode - if(i&PAD_START) - { - game_paused^=TRUE; - music_pause(game_paused); - } + if(i&PAD_START) + { + game_paused^=TRUE; + music_pause(game_paused); + } - //don't process anything in pause mode, just display latest game state + //don't process anything in pause mode, just display latest game state - if(game_paused) continue; + if(game_paused) continue; - //CHR bank switching animation with different speed for background and sprites + //CHR bank switching animation with different speed for background and sprites - bank_bg((frame_cnt>>4)&1); - bank_spr((frame_cnt>>3)&1); + 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 + //a counter that does not allow objects to move while spawn animation plays - if(wait) - { - --wait; + if(wait) + { + --wait; - if(!wait) music_play(MUSIC_GAME);//start the music when all the objects spawned - } + if(!wait) music_play(MUSIC_GAME);//start the music when all the objects spawned + } - //check for level completion condition + //check for level completion condition - if(items_collected==items_count) - { - music_play(MUSIC_CLEAR); - game_done=TRUE; - game_clear=TRUE; - } + 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 + //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); + 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); } @@ -840,26 +840,26 @@ extern const void music_data[]; void main(void) { - famitone_init(&music_data); - sfx_init(&sound_data); - nmi_set_callback(famitone_update); + famitone_init(&music_data); + sfx_init(&sound_data); + nmi_set_callback(famitone_update); - while(1)//infinite loop, title-gameplay - { - title_screen(); + while(1)//infinite loop, title-gameplay + { + title_screen(); - game_level=0; - game_lives=4; + game_level=0; + game_lives=4; - while(game_lives&&game_level