program Tutorial_3_hello_level;

/*
	
	This example shows how to render levels as single screens and use a sprite to traverse the dungeon 

	Level can be found/edited under levels/game_level.flf
	
	Level uses 2x2 meta tiles that are defined in tiles/game_meta_tiles.flf
	
	Meta tiles uses main 8kb tileset located in tiles/game_tiles.flf
	
*/

var  
   	i,k,j,colorSize: byte; 
   	// Use the original zelda music score
	music: incnsf("music/zelda.nsf");
	
	// Location of the player sprite
	sprite_x, sprite_y : byte;

	@define spriteLoc $204

// Make sure game tiles are being automatically exported
@export "tiles/game_tiles.flf" "tiles/game_tiles.bin" 256

// Include the palette defined in game_tiles.pal. Automatically exported with @export
	palette: incbin("tiles/game_tiles.pal");
	

// Make sure that the sprites are automatically exported! 
@export "tiles/game_sprite_meta.flf" "tiles/spritedata.bin" 0
	spriteData : incbin("tiles/spritedata.bin");	
	// Format: Y, sprite, palette, X

// Export & include level data
@export "levels/game_levels.flf" "levels/game_levels.bin" 0
	levels : incbin("levels/game_levels.bin");	
	

// Export & include meta tiles
@export "tiles/game_meta_tiles.flf" "tiles/metatiles.bin" 0
	meta : incbin("tiles/metatiles.bin");	



@define levelpointer lp

	// Level information
    m_rl_width, m_rl_height : byte;
    m_rl_sizex, m_rl_sizey : byte;
    m_rl_startx, m_rl_starty : byte;
    m_rl_dataSize, m_rl_pos : byte;
    m_rl_i, m_rl_j, m_rl_val, m_rl_idx: byte;
    m_rl_chunksize, m_rl_chunks : byte;
    // Current position in level x,y
    m_li, m_lj : byte;


	isRendering: byte;
	zp2,zp, lp,dp:pointer;




procedure ReadHeader();

begin
	@levelpointer := #levels;

	// Read level header info

	m_rl_sizex := @levelpointer[0];
	m_rl_sizey := @levelpointer[1];

	m_rl_width := @levelpointer[2];
	m_rl_height := @levelpointer[3];

	m_rl_startx := @levelpointer[4];
	m_rl_starty := @levelpointer[5];

	m_rl_chunks := @levelpointer[6];
	m_rl_chunksize := @levelpointer[7];

	m_rl_dataSize := @levelpointer[8];

end;


/*
procedure RenderMetaBlock(zz : pointer);
begin
	
	zp2:=zz;
	for i:=0 to 2 do
	begin	
		ppusingle(hi(zp2),lo(zp2), dp[0]);
		zp2:=zp2+1;
		ppusingle(hi(zp2),lo(zp2), dp[1]);
		zp2:=zp2+31;
		dp:=dp+2;
	end;

end;
*/

procedure RenderMetaBlock(zz : pointer);
begin
	ppupointi(zz);
	ppuwrite(dp[0]);	
	ppuwrite(dp[1]);
	zz:=zz+32;
	ppupointi(zz);
	ppuwrite(dp[2]);	
	ppuwrite(dp[3]);
end;



procedure RenderLevel();
begin
	// Turn off NMI, or else everything will crash while rendering...
	ToggleNMI(0);
	ToggleSprites(0);
	ToggleBackground(0);
	isRendering:=true;
	togglevram32inc(false);
	
	
	// Go past header
	@levelpointer := #levels;
	@levelpointer := @levelpointer +32;

	// Calculate position in level layout

	m_rl_pos := m_rl_sizex*m_lj + m_li;
	
	m_rl_val := m_rl_width*m_rl_height;
	colorSize:=m_rl_val/4;

	@levelpointer := @levelpointer +m_rl_val*m_rl_pos;
	@levelpointer := @levelpointer +colorSize*m_rl_pos;
	@levelpointer := @levelpointer +m_rl_dataSize*m_rl_pos;

	zp:=$2000;
	for m_rl_j:=0 to m_rl_height do begin
		for m_rl_i:=0 to m_rl_width do begin
			dp:=#meta + lp[m_rl_i]*4;	
			RenderMetaBlock(zp);
			zp:=zp+2;
			
		 end;
		 zp:=zp+32;
		 @levelpointer:=@levelpointer+m_rl_width;
		 
	end;
	ppupoint($23,$C0);
	// Print color attribute data
	for k:=0 to colorSize do
		ppuWrite(lp[k]);
		
	@levelpointer:=@levelpointer+3 + m_rl_val/4;
	ppusingle($3F,00, $E); // Set background color to black
	// Turn on NMI again, and resume the game!
	ToggleNMI(1);
	isRendering:=false;

