program part8_Adding_entities;
var  

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

   	charset1: incbin("charsets/charset1.bin", @charsetPosition);
	mySprites:incbin("sprites/sprites.bin", $3200);
	levels: incbin("levels/level1.flf", @levelPosition);

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

	k,l,blockx, blocky,map_player_direction : byte = 0;
	player_x: integer;
	player_y: byte;

	background_mask: array[256] 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);




	spriteLocations : array[1] of byte=(0, $C9);

	@define numEntities 6
	@define crashDistance 6

	entity_id : array[@numEntities] of byte;
	entity_pos_x : array[@numEntities] of byte;
	entity_pos_y : array[@numEntities] of byte;
	entity_color : array[@numEntities] of byte;


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

	@define playerSprite 0



@define colorpointer colormemory

// RasLib contains all level rendering routines
@include "../../RasLib/levels.ras"




// Sets up sprite
procedure InitMainSprite();
begin
	// Set sprite position at bank 0 to $C8
	setspriteloc(@playerSprite,$C8,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;


procedure Initialize();
begin
	clearscreen(LIGHT_BLUE, ^$D800);
	clearscreen($20, ^$0400);
	SCREEN_BG_COL:=BLACK;
	SCREEN_FG_COL:=BLACK;
	// Pointer to character color definitions
	colors:=$2800;

	MULTICOLOR_CHAR_COL[1]:=GREY;
	MULTICOLOR_CHAR_COL[2]:=BROWN;

	setmulticolormode();

	setcharsetlocation(@charsetPosition);

	levelpointer:=@levelPosition;
	ReadHeader();
end;

procedure LoadEntities();
begin
	// Make sure all ID's are set to zer0
	fill(entity_id,0, @numEntities);
	for i:=0 to @numEntities do begin
		if levelpointer[0]=0 then return();
		entity_id[i] := levelpointer[0];
		entity_pos_x[i] := levelpointer[1]*4 + 10;
		entity_pos_y[i] := levelpointer[2]*8 + 60;
		entity_color[i] := levelpointer[3];
		inczp(levelpointer, m_rl_chunksize);
	end;
end;


procedure InitEntities();
var
	ex: integer;
begin
	SPRITE_BITMASK:=%00000001; // Only enable player sprite
	for i:=0 to @numEntities do begin
		k:=i+1;
		if entity_id[i]=0 then return();
		j:=spriteLocations[entity_id[i]];
		setspriteloc(k,j,0);
		togglebit(SPRITE_BITMASK,k,1);
		SPRITE_COLOR[k]:=entity_color[i];
		ex:=entity_pos_x[i]*2;
		spritepos(ex,entity_pos_y[i], k);		
	end;
end;

/*
 	Calculates the nearest entity. 
   	Entity index stored in nearestEntity
*/

procedure EntityCollider();
var 
	maxDistance:byte;
	nearestEntity:byte;
	px:byte;
begin
	maxDistance := 255;
	px:=player_x/2;
	nearestEntity := 255; 
	
	for i:=0 to @numEntities  do begin
		if (entity_id[i]=0) then return();

		k := abs(entity_pos_x[i] - px);
		l := abs(entity_pos_y[i] - player_y);
			
		k:=k/2 + l/2;
			
		if k<maxDistance then begin
			maxDistance:=k;
			nearestEntity:=i;
		end;
	end;
	
end;


procedure CrashWithEntities();
begin
	EntityCollider();
	if (maxDistance<@crashDistance) then begin
		entity_id[nearestEntity]:=0;
		togglebit(SPRITE_BITMASK, nearestEntity+1,0);
		PlaySound(SID_CHANNEL1,
			13,  // Volume
			20,  // Hi byte frequency
			0*16+0,  // Attack voice 1
			3*16 + 12,   // Sustain = 16*15 + release=6
			1 +SID_SAW,  // Waveform
			SID_SAW);  // waveform*/
	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 PlayerBackgroundCollide();
var
	tmp:integer;
	
begin
	tmp:=(player_x - 12);
	i:=tmp/8;
	j:=(player_y-38)/8;
	moveto(i-1,j-1, $04);
	blockx:=0;
	blocky:=0;
	
	k:=screenmemory[ map_player_direction +40+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;



procedure TraverseLevels();
var 
   redraw:byte=0;
begin
	redraw:=0;
	
	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<>m_rl_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<>m_rl_height) then begin
			inc(curLevel_y);
			player_y:=@miny;
			redraw:=1;
		end
		else
			player_y:=@maxy-1;
	end;


	if redraw=1 then begin
		levelpointer:=@levelPosition;
		RenderCharsetColorLevel(curLevel_x,curLevel_y,$04);
		LoadEntities();
		InitEntities();
	end;


end;

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

	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+40;
	if (joystickup=1) then map_player_direction:=map_player_direction-40;

	PlayerBackgroundCollide();

	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();
	CrashWithEntities();
	CloseIRQ();
end;



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


	levelpointer:=@levelPosition;
	RenderCharsetColorLevel(curLevel_x,curLevel_y,$04);
	LoadEntities();
	InitEntities();
	// Turn off all interrupts
	// Turn off kernal
	SetMemoryConfig(1,@useKernal,0);
	// Set up raster IRQ
	StartRasterChain(RasterRenderLevels(),$00,@useKernal);
	Loop(); 
end.
