program part8_Collisions;
@use "gfx/Levels"

// levels are loaded to/from $5000
@define levelPosition $5000
// User $2000 for charset (vic 0)
@define charsetPosition $2000
// Turn off kernal
@define useKernal 0

@define spriteLoc $3200

// Automatically export sprites on each build, so you don't have to manually export
@export "sprites/Sprites.flf" "sprites/sprites.bin" 256
// Automatically export charsets 
@export "charsets/charset1.flf" "charsets/charset1.bin" 256
var  

   	charset1: incbin("charsets/charset1.bin", @charsetPosition);
   	colors: incbin("charsets/charset1_color.bin", @charsetPosition+$800);
	mySprites:incbin("sprites/sprites.bin", @spriteLoc);
	levels: incbin("levels/level1.flf", @levelPosition);

	i,j:byte = 0;
	curLevel_x, curLevel_y : byte=0;
	// Defines if x/y directions are being blocked

	player_x: integer;
	player_y: byte;

	// k & l are just two more counters. blockx/y is set to 1 if x/y movement i hindered.
	// map player direction specifies the (screen) direction of the player: -1, -40, 1, 41 etc
	k,blockx, blocky,map_player_direction : byte = 0;

	// Background mask of the character set. 1 means collisions is enabled.
	background_mask: array[240] of byte=(
	1,1,1,1,1,1,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0);


// Max / min movement position for player sprite x,y	
	@define miny 66
	@define maxy 210
	@define minx 16
	@define maxx 320 

	@define playerSprite 0




// Sets up sprite
procedure InitMainSprite();
begin
	// Set sprite position at bank 0 to $C8=$3200/64
	setspriteloc(@playerSprite,@spriteLoc/64,0);
	player_x:=80;
	player_y:=120;
	// Set sprite x,y pos
	spritepos(player_x,player_y,@playerSprite);

	// Turn on only player sprite
	sprite_bitmask:=%00000001;

 	// All sprites are multicolor
	sprite_multicolor:=$FF;
	sprite_multicolor_reg1:=light_green;
	sprite_multicolor_reg2:=grey;

	sprite_color[@playerSprite]:=red;
end;

// This method initializes charset data, and is similar to the one in part 6
procedure Initialize();
begin
	clearscreen(light_blue, screen_col_loc);
	clearscreen(key_space, screen_char_loc);
	screen_bg_col:=black;
	screen_fg_col:=black;
	// Pointer to character color definitions
	Levels::cp := #colors;
	multicolor_char_col[1]:=grey;
	multicolor_char_col[2]:=brown;

	setmulticolormode();

	setcharsetlocation(@charsetPosition);

	Levels::ReadHeader(@levelPosition);
	Levels::starty:=2;

end;


/*
	Performs the check on whether the player sprite collides with
	a collideable character set

*/
procedure PlayerBackgroundCollide();
var
	tmp:integer;
	
begin
	tmp:=(player_x - 12);
	i:=tmp/8;
	j:=(player_y-38)/8;
	moveto(i-1,j-1, hi(screen_char_loc));
	blockx:=0;
	blocky:=0;
	
	k:=screenmemory[ map_player_direction +screen_width+1]; // down
	if (background_mask[k]=1) then begin
		if (joystickdown=1 or joystickup=1) then blocky:=1;
		if (joystickleft=1 or joystickright=1) then blockx:=1;
	end;
end;

/*	
	Traverselevels checkes whether the player has moved close to a border.
    If true, then the neighbouring screen is loaded and the player sprite is placed
	on the opposite part of the screen. 

*/

procedure TraverseLevels();
var 
   redraw:byte=0;
begin
	
	if (player_x<@minx) then begin
		if (curLevel_x<>0) then begin
			dec(curLevel_x);
			player_x:=@maxx-1;
			redraw:=1;
		end
		else
			player_x:=@minx+1;
	end;
	if (player_x>@maxx) then begin
		if (curLevel_x+1<>Levels::width) then begin
			inc(curLevel_x);
			player_x:=@minx+1;
			redraw:=1;
		end
		else 
			player_x:=@maxx-1;

	end;

	if (player_y<@miny) then begin
		if (curLevel_y<>0) then begin
			dec(curLevel_y);
			player_y:=@maxy-1;
			redraw:=1;
		end
		else
			player_y:=@miny+1;
	end;
	if (player_y>@maxy) then begin
		if (curLevel_y+1<>Levels::height) then begin
			inc(curLevel_y);
			player_y:=@miny;
			redraw:=1;
		end
		else
			player_y:=@maxy-1;
	end;


	if redraw=1 then begin
		Levels::RenderCharsetColorLevel(curLevel_x,curLevel_y,hi(screen_char_loc));
		redraw:=0;
	end;


end;

/*
	 Moves the player
*/
procedure MovePlayerSprite();
begin
	joystick(2);

	// Calculate map_direction
	map_player_direction:=0;
	if (joystickleft=1) then map_player_direction:=map_player_direction-1;
	if (joystickright=1) then map_player_direction:=map_player_direction+1;
	if (joystickdown=1) then map_player_direction:=map_player_direction+screen_width;
	if (joystickup=1) then map_player_direction:=map_player_direction-screen_width;

	PlayerBackgroundCollide();

	// Perform blocks
	if (blockx=0) then
		player_x:=player_x+joystickright-joystickleft;
	if (blocky=0) then
		player_y:=player_y+joystickdown-joystickup;

	spritepos(player_x,player_y,@playerSprite);
end;


// Main raster function: Contains all high-level procedure calls to sprite & entity handling
interrupt RasterRenderLevels();
begin
	StartIRQ(@useKernal);
	MovePlayerSprite();	
	TraverseLevels();
	CloseIRQ();
end;



// Main block
begin
	Initialize();
	InitMainSprite();

	Levels::lp:=@levelPosition;
	Levels::cdp:=#colors;
	redraw:=1;
	// Turn off all interrupts
	DisableCIAInterrupts();
	// Turn off kernal
	SetMemoryConfig(1,@useKernal,0);
	// Set up raster IRQ
	RasterIRQ(RasterRenderLevels(),$00,@useKernal);
	EnableRasterIRQ();
	enableirq();
	Loop(); 
end.