end;



// Forward decleration
procedure SetSpritePosition();

// Empty
interrupt IRQ();
begin

end;


// Moves the player. ReadInput populates the "buttons" variable
procedure MovePlayer();
begin
	ReadInput();
	sprite_x:=sprite_x+getbit(buttons,1)-getbit(buttons,0); // x axis
	sprite_y:=sprite_y-getbit(buttons,2)+getbit(buttons,3); // y axis
	// Set sprite location after update
	SetSpritePosition();	

	if (sprite_x>249) then 
	begin
		inc(m_li);
		sprite_x:= 16;
		RenderLevel();
		return();
	end;
	if (sprite_x<10) then 
	begin
		dec(m_li);
		sprite_x:= 248;
		RenderLevel();
		return();
	end;

	if (sprite_y>209) then 
	begin
		inc(m_lj);
		sprite_y:= 16;
		RenderLevel();
		return();
	end;
	if (sprite_y<10) then 
	begin
		dec(m_lj);
		sprite_y:=$20d8;
		RenderLevel();
		return();
	end;

end;


// NMI will automatically be called on every vblank. 

interrupt NMI();
begin
	// Startirq pushes a,x,y onto the stack
	Startirq(0);
/*	if (isRendering=true) then
	begin
		closeirq();
		asm("rti");
	end;
*/
	// First, make sure that the PPU knows that sprites are located at @spriteLoc
	SetSpriteLocation(hi(^@spriteLoc));
	// Make sure nametable 0 is set
	SetNametable(0);
	// Play the tune!
	PlaySong();

	ToggleSpriteSize(0);
	ToggleSpriteTableAddress(0);
	ToggleBackgroundTableAddress(1);

	// Set colours
	PPUMASK:=%00011000;

	MovePlayer();
	
	CloseIRQ();
end;




/*
	SetSpritePosition sets the sprite location (x,y) for the four sprites
	to (sprite_x, sprite_y)+(offset_x, offset_y), where the offsets are defined in colum 0,3 in 
 	the spritedata table. 
	
*/
procedure SetSpritePosition();
begin
	// Use sprite 4-8
	k:=16;
	j:=16;
	for i:=0 to 4 do begin
		poke(^@spriteLoc,k+0,spriteData[j+0]+sprite_y);
		poke(^@spriteLoc,k+3,spriteData[j+3]+sprite_x);
		k:=k+4;
		j:=j+4;
	end;

end;
/*
 	Simple sprite initialization: Copy sprite data, update sprite position
*/
procedure InitSprites();
begin
	fill(^$200,$FF,0);
	memcpy(#spriteData, 0,^@spriteLoc,250);
	SetSpritePosition();
end;

/*
	Initializes variables
*/
procedure InitVariables();
begin
	sprite_x:=100;
	sprite_y:=120;
	// Initialize level at 0,0	
	m_li:=0;
	m_lj:=0;

end;


// Starting point after resetting the NES
begin


	// Load palette
	LoadPalette(#palette);
	// Read level header data	
	ReadHeader();
	// Initialize sprite data
	InitSprites();

	// Turn on background
	ToggleBackground(1);
	// Display background in border 0
	ToggleBorderBackground(1);

	// Load music
	LoadSong(0);
	InitVariables();
	// Render initial level
	ToggleNMI(1);
	RenderLevel();
	// Turn on NMI and RUN!
	Loop();
end.
