	
FNAME "Super.com"

; ************************************************************
; * COMPILATION OPTIONS                                      *
; ************************************************************






SPRITE_WIDTH:				EQU 16			; If other size than 16x16, RenderOneSprite needs some rework adding OUTIs/OUTDs and calculating correctly the pattern address in RAM.
SPRITE_HEIGHT:				EQU 16
NO_CACHE:							; Reffers to sprite render with pattern cache in VRAM or not. Comment it for CACHE. If cache enable, sprites always must be 16x16.



; ************************************************************
; * BIOS DECLARATIONS                                        *
; ************************************************************






RST:
	.SYNCHR:			EQU $08
	.CHRGTR:			EQU $10
	.OUTDO:				EQU $18
	.DCOMPR:			EQU $20
	.GETYPR:			EQU $28
	.CALLF:				EQU $30
	.KEYINT:			EQU $38

BIOS.CALL:
	.CHKRAM:			EQU $00
	.SYNCHR:			EQU $08			; IN (HL) = char to test. (PC) = valid char. OUT hl=hl+1, A = next basic character, OUT CF = 1 is numerical, ZF = 1 end of statement.
	.RDSLT:				EQU $0C			; IN A = ExxxSSPP, HL = address to read. OUT DI, A = byte value.
	.CHRGTR:			EQU $10			; IN HL = pointer to basic-text or token. OUT A = character, CF = 1 is a number, ZF = 1 is the end of statement. HL += 1.
	.WRSLT:				EQU $14			; IN A = ExxxSSPP, HL = address to write. E = value to write. OUT DI.
	.OUTDO:				EQU $18			; IN A = Char number. Output to the current output device.
	.CALSLT:			EQU $1C			; IN IYh = ExxxSSPP, IX = address to call. MOD: AF', BC', DE', HL'.
	.DCOMPR:			EQU $20			; CP HL, DE. MODs A.
	.ENASLT:			EQU $24			; IN A = ExxxSSPP, H = ppxxxxxx pp = (page 00 to 11).
	.GETYPR:			EQU $28
	.CALLF:				EQU $30			; IN (PC) = ExxxSSPP, (PC+1,2) = Address.
	.KEYINT:			EQU $38			; ISR.
	.INITIO:			EQU $3B			; None.
	.INIFNK:			EQU $3E			; None.
	.DISSCR:			EQU $41			; None. MODs AF, BC.
	.ENASCR:			EQU $44			; None. MODs AF, BC.
	.WRTVDP:			EQU $47			; B= data, C= register number. MODs AF, BC.
	.RDVRM:				EQU $4A			; IN HL = address to read. OUT A = value. MODs AF.
	.WRTVRM:			EQU $4D			; IN HL = address to write, A = Value. MODs AF.
	.SETRD:				EQU $50			; IN HL = address to read. OUT DI. MODs AF.
	.SETWRT:			EQU $53			; IN HL = address to write. OUT DI. MODs AF.
	.FILVRM:			EQU $56			; IN A = Data byte, HL = start address, BC = length.
	.LDIRMV:			EQU $59			; IN HL = VRAM address source, DE = RAM address destination, BC = Length.
	.LDIRVM:			EQU $5C			; IN HL = RAM address source, DE = VRAM address destination, BC = Length.
	.CHGMOD:			EQU $5F			; A = screen mode number.
	.CHGCLR:			EQU $62			; IN (BIOS.VAR.FORCLR), (BIOS.VAR.BAKCLR), (BIOS.VAR.BDRCLR).
	.NMI:				EQU $66
	.CLRSPR:			EQU $69
	.INITXT:			EQU $6C
	.INIT32:			EQU $6F
	.INIGRP:			EQU $72
	.INIMLT:			EQU $75
	.SETTXT:			EQU $78
	.SETT32:			EQU $7B
	.SETGRP:			EQU $7E
	.SETMLT:			EQU $81
	.CALPAT:			EQU $84
	.CALATR:			EQU $87
	.GSPSIZ:			EQU $8A
	.GRPPRT:			EQU $8D			; IN A = char number to print.
	.GICINI:			EQU $90
	.WRTPSG:			EQU $93			; IN A = PSG register number, E = Data.
	.RDPSG:				EQU $96			; IN A = PSG register number. OUT A = Data.
	.STRTMS:			EQU $99
	.CHSNS:				EQU $9C			; OUT ZF = 1 if keyboard buffer is empty.
	.CHGET:				EQU $9F			; OUT A = chr number. Waits for a keyboard type.
	.CHPUT:				EQU $A2			; IN A = chr number.
	.LPTOUT:			EQU $A5			; IN A = chr number. OUT CF=1 if error.
	.LPTSTT:			EQU $A8			; OUT A = $FF and ZF = 0, if ready, A = 0 and ZF = 1 if not ready.
	.CNVCHR:			EQU $AB			; IN A = char number. OUT ...
	.PINLIN:			EQU $AE
	.INLIN:				EQU $B1
	.QINLIN:			EQU $B4
	.BREAKX:			EQU $B7			; OUT CF = 1, if CTRL+STOP was pressed.
	.ISCNTC:			EQU $BA
	.CKCNTC:			EQU $BD
	.BEEP:				EQU $C0	
	.CLS:				EQU $C3			; IN ZF = 1 for CLS.
	.POSIT:				EQU $C6			; IN H = y, L = x cursor position.
	.FNKSB:				EQU $C9			; If enable, it prints the function keys on the screen. If disabled it erases, IN (BIOS.VAR.FNKFLG).
	.ERAFNK:			EQU $CC			; Erase the display of function keys.
	.DSPFNK:			EQU $CF			; Prints the display of function keys.
	.TOTEXT:			EQU $D2			; Forces the screen to be in text mode.
	.GTSTCK:			EQU $D5			; A=STICK(A).
	.GTTRIG:			EQU $D8			; A=STRIG(A). A = $FF pressed.
	.GTPAD:				EQU $DB			; IN A = touchpad number. OUT A = value.
	.GTPDL:				EQU $DE			; IN A = paddle number. OUT A = value.
	.GETCPU:			EQU $183		; OUT A = cpu ID. 0 = z80 ROM, 1 = R800 ROM, 2 = R800 DRAM.

BIOS.VAR:
	.RG0SAV:			EQU $F3DF
	.RG1SAV:			EQU $F3E0
	.RG2SAV:			EQU $F3E1
	.RG3SAV:			EQU $F3E2
	.RG4SAV:			EQU $F3E3
	.RG5SAV:			EQU $F3E4
	.RG6SAV:			EQU $F3E5
	.RG7SAV:			EQU $F3E6

	.STATFL:			EQU $F3E7

	.RG08SAV:			EQU $FFE7
	.RG09SAV:			EQU $FFE8
	.RG10SAV:			EQU $FFE9
	.RG11SAV:			EQU $FFEA
	.RG12SAV:			EQU $FFEB
	.RG13SAV:			EQU $FFEC
	.RG14SAV:			EQU $FFED
	.RG15SAV:			EQU $FFEE
	.RG16SAV:			EQU $FFEF
	.RG17SAV:			EQU $FFF0
	.RG18SAV:			EQU $FFF1
	.RG19SAV:			EQU $FFF2
	.RG20SAV:			EQU $FFF3
	.RG21SAV:			EQU $FFF4
	.RG22SAV:			EQU $FFF5
	.RG23SAV:			EQU $FFF6

	.RG25SAV:			EQU $FFFA
	.RG26SAV:			EQU $FFFB
	.RG27SAV:			EQU $FFFC

	.BAKCLR:			EQU $F3EA
	.BDRCLR:			EQU $F3EB



; ************************************************************
; * GAME CONSTANTS DECLARATIONS                              *
; ************************************************************






MapAttribs:
	.MaskMajor:			EQU $E0
	.MaskMinor:			EQU $1E
	.MaskAttribs:			EQU $FE
	.MaskTileNumber:		EQU $01

	.Major.Nothing:			EQU $00
	.Major.EnemyRespawn:		EQU $20
	.Major.RoomDoor:		EQU $40
	.Major.AnimatedTile:		EQU $60
	.Major.AutoBreakable:		EQU $80				; If minor: 	= 0 break to empty black. Tile 320.
									; 		= 2 break to empty blue. Tile 324.
									;		= 4 break to ramdon fruit/food on black. Tiles, (any 0-29) and 2.
									;		= 6 break to ramdon fruit/food on blue. Tiles, (any 0-29) and 2 or 1.
									;		= 8 break to yellow key. Tile 42.
									;		= A break to orange key. Tile 44.
									;		= C break to cyan key. Tile 46.
									;		= E break to violet key. Tile 48.
									;		= 10 break to bombs. Tile 30.
									;		= 12 break to twisters. Tile 32.
									;		= 14 break to fireballs. Tile 34.
									;		= 16 break to thunders. Tile 36.
									;		= 18 break to knifes. Tile 38.
									;		= 1A break to potions. Tile 40.
									;		= 1C break to nedles (death) on black. Tile 98.
									;		= 1E break to nedles (death) on blue. Tile 102.
	.Major.Special:			EQU $A0
	.Major.BreakableToBlack:	EQU $C0				; Minor = number of hit needed (as countdown). Tile 320.
	.Major.BreakableToBlue:		EQU $E0				; Minor = number of hit needed (as countdown). Tile 324.

	.Special.PlayerRespawn:		EQU $00
	.Special.PlayerCheckPoint:	EQU $02
									; Top/Left corner mark for the special rooms shops (where them are located in the map).
	.Special.CocktailsShop:		EQU $04				; Codes Ax00.
	.Special.WeaponsShop:		EQU $06
	.Special.FruitsShop:		EQU $08
	.Special.UpgradesShop:		EQU $0A
	.Special.SpecialDeals:		EQU $0C
	.Special.Hospital:		EQU $0E
	.Special.MonsterRoom1:		EQU $10				; Codes Bx00.
	.Special.MonsterRoom2:		EQU $12
	.Special.MonsterRoom3:		EQU $14
	.Special.MonsterRoom4:		EQU $16
	.Special.MonsterRoom5:		EQU $18

	.Special.TeleTransport:		EQU $1C				; The following 5 tiles in the map (horizontally),
									; must contain enconded in the "MINOR attribute" the following:
									; "lsnX, msnX, lsnY, msnY, map_numberM" values to where tele-transport all players.
									; HINTS: lsn = less significant nibble, msn = most significant nibble,
									; first map = 1. Current map = 0.
									; Remember that "minor attribute" is bits 9 up to 12 (NOT 8 up to 11).
									; Switching to other map, make the game lose track of all what is done in the current map, so it is used only for advance to the next level.

	.Special.RoomRespawn:		EQU $1E				; It marks the entrance for the players in a monster room.

	.RoomDoor.Cocktails:		EQU $04				; Door accesses to special rooms.
	.RoomDoor.Weapons:		EQU $06				; Codes 4x00.
	.RoomDoor.Fruits:		EQU $08
	.RoomDoor.Upgrades:		EQU $0A
	.RoomDoor.SpecialDeals:		EQU $0C
	.RoomDoor.Hospital:		EQU $0E
	.RoomDoor.Monster1:		EQU $10				; Codes 5x00.
	.RoomDoor.Monster2:		EQU $12
	.RoomDoor.Monster3:		EQU $14
	.RoomDoor.Monster4:		EQU $16
	.RoomDoor.Monster5:		EQU $18






TileMap:
	.Items_StartAddress:		EQU 0
	.Fruits_StartAddress:		EQU 0
	.Fruits_StopAddress:		EQU 26
	.Fruits_Count:			EQU 13
	.Bonus_Address:			EQU 26
	.Gold_Address:			EQU 28
	.Weapons_StartAddress:		EQU 30
	.Weapon_Bomb:			EQU 30
	.Weapon_Twister:		EQU 32
	.Weapon_Fireball:		EQU 34
	.Weapon_Thunder:		EQU 36
	.Weapon_Knife:			EQU 38
	.Weapons_StopAddress:		EQU 40
	.Potion_Address:		EQU 40
	.Keys_StartAddress:		EQU 42
	.Keys_StopAddress:		EQU 56
	.Shoes_StartAddress:		EQU 56
	.Shoes_StopAddress:		EQU 62
	.Armour_StartAddress:		EQU 62
	.Armour_StopAddress:		EQU 68
	.HandWeapon_StartAddress:	EQU 68
	.HandWeapon_Dagger:		EQU 68
	.HandWeapon_Axe:		EQU 70
	.HandWeapon_Knife:		EQU 72
	.HandWeapon_RamboKnife:		EQU 74
	.HandWeapon_Sword:		EQU 76
	.HandWeapon_StopAddress:	EQU 78
	.Helmet_StartAddress:		EQU 78
	.Helmet_StopAddress:		EQU 84
	.Cocktail_Address:		EQU 84
	.Jim_Address:			EQU 86
	.Vodka_Address:			EQU 88
	.Beer_Address:			EQU 90



	.Coin_Address:			EQU 94
	.Items_StopAddress:		EQU 96

	.KillTiles_StartAddress:	EQU 96
	.KillNeddles_StartAddress:	EQU 96
	.KillNeddlesDownBlack_Address:	EQU 98
	.KillNeddlesDownBlue_Address:	EQU 102
	.KillNeddles_StopAddress:	EQU 104
	.KillLava_StartAddress:		EQU 104
	.KillLava_StopAddress:		EQU 112
	.KillTiles_StopAddress:		EQU 128

	.Scenography_StartAddress:	EQU 128
	.DoorOpenTile_Address:		EQU 255
	.Scenography_StopAddress:	EQU 288

	.ClimbableLadder_StartAddress:	EQU 288
	.ClimbableLadder_StopAddress:	EQU 304

	.StandUpFloor_StartAddress:	EQU 304
	.StandUpFloor_StopAddress:	EQU 320

	.BitCodedWalls_BaseAddress:	EQU 320

	.BlackBackgroundTile_Address:	EQU 320
	.BlueBackgroundTile_Address:	EQU 324

	.SolidWalls_StartAddress:	EQU 448

	.UpDoorOpen_BaseAddress:	EQU 448				; Even = rock, odd = bricks.
	.UpDoor_StartAddress:		EQU 450				; Even = rock, odd = bricks.
	.UpDoor_StopAddress:		EQU 464
	.Door_StartAddress:		EQU 464
	.Door_StopAddress:		EQU 471

	.BeltTransportLeft_StartAddress:	EQU	496
	.BeltTransportLeft_StopAddress:		EQU	500
	.BeltTransportRight_StartAddress:	EQU	500
	.BeltTransportRight_StopAddress:	EQU	504

	.SolidWalls_StopAddress:	EQU 512



; ************************************************************
; * GAME STRUCTURES DECLARATIONS                             *
; ************************************************************






SpritesPoolSize:			EQU 32

SpriteClasses:
	.LiftsStopAddress:		EQU 2
	.BoxesClass:			EQU 2
	.SpacingClassesStopAddress:	EQU 3
	.CharactersStartClass:		EQU 3
	.TurtleCharacterClass:		EQU 3
	.DogCharacterClass:		EQU 8
	.CharactersStopClass:		EQU 16
	.SecondaryWeaponStartAddress:	EQU 16

	.RemoveMeClass:			EQU 27
	.CrushedClass:			EQU 28
	.ShieldClass:			EQU 29
	.HandWeaponClass:		EQU 30
	.BornClass:			EQU 31

	.EmptySlotClass:		EQU 128
	.UnassignedClass:		EQU 129				; A step between reserving the slot and registering it with a proper class.

	.PlayerClass:			EQU 255



SpritePatterns:
	.Empty:				EQU 43				; It must be a zeroed empty sprite pattern.



ORG $0
Sprite:
	.Class:				RB 1				; Logic class. Format: $0000CCCC. Exception $FF = Player character.
	.Pattern:			RB 1				; Current sprite pattern.
	.PatternFlip:			RB 1
	.OldPatternFlip:		RB 1				; Used in SetSpritePattern.

	.HighHitBoxLeft:		RB 1
	.HighHitBoxWidth:		RB 1				; Width & height is << 3 instead of << 4. For allowing the value 16 to be set.
	.HighHitBoxTop:			RB 1
	.HighHitBoxHeight:		RB 1
	.LowHitBoxLeft:			RB 1
	.LowHitBoxWidth:		RB 1
	.LowHitBoxTop:			RB 1
	.LowHitBoxHeight:		RB 1

	.Energy:			RW 1				; Health.

	.MapOy:				RB 1				; Where in the map actually is the sprite.
	.MapTy:				RB 1				; This information is needed for the logic, How the character interacts with the map?.
	.MapOx:				RB 1				; As is a waste of time calculating all the time, map coordinates / screen coordinates, both systems must be maintain.
	.MapTx:				RB 1
	.Bf0ScreenX:			RB 1
	.Bf0ScreenY:			RB 1
	.Bf0ScreenWidth:		RB 1
	.Bf0ScreenHeight:		RB 1
	.Bf1ScreenX:			RB 1
	.Bf1ScreenY:			RB 1
	.Bf1ScreenWidth:		RB 1
	.Bf1ScreenHeight:		RB 1
	.ScreenX:			RB 1				; Where it was drew?, current active buffer.
	.ScreenY:			RB 1
	.ScreenWidth:			RB 1				; Width = 0, not drew.
	.ScreenHeight:			RB 1				; Height = 0, not drew.

	.MapRespawnTy:			RB 1				; From which respawn spot it is?. It is for don't let the respawn spot to create characters as crazy.
	.MapRespawnTx:			RB 1
	.LogicArea:			RB 24				; Used for the logic of the game to store previous behaviour desicions, in order to have a smart action.

	.Reserved:			RB 8				; Used: for maintain a backup of where this character was when entering in a monster room.
	.SizeOf:							; 64 bytes. IF CHANGED adjust "Sprite size formula" at: IACharacters, GetFreeSpriteSlot, SetSprite.



IFDEF NO_CACHE
ELSE

ORG $0
PatternCacheSize:			EQU 64
PatternCache:
	.PatternNumber:			RB 1
	.PatternFlipControl:		RB 1
	.Expiration:			RB 1				; $FF = cache slot unused. $00 - $FE = oldness.
	.SizeOf:

ENDIF



; ************************************************************
; * CODE	                                             *
; ************************************************************






ORG $100

Run:			ld hl, VAR_START			; It erase the variable area.
			ld de, VAR_START+1
			ld bc, VAR_END - VAR_START - 1
			ld (hl),0
			ldir

			call SetupDOS2RamHandling	
			jp c, .Esc

			ld de, (DOS2.CALL.PUT_P2+1)		; It speeds up some function.
			ld ix, .P2Addresses
			ld b, (.P2Addresses_END - .P2Addresses_START) / 2

.P2WriteLoop:		ld l, (ix+0)
			ld h, (ix+1)
			inc ix
			inc ix
			ld (hl), e
			inc hl
			ld (hl), d
			djnz .P2WriteLoop

			ld hl, (DOS2.CALL.GET_P2+1)
			ld (VGMPlayer.G2_1+1), hl
			ld (ISR.G2_1+1), hl			; It speeds up some function.

			call ReserveDOS2Memory
			jp c, .Esc

			call CPUDetection			; It setup the CPUDetected variable.

			; ---------------------

			ld de, .ResourceFileName		; Loads the resources files.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			jp nz, .Esc

			ld a, b
			ld (FileHandle), a

			ld a, (ResourcesSegment)
			call DOS2.CALL.PUT_P2

			ld c, $4A				; It positions the pointer at the end of the file. So, now we know its length.
			ld a, 2
			ld hl, 0
			ld e, l
			ld d, h
			call DOS2.CALL.BDOS

			push de
			push hl

			ld a, (FileHandle)			; It must move the pointer to the start.
			ld b, a
			ld c, $4A
			xor a
			ld hl, 0
			ld e, l
			ld d, h
			call DOS2.CALL.BDOS

			pop hl					; LENGTH = DE HL.
			pop de

			ld a, e
			or d
			jp nz, .Esc

			ld a, h
			cp $40
			jp nc, .Esc

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			call DOS2.CALL.BDOS
			and a
			jp nz, .Esc

			call CloseFileOk

			; ---------------------

			ld de, .PlayerPatternsFileName		; Loads the player's patterns compressed binary.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			jp nz, .Esc

			ld a, b
			ld (FileHandle), a

			ld a, (PlayerPatternsSegment)
			call DOS2.CALL.PUT_P2

			ld c, $4A				; It positions the pointer at the end of the file. So, now we know its length.
			ld a, 2
			ld hl, 0
			ld e, l
			ld d, h
			call DOS2.CALL.BDOS

			push de
			push hl

			ld a, (FileHandle)			; It must move the pointer to the start.
			ld b, a
			ld c, $4A
			xor a
			ld hl, 0
			ld e, l
			ld d, h
			call DOS2.CALL.BDOS

			pop hl					; LENGTH = DE HL.
			pop de

			ld a, e
			or d
			jp nz, .Esc

			ld a, h
			cp $40
			jp nc, .Esc

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			call DOS2.CALL.BDOS
			and a
			jp nz, .Esc

			call CloseFileOk

			; ---------------------

			xor a					; Black back and border color.
			ld (BIOS.VAR.BAKCLR), a
			ld (BIOS.VAR.BDRCLR), a

			ld a, 8					; Screen 8.
			rst RST.CALLF
			db $80
			dw BIOS.CALL.CHGMOD

			ld a, (BIOS.VAR.RG08SAV)
			or $02					; Disable Sprites.
			ld b, a
			ld c, 8
			rst RST.CALLF
			db $80
			dw BIOS.CALL.WRTVDP

			xor a
			or $02					; Masks left border.
			ld b, a
			ld c, 25
			rst RST.CALLF
			db $80
			dw BIOS.CALL.WRTVDP

		; ********************************
		; *                              *
			call GameEngine
			jr c, .Esc
		; *                              *
		; ********************************

			call ISRUnHooker

			xor a					; Screen 0.
			rst RST.CALLF
			db $80
			dw BIOS.CALL.CHGMOD

			call .FreeReservation

			ld b, 0					; Returns Ok to DOS2 prompt and exit.
			ld c, $62
			call DOS2.CALL.BDOS
			ret

.ResourceFileName:	DB "Resource.bin", 0
.PlayerPatternsFileName:DB "Playerp.bin", 0

.Esc:			ld hl, (TileBaseSegment)		; If already reserved ram segments, free them.
			ld a, l
			or h
			call nz, .FreeReservation

.Finish:		call CloseFileOk

			ld b, 1					; Returns error to DOS2 prompt and exit.
			ld c, $62
			call DOS2.CALL.BDOS
			ret

.FreeReservation:	ld a, (TileBaseSegment)
			ld b, 22
			
.FreeLoop:		push af	
			push bc
			ld b, 0
			call DOS2.CALL.FRE_SEG
			pop bc
			pop af

			inc a
			djnz .FreeLoop

			ret
.P2Addresses_START:
.P2Addresses:		DW MapRender.P2_1+1, MapRender.P2_2+1, MapRender.P2_3+1, MapRender.P2_4+1
			DW MapRender.P2_5+1, MapRender.P2_6+1, MapRender.P2_7+1, MapRender.P2_8+1
			DW MapRender.P2_9+1, MapRender.P2_10+1, MapRender.P2_11+1, MapRender.P2_12+1
			DW MapRender.P2_13+1, MapRender.P2_14+1, MapRender.P2_15+1, MapRender.P2_16+1
			DW MapRender.P2_17+1
			DW AnimateTiles.P2_1+1, AnimateTiles.P2_2+1, AnimateTiles.P2_3+1
IFDEF NO_CACHE
			DW RenderOneSprite.P2_1+1
ELSE
			DW RenderOneSprite.P2_2+1
ENDIF
			DW ScanForCharactersRespawn.P2_1+1, ScanForCharactersRespawn.P2_2+1
			DW UpdateOverlayGrabItem.P2_1+1
			DW BuildUpPlayerPatterns.P2_1+1, BuildUpPlayerPatterns.P2_2+1, BuildUpPlayerPatterns.P2_3+1
			DW GetMapTileNumber.P2_1+1
			DW SetSpritePattern.P2_1+1
			DW PrintText.P2_1+1, PrintText.P2_2+1
			DW VGMPlayer.P2_1+1, VGMPlayer.P2_2+1, VGMPlayer.P2_3+1
			DW ISR.P2_1+1, ISR.P2_2+1
.P2Addresses_END:






SetupDOS2RamHandling:	ld a, ($FB20)
			and 1
			scf
			ret z

			xor a
			ld d, 4
			ld e, 2
			call $FFCA

			ld de, DOS2.CALL.ALL_SEG
			ld bc, 48
			ldir

			and a
			ret






ReserveDOS2Memory:	xor a
			ld b, a
			call DOS2.CALL.ALL_SEG			; It request a free page from primary segment.
			ret c

			ld (TileBaseSegment), a
			ld b, a
			ld c, 22 - 1				; Wanted segments.

.Loop:			push bc

			xor a
			ld b, a
			call DOS2.CALL.ALL_SEG			; It request a free page from primary segment.

			pop bc
			inc b

			ret c

			dec c
			jr z, .Ok

			cp b
			jr z, .Loop

.FreeLoop:		push af					; At this point was unable to reserve all segments.
			ld b, 0
			call DOS2.CALL.FRE_SEG
			pop af

			dec a
			ld hl, TileBaseSegment
			cp (hl)

			jr nz, .FreeLoop

			ld b, 0
			call DOS2.CALL.FRE_SEG			; Last one.
			xor a
			ld (TileBaseSegment), a
			ld (MapBaseSegment), a
			ld (SpritesBaseSegment), a
			scf
			ret

.Ok:			ld a, (TileBaseSegment)
			add a, 8
			ld (MapBaseSegment), a
			add a, 4
			ld (SpritesBaseSegment), a
			add a, 4
			ld (PlayerPatternsSegment), a
			inc a
			ld (FMSongBaseSegment), a
			add a, 4
			ld (ResourcesSegment), a

			ret






CPUDetection:		ld a, $80
			ld hl, $2D
			call BIOS.CALL.RDSLT			; IN A = ExxxSSPP, HL = address to read. OUT DI, A = byte value.
			ei

			cp 3
			jr c, .NoR800

			rst RST.CALLF
			DB $80
			DW BIOS.CALL.GETCPU			; OUT A = cpu ID. 0 = z80 ROM, 1 = R800 ROM, 2 = R800 DRAM.

			and a
			jr z, .NoR800

			ld a, 2
			ld (CPUDetected), a
			ret

.NoR800:		halt

			xor a
			ld (VBlankCountInFrame), a

			call ISRHooker

			halt
			ld bc, 0

.Loop:			dec bc
			nop
			nop
			ld a, c
			or b
			jr nz, .Loop

			call ISRUnHooker

			ld a, (VBlankCountInFrame)

			cp $18

			ld a, 1
			jr c, .SaveDetected
			dec a

.SaveDetected:		ld (CPUDetected), a
			ret






ISRHooker:		di

			ld a, (BIOS.CALL.KEYINT)		; Back up the original ISR jump.
			ld (HookBackUp), a
			ld hl, (BIOS.CALL.KEYINT+1)
			ld (HookBackUp+1), hl

			ld a, $C3				; JP opcode.
			ld (BIOS.CALL.KEYINT), a

			ld hl, ISR
			ld (BIOS.CALL.KEYINT+1), hl

			call OPL4_SetTimer

			ei
			ret






ISRUnHooker:		di

			call OPL4_StopTimer

			call Opl4_Stop
			call PSG_Stop

			ld a, (HookBackUp)
			ld (BIOS.CALL.KEYINT), a
			ld hl, (HookBackUp+1)
			ld (BIOS.CALL.KEYINT+1), hl

			ei
			ret






CloseFileError:		and a
			jr CloseFileOk.Cf






CloseFileOk:		and a

.Cf:			push af
			ld a, (FileHandle)			; File Close.
			ld b, a
			ld c, $45
			call DOS2.CALL.BDOS
			pop af
			ret






LoadTileSet:		ld a, (CurrentMapNumber)		; Sets the specific per map filename.
			ld b, a
			add a, '0'
			ld (.TilesetFileName2 + 7), a

			ld a, (CurrentTileSetLoaded)
			cp b
			ret z					; Escape if already loaded.

			ld de, .TilesetFileName2		; Try to load the specific map tileset.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			jr nz, .LoadGlobalFileName
								; Specific per map filename found!.
			ld a, b
			ld (FileHandle), a	

			ld a, (TileBaseSegment)
			ld b, a
			ld c, 8

.LoadTilesLoopS:	push bc
			ld a, b
			call DOS2.CALL.PUT_P2

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			call DOS2.CALL.BDOS
			and a
			pop bc
			jp nz, CloseFileError

			inc b
			dec c
			jr nz, .LoadTilesLoopS

			ld a, (CurrentMapNumber)
			ld (CurrentTileSetLoaded), a

			jp CloseFileOk

.LoadGlobalFileName:	ld a, (CurrentTileSetLoaded)
			cp 255
			ret z					; Escape if already loaded.

			ld de, .TilesetFileName			; Loads the tileset binary.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			scf
			ret nz

			ld a, b
			ld (FileHandle), a	

			ld a, (TileBaseSegment)
			ld b, a
			ld c, 8

.LoadTilesLoop:		push bc
			ld a, b
			call DOS2.CALL.PUT_P2

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			call DOS2.CALL.BDOS
			and a
			pop bc
			jp nz, CloseFileError

			inc b
			dec c
			jr nz, .LoadTilesLoop

			ld a, 255
			ld (CurrentTileSetLoaded), a

			jp CloseFileOk

.TilesetFileName:	DB "ScTiles.bin", 0

.TilesetFileName2:	DB "ScTiles0.bin", 0






LoadSpriteSet:		ld a, (CurrentMapNumber)		; Sets the specific per map filename.
			ld b, a
			add a, '0'
			ld (.SpritesFileName2 + 7), a

			ld a, (CurrentSpriteSetLoaded)
			cp b
			ret z					; Escape if already loaded.

			ld de, .SpritesFileName2		; Try to load the specific map spriteset.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			jr nz, .LoadGlobalFileName
								; Specific per map filename found!.
			ld a, b
			ld (FileHandle), a	

			ld a, (SpritesBaseSegment)
			ld b, a
			ld c, 4

.LoadSpritesLoopS:	push bc
			ld a, b
			call DOS2.CALL.PUT_P2

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			call DOS2.CALL.BDOS
			and a
			pop bc
			jp nz, CloseFileError

			inc b
			dec c
			jr nz, .LoadSpritesLoopS

			ld a, (CurrentMapNumber)
			ld (CurrentSpriteSetLoaded), a

			jp CloseFileOk

.LoadGlobalFileName:	ld a, (CurrentSpriteSetLoaded)
			cp 255
			ret z					; Escape if already loaded.

			ld de, .SpritesFileName			; Loads the Spriteset binary.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			scf
			ret nz

			ld a, b
			ld (FileHandle), a	

			ld a, (SpritesBaseSegment)
			ld b, a
			ld c, 4

.LoadSpritesLoop:	push bc
			ld a, b
			call DOS2.CALL.PUT_P2

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			call DOS2.CALL.BDOS
			and a
			pop bc
			jp nz, CloseFileError

			inc b
			dec c
			jr nz, .LoadSpritesLoop

			ld a, 255
			ld (CurrentSpriteSetLoaded), a

			jp CloseFileOk

.SpritesFileName:	DB "ScSprite.bin", 0

.SpritesFileName2:	DB "ScSprit0.bin", 0






LoadSong:		ld b, a
			ld a, (FMCurrentSongNumber)
			cp b
			ret z

			ld a, b
			ld (FMCurrentSongNumber), a

			add a, '0'				; In A = song number.
			ld (.FileName + 4), a

			ld de, .FileName			; Loads the required song.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			jr nz, .LoadErr

			ld a, b
			ld (FileHandle), a

			ld c, $4A				; It reads the filelength by using a trick.
			ld a, 2
			ld hl, 0
			ld e, l
			ld d, h
			call DOS2.CALL.BDOS

			push de
			push hl

			ld a, (FileHandle)			; It moves the pointer back to the start of the file.
			ld b, a
			ld c, $4A
			xor a
			ld hl, 0
			ld e, l
			ld d, h
			call DOS2.CALL.BDOS

			pop hl
			pop de					; DEHL = file length.

			ld a, e
			or d
			jr nz, .LoadErr
								; The file is up to 65535 or smaller.
			ld a, (FMSongBaseSegment)
			ld b, a

.LoadLoop:		ld de, $4000
			and a
			sbc hl, de
			jr c, .SmallerThan16K

			push bc

			ld a, b
			call DOS2.CALL.PUT_P2

			push hl
			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			call DOS2.CALL.BDOS
			pop hl
			and a
			pop bc
			jr nz, .LoadErr

			inc b
			jp .LoadLoop

.SmallerThan16K:	add hl, de				; Restore length value.

			ld a, l
			or h
			jr z, .LoadCompleted

			ld a, b
			call DOS2.CALL.PUT_P2

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			call DOS2.CALL.BDOS
			and a
			jr nz, .LoadErr

.LoadCompleted:		call  Opl4_Start			; Start playing the current FM song.

			jp CloseFileOk

.LoadErr:		xor a
			ld (FMCurrentSongNumber), a
			ld (FMPlay), a
			ld (FMPlayMusicLoopedFlag), a

			jp CloseFileError

.FileName:		DB "Song0.fm", 0






LoadMap:		ld a, (CurrentMapNumber)		; Setup the correct filename.
			add a, '0'
			ld (.MapFileName + 5), a

			ld de, .MapFileName			; Loads the original game map.
			ld c, $43				; File open for read only.
			ld a, 1
			call DOS2.CALL.BDOS
			and a
			scf
			ret nz

			ld a, b
			ld (FileHandle), a	

			ld a, (MapBaseSegment)
			ld b, a
			ld c, 4

.LoadMapLoop:		push bc
			ld a, b
			call DOS2.CALL.PUT_P2

			ld c, $48
			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			call DOS2.CALL.BDOS
			and a
			pop bc
			jp nz, CloseFileError

			inc b
			dec c
			jr nz, .LoadMapLoop

			jp CloseFileOk

.MapFileName:		DB "ScMap1.bin", 0






LoadGame:		ld c, $43				; IN DE = filename null terminated string.
			ld a, 1					; File open for read only.
			call DOS2.CALL.BDOS
			and a
			scf
			ret nz
								; File opened.
			ld a, b
			ld (FileHandle), a

			ld a, (MapBaseSegment)
			ld b, a
			ld c, 4

.LoadMapLoop:		push bc
			ld a, b
			call DOS2.CALL.PUT_P2

			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			ld c, $48
			call DOS2.CALL.BDOS
			and a
			pop bc
			jp nz, CloseFileError

			inc b
			dec c
			jr nz, .LoadMapLoop

			ld a, (FileHandle)
			ld b, a
			ld de, VarSaveState_Start
			ld hl, VarSaveState_End - VarSaveState_Start
			ld c, $48
			call DOS2.CALL.BDOS
			and a
			jp nz, CloseFileError

			call CloseFileOk

			call LoadTileSet
			ret c

			jp LoadSpriteSet






SaveGame:		ld c, $44				; IN DE = filename null terminated string.
			xor a					; File open for read only.
			ld b, a
			call DOS2.CALL.BDOS
			and a
			scf
			ret nz
								; File created.
			ld a, b
			ld (FileHandle), a

			ld a, (MapBaseSegment)
			ld b, a
			ld c, 4

.SaveMapLoop:		push bc
			ld a, b
			call DOS2.CALL.PUT_P2

			ld a, (FileHandle)
			ld b, a
			ld de, 32768
			ld hl, 16384
			ld c, $49
			call DOS2.CALL.BDOS
			and a
			pop bc
			jp nz, CloseFileError

			inc b
			dec c
			jr nz, .SaveMapLoop

			ld a, (FileHandle)
			ld b, a
			ld de, VarSaveState_Start
			ld hl, VarSaveState_End - VarSaveState_Start
			ld c, $49
			call DOS2.CALL.BDOS
			and a
			jp nz, CloseFileError
			jp CloseFileOk



; ************************************************************
; * TILE BASED MAP ENGINE                                    *
; ************************************************************






SetUpVdpScrolls:	di					; MODs: A, BC. This setup the vdp registers with the scrolls variables for the current active frame (not the frame visualized).

			ld a, (ScreenCurrentY)
			out ($99), a
			ld a, 23 + 128
			out ($99), a

.No23Adj:		ld a, (ScreenCurrentX)
			ld c, a
			and $F8
			rrca
			rrca
			rrca
			ld b, a
			ld a, c
			and 7
			jr z, .NoOffsets

			inc b

.NoOffsets:		ld a, b
			out ($99), a
			ld a, 26 + 128				; V9958 feature.
			out ($99), a

			ld a, 8
			sub c
			and 7
			out ($99), a
			ld a, 27 + 128				; V9958 feature.
			out ($99), a			

			ei
			ret






ScreenMapRedraw:	ld hl, 212				; It renders the map in the current frame for the whole screen.
			ld de, 0				; H = Top, L = Height. 
			ld c, 1					; D = Left, CE = Width.
			jp MapRender






ScrollsOnDemand:	ld a, (SpritesCount)			; It updates the Ideal map scope variables seeing the players positions,
			and a					; Then it calculates the differences with the current active frame map visual scope,
			ret z					; And last it order to scroll those differences.
								; "Ideal map scope variables" is a must have for preventing earthquakes between odd active frames and even active frames (desinchronization).
			ld hl, 0
			exx					; hl' = current wanted Y displacement for the map.
			ld hl, 0				; hl = current wanted X displacement for the map.

			ld b, a					; B = Sprite count.
			ld ix, SpritesPool

.SpriteLoop:		push bc

			ld a, (ix + Sprite.Class)
			inc a					; Player class = 255.
			jp nz, .NextSprite

			ex de, hl
			ld l, (ix + Sprite.MapOx)		; Is a player.
			ld h, (ix + Sprite.MapTx)
			ld bc, (IdealMapTileOffsetX)
			and a
			sbc hl, bc				; Player position - Ideal Map Position
			ex de, hl

			ld c, d					; Save the high byte difference for later test.
			ld a, c

			ld de, $FF80
			jr c, .XDone				; It is the player position behind the left border?.

			and a
			ld de, $FFA0
			jr z, .XDone				; It is the player position near the left border?.

			dec a
			ld de, $FFC0
			jr z, .XDone				; It is the player position approaching the left border?.

			dec a
			ld de, $FFE0
			jr z, .XDone				; It is the player position in a middle distance to the left border?.

			dec a
			ld de, $FFF0
			jr z, .XDone				; It is the player position in a far distance but oriented to the left border?.

			ld a, c
			cp 16

			ld de, $0080
			jr nc, .XDone				; It is the player position behind the right border?.

			cp 15
			ld de, $0060
			jr nc, .XDone				; It is the player position near the right border?.

			cp 14
			ld de, $0040
			jr nc, .XDone				; It is the player position approaching to the right border?.

			cp 13
			ld de, $0020
			jr nc, .XDone				; It is the player position in a middle distance to the right border?.

			cp 12
			ld de, $0010
			jr nc, .XDone				; It is the player position in a far distance but oriented to the right border?.

			ld de, $0000				; So, the player position is in the center of the screen.

.XDone:			add hl, de				; current wanted X displacement for the map = current wanted X displacement for the map + Player position reaction.

			exx					; switch to: hl' = current wanted Y displacement for the map.

			ex de, hl
			ld l, (ix + Sprite.MapOy)
			ld h, (ix + Sprite.MapTy)
			ld bc, (IdealMapTileOffsetY)
			and a
			sbc hl, bc				; Player position - Ideal Map Position.
			ex de, hl

			ld c, d					; Save the high byte difference for later test.
			ld a, c

			ld de, $FF80
			jr c, .YDone				; It is the player position behind the upper border?.

			and a
			ld de, $FFA0
			jr z, .YDone				; It is the player position near the upper border?.

			dec a
			ld de, $FFC0
			jr z, .YDone				; It is the player position approaching to the upper border?.

			dec a
			ld de, $FFE0
			jr z, .YDone				; It is the player position in a middle distance to the upper border?.

			dec a
			ld de, $FFF0
			jr z, .YDone				; It is the player position in a far distance but oriented to the upper border?.

			ld a, c
			cp 13

			ld de, $0080
			jr nc, .YDone				; It is the player position behind the lower border?.

			cp 12
			ld de, $0060
			jr nc, .YDone				; It is the player position near the lower border?.

			cp 11
			ld de, $0040
			jr nc, .YDone				; It is the player position approaching to the lower border?.

			cp 10
			ld de, $0020
			jr nc, .YDone				; It is the player position in a middle distance to the lower border?.

			cp 9
			ld de, $0010
			jr nc, .YDone				; It is the player position in a far distance but oriented to the lower border?.

			ld de, $0000				; So, the player position is in the center of the screen.

.YDone:			add hl, de				; current wanted Y displacement for the map = current wanted Y displacement for the map + Player position reaction.

			exx					; switch back to: hl = current wanted X displacement for the map.

.NextSprite:		ld bc, Sprite.SizeOf
			add ix, bc

			pop bc
			dec b
			jp nz, .SpriteLoop



			ld de, $0100				; Apply reasonable limits on how much it want to scroll per frame.
			push hl
			sbc hl, de
			pop hl
			jp c, .XOk				; Jump is most usual.

			ld de, $FF10
			push hl
			sbc hl, de
			pop hl
			jp nc, .XOk				; Jump is most usual.

			bit 7,h
			jr nz, .XNeg

			ld hl, $00F0
			jp .XOk

.XNeg:			ld hl, $FF10

.XOk:			exx

			ld de, $0100
			push hl
			and a
			sbc hl, de
			pop hl
			jp c, .YOk				; Jump is most usual.

			ld de, $FF10
			push hl
			sbc hl, de
			pop hl
			jp nc, .YOk				; Jump is most usual.

			bit 7,h
			jr nz, .YNeg

			ld hl, $00F0
			jp .YOk

.YNeg:			ld hl, $FF10

.YOk:

			ex de, hl				; Apply displacement to the ideal-map-visual-scope variables.
			ld hl, (IdealMapTileOffsetY)
			ld c, h					; Save the sign for map boundaries check.
			add hl, de
			ld (IdealMapTileOffsetY), hl
			ld b, h					; Save the sign for map boundaries check.
			ex de, hl

			exx

			ex de, hl
			ld hl, (IdealMapTileOffsetX)
			ld c, h					; Save the sign for map boundaries check.
			add hl, de
			ld (IdealMapTileOffsetX), hl
			ld b, h					; Save the sign for map boundaries check.
			ex de, hl




			ld a, c					; Apply map-size-limits on ideal-visual-scope variables.
			xor b
			rla
			jp nc, .XMapOk				; Jump is most usual.

			bit 7, c
			jr nz, .XMapRight

			ld hl, 0
			ld (IdealMapTileOffsetX), hl
			jp .XMapOk

.XMapRight:		ld hl, 65536 - 256 * 16
			ld (IdealMapTileOffsetX), hl

.XMapOk:		exx

			ld a, c
			xor b
			rla
			jp nc, .YTest2				; No jump is most usual.

			bit 7, c
			jp nz, .YMapDown			; No jump is most usual.

			ld hl, 0
			ld (IdealMapTileOffsetY), hl
			jp .YMapOk

.YMapDown:		ld hl, 32768 - 212 * 16
			ld (IdealMapTileOffsetY), hl
			jp .YMapOk

.YTest2:		ld de, 32768 - 212 * 16 + 1
			ld hl, (IdealMapTileOffsetY)
			sbc hl, de
			jp c, .YMapOk				; Jump is most usual.

			ld hl, 32768 - 212 * 16
			ld (IdealMapTileOffsetY), hl
.YMapOk:							; Ok, Ideal Map overflows fixed.



			ld de, (MapCurrentTileOffsetX)		; Calculate differences Ideal visual scope VS active frame scope.
			ld hl, (IdealMapTileOffsetX)
			and a
			sbc hl, de
			jr nc, .XDifIsOk

			ld a, h
			cpl
			ld h, a
			ld a, l
			cpl
			ld l, a
			inc hl

			set 0, l				; Sets the sign as ScrollMap format.

.XDifIsOk:		ex de, hl
			push de

			ld de, (MapCurrentTileOffsetY)
			ld hl, (IdealMapTileOffsetY)
			and a
			sbc hl, de
			jr nc, .YDifIsOk

			ld a, h
			cpl
			ld h, a
			ld a, l
			cpl
			ld l, a
			inc hl

			set 0, l				; Sets the sign as ScrollMap format.

.YDifIsOk:		pop de

			ld a, h					; Checks if it really scrolled something.
			or l
			or d
			or e
			ret z					; Escape if not.






ScrollsMap:		bit 0, l				; It adjusts the map's visual scope variables and vdp's scroll register variables, then calls to the MapRender routine for the new area to render.
			res 0, l				; IN H = Ty, L = Oy (yyyy000s).
								; IN D = Tx, E = Ox (xxxx000s).
			push de

			ex de, hl
			jp nz, .Up
								; Down.
			ld hl, (MapCurrentTileOffsetY)		; MAP_TOP = MAP_TOP + Distance to scroll.
			add hl, de
			ld (MapCurrentTileOffsetY), hl
			jp c, .DownOverflow			; No jump is most usual.

			ld bc, 29376 + 1			; (128 tiles * 16 pixels) - 212 = 1836 << 4 = 29376.
			sbc hl, bc				; ( map height in pixels  ) - (screen height for map) = 1860 pixels, shift 4 bits to the left for match the variable usage format. That is the maximun value that the TOP MAP variables can store.
			jp c, .DownContinue			; Jump is most usual.

			inc hl					; inc hl as we test with MAP_BOTTOM value for top + 1. So, it must take away one more.
			ex de, hl
			sbc hl, de
			ex de, hl				; New_distance = (MAP_BOTTOM value for TOP variable) - distance.

			ld hl, 29376
			ld (MapCurrentTileOffsetY), hl

.DownContinue:		ld a, e					; Calculates the scroll distance in pixels.
			and $F0
			rr d
			rra
			rr d
			rra
			rr d
			rra
			rr d
			rra
			ld e, a

			ld a, (ScreenCurrentY)
			add a, e
			ld (ScreenCurrentY), a

			ld a, d
			and a
			jp nz, .YAll				; Jumps, if it scrolls all the screen.

			ld a, e
			cp 212					; Screen height.
			jp nc, .YAll				; Jumps, if it scrolls all the screen.

			exx
			ld l, a
			ld a, 212
			sub l
			ld h, a
			exx

.ChecksOnHorizontal:	pop de

			bit 0, e
			res 0, e
			jp nz, .Left
								; Right.
			ld hl, (MapCurrentTileOffsetX)		; MAP_LEFT = MAP_LEFT + Distance to scroll.
			add hl, de
			ld (MapCurrentTileOffsetX), hl
			jr c, .RightOverFlow			; No jump is most usual.

			ld bc, 61440 + 1			; (256 tiles * 16 pixels) - 256 = 3840 << 4 = 61440.
			sbc hl, bc				; ( map width in pixels  ) - (screen width for map) = 3840 pixels, shift 4 bits to the left for match the variable usage format. That is the maximun value that the LEFT MAP variables can store.
			jp c, .RightContinue			; Jump is most usual.

			inc hl					; inc hl as we test with MAP_RIGHT value for left + 1. So, it must take away one more.
			ex de, hl
			sbc hl, de
			ex de, hl				; DE = New_distance = (MAP_RIGHT value for LEFT variable) - distance.

			ld hl, 61440
			ld (MapCurrentTileOffsetX), hl

.RightContinue:		ld a, e					; Calculates the scroll distance in pixels.
			and $F0
			rr d
			rra
			rr d
			rra
			rr d
			rra
			rr d
			rra
			ld e, a

			ld a, (ScreenCurrentX)
			add a, e
			ld (ScreenCurrentX), a

			ld a, d
			and a
			jp nz, .XAll

			ld a, e

			exx
			ld c, 0
			ld e, a

			xor a
			sub e
			ld d, a					; Left = 256 - displacement X.

.Render:							; Now D = Left, CE = Width of vertical bar to render.
								; First checks if it needs to render all the screen, like if one of the bars is wider as to fill all the screen.
			ld a, c
			and a
			jr nz, .RenderAll			; Checks the vertical bar width.

			ld a, l
			cp 212
			jr nc, .RenderAll			; Checks the horizontal bar height.
								; Ok, It don't needs to render all.

			and a					; Now checks if it have some vertical scroll.
			jr z, .NoVerticalScroll

			push hl					; Renders the horizontal bar of the vertical scrolling.
			push de
			ld de, 0
			ld c, 1
			call MapRender
			pop de
			pop hl

			ld a, e					; Now checks if it have some horizontal scroll. At this point C = 0, but the register was destroyed.
			and a
			ret z					; Escape if no horizontal scroll.

			ld a, h					; Checks where was rendered the horizontal bar of the vertical scrolling.
			and a
			jr nz, .VScrollWasRenderingAtBottom
								; VScroll was rendered at top.
			ld h, l					; TOP = HEIGHT of rendered bar.
			ld a, 212				; Screen height.
			sub l
			ld l, a					; It suprime the area already rendered.
			ld c, 0

			jp MapRender



.RightOverFlow:		and a
			sbc hl, de
			ld (MapCurrentTileOffsetX), hl
			ld de, 0
			jp .RightContinue



.VScrollWasRenderingAtBottom:

			ld h, 0
			ld a, 212				; Screen height.
			sub l					; HEIGHT = Screen height - height of rendered bar.
			ld l, a
			ld c, 0

			jp MapRender



.NoVerticalScroll:	ld h, 0
			ld l, 212
			jp MapRender				; If no vertical scroll, it needs to see if scrolling to the left or the right. For sort "render first, adjust register next, or backward.", the goal is to render always on the hiddem border.



.RenderAll:		xor a					; It aligns the scroll registers when it can.
			ld (ScreenCurrentX), a
			ld (ScreenCurrentY), a

			ld h, 0
			ld l, 212
			ld de, 0
			ld c, 1

			jp MapRender



.Up:			ld hl, (MapCurrentTileOffsetY)		; MAP_TOP = MAP_TOP - Distance to scroll.
			and a
			sbc hl, de
			ld (MapCurrentTileOffsetY), hl
			jr nc, .UpContinue

			add hl, de
			ex de, hl				; DE = New_distance = current MAP_TOP.

			ld hl, 0
			ld (MapCurrentTileOffsetY), hl		; MAP_TOP = TOP BORDER = 0.

.UpContinue:		ld a, e					; Calculates the scroll distance in pixels.
			and $F0
			rr d
			rra
			rr d
			rra
			rr d
			rra
			rr d
			rra
			ld e, a

			ld a, (ScreenCurrentY)
			sub e
			ld (ScreenCurrentY), a

			ld a, d
			and a
			jr nz, .YAll				; Jumps, if it scrolls all the screen.

			ld a, e
			cp 212					; Screen height.
			jr nc, .YAll				; Jumps, if it scrolls all the screen.

			exx
			ld h, 0
			ld l, a
			exx
			jp .ChecksOnHorizontal

.YAll:			exx
			ld h, 0
			ld l, 212
			exx
			jp .ChecksOnHorizontal



.XAll:			exx
			ld de, $0000
			ld c, 1					; C = 1 = 256.
			jp .Render

.Left:			ld hl, (MapCurrentTileOffsetX)		; MAP_LEFT = MAP_LEFT - Distance to scroll.
			and a
			sbc hl, de
			ld (MapCurrentTileOffsetX), hl

			jr nc, .LeftContinue

			add hl, de				; New_distance = distance + MAP_LEFT
			ex de, hl

			ld hl, 0
			ld (MapCurrentTileOffsetX), hl		; MAP_LEFT = LEFT BORDER.

.LeftContinue:		ld a, e					; Calculates the scroll distance in pixels.
			and $F0
			rr d
			rra
			rr d
			rra
			rr d
			rra
			rr d
			rra
			ld e, a

			ld a, (ScreenCurrentX)
			sub e
			ld (ScreenCurrentX), a

			ld a, d
			and a
			jr nz, .XAll

			ld a, e

			exx
			ld d, 0
			ld e, a
			ld c, 0
			jp .Render



.DownOverflow:
			and a
			sbc hl, de
			ld (MapCurrentTileOffsetY), hl
			ld de, 0
			jp .DownContinue






MapRender:		ld a, c					; First, checks that exists a box to render, because if width or height = 0 there is nothing to do.
			or e					; IN H L = Top/Height, D CE = Left/Width, coordinates are in real visual screen (no vdp command coordinates system).
			ret z
			ld a, l
			or a
			ret z

			ld a, (ScreenCurrentX)			; The second thing is to check if it needs to split the command horizontally. For solving the wrap problems.
			add a, d				; Ok, the box starts at = (ScreenCurrentX) + (real visual) Left.
			ld b, a					; B = LEFT (in command coordinates system).
			jr z, .HorizontalIntegrityOk		; If the command LEFT is = 0, it can't wrap, because it can render all the 256 pixels without wrapping.

			ld a, c					; If it not aligned to the command coordinate LEFT = 0, and width = 256, it must forcefully wrap.
			and a
			jp z, .MoreHChecks

			xor a					; It is width = 256, so must split it horizontally.
			sub b					; A = Space that Left, before to split it horizontally.
			jp .HorizontalSplit

.MoreHChecks:		xor a					; 256 - LEFT (in command coordinates system).
			sub b					; A = Space that Left, before to wrap it horizontally.
			cp e					; Compare with the width of the box to render.

			jp nc, .HorizontalIntegrityOk		; Is the space bigger or equal? don't split the render.

.HorizontalSplit:	push hl					; HL = TOP/Height.
			push af					; A = space that left before to wrap it.
			push de					; DE = LEFT/WIDTH original.
			push bc					; C = WIDTH MSB.
			ld c, 0
			ld e, a					; Reduce the box width to what it can do.
			call .HorizontalIntegrityOk
			pop bc
			pop de

			pop hl
								; New left = left + just rendered width.
			ld a, d
			add a, h
			ld d, a
								; CE = CE - A. New width = total box width - width already rendered.
			ld a, e
			sub h
			ld e, a
			jr nc, .JJJ100
			dec c
.JJJ100:
			pop hl
			
.HorizontalIntegrityOk:	ld a, (ScreenCurrentY)			; IN H L = Top/Height, D CE = Left/Width, coordinates are in real visual screen (no vdp command coordinates).
			add a, h				; The third thing is to check if it needs to split the command vertically.
			ld b, a					; B = in command coordinates TOP. The box starts at = (ScreenCurrentY) + (real visual) Top.
			cp 256 - 212 +1				; It have enough space to render the maximun height? 212?.
			jp c, .VerticalIntegrityOk		; 

			xor a					; 256 - TOP (in command coordinates).
			sub b					; A = Space that left, before to wrap it vertically.
			cp l					; Compare with the height of the box to render.

			jp nc, .VerticalIntegrityOk		; Is the space bigger? no wrap.

			push de
			push bc
			push af
			push hl
			ld l, a					; Reduce the box height to what it can do.
			call .VerticalIntegrityOk
			pop hl
			pop de

			ld a, h
			add a, d				; New top = top + just rendered height.
			ld h, a

			ld a, l
			sub d
			ld l, a					; New height = height - height just rendered.

			pop bc
			pop de

			; ********************************* THE MAGIC START HERE ***************************************************

.VerticalIntegrityOk:	ld a, e					; IN H L = Top/Height, D CE = Left/Width, coordinates are in real visual screen (no vdp command coordinates).
			exx
			ld c, a					; Inputed CE can't be 0 at this point and CE>256 is not valid, so, C is not needed. for E = 0 will means 256.
			exx					; C' Total Pixels per row.
			ex af, af'				; A' pixels countdown for current row.

			ld a, l
			exx
			ld b, a
			exx					; B' Rows Countdown.

			push hl					; Save the real visual coordinates for setup the vdp command later.
			push de
								; In wich exact point of the map is the TOP/LEFT corner of the box to render in the screen?.
			ld l, h					; Top in pixels, so Top*16 for aligning it to Map variables system.
			ld h, 0
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl

			ld bc, (MapCurrentTileOffsetY)		; Its Format is tttttttt pppp0000. t = tile, p = pixel offset within tile.
			add hl, bc
			ex de, hl				; DE = Ty/Oy of the box top border.
			
			ld l, h					; Left in pixels, so Left*16 for aligning it to the Map variables system.
			ld h, 0
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl

			ld bc, (MapCurrentTileOffsetX)		
			add hl, bc 				; HL = Tx/Ox of the box left border. Format: tttttttt pppp0000.

			; ************************************************************************************************************

			ld a, l					; It calculate the pixel offset within the tile texture.
			and $F0
			rra
			rra
			rra
			rra
			or e					; Offset = Oy*16+Ox.
			ld e, a					; E = LSB for texture pointer.

			; ************************************************************************************************************

			ld l, h					; Once we got Tx & Ty (map tile position x & y), it needs to build up a pointer to that tile.
			ld h, d					; Map pointer = D * 256 * 2 + H * 2...


			add hl, hl				; Map address = Y * 256 * 2 + X * 2.
								; Map address = (Y * 256 + X) * 2.
			ld a, h					; Now, it pages that address pointer.
			rlca					; and add $8000 for PAGE2.
			rlca
			and 3
			set 7, h
			res 6, h

			exx
			ld d, a
			ld a, (MapBaseSegment)
			add a, d
			ld d, a
; /// New code.
.P2_1:			call DOS2.CALL.PUT_P2			; Set the Map ram segment.
; /// New code.
			exx					; D' = Map pointer segment.
			push hl
			pop iy					; IY = Map address for start row tile.
			push hl
			pop ix					; IX = Map address for current tile.

			call .GetTileTexturePointer
; /// New code.
			exx
			ld e, a
.P2_2:			call DOS2.CALL.PUT_P2			; Set the tile texture ram segment.
			exx
; /// New code.
			ld l, e					; L = LSB for texture pointer.
			
			ld a, l					; Now calculates how many pixels left within the current tile texture.
			cpl
			and $0F					; A = Ox.
			inc a
			ld b, a					; B = pixels within first tile texture count. Range 1-16.

			ex af, af'				; Checks if the width of the box to render is smaller or not than the rest of this tile texture.
			cp b
			jp nc, .NoShorted			; It jumps very much with sprites that its width = 16, so JP.

			and a					; A' = 0 = 256. As it is the LSB of the "CE" parameter.
			jr z, .NoShorted			; Most usual, no jump.

			ld b, a					; B = take the box width as it was shorter.

.NoShorted:		ex af, af'

			; ************************************************************************************************************

			pop de					; It's time to setup the vdp command.
								; DE = LEFT/WIDTH, real visual coordinates.
			; /// call VdpWait
			; /// really is required to check if everything is outputed by the CPU?.

			di

			ld a, 36 
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, (ScreenCurrentX)
			add a, d				; Sum and wrap as it is 8bits sum.
			out ($9B), a
			xor a
			out ($9B), a				; DX = ScreenCurrentX + Left box.

			ld c, e
			pop de					; DE = TOP/HEIGHT, real visual coordinates.

			ld a, (ScreenCurrentY)
			add a, d
			out ($9B), a
			ld a, (ActiveBuffer)
			out ($9B), a				; DY = ActiveBuffer + ScreenCurrentY + Top box.

			ld a, c					; If Width = 0 = 256.
			out ($9B), a
			and a
			ld a, 0
			jp nz, .NoWidth_256			; Most usual, jump.
			inc a
.NoWidth_256:		out ($9B), a				; NX = RenderRowPixelsCount.

			ld c, $9B
			out (c), e
			xor a
			out ($9B), a				; NY = Height box.

			ld a, l
			and $0F
			ld e, a					; E = back up for OffsetX within texture of first tile of each row.

			ld d, b					; D = pixels count within the first tile of a row (backup). Range 1-16.

			outi

			ld a, 0					; No modify ZF.
			out ($9B), a				; DIX/DIY Down-Right.

			ld a, $F0
			out ($9B), a				; sends HMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ei

; /// New code starts here!.
								; Checks if it already completes the first tile texture row.
			jr z, .FirstTileTextureRowDone		; Most usual, no jump.

			ex af, af'				; Is rendering the whole screen width?.
			and a
			jp z, .FastAndFuriousRender
			cp d					; Is the width within the first tile texture?.
			jp z, .SingleTileWidthRender
			cp 17
			jp c, .DualTileWidthRender
			cp 33
			jp nc, .FastAndFuriousRender
			add a, e				; It additions the texture offsetX for checking if it reaches a third tile texture.
			cp 33
			jr c,  .DTFixA
			sub e					; Restores A'.
			jp .FastAndFuriousRender

.DTFixA:		sub e
			jp .DualTileWidthRender

.FirstTileTextureRowDone:

			ex af, af'
			and a
			jp z, .FastAndFuriousRender_FD
			cp d					; Is the width within the first tile texture?.
			jp z, .SingleTileWidthRender_FD
			cp 17
			jp c, .DualTileWidthRender_FD
			cp 33
			jp nc, .FastAndFuriousRender_FD
			add a, e				; It additions the texture offsetX for checking if it reaches a third tile texture.
			cp 33
			jr c,  .DTFixA_FD
			sub e					; Restores A'.
			jp .FastAndFuriousRender_FD

.DTFixA_FD:		sub e
			jp .DualTileWidthRender_FD



; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
; /// IDEAL CASE When rendering thin boxes that its width is within a single tile texture. (Ex. when scrolling horizontally that draws thin vertical bars or erasing sprites aligned with the tile grid). ///
; /// ShortCuts: Don't checks for more than the single tile render, is to say don't checks looking for a next tile in the same row.                                                                       ///
; ///            It uses the initial preset tile texture pointer and RAM setup.                                                                                                                           ///
; ///            If rendering the whole tile square it uses an even faster routine.                                                                                                                       ///
; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

			; IN B = remain width to render within the first tile texture.
			; IN D = backup of width to render within the first tile texture.
			; IN C' = width, B' height of the box to render.
			; IN (D') IX = IY = top/left map pointers address of the box to render, being D' the ram segment.
			; IN E = Texture OffsetX for the first tile to render on each row.
			; IN L = Current next texture offsetY & X to render.
			; IN H = Current tile texture base address.
			; IN E' = Current tile texture ram segment number.
			; IN C = VDP port number.
			; IN A = box width to render.

.SingleTileWidthRender:	bit 4, d				; It already has the single tile texture in place.
			jr nz, .SingleTileWholeTextureEntry	; Most usual no jump.

.SingleTextureRowLoop:	otir

.SingleTileWidthRender_FD:

			exx
			dec b
			ret z
			exx

			ld a, l
			dec a
			and $F0
			or e
			add a, 16
			ld l, a

			ld b, d

			jp nc, .SingleTextureRowLoop

.SingleTextureNextTile:	inc ixh					; It does the next tile vertically.
			inc ixh		

			ld a, ixh				; PAGE2 wrap.
			and $40
			jp z, .SingleTextureNextTile2		; Most usual jump.

			ld a, ixh
			and $BF
			ld ixh, a
			exx
			inc d
			exx

.SingleTextureNextTile2:ld b, l					; Save the offsets.

			exx
			ld a, d
			exx
.P2_3:			call DOS2.CALL.PUT_P2			; Set the Map ram segment.
			call .GetTileTexturePointer
.P2_4:			call DOS2.CALL.PUT_P2

			ld l, b					; Restore the offsets.

			ld b, d					; Width within the tile texture.
			jp .SingleTextureRowLoop

.SingleTileWholeTextureLoop:

			outi

.SingleTileWholeTextureEntry:

			outi
			outi
			outi
			
			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			exx
			dec b
			ret z
			exx

			ld a, l
			and a
			jp nz, .SingleTileWholeTextureLoop	; Most usual jump.

			inc ixh					; It does the next tile vertically.
			inc ixh		

			ld a, ixh				; PAGE2 wrap.
			and $40
			jp z, .SingleTextureNextTile3		; Most usual jump.

			ld a, ixh
			and $BF
			ld ixh, a
			exx
			inc d
			exx

.SingleTextureNextTile3:exx
			ld a, d
			exx
.P2_5:			call DOS2.CALL.PUT_P2			; Set the Map ram segment.
			call .GetTileTexturePointer
.P2_6:			call DOS2.CALL.PUT_P2

			ld l, 0					; At the top/left of the texture.
			jp .SingleTileWholeTextureLoop



; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
; /// IDEAL CASE: When two tiles are in the row (Ex: erasing sprites missaligned horizontally with the tile grid).                           ///
; /// ShortCuts: Don't handle the DressBuffer for storing textures pointers, stores both textures pointer in z80 registers.                  ///
; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

			; IN B = remain width to render within the first tile texture.
			; IN D = backup of width to render within the first tile texture.
			; IN C' = width, B' height of the box to render.
			; IN (D') IX = IY = top/left map pointers address of the box to render, being D' the ram segment.
			; IN E = Texture OffsetX for the first tile to render on each row.
			; IN L = Current next texture offsetY & X to render.
			; IN H = Current tile texture base address.
			; IN E' = Current tile texture ram segment number.
			; IN C = VDP port number.
			; IN A = box width to render.

.DualTileWidthRender_FD:					; Executes this if the first pixel already sent was the last pixel of the first tile texture in the row.

			sub d					; IN E' = ram segment for the left tile texture. H = base address for the left tile texture.
			cp 16					; Pre-test if second tile texture is full rendered.
			ex af, af'				; Now A' = width to render of the second tile.

			exx
			ld a, d
			exx
.P2_7:			call DOS2.CALL.PUT_P2			; Set the Map ram segment.

			push hl					; Save H L = first tile texture pointer.

			call .GetRightTileTexturePointer	; Gets the second tile texture pointer.
								; OUT H' = base address of the second tile texture.
			exx
			ld l, a					; Save the ram segment of the second tile texture.
.P2_8:			call DOS2.CALL.PUT_P2			; Sets the ram for the second tile texture.

			ld a, h					; Gets the base addres of the second tile texture.
			exx

			pop hl					; Restore & hold on stack H L = first tile texture pointers.
			push hl

			ld h, a					; Sets the second tile base address.

			jp .In_FD



.DualTileWidthRender:						; Main routine.

			sub d					; IN E' = ram segment for the left tile texture. H = base address for the left tile texture.
			cp 16					; Pre-test if second tile texture is full rendered.
			ex af, af'				; Now A' = width to render of the second tile.

			exx
			ld a, d
			exx
.P2_9:			call DOS2.CALL.PUT_P2			; Set the Map ram segment.

.DualRowTileLoop:	push hl					; Save H L = first tile texture pointer.

			call .GetRightTileTexturePointer	; Gets the second tile texture pointer.
								; OUT H' = base address of the second tile texture.
			exx
			ld l, a					; Save the ram segment of the second tile texture, but don't set the RAM mapper on that.
			exx

			pop hl					; Restore H L = first tile texture pointer.

.DualRowTextureLoop:	exx
			ld a, e					; Get the ram segment of the first tile texture.
			exx
.P2_10:			call DOS2.CALL.PUT_P2			; Sets the ram for the first tile texture.

			bit 4, b				; valid range for B (1-16), so, bit4=1 of B = 16.
			jr nz, .DualFullTextureRow		; no jump is most usual.



			otir

.DualBackFromFull1:	exx
			ld a, l
.P2_11:			call DOS2.CALL.PUT_P2			; Sets the ram for the second tile texture.
			ld a, h
			exx

			push hl					; Save FIRST TILE TEXTURE POINTER & L = is in the home position but for the next row.

			ld h, a					; Set H = second tile texture base address.

.In_FD:			ld a, l
			sub 16
			ld l, a

			ex af, af'
			jr z, .DualFullTextureRow2		; Most usual no jump.
			ld b, a
			ex af, af'



			otir

.DualBackFromFull2:	pop hl					; Restore FIRST TILE TEXTURE POINTER & L = is in the home position but for the next row.

			exx
			dec b
			ret z
			exx

			ld a, l					; L = is in the home position but for the next row because the PUSH/POP saved that situation.
			and a
			jr z, .DualTileNext

			or e
			ld l, a

			ld b, d					; Sets width to render within the first tile texture

			jp .DualRowTextureLoop

.DualTileNext:		inc ixh					; It does the next tile vertically.
			inc ixh		

			ld a, ixh				; PAGE2 wrap.
			and $40
			jp z, .DualTileNext2			; Most usual jump.

			ld a, ixh
			and $BF
			ld ixh, a
			exx
			inc d
			exx

.DualTileNext2:		exx					; Get the first tile texture.
			ld a, d
			exx
.P2_12:			call DOS2.CALL.PUT_P2			; Set the Map ram segment.
			call .GetTileTexturePointer		; Gets the first tile texture pointer.
			exx
			ld e, a
			exx

			xor a
			or e
			ld l, a

			ld b, d					; Sets width to render within the first tile texture

			jp .DualRowTileLoop

.DualFullTextureRow:	outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			jp .DualBackFromFull1

.DualFullTextureRow2:	ld b, a
			ex af, af'

			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			jp .DualBackFromFull2

								; IN IX = Map address Pointer (Must be already visible).
								; OUT H' = Texture base address (for page 2), A = ram segment number for the texture.
.GetRightTileTexturePointer:
			ld l, (ix+3)				; L = MSB Tile Number + attribs bits.
			ld a, l
			cp 2					; Only values 0 or 1 for non special.
			jr nc, .SomethingSpecialR		; No jump as most usual.
								; L = valid values 0 or 1.
.BackFromSpecial1R:	ld a, (ix+2)				; A = LSB Tile Number. (Texture address = Tile number * 256).

.BackFromSpecial2R:	rla					; Splits the addressing into RAM segments. And set it for PAGE2.
			rl l
			rla
			rl l

			rra
			scf
			rra

			exx
			ld h, a
			exx

			ld a, (TileBaseSegment)
			add a, l
			ret

.SomethingSpecialR:	and MapAttribs.MaskMajor
			cp MapAttribs.Major.AnimatedTile
			jr nz, .FixLR				; No most usual, lets say 50% and %0%.
								; It is an animated tile.
			ld a, l					; Fix L.
			and MapAttribs.MaskTileNumber
			ld l, a
								; Animated tiles are in group of 4 tiles...
								; and the group is aligned to 4 multiplier...
			ld a, (AnimatedTilesSequencer)		; so bits 1 and 0 of the TileNumber are exchangeables for the animation.
			add a, (ix+2)
			and $03
			ld h, a
			ld a, (ix+2)
			and $FC
			or h
			jp .BackFromSpecial2R

.FixLR:			ld a, l
			and MapAttribs.MaskTileNumber
			ld l, a
			jp .BackFromSpecial1R



; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
; /// IDEAL CASE: when more than two tiles are in the row (Ex: vertical scrolling drawing large horizontal bars).                            ///
; /// Shortcuts: Don't check if row is complete when just do the first tile in the row.                                                      ///
; ///            Use the DressBuffer for storing all texture pointers, for calculating them just one time.                                   ///
; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

			; IN B = remain width to render within the first tile texture.
			; IN D = backup of width to render within the first tile texture.
			; IN C' = width, B' height of the box to render.
			; IN (D') IX = IY = top/left map pointers address of the box to render, being D' the ram segment.
			; IN E = Texture OffsetX for the first tile to render on each row.
			; IN L = Current next texture offsetY & X to render.
			; IN H = Current tile texture base address.
			; IN E' = Current tile texture ram segment number.
			; IN C = VDP port number.
			; IN A = box width to render.

.FastAndFuriousRender_FD:
			ex af, af'				; Routine executed when the first byte already sent just completed the previous tile texture row, PROVABILITY: 1/16.

			ld c, l					; Save L = is already in the home position of the next row.

			call .GetRowTileTexturePointers		; IN IX = Current TILE PTR (in the map). IN D' = map current Segment.
								; IN A' = box width to render. IN D = width within the first tile of the row.
								; MODs: A, A', HL, HL'.

			ld l, c					; Restore L = is already in the home position of the next row.
			ld c, $9B				; If push/pop hl = 21 ticks, 3x ld instructions = 4+4+7 = 15 ticks.



			exx
			ld hl, DressBuffer + 2			; HL = Textures pointer addresses buffer pointer + 2 (so points to the second tile texture in the row).

			ld a, c					; C' = Backup of box width to render.
			ex af, af'				; A' = Box Width to render.
			exx

			jp .FFIn_FD



.FastAndFuriousRender:	ex af, af'				; Main routine.

			ld a, b					; Checks if for the initial tile its whole texture row will be render, but the first byte of it was already sent.
			cp 15
			jp nz, .PartialRowEntry

								; It will render the whole tile texture for this first row, So it must jump to the fast code.

			push hl					; Save H L = current next offsetY & X to render and texture base address.

			call .GetRowTileTexturePointers		; IN IX = Current TILE PTR (in the map). IN D' = map current Segment.
								; IN A' = box width to render. IN D = width within the first tile of the row.
								; MODs: A, A', HL, HL'.

			pop hl					; Restore H L = current next offsetY & X to render and texture base address.

			exx
			ld hl, DressBuffer + 2			; ; HL = Textures pointer addresses buffer pointer + 2 (so points to the second tile texture in the row).

			ld a, e					; E' = Current tile texture ram segment number.
.P2_13:			call DOS2.CALL.PUT_P2			; Set the texture ram segment for the first tile in the row.

			ld a, c					; C' = Backup of box width to render.
			exx
								; It enters with A = box width to render.
			jp .InitialCaseEntry

.PartialRowEntry:
.TileRowLoop:		push hl					; Save L = current offsetY & X.

			call .GetRowTileTexturePointers		; IN IX = Current TILE PTR (in the map). IN D' = map current Segment.
								; IN A' = box width to render. IN D = width within the first tile of the row.
								; MODs: A, A', HL, HL'.

			pop hl					; Restore L = Current offsetY & X.

.PixelRowLoop:		exx
			ld hl, DressBuffer			; HL = Textures pointer addresses buffer pointer.

			ld a, c					; C' = Backup of box width to render.
			ex af, af'				; A' = Box Width to render.

			ld a, (hl)
			inc hl
.P2_14:			call DOS2.CALL.PUT_P2			; Set the texture ram segment.
			ld a, (hl)
			inc hl
			exx
			ld h, a					; Set the base texture address.

			bit 4, b				; Range for B (1 up to 16), so bit4 = 1 = render the whole texture row.
			jp nz, .FirstFullTextureRow



.ShortFirstTile:	otir					; render the first tile texture row.

.FFIn_FD:		ld a, l					; L is already in the home position of the next row.
			sub 16
			ld l, a

			exx
			ld a, (hl)
			inc hl
.P2_15:			call DOS2.CALL.PUT_P2			; Set the new texture ram segment.
			ld a, (hl)
			inc hl
			exx
			ld h, a					; Set the new texture base address.

			ex af, af'
			sub d					; It can't be zero because this routine is not executed for rendering a thin vertical bar that fits all within the first tile texture.



.FullTextureRow:	outi					; Must enter in A' mode and don't care about B.

.InitialCaseEntry:	outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi

			sub 16
			jr z, .NextPixelRow			; Checks if with the avobe, just completes the box width to render.
			ex af, af'

			ld a, l					; L is already positioned in the home of the next row.
			sub 16					; Return to the same row.
			ld l, a

			exx
			ld a, (hl)
			inc hl
.P2_16:			call DOS2.CALL.PUT_P2			; Set the new texture ram segment.
			ld a, (hl)
			inc hl
			exx
			ld h, a					; Set the new texture base address.

			ex af, af'
			cp 16
			jp nc, .FullTextureRow

			ld b, a					; Visual remain to render (not a complete row).



			otir					; Render final tile of the row.

			exx					; Row completed, check if there is more rows to render.
			dec b
			ret z
			exx

			ld a, l
			dec a
			and $F0
			add a, 16
			jr c, .NextTileRowWorkL
								; Setup it for the first tile (offset X).
			or e					; E = OffsetX for the first tile of the row. And it was on the HOME position can be ORd without doing AND $F0.
			ld l, a

			ld b, d					; Setup width to render within the first tile texture.

			jp .PixelRowLoop

.NextPixelRow:		exx
			dec b
			ret z
			exx

			ld a, l					; L = home position of the next pixel row.
			and a					; Checks if it out of the tile texture.
			jr z, .NextTileRowWorkL
								; Setup it for the first tile (offset X).
			or e					; E = OffsetX for the first tile of the row. And it was on the HOME position can be ORd without doing AND $F0.
			ld l, a

			ld b, d					; Setup width to render within the first tile texture.

			jp .PixelRowLoop

.NextTileRowWorkL:						; Setup it for the first tile (offset X).
			or e					; E = OffsetX for the first tile of the row. And it was on the HOME position can be ORd without doing AND $F0.
			ld l, a

.NextTileRow:		inc iyh
			inc iyh		

			ld a, iyh				; PAGE2 wrap.
			and $40
			jp z, .NextTileRow2			; Most usual jump.

			ld a, iyh
			and $BF
			ld iyh, a
			exx
			inc d
			exx

.NextTileRow2:		exx
			ld a, c					; C' = Backup of box width to render.
			ex af, af'				; A' = Box Width to render.
			exx

			push iy
			pop ix

			ld b, d					; First tile of the row, setup B = Texture width to render.

			jp .TileRowLoop

.FirstFullTextureRow:	ex af, af'
			jp .FullTextureRow



.GetRowTileTexturePointers:					; IN IX = Current TILE PTR (in the map). IN D' = map current Segment.
								; IN A' = box width to render. IN D = width within the first tile of the row.
			exx					; MODs: A, A', HL, HL'.
			ld hl, DressBuffer
			ld a, d
			exx
.P2_17:			call DOS2.CALL.PUT_P2			; Set the Map ram segment.

			call .GetTileTexturePointer

			exx
			ld (hl), a
			inc hl
			exx

			ld a, h

			exx
			ld (hl), a
			inc hl
			exx

			inc ix
			inc ix					; Horizontally can't overflow the map ram segment, as the game never will visualize over the map right edge.

			ex af, af'
			sub d					; First tile texture width.
			ex af, af'				; This routine is never executed for a single tile box width, so don't checks here if IT complete the row, because will not.

.TexturesPointerLoop:	call .GetTileTexturePointer

			exx
			ld (hl), a
			inc hl
			exx

			ld a, h

			exx
			ld (hl), a
			inc hl
			exx

			inc ix
			inc ix					; Horizontally can't overflow the map ram segment, as the game never will visualize over the map right edge.

			ex af, af'

			sub 16
			ret c
			ret z

			ex af, af'

			jp .TexturesPointerLoop

								; IN IX = Map address Pointer (Must be already visible).
								; OUT H = Texture base address (for page 2), A = ram segment number for the texture.
.GetTileTexturePointer:	ld l, (ix+1)				; L = MSB Tile Number + attribs bits.
			ld a, l
			cp 2					; Only values 0 or 1 for non special.
			jr nc, .SomethingSpecial		; No jump as most usual.
								; L = valid values 0 or 1.
.BackFromSpecial1:	ld a, (ix+0)				; A = LSB Tile Number. (Texture address = Tile number * 256).

.BackFromSpecial2:	rla					; Splits the addressing into RAM segments. And set it for PAGE2.
			rl l
			rla
			rl l

			rra
			scf
			rra
			ld h, a

			ld a, (TileBaseSegment)
			add a, l
			ret

.SomethingSpecial:	and MapAttribs.MaskMajor
			cp MapAttribs.Major.AnimatedTile
			jr nz, .FixL				; No most usual, lets say 50% and %0%.
								; It is an animated tile.
			ld a, l					; Fix L.
			and MapAttribs.MaskTileNumber
			ld l, a
								; Animated tiles are in group of 4 tiles...
								; and the group is aligned to 4 multiplier...
			ld a, (AnimatedTilesSequencer)		; so bits 1 and 0 of the TileNumber are exchangeables for the animation.
			add a, (ix+0)
			and $03
			ld h, a
			ld a, (ix+0)
			and $FC
			or h
			jp .BackFromSpecial2

.FixL:			ld a, l
			and MapAttribs.MaskTileNumber
			ld l, a
			jp .BackFromSpecial1






AnimateTiles:		ld a, (MapCurrentTileY)			; It scans all the visual map scope looking for animated tiles, and redraw them for updating its pattern. This must be done before rendering the sprites.
			ld h, a
			ld a, (MapCurrentTileX)
			ld l, a
			add hl, hl

			ld a, (MapBaseSegment)
			ld c, a

			ld a, h
			rlca
			rlca
			and 3
			add a, c

			res 6, h
			set 7, h

			exx
			ld d, a
			exx

.P2_1:			call DOS2.CALL.PUT_P2			; Sets the current map ram segment.

			ld e, l					; HL = Current tile address to inspect.
			ld d, h					; DE = Current row home position.
								; D' = Map current ram segment.


			ld b, 13				; If offset is near the end of a texture, can be shown 14 tiles vertically. The screen shows 13 tiles and 4 pixel rows.
			ld a, (MapCurrentTileOffsetY)
			cp 13
			jp c, .VLoop				; Jump is most usual.
			inc b

.VLoop:			push bc

			ld b, 16				; If offset is in the middle of a texture, can be shown 17 tiles horizontally.
			ld a, (MapCurrentTileOffsetX)
			and a
			jr z, .HLoop				; No jump is most usual.
			inc b



.HLoop:			inc hl
			ld a, (hl)
			inc hl

			and MapAttribs.MaskMajor
			cp MapAttribs.Major.AnimatedTile
			jr z, .AnimateIt			; Most usual no jump.

.Continue:		djnz .HLoop

			inc d					; Update current row home position.
			inc d					; DE += 512.
			
			bit 6, d
			jp z, .NextRow				; Jump is most usual.

			res 6, d				; Wraps in page2.

			exx
			inc d					; Updates D' = Map current ram segment.
			ld a, d
			exx

.P2_2:			call DOS2.CALL.PUT_P2			; Setup the next map page.

.NextRow:		ld l, e					; DE = Current row home position.
			ld h, d

			pop bc
			djnz .VLoop
			ret


								; Now must calculate the visual position in the screen.
.AnimateIt:		ld c, 16				; If offset is in the middle of a texture, can be shown 17 tiles horizontally.
			ld a, (MapCurrentTileOffsetX)
			and a
			jr z, .HTotal				; No jump is more usual.
			inc c
.HTotal:		ld a, c					; A = C = total of tiles horizontally.
			sub b					; Tx = Total of tiles - X loop count.

			add a, a
			add a, a
			add a, a				; A = Tx * 16.
			add a, a
			jp nc, .No17thTile
								; It overflowed so it is the offset Tx=16 (17th tile in the row) (16x16=256 so overflow).
			ld a, (MapCurrentTileOffsetX)		; Offset format is ppppffff, so remove the fraction and move to pixels value.
			rrca
			rrca
			rrca
			rrca
			and $0F
			cpl
			inc a
			ld ixh, a				; ixh = LEFT.

			xor a
			sub ixh
			ld ixl, a				; ixl = WIDTH.

			jp .XOk

.No17thTile:		ex af, af'				; A' = Tx * 16.

			ld a, (MapCurrentTileOffsetX)		; Offset format is ppppffff, so remove the fraction and move to pixels value.
			rrca
			rrca
			rrca
			rrca
			and $0F
			ld c, a

			ex af, af'
			sub c
			jr c, .HideOnLeftBorder			; No jump most usual.

			ld ixh, a				; ixh = LEFT.
								; The only one way it is partially hidden on right border is if it is the 17th tile in the row, and that was already handled.
			ld ixl, 16				; ixl = WIDTH.

			jp .XOk

.HideOnLeftBorder:	ld ixh, 0				; LEFT = 0.
			ld c, a					; C = A = Negative LEFT coordinate.

			ld a, 16
			add a, c
			ld ixl, a				; WIDTH = 16 + (negative LEFT coordinate).

.XOk:			pop iy					; Gets the Y bucle decrementer.
			push iy

			ld c, 13				; If offset is near the end of a texture, can be shown 14 tiles vertically. The screen shows 13 tiles and 4 pixel rows.
			ld a, (MapCurrentTileOffsetY)
			cp 13
			jp c, .VTotal				; Jump is most usual.
			inc c
.VTotal:
			ld a, c
			sub iyh					; Ty = Total of tiles - Y loop count.

			add a, a
			add a, a
			add a, a				; A = Ty * 16.
			add a, a

			ex af, af'				; A' = Ty * 16.

			ld a, (MapCurrentTileOffsetY)		; Offset format is ppppffff, so remove the fraction and move to pixels value.
			rrca
			rrca
			rrca
			rrca
			and $0F
			ld c, a

			ex af, af'
			sub c
			jr c, .HiddenOnUpperBorder		; No jump most usual.

			ld iyh, a				; iyh = TOP.

			ld a, 212
			sub iyh
			cp 16+1
			jp c, .ValidHeight			; Jump is most usual.
			ld a, 16
.ValidHeight:
			ld iyl, a				; iyl = HEIGHT.

			jp .YOk
			
.HiddenOnUpperBorder:	ld iyh, 0				; TOP = 0.
			ld c, a					; A = negative Top coordinate.

			ld a, 16
			add a, c
			ld iyl, a				; WIDTH = 16 + (negative TOP coordinate).

.YOk:			exx
			push de					; Save current map ram segment.
			exx
			push bc					; Save current H loop decrementer.

			push de
			push hl					; Save current tiles pointers.

			ld e, iyl
			ld d, iyh
			ex de, hl

			ld e, ixl
			ld d, ixh
			ld c, 0
			call MapRender				; IN H L = Top/Height, D CE = Left/Width, coordinates are in real visual screen (no vdp command coordinates system).

			pop hl
			pop de

			pop bc
			exx
			pop de
			ld a, d
.P2_3:			call DOS2.CALL.PUT_P2			; Sets the current map ram segment.
			exx

			jp .Continue






UpdateTileVisuals:	push hl					; It updates the tile visualization in both screen buffers.
			call GetTileVisualPosition		; IN H L = ty/tx.
			push ix
			call MapRender
			pop ix
			call SaveCurrentBufferVars

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			call LoadCurrentBufferVars
			pop hl
			call GetTileVisualPosition
			push ix
			call MapRender
			pop ix
			call SaveCurrentBufferVars

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			jp LoadCurrentBufferVars






; ************************************************************
; * SOFTWARE SPRITE ENGINE                                   *
; ************************************************************






RenderOneSprite:

IFDEF NO_CACHE

			ld de, $0000				; DE = SY/SX (or as it is, the start point offsets within the sprite pattern to copy).

			ld (ix + Sprite.ScreenWidth), 0		; Must check for screen visibility and calculate in screen position.

			ld l, (ix + Sprite.MapOx)		; Screen position regardless the scrolling registers.
			ld h, (ix + Sprite.MapTx)

			ld bc, (MapCurrentTileOffsetX)
			and a
			sbc hl, bc				; HL in (tttttttt pppp 0000) format.

			ld a, l
			sra h
			rra
			sra h
			rra
			sra h
			rra
			sra h
			jp m, ._HiddenOnLeftMargin
			jr z, ._InScreen1			; Jump is more usual.

			dec h
			ret z					; Escapes if relative X was >= 256 and =<511, so completely hidden on the right margin, but near.

._SpriteFar:		push ix					; Sprite is in a galaxy far far away, must extinct it. This sprite slot freed.
			call FreeOffScreenSpriteSlot
			call UpdateSpriteCount
			pop ix
			ret

._InScreen1:		rra
			ld l, a					; HL = ssss tttt tttt pppp.

			xor a
			sub l
			jr z, ._MaxX				; if relative X = 0, it will result in 0 = 256. So ZF=1 is signaling it. No jump is most usual.
			cp SPRITE_WIDTH
			jr c, ._HiddenPartiallyOnRightMargin	; No jump is most usual.

._MaxX:			ld a, SPRITE_WIDTH			; Complete width visibility in screen.

._HiddenPartiallyOnRightMargin:
			ld (ix + Sprite.ScreenWidth), a
			ld (ix + Sprite.ScreenX), l
			jp ._VisibilityXDone

._HiddenOnLeftMargin:	rra
			ld l, a					; HL = ssss tttt tttt pppp.

			inc h
			jr nz, ._SpriteFar			; It is far as Relative X < -255?. No jump is most usual.

			xor a
			sub l					; A = 256 - -x.
			ret z					; 0 = overflow, means X = L = -256.
			cp SPRITE_WIDTH
			ret nc					; Escapes if it is completely hidden on the left margin, but near.

			ld l, a

			add a, e
			ld e, a					; Adjust SX.
			
			ld a, SPRITE_WIDTH
			sub l					; Adjust Width to show.
			ld (ix + Sprite.ScreenWidth), a
			ld (ix + Sprite.ScreenX), 0		; Adjust visual LEFT.

._VisibilityXDone:	ld (ix + Sprite.ScreenHeight), 0	; IN DE = SY / SX.
								; Sets, not drew yet.
			ld l, (ix + Sprite.MapOy)
			ld h, (ix + Sprite.MapTy)		; Now must calculate the real visual position in the screen, regardless of the scrolling registers Values.

			ld bc, (MapCurrentTileOffsetY)
			and a
			sbc hl, bc				; HL in (tttttttt pppp 0000) format.

			ld a, l
			sra h
			rra
			sra h
			rra
			sra h
			rra
			sra h
			jp m, ._HiddenOnTopMargin		; No jump is most usual.
			jr nz, ._OutOfScreenY			; No jump is most usual.

			rra
			ld l, a					; HL = ssss tttt tttt pppp.
								; Sprite is after the top margin and visual Y =< 256.
			ld a, 211
			cp l					; Valid range = 0-211.
			ret c					; Escapes if relative Y >= 212 (not actually visible).

			inc a
			sub l					; A = 212 - sprite visual Y.
			cp SPRITE_HEIGHT
			jr c, ._HiddenPartiallyOnBottomMargin	; No jump is most usual.

			ld a, SPRITE_HEIGHT			; Complete Height visibility in screen.

._HiddenPartiallyOnBottomMargin:
			ld (ix + Sprite.ScreenHeight), a
			ld (ix + Sprite.ScreenY), l
			jp ._VisibilityYDone

._OutOfScreenY:		dec h
			ret z					; Escapes if relative Y =< 511.

			jp ._SpriteFar

._HiddenOnTopMargin:	rra
			ld l, a					; HL = ssss tttt tttt pppp.

			inc h
			jr nz, ._SpriteFar			; It is far as Relative Y < -255?. No jump is most usual.

			xor a
			sub l					; A = 256 - -y.
			ret z					; 0 = overflow, means Y = L = -256.
			cp SPRITE_HEIGHT
			ret nc					; Escapes if it is completely hidden on the top margin, but near.

			ld l, a

			add a, d
			ld d, a					; Adjust SY.

			ld a, SPRITE_HEIGHT
			sub l					; Adjust height to show.
			ld (ix + Sprite.ScreenHeight), a
			ld (ix + Sprite.ScreenY), 0		; Adjust visual TOP.

._VisibilityYDone:	; /// call VdpWait			; Check once if the vdp is busy, saving time if the sprite is splitted.
			; /// really is needed to check if everything is outputed by the CPU.?.


			ld l, (IX + Sprite.ScreenX)		; Now checks if it needs to split the sprite render in halfs and/or quarters.
			ld a, (ScreenCurrentX)			; Compare sprite in screen position with scrolls registers.
			add a, l
			ld l, (IX + Sprite.ScreenWidth)
			add a, l
			jp nc, ._NoHorizontalWrap		; jump is most usual.
			jr z, ._NoHorizontalWrap		; no jump is most usual.

			push af					; First fase (horizontal split).
								; A = how many pixels columns overflows the bitmap on its logical right edge.
			ld c, a
			ld a, (ix + Sprite.ScreenWidth)
			sub c
			push af
			ld c, a					; C = new width to render.
			ld l, (ix + Sprite.ScreenX)

			push de
			push bc
			push hl
			call ._CheckForVerticalWrap
			pop hl
			pop bc
			pop de
			pop af

			add a, e
			ld e, a					; Adjust SX.

			pop af
			ld c, a					; C = new width to render.

			ld a, (ScreenCurrentX)			; Adjust new visual LEFT.
			ld l, a
			xor a
			sub l
			ld l, a					; When the .DrawArea executes it will do DX = L + (ScreenCurrentX), so we adjust here for a result DX = 0.

			jp ._CheckForVerticalWrap

._NoHorizontalWrap:	ld c, l
			ld l, (IX + Sprite.ScreenX)

._CheckForVerticalWrap:	ld h, (IX + Sprite.ScreenY)		; IN DE = SY / SX, C = Width, L = Screen visual LEFT.
			ld a, (ScreenCurrentY)
			add a, h
			ld h, (IX + Sprite.ScreenHeight)
			add a, h
			jp nc, ._NoVerticalWrap			; jump is most usual.
			jr z, ._NoVerticalWrap			; no jump is most usual.

			push af					; FirstFase (vertical split).
								; A = how many pixels rows overflows the bitmap on its logical bottom edge.
			ld b, a
			ld a, (IX + Sprite.ScreenHeight)
			sub b
			push af
			ld b, a					; B = new height to render.
			ld h, (IX + Sprite.ScreenY)

			push de
			push bc
			push hl
			call ._DrawArea
			pop hl
			pop bc
			pop de
			pop af

			add a, d
			ld d, a					; Adjust SY.

			pop af
			ld b, a					; B = new height to render.

			ld a, (ScreenCurrentY)			; Adjust new visual LEFT.
			ld h, a
			xor a
			sub h
			ld h, a					; When the .DrawArea executes it will do DY = H + (ScreenCurrentY), so we adjust here for a result DY = 0.

			jp ._DrawArea

._NoVerticalWrap:	ld b, h
			ld h, (IX + Sprite.ScreenY)

._DrawArea:							; IN B C = Height / Width. D E = SY / SX. H L = Screen visual TOP / LEFT.

			di					; HL = Pattern address in RAM.

			ld a, 36
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, (ScreenCurrentX)
			add a, l				; Setup the screen area where to render the sprite.
			out ($9B), a
			xor a
			out ($9B), a				; DX.

			ld a, (ScreenCurrentY)
			add a, h
			out ($9B), a
			ld a, (ActiveBuffer)
			out ($9B), a				; DY.

			ld a, c
			out ($9B), a
			push bc

			ld c, $9B
			xor a
			out ($9B), a				; NX.
			out (c), b
			out ($9B), a				; NY.

			; /// This code calculates the base address for the sprite pattern in RAM. Change it if no 16x16 sprite ///.

			ld a, (SpritesBaseSegment)
			ld b, a

			ld h, (Ix + Sprite.Pattern)		; Calculate the pattern address in RAM.
			ld a, h
			rlca
			rlca
			and 3
			add a, b

			res 6, h
			set 7, h
			ld l, 0

.P2_1:			call DOS2.CALL.PUT_P2

			; /// This previous code calculates the base address for the sprite pattern in RAM. Change it if no 16x16 sprite ///.

								; HL = Sprite pattern base address in RAM.
			ld a, (Ix + Sprite.PatternFlip)		; Original D E = SX / SY (currently X/Y in pattern offsets).
			and 4					; Adjust the start point within the sprite pattern for doing a flipped render.
			jr z, ._NoXFlip

			ld a, SPRITE_WIDTH - 1
			sub e
			ld e, a

._NoXFlip:		ld a, (Ix + Sprite.PatternFlip)
			and 8
			jr z, ._NoYFlip

			ld a, SPRITE_HEIGHT - 1
			sub d
			ld d, a

._NoYFlip:		; /// This code merge the pattern base adress + offsets initial pixel in pattern. This code is relying in the pattern size in memory =< 256 bytes. Change it if no 16x16 sprite ///.
			ld a, d
			add a, a
			add a, a
			add a, a
			add a, a				; A * 16.
			add a, e				; HL = pattern address + OffsetY*16 + OffsetX.
			ld l, a					; HL = memory address of the initial pixel in pattern to render.

			; /// This previous code merge the pattern base adress + offsets initial pixel in pattern. This code is relying in the pattern size in memory =< 256 bytes. Change it if no 16x16 sprite ///.

			pop de					; D = Y count down, E = X count down.
			ld b, e					; Set up the X countdown for the first row.

			ld a, e
			cp SPRITE_WIDTH				; If the sprite is not partially hidden or splitted, the usual, can run faster routines (in z80 mode).
			jp z, .SpeedUp
								; Run this part when X splitted or partially hidden.
			ld a, (Ix + Sprite.PatternFlip)
			and a
			jr z, .NormalRender

			cp 4
			jr z, .XFlippedRender

			cp 8
			jr z, .YFlippedRender



			push hl					; X & Y Flipped Render.
			outd

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .XYFlippedRenderLoop
			jp .XYFlippedRenderNextRow

.XYFlippedRenderRowLoop:push hl

.XYFlippedRenderLoop:	otdr

.XYFlippedRenderNextRow:pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
			ld a, l
			sub a, SPRITE_WIDTH			; One row up.
			ld l, a
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			ld b, e

			dec d
			jp nz, .XYFlippedRenderRowLoop

			ei

			ret



.NormalRender:		push hl
			outi

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .NormalRenderLoop		; D = Y count down, E = X count down.
			jp .NormalRenderNextRow

.NormalRenderRowLoop:	push hl

.NormalRenderLoop:	otir

.NormalRenderNextRow:	pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
			ld a, l
			add a, SPRITE_WIDTH			; One row down.
			ld l, a
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			ld b, e

			dec d
			jp nz, .NormalRenderRowLoop

			ei

			ret



.XFlippedRender:	push hl
			outd

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .XFlippedRenderLoop
			jp .XFlippedRenderNextRow

.XFlippedRenderRowLoop:	push hl

.XFlippedRenderLoop:	otdr

.XFlippedRenderNextRow:	pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
			ld a, l
			add a, SPRITE_WIDTH			; One row down.
			ld l, a
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			ld b, e

			dec d
			jp nz, .XFlippedRenderRowLoop

			ei

			ret



.YFlippedRender:	push hl
			outi

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .YFlippedRenderLoop
			jp .YFlippedRenderNextRow

.YFlippedRenderRowLoop:	push hl

.YFlippedRenderLoop:	otir

.YFlippedRenderNextRow:	pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
			ld a, l
			sub a, SPRITE_WIDTH			; One row up.
			ld l, a
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			ld b, e

			dec d
			jp nz, .YFlippedRenderRowLoop

			ei

			ret



.SpeedUp:		ld a, (Ix + Sprite.PatternFlip)
			and a
			jr z, .SpeedNormalRender

			cp 4
			jp z, .SpeedXFlippedRender

			cp 8
			jp z, .SpeedYFlippedRender



; /// If rendering the whole width of the pattern the RAM address is automatically pointing on the previous row last pixel.
; ///			push hl					; X & Y Flipped Render.
			outd

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .SpeedXYFlippedRenderLoop
			jp .SpeedXYFlippedRenderNextRow

.SpeedXYFlippedRenderRowLoop:
; ///			push hl

			outd
.SpeedXYFlippedRenderLoop:
			outd
			outd
			outd

			outd
			outd
			outd
			outd

			outd
			outd
			outd
			outd

			outd
			outd
			outd
			outd

.SpeedXYFlippedRenderNextRow:
; ///			pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
; ///			ld a, l
; ///			sub a, SPRITE_WIDTH			; One row up.
; ///			ld l, a
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			dec d
			jp nz, .SpeedXYFlippedRenderRowLoop

			ei

			ret



; /// If rendering the whole width of the pattern the RAM address is automatically pointing on the next row first pixel.
.SpeedNormalRender:
; ///			push hl
			outi

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .SpeedNormalRenderLoop		; D = Y count down, E = X count down.
			jp .SpeedNormalRenderNextRow

.SpeedNormalRenderRowLoop:
; ///			push hl

			outi
.SpeedNormalRenderLoop:
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

.SpeedNormalRenderNextRow:
; ///			pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
; ///			ld a, l
; ///			add a, SPRITE_WIDTH			; One row down.
; ///			ld l, a
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			dec d
			jp nz, .SpeedNormalRenderRowLoop

			ei

			ret



.SpeedXFlippedRender:
; ///			push hl
			outd

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .SpeedXFlippedRenderLoop
			jp .SpeedXFlippedRenderNextRow

.SpeedXFlippedRenderRowLoop:
; ///			push hl

			outd
.SpeedXFlippedRenderLoop:
			outd
			outd
			outd

			outd
			outd
			outd
			outd

			outd
			outd
			outd
			outd

			outd
			outd
			outd
			outd

.SpeedXFlippedRenderNextRow:
; ///			pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
			ld a, l
			add a, SPRITE_WIDTH + SPRITE_WIDTH	; One row down + compenzation.
			ld l, a
			jr nc, .JJJ1
			inc h
.JJJ1:
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			dec d
			jp nz, .SpeedXFlippedRenderRowLoop

			ei

			ret



.SpeedYFlippedRender:
; ///			push hl
			outi

			ld a, 0
			out ($9B), a				; DIX/DIY always right/down.

			ld a, $B8				; TIMP.
			out ($9B), a				; sends LMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			jp nz, .SpeedYFlippedRenderLoop
			jp .SpeedYFlippedRenderNextRow

.SpeedYFlippedRenderRowLoop:
; ///			push hl

			outi
.SpeedYFlippedRenderLoop:
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

.SpeedYFlippedRenderNextRow:
; ///			pop hl

			; /// This code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.
			ld a, l
			sub a, SPRITE_WIDTH + SPRITE_WIDTH			; One row up + compenzation.
			ld l, a
			jr nc, .JJJ101
			dec h
.JJJ101:
			; /// This previous code is relying in that the sprite pattern <= 256 bytes and RAM address XX00 alignment. Change it if no 16x16 sprite ///.

			dec d
			jp nz, .SpeedYFlippedRenderRowLoop

			ei

			ret



ELSE



			ld a, (Bf0PatternCacheRow0Enabled)			; First search for the pattern in the pattern buffers.
			and a
			jr z, .Search16						; No jump is most usual.

			ld hl, PatternCachePool
			ld a, (ix + Sprite.Pattern)
			ld bc, 64

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

.Search16:		ld a, (Bf0PatternCacheRow1Enabled)
			and a
			jr z, .Search32						; No jump is most usual.

			ld hl, PatternCachePool + PatternCache.SizeOf * 16
			ld a, (ix + Sprite.Pattern)
			ld bc, 48

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

.Search32:		ld a, (Bf1PatternCacheRow0Enabled)
			and a
			jr z, .Search48						; No jump is most usual.

			ld hl, PatternCachePool + PatternCache.SizeOf * 32
			ld a, (ix + Sprite.Pattern)
			ld bc, 32

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

.Search48:		ld a, (Bf1PatternCacheRow1Enabled)
			and a
			jr z, .PatternNotFound					; No jump is most usual.

			ld hl, PatternCachePool + PatternCache.SizeOf * 48
			ld a, (ix + Sprite.Pattern)
			ld bc, 16

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

			cpi
			call z, .CheckFlip
			inc l
			inc l

.PatternNotFound:	ld e, 255						; Flags that didn't choise any slot.
			xor a							; Currently has this oldness.
			ld c, a

			ld a, (Bf0PatternCacheRow0Enabled)			; First search for the pattern in the pattern buffers.
			and a
			jp z, .Older16

			ld hl, PatternCachePool + 2
			ld a, c
			ld b, 0

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot

			ld c, a

.Older16:		ld a, (Bf0PatternCacheRow1Enabled)
			and a
			jp z, .Older32

			ld hl, PatternCachePool + PatternCache.SizeOf * 16 + 2
			ld a, c
			ld b, 16

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot

			ld c, a

.Older32:		ld a, (Bf1PatternCacheRow0Enabled)
			and a
			jp z, .Older48

			ld hl, PatternCachePool + PatternCache.SizeOf * 32 + 2
			ld a, c
			ld b, 32

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot

			ld c, a

.Older48:		ld a, (Bf1PatternCacheRow1Enabled)
			and a
			jp z, .DesicionTaker

			ld hl, PatternCachePool + PatternCache.SizeOf * 48 + 2
			ld a, c
			ld b, 48

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot
			inc l
			inc l
			inc l
			inc b

			cp (hl)
			call c, .SelectThisSlot

.DesicionTaker:		ld a, e
			cp $FF
			ret z					; Escape if didn't choice any slot.

.DesicionTaker2:	ld l, a					; A = selected slot number in the pattern cache.
			ex af, af'

			ld h, 0
			ld e, l
			ld d, h
			add hl, hl
			add hl, de
			ld de, PatternCachePool
			add hl, de				; HL = address of the selected slot.

			ld a, (Ix + Sprite.Pattern)
			ld (hl), a				; Set in it the sprite pattern number.
			inc l
			ld a, (Ix + Sprite.PatternFlip)
			ld (hl), a
			inc l
			ld (hl), $00				; Set it as the newer.

			ex af, af'				; A = selected slot number in cache.

			ld d, a					; Now it must copy the pattern from RAM to VRAM.
			add a, a
			add a, a
			add a, a
			add a, a				; X = (slot_number mod 16) * 16
			ld e, a					; E = DX.

			ld a, d
			and $F0
			ld d, a					; D = DY.

			push de					; save DY/DX.

			xor a
			ld l, a
			ld h, (Ix + Sprite.Pattern)		; Now must calculate the pattern address in RAM.
			rl h
			rla
			rl h
			rla

			rr h
			scf
			rr h

			ld b, a
			ld a, (SpritesBaseSegment)
			add a, b
.P2_2:			call DOS2.CALL.PUT_P2

			call VdpWait

			di					; HL = Pattern address in RAM.

			ld a, 36
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, (Ix + Sprite.PatternFlip)
			and $04					; FlipX.
			jp z, .NoFlipX				; No jump is most usual.

			ld a, e
			and $F0
			add a, 15
			ld e, a

.NoFlipX:		ld a, e
			out ($9B), a
			xor a
			out ($9B), a				; DX.

			bit 5, d
			jr nz, .ThirdFourRowCache

			ld a, (Ix + Sprite.PatternFlip)
			and $08					; FlipY.
			jp z, .NoFlipY1				; No flip is most usual.

			ld a, (Bf0PatternCacheY)
			add a, d				; Sum first or second row of patterns in page 0.
			add a, 15

			out ($9B), a
			xor a
			out ($9B), a				; DY at Bf0.
			jp .NX

.NoFlipY1:		ld a, (Bf0PatternCacheY)
			add a, d				; Sum first or second row of patterns in page 0.

			out ($9B), a
			xor a
			out ($9B), a				; DY at Bf0.
			jp .NX

.ThirdFourRowCache:	ld a, (Ix + Sprite.PatternFlip)
			and $08					; FlipY.
			jp z, .NoFlipY2				; No flip is most usual.

			ld a, (Bf1PatternCacheY)
			res 5, d
			add a, d				; Sum first or second row of patterns in page 1.
			add a, 15

			out ($9B), a
			ld a, 1
			out ($9B), a				; DY at Bf1.
			jp .NX

.NoFlipY2:		ld a, (Bf1PatternCacheY)
			res 5, d
			add a, d				; Sum first or second row of patterns in page 1.

			out ($9B), a
			ld a, 1
			out ($9B), a				; DY at Bf1.

.NX:			ld a, 16
			out ($9B), a
			xor a
			out ($9B), a				; NX.

			ld a, 16
			out ($9B), a
			xor a
			out ($9B), a				; NY.

			ld c, $9B
			outi

			ld a, (Ix + Sprite.PatternFlip)
			and $0C					; Flips.
			out ($9B), a				; DIX/DIY as flip member.

			ld a, $F0
			out ($9B), a				; sends HMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ei

			ld a, 16
			jp .In1

.VpokePatternLoop:	outi
.In1:			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			outi
			dec a
			jp nz, .VpokePatternLoop		; Jump is most usual.
								; Now the cache slot is set.
			pop de					; Restores DE = SY / SX of sprite character in cache.
			jp .Print

.SelectThisSlot:	ld e, b					; Part of looking for the oldest pattern in the cache.
			ld a, (hl)

			cp $FF					; Checks if it is an unused slot.
			ret nz

			pop af					; Drops the call .SelectThisSlot.
			jp .DesicionTaker2

.CheckFlip:		ld a, (ix + Sprite.PatternFlip)		; Checks if is the same flips attribs.
			cp (hl)
			ld a, (ix + Sprite.Pattern)
			ret nz

			inc l
			ld b, (hl)
			dec l

			inc b					; Checks if pattern oldness is not expired. $FF = expired.
			ret z
								; Pattern found in Cache!.
			inc l
			ld (hl), $00				; Reset oldness of the found pattern.
			pop af					; Clears the "Call .CheckFlip".

			ld a, PatternCacheSize - 1		; The CPI already decreased C, so 63 = slot 0.
			sub c					; C = Pattern slot countdown done by instructions CPI.

			ld d, a					; A = found cache slot number.

			add a,a
			add a,a
			add a,a
			add a,a
			ld e, a

			ld a, d
			and $F0
			ld d, a					; D E = SY / SX of sprite character in cache.



.Print:			ld (ix + Sprite.ScreenWidth), 0		; Makes the final sprite render on the visible screen, IN DE = SY / SX. CF = 0.
								; Sets, not drew yet.
			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)		; Now must calculate the real visual position in the screen, regardless of the scrolling registers values.

			ld bc, (MapCurrentTileOffsetX)
			sbc hl, bc				; HL in (tttttttt pppp 0000) format.

			sra h
			rr l
			sra h
			rr l
			sra h
			rr l
			sra h
			jp m, .HiddenOnLeftMargin
			jr z, .InScreen1

			rr l					; HL = ssss tttt tttt pppp.

			dec h
			ret z					; Escapes if relative X was >= 256 and =<511, so completely hidden on the right margin, but near.

.SpriteFar:		push ix					; Sprite is in a galaxy far far away, must extinct it. This sprite slot freed.
			call FreeOffScreenSpriteSlot
			call UpdateSpriteCount
			pop ix
			ret

.InScreen1:		rr l

			xor a
			sub l
			jr z, .MaxX				; if relative X = 0, it will result in 0 = 256. So ZF=1 is signaling it.
			cp 16
			jr c, .HiddenPartiallyOnRightMargin	; No jump is most usual.

.MaxX:			ld a, 16				; Complete width visibility in screen.

.HiddenPartiallyOnRightMargin:
			ld (ix + Sprite.ScreenWidth), a
			ld (ix + Sprite.ScreenX), l
			jp .VisibilityXDone

.HiddenOnLeftMargin:	rr l

			inc h
			jr nz, .SpriteFar			; It is far as Relative X < -255?. No jump is most usual.

			xor a
			sub l					; A = 256 - -x.
			ret z					; 0 = overflow, means X = L = -256.
			cp 16
			ret nc					; Escapes if it is completely hidden on the left margin, but near.

			ld l, a

			add a, e
			ld e, a					; Adjust SX.
			
			ld a, 16
			sub l					; Adjust Width to show.
			ld (ix + Sprite.ScreenWidth), a
			ld (ix + Sprite.ScreenX), 0		; Adjust visual LEFT.

.VisibilityXDone:	ld (ix + Sprite.ScreenHeight), 0	; IN DE = SY / SX.
								; Sets, not drew yet.
			ld l, (ix + Sprite.MapOy)
			ld h, (ix + Sprite.MapTy)		; Now must calculate the real visual position in the screen, regardless of the scrolling registers Values.

			ld bc, (MapCurrentTileOffsetY)
			and a
			sbc hl, bc				; HL in (tttttttt pppp 0000) format.

			sra h
			rr l
			sra h
			rr l
			sra h
			rr l
			sra h
			jp m, .HiddenOnTopMargin
			jr nz, .OutOfScreenY

			rr l					; HL = ssss tttt tttt pppp.
								; Sprite is after the top margin and visual Y =< 256.
			ld a, 211
			cp l					; Valid range = 0-211.
			ret c					; Escapes if relative Y >= 212 (not actually visible).

			inc a
			sub l
			cp 16
			jr c, .HiddenPartiallyOnBottomMargin	; No jump is most usual.

			ld a, 16				; Complete Height visibility in screen.

.HiddenPartiallyOnBottomMargin:
			ld (ix + Sprite.ScreenHeight), a
			ld (ix + Sprite.ScreenY), l
			jp .VisibilityYDone

.OutOfScreenY:		dec h
			ret z					; Escapes if relative Y =< 511.

			jp .SpriteFar

.HiddenOnTopMargin:	rr l

			inc h
			jr nz, .SpriteFar			; It is far as Relative Y < -255?. No jump is most usual.

			xor a
			sub l					; A = 256 - -y.
			ret z					; 0 = overflow, means Y = L = -256.
			cp 16
			ret nc					; Escapes if it is completely hidden on the top margin, but near.

			ld l, a

			add a, d
			ld d, a					; Adjust SY.

			ld a, 16
			sub l					; Adjust height to show.
			ld (ix + Sprite.ScreenHeight), a
			ld (ix + Sprite.ScreenY), 0		; Adjust visual TOP.

.VisibilityYDone:	ld l, (IX + Sprite.ScreenX)		; IN DE = SY / SX.
			ld a, (ScreenCurrentX)			; Now must check if it need to split the render horizontally, for horizontal wrap on the real bitmap right edge.
			add a, l
			ld l, (IX + Sprite.ScreenWidth)
			add a, l
			jp nc, .NoHorizontalWrap		; jump is most usual.
			jr z, .NoHorizontalWrap			; no jump is most usual.

			push af					; First fase (horizontal split).
								; A = how many pixels columns overflows the bitmap on its logical right edge.
			ld c, a
			ld a, (ix + Sprite.ScreenWidth)
			sub c
			push af
			ld c, a					; C = new width to render.
			ld l, (ix + Sprite.ScreenX)

			push de
			call .CheckForVerticalWrap
			pop de
			pop af

			add a, e
			ld e, a					; Adjust SX.

			pop af
			ld c, a					; C = new width to render.

			ld a, (ScreenCurrentX)			; Adjust new visual LEFT.
			ld l, a
			xor a
			sub l
			ld l, a					; When the .DrawArea executes it will do DX = L + (ScreenCurrentX), so we adjust here for a result DX = 0.

			jp .CheckForVerticalWrap

.NoHorizontalWrap:	ld c, l
			ld l, (IX + Sprite.ScreenX)

.CheckForVerticalWrap:	ld h, (IX + Sprite.ScreenY)		; IN DE = SY / SX, C = Width, L = Screen visual LEFT.
			ld a, (ScreenCurrentY)
			add a, h
			ld h, (IX + Sprite.ScreenHeight)
			add a, h
			jp nc, .NoVerticalWrap			; jump is most usual.
			jr z, .NoVerticalWrap			; no jump is most usual.

			push af					; FirstFase (vertical split).
								; A = how many pixels rows overflows the bitmap on its logical bottom edge.
			ld b, a
			ld a, (IX + Sprite.ScreenHeight)
			sub b
			push af
			ld b, a					; B = new height to render.
			ld h, (IX + Sprite.ScreenY)

			push de
			call .DrawArea
			pop de
			pop af

			add a, d
			ld d, a					; Adjust SY.

			pop af
			ld b, a					; B = new height to render.

			ld a, (ScreenCurrentY)			; Adjust new visual LEFT.
			ld h, a
			xor a
			sub h
			ld h, a					; When the .DrawArea executes it will do DY = H + (ScreenCurrentY), so we adjust here for a result DY = 0.

			jp .DrawArea

.NoVerticalWrap:	ld b, h
			ld h, (IX + Sprite.ScreenY)

.DrawArea:		call VdpWait				; IN B C = Height / Width. D E = SX / SY. H L = Screen visual TOP / LEFT.

			di

			ld a, 32
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, e
			out ($9B), a
			xor a
			out ($9B), a				; SX.

			bit 5, d
			jr nz, .SecondPageCache2

			ld a, (Bf0PatternCacheY)
			add a, d
			out ($9B), a
			xor a
			out ($9B), a				; SY at Bf0.
			jp .DX

.SecondPageCache2:	ld a, (Bf1PatternCacheY)
			push de
			res 5, d
			add a, d
			pop de
			out ($9B), a
			ld a, 1
			out ($9B), a				; SY at Bf1.

.DX:			ld a, (ScreenCurrentX)
			add a, l
			out ($9B), a
			xor a
			out ($9B), a				; DX.

			ld a, (ScreenCurrentY)
			add a, h
			out ($9B), a
			ld a, (ActiveBuffer)
			out ($9B), a				; DY.

			ld a, c
			out ($9B), a
			xor a
			out ($9B), a				; NX.

			ld a, b
			out ($9B), a
			xor a
			out ($9B), a				; NY.

			out ($9B), a

			xor a
			out ($9B), a				; DIX/DIY Down-Right.

			ld a, $98				; TIMP.
			out ($9B), a				; sends HMMC command #46.

			ei
			ret

ENDIF






InitSpriteSystem:

IFDEF NO_CACHE
ELSE

			ld hl, PatternCachePool			; Clears the pattern cache.
			ld de, PatternCachePool + 1
			ld bc, PatternCache.SizeOf * PatternCacheSize - 1
			ld (hl), $FF
			ldir

ENDIF

			ld hl, SpritesPool
			ld de, SpritesPool + 1
			ld bc, Sprite.SizeOf * SpritesPoolSize - 1
			ld (hl), 0
			ldir

			ld b, SpritesPoolSize
			ld de, Sprite.SizeOf
			ld ix, SpritesPool

.Loop:			ld (ix + Sprite.Class), SpriteClasses.EmptySlotClass
			add ix, de

			djnz .Loop

			ld hl, 0
			ld (SpacingObjectsList), hl

IFDEF NO_CACHE
ELSE

			xor a
			ld (Bf0PatternCacheRow0Enabled), a
			ld (Bf0PatternCacheRow1Enabled), a
			ld (Bf1PatternCacheRow0Enabled), a
			ld (Bf1PatternCacheRow1Enabled), a

ENDIF

			ret






GetFreeSpriteSlot:	ld a, (SpritesCount)			; OUT A = ID sprite slot, IX = sprite pointer, CF = 1 error.
			and a
			jr z, .ExpandCount			; No jump is most usual.

			ld b, a
			ld ix, SpritesPool

.SpriteLoop:		ld a, (IX + Sprite.Class)
			cp SpriteClasses.EmptySlotClass
			jp nz, .NextSprite			; For gaining speed when too much characters, jump is most usual.

			ld a, (SpritesCount)
			sub b

			jp .PrepareIt

.NextSprite:		push bc
			ld bc, Sprite.SizeOf
			add ix, bc
			pop bc

			djnz .SpriteLoop

.ExpandCount:		ld a, (SpritesCount)
			cp SpritesPoolSize
			ccf
			ret c

			inc a
			ld (SpritesCount), a

			dec a					; This is the "Sprite size formula".
			ld l, a
			ld h, 0
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl				; * 64.
			ex de, hl
			ld ix, SpritesPool
			add ix, de				; IX = last sprite in the pool.

.PrepareIt:		ld (ix + Sprite.Class), SpriteClasses.UnassignedClass
			ld (ix + Sprite.ScreenWidth), 0		; Marks it as not drew in any screen buffer.
			ld (ix + Sprite.Bf0ScreenWidth), 0
			ld (ix + Sprite.Bf1ScreenWidth), 0

			ld c, ixl
			ld b, ixh
			ld hl, Sprite.LogicArea
			add hl, bc

			ld e, l
			ld d, h
			inc de
			ld bc, 24 - 1				; 24 = Logic area length.
			ld (hl), 0
			ldir

			ret






SetSprite:		push hl					; IN A = ID, H L = Ty/Oy, D E = Tx/Ox, C = Class, H' L' = Ty, Tx respawn ID.
			push de

			ld l, a					; This is the "Sprite size formula".
			ld h, 0
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl				; * 64.
			ex de, hl
			ld ix, SpritesPool
			add ix, de				; IX = last sprite in the pool.

			ld (ix + Sprite.Class), c
			ld (ix + Sprite.PatternFlip), $0

			pop de
			ld (ix + Sprite.MapTx), d
			ld (ix + Sprite.MapOx), e

			pop hl
			ld (ix + Sprite.MapTy), h
			ld (ix + Sprite.MapOy), l

			exx
			ld (ix + Sprite.MapRespawnTx), l
			ld (ix + Sprite.MapRespawnTy), h

			ret






SpacingListSubscribe:	ld hl, SpacingObjectsList		; IN DE = Object address.

.Loop:			ld a, (hl)
			cp e
			inc hl
			jr z, .LSBAddressMatch

			or (hl)
			jr z, .Subscribe			; Check if terminating null address.

			inc hl
			jp .Loop

.LSBAddressMatch:	or (hl)
			jr z, .Subscribe			; Check if terminating null address.

			ld a, (hl)
			cp d
			ret z					; Escape if the same address is already registered.

			inc hl
			jp .Loop

.Subscribe:		ld (hl), d				; Subscribe the address.
			dec hl
			ld (hl), e

			inc hl
			inc hl

			xor a					; Tail the list with null address.
			ld (hl), a
			inc hl
			ld (hl), a

			ret






FreeOffScreenSpriteSlot:
			ld a, (ix + Sprite.Class)		; Unregister if a spacing class.
			cp 3
			ld (ix + Sprite.Class), SpriteClasses.EmptySlotClass	; Mark it as unused.

			ret nc

			ld e, ixl
			ld d, ixh






SpacingListUnsubscribe:	ld hl, SpacingObjectsList		; IN DE = Object address.

.Loop:			ld a, (hl)
			cp e
			inc hl
			jp nz, .Next

			or (hl)					; Escape if terminating null address (address not found).
			ret z

			ld a, (hl)
			cp d

.Next:			inc hl
			jp nz, .Loop				; Isn't the address, loop.

			ld e, l
			ld d, h
			dec de
			dec de

.MoveLoop:		ld a, (hl)
			ld (de), a
			inc hl
			inc de
			and a
			jp nz, .NoZero				; Jump is most usual.

			ld a, (hl)
			ld (de), a
			inc hl
			inc de

			and a					; Escape if just copied the terminating null address.
			ret z

			jp .MoveLoop

.NoZero:		ld a, (hl)
			ld (de), a
			inc hl
			inc de

			jp .MoveLoop






SetSpritePattern:	ld l, a						; In IX = Sprite. A = Pattern. MODs: AF, HL, DE.

			ld a, (ResourcesSegment)
.P2_1:			call DOS2.CALL.PUT_P2

			ld a, (ix + Sprite.PatternFlip)
			cp (ix + Sprite.OldPatternFlip)
			ld (ix + Sprite.OldPatternFlip), a
			jr nz,	.DoIt					; No jump is most usual.

			ld a, l
			cp (ix + Sprite.Pattern)
			ret z

.DoIt:			ld (ix + Sprite.Pattern), l
			ld h, 0
			add hl, hl
			add hl, hl
			add hl, hl
			ld de, HitBoxesTable
			add hl, de
			
			ld a, (ix + Sprite.PatternFlip)
			rrca
			rrca
			and a
			jp z, .NoFlipped				; Jump is most usual.
			dec a
			jp z, .XFlipped					; Jump is most usual.
			dec a
			jr z, .YFlipped
									; X & Y flipped.
			call .Mirroring

			ld (ix + Sprite.HighHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxWidth), a

			call .Mirroring

			ld (ix + Sprite.HighHitBoxTop), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxHeight), a

			call .Mirroring

			ld (ix + Sprite.LowHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxWidth), a

			call .Mirroring

			ld (ix + Sprite.LowHitBoxTop), a

			ld a, (hl)

			ld (ix + Sprite.LowHitBoxHeight), a

			ret

.YFlipped:		ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxWidth), a

			call .Mirroring

			ld (ix + Sprite.HighHitBoxTop), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxHeight), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxWidth), a

			call .Mirroring

			ld (ix + Sprite.LowHitBoxTop), a

			ld a, (hl)

			ld (ix + Sprite.LowHitBoxHeight), a

			ret

.XFlipped:		call .Mirroring

			ld (ix + Sprite.HighHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxWidth), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxTop), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxHeight), a

			call .Mirroring

			ld (ix + Sprite.LowHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxWidth), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxTop), a

			ld a, (hl)

			ld (ix + Sprite.LowHitBoxHeight), a

			ret

.NoFlipped:		ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxWidth), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxTop), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.HighHitBoxHeight), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxLeft), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxWidth), a

			ld a, (hl)
			inc hl

			ld (ix + Sprite.LowHitBoxTop), a

			ld a, (hl)

			ld (ix + Sprite.LowHitBoxHeight), a

			ret

.Mirroring:		ld a, (hl)
			cpl
			and $F0
			inc hl
			ld b, (hl)
			rl b
			jr c, .j16_1					; No jump is most usual.
			sub b
			add a, $10
			ret

.j16_1:			xor a						; When size is 16 pixels, left/top only can be 0.
			ret






SpriteRestoreBackground:ld a, (SpritesCount)
			and a
			ret z

			ld b, a

			ld ix, SpritesPool

.SpriteLoop:		push bc

			ld a, (IX + Sprite.Class)
			cp SpriteClasses.EmptySlotClass
			jr z, .NextSprite			; No jump is most usual when full of characters.

			ld a, (ActiveBuffer)
			and a
			jr nz, .Bf1

			ld e, (IX + Sprite.Bf0ScreenWidth)	; It is the visual position in the screen when the sprite was drew in the buffer 0.
			ld l, (IX + Sprite.Bf0ScreenHeight)
			ld d, (IX + Sprite.Bf0ScreenX)
			ld h, (IX + Sprite.Bf0ScreenY)
			jp .Cont

.Bf1:			ld e, (IX + Sprite.Bf1ScreenWidth)	; It is the visual position in the screen when the sprite was drew in the buffer 1.
			ld l, (IX + Sprite.Bf1ScreenHeight)
			ld d, (IX + Sprite.Bf1ScreenX)
			ld h, (IX + Sprite.Bf1ScreenY)

.Cont:			ld c, 0
			push ix
			call MapRender				; Orders to render the map background only where the sprite was drew.
			pop ix					; IN H L = Top/Height, D CE = Left/Width, coordinates are in real visual screen (no vdp command coordinates).

.NextSprite:		ld bc, Sprite.SizeOf
			add ix, bc

			pop bc
			djnz .SpriteLoop
			ret






RemoveMe:		ld (ix + Sprite.Class), SpriteClasses.RemoveMeClass
			ld (ix + Sprite.Pattern), SpritePatterns.Empty
			ld (ix + Sprite.PatternFlip), 0
			ld (ix + Sprite.LogicArea), 0
			ret






UpdateSpriteCount:	ld a, (SpritesCount)			; This routine revert the sprite count if possible (like when the last slot is freed).
			and a
			ret z

			ld b, a
			ld c, 255				; -1.
			ld l, 0
			ld de, Sprite.SizeOf
			ld ix, SpritesPool

.SpriteLoop:		ld a, (ix + Sprite.Class)
			cp SpriteClasses.EmptySlotClass
			jr z, .NoUsed

			ld c, l

.NoUsed:		inc l
			add ix, de

			djnz .SpriteLoop

			ld a, c
			inc a
			ld (SpritesCount), a
			ret






IFDEF NO_CACHE
ELSE

TestIfCacheIsNowInvalid:ld a, (ScreenCurrentY)
			ld c, a

			ld a, (ActiveBuffer)
			and a
			ld a, (Bf0PatternCacheY)
			jr z, .Cont
			ld a, (Bf1PatternCacheY)

.Cont:			sub c					; Unwrap the Y coordinates. Now can do the test in linear way.

			cp 212
			jr c, .Invalid

			cp 256-31				; up to 224 is valid.
			ret c

.Invalid:		call GetBestCachePosition		; Reset the position to its new best.

			ld a, (ActiveBuffer)			; Clears the realted portion of the buffer.
			and a
			jr nz, .Bf1
								; Bf0.
			ld hl, PatternCachePool
			ld de, PatternCachePool + 1
			ld bc, PatternCache.SizeOf * 32 - 1
			ld (hl), $FF
			ldir
			ret

.Bf1:			ld hl, PatternCachePool + PatternCache.SizeOf * 32
			ld de, PatternCachePool + PatternCache.SizeOf * 32 + 1
			ld bc, PatternCache.SizeOf * 32 - 1
			ld (hl), $FF
			ldir
			ret

ENDIF






IFDEF NO_CACHE
ELSE

GetBestCachePosition:	ld a, (ScreenCurrentY)			; It sets the best cache position for the current Y scroll, in the active buffer.
			ld c, a

			ld a, (ActiveBuffer)
			and a
			ld a, (Bf0PatternCacheY)
			jr z, .Cont
			ld a, (Bf1PatternCacheY)

.Cont:			sub c					; Unwrap the Y coordinates. Now can do the test in linear way.

			cp 212 + 6				; 212 + 6 is the ideal CENTER place.
			ld a, (ScreenCurrentY)
			jr nc, .Top

			add a, 256 - 32				; Currently the cache is overlapping on the bottom of the visible screen, this means, the screen is scrolling down.
			jp .Save

.Top:			add a, 212				; Currently the cache is overlapping on the top of the visible screen, this means, the screen is scrolling up.

.Save:			ld e, a

			ld a, (ActiveBuffer)
			and a

			ld a, e
			jr nz, .Bf1

			ld (Bf0PatternCacheY), a
			cp 256-15
			jr c, .Row0Ok

			xor a
			ld (Bf0PatternCacheRow0Enabled), a	; Disable.
			dec a
			ld (Bf0PatternCacheRow1Enabled), a	; Enable.
			ret

.Row0Ok:		add a, 16
			cp 256-15
			jr c, .Row1Ok

			ld a, 255
			ld (Bf0PatternCacheRow0Enabled), a	; Enable.
			xor a
			ld (Bf0PatternCacheRow1Enabled), a	; Disable.
			ret

.Row1Ok:		ld a, 255
			ld (Bf0PatternCacheRow0Enabled), a	; Enable.
			ld (Bf0PatternCacheRow1Enabled), a	; Enable.
			ret

.Bf1:			ld (Bf1PatternCacheY), a
			cp 256-15
			jr c, .Row0Ok_Page1

			xor a
			ld (Bf1PatternCacheRow0Enabled), a	; Disable.
			dec a
			ld (Bf1PatternCacheRow1Enabled), a	; Enable.
			ret

.Row0Ok_Page1:		add a, 16
			cp 256-15
			jr c, .Row1Ok_Page1

			ld a, 255
			ld (Bf1PatternCacheRow0Enabled), a	; Enable.
			xor a
			ld (Bf1PatternCacheRow1Enabled), a	; Disable.
			ret

.Row1Ok_Page1:		ld a, 255
			ld (Bf1PatternCacheRow0Enabled), a	; Enable.
			ld (Bf1PatternCacheRow1Enabled), a	; Enable.
			ret

ENDIF






IFDEF NO_CACHE
ELSE

UpdateOldnessInCache:	ld hl, PatternCachePool
			ld b, PatternCacheSize

.Loop:			inc hl
			inc hl
			ld a, (hl)
			cp $FE
			jr nc, .Next				; No jump is most usual.

			inc (hl)

.Next:			inc hl
			djnz .Loop
			ret

ENDIF






ScanForCharactersRespawn:
			ld a, (MapCurrentTileX)			; Scans all the in screen visible tiles from the map looking for characters respawn attributes.
			ld l, a					; Calculate the map address of the top/left visible tile.
			ld a, (MapCurrentTileY)
			ld h, a
			add hl, hl

			ld a, (MapBaseSegment)
			ld c, a

			ld a, h
			rlca
			rlca
			and 3
			add a, c

			res 6, h
			set 7, h				; HL = address in page2.

			exx
			ld d, a
			exx					; D' = Relevant map ram segment number.
.P2_1:			call DOS2.CALL.PUT_P2

			ld e, l
			ld d, h					; DE = back up for next tile row address.

			ld b, 13				; If offset is near the end of a texture, can be shown 14 tiles vertically. The screen shows 13 tiles and 4 pixel rows.
			ld a, (MapCurrentTileOffsetY)		; yyyy0000 pixel offset for the current top tile, that is showing at the most top pixel of the screen.
			cp $D0					; If the texture offset is on the 13th pixel or more, in the screen will fit 14 tiles vertically.
			jp c, .VLoop				; Jump is most usual.
			inc b



.VLoop:			push bc					; Save vertical countdown.

			ld b, 16				; If offset is in the middle of a texture, can be shown 17 tiles horizontally.
			ld a, (MapCurrentTileOffsetX)		; xxxx0000 pixel offset for the current left tile, that is showing at the most left pixel of the screen.
			and a
			jr z, .HLoop				; No jump is most usual.
			inc b



.HLoop:			inc hl
			ld a, (hl)
			and MapAttribs.MaskMajor
			cp MapAttribs.Major.EnemyRespawn
			jr z, .EnemyRespawn			; No jump is most usual.

.Continue:		inc hl

			djnz .HLoop

			inc d
			inc d					; DE += 512 (512 bytes is the map rowsize in bytes).
			
			bit 6, d				; Checks if the address overflows page2.
			jp z, .NextRow				; Jump is most usual.

			res 6, d				; Wraps in page2.

			exx					; Next map ram segment.
			inc d
			ld a, d
			exx
.P2_2:			call DOS2.CALL.PUT_P2

.NextRow:		ld l, e
			ld h, d

			pop bc
			djnz .VLoop

			ret



.EnemyRespawn:		inc hl					; Important by now is: HL = Current tile in MAP address, DE = home position current row in the map, D' = map ram segment, B = XLoop count, POP BC = YLoop.
			inc hl
			ld a, (hl)
			dec hl
			dec hl
			and MapAttribs.MaskMajor + MapAttribs.MaskMinor
			cp $1E
			jr nz, .NoMPFlag
								; It has the multiplayer flag.
			ld a, (PlayersCount)
			cp 2
			jr c, .Continue				; Escape if single player.

.NoMPFlag:		ld a, (hl)				; Fetches the special code from the map.
			and MapAttribs.MaskMinor
			ld ixl, a				; IXL = CLASS map encoded.

			ld c, 16				; First must know the exact Ty/Tx in the map.
			ld a, (MapCurrentTileOffsetX)
			and a
			jr z, .HTotal2				; No jump is most usual.
			inc c
.HTotal2:		
			ld a, c					; A = total of tiles horizontally.
			sub b					; Tx = Total of tiles - X loop count.
			ld c, a

			ld a, (MapCurrentTileX)
			add a, c
			ld iyl, a				; IYL = Respawn Tx.

			ld a, b					; Backup the current HLoop decrementer.
			ex af, af'

			pop bc					; Gets the VLoop decrementer.
			push bc

			ld c, 13				; If offset is near the end of a texture, can be shown 14 tiles vertically. Because the screen shows 13 tiles and 4 pixel rows.
			ld a, (MapCurrentTileOffsetY)
			cp $D0
			jp c, .VTotal2				; Jump is most usual.
			inc c
.VTotal2:
			ld a, c					; A = total of tiles Vertically.
			sub b					; Ty = Total of tiles - Y loop count.
			ld c, a

			ld a, (MapCurrentTileY)
			add a, c
			ld iyh, a				; IYH = Respawn Ty.

			ex af, af'
			ld b, a					; Restore the current HLoop decrementer.

			push bc					; Save HLoop decrementer.
			push de					; Save the current map row address.
			push hl					; Save the current map tile address.

			ld e, ixl				; Move Class map encoded to a easier register.
								; Iyh = Ty, Iyl = Tx, E = Class raw map encoding.


			ld ix, SpritesPool			; Now must test if the related character is already present.
			ld a, (SpritesCount)
			ld b, a

.SpriteLoop:		push bc					; Save the sprite count.

			ld a, (ix + Sprite.Class)
			cp SpriteClasses.EmptySlotClass
			jr z, .NextSprite			; No jump is most usual.

			ld a, iyl
			cp (ix + Sprite.MapRespawnTx)
			jp nz, .NextSprite			; Jump is most usual.

			ld a, iyh
			cp (ix + Sprite.MapRespawnTy)
			jp nz, .NextSprite			; Jump is most usual.

								; Found a character already with the same respawn ID.
			pop bc					; Drops the sprite count.

.Escape:		pop hl					; Restore current map tile address.
			pop de					; Restore current map row address.
			pop bc					; Restore HLoop decrementer.

			jp .Continue				; So, escape, nothing to do. To check next tile in screen.

.NextSprite:		ld bc, Sprite.SizeOf
			add ix, bc

			pop bc					; Restore sprite count.
			djnz .SpriteLoop
								; The character wasn't found live.
								; Check if it is in the recent death list. Because we don't want to respawn him if just dead, else case would be an endless fight.

			push de					; Save E = character class raw map encoded.

			ld e, iyl				; respawn Tx.
			ld d, iyh				; respawn Ty.
			call TestForRecentDeath			; In DE = respawn map location. OUT ZF = 1, exists as a recent death.

			pop de					; Restore E = character class map encoded.
			jp z, .Escape				; Jump is most usual.


								; Is not in recent death's list. It can respawn.
			exx
			push de					; Save D' = relevant map ram segment number.
			exx

			push de					; Save E = character class map encoded.

								; Request a sprite slot for the character.
			call GetFreeSpriteSlot			; OUT A = ID sprite slot, CF = 1 error (out of slots).
			pop bc					; Restore C = character class map encoded.
			jr c, .EscapeInner			; No jump is most usual.

			rr c					; C = Class. Original format was masked $1E, so >> 1 = $0F, up to 16 classes creatable from the map, $00-$0F format used in Sprite.Class.

			ld d, iyl				; Sets position ty/oy tx/ox.
			ld b, iyh
			ld h, b
			ld l, 0
			ld e, l

			exx
			push iy
			pop hl					; Sets the respawn ID.
			exx

			push bc					; Save class number.
			ld c, SpriteClasses.BornClass		; BornClass.
			call SetSprite				; IN A = ID, H L = Ty/Oy, D E = Tx/Ox, C = Class, H' L' = Ty, Tx respawn ID.
			pop bc					; Restore class number = C.

			ld (ix + Sprite.LogicArea + 2), c	; Stores the target class elsewhere.

								; Initial pattern for BornClass.
			ld (ix + Sprite.Pattern), 252		; In IX = Sprite. A = Pattern. MODs: AF, HL, DE. BornClass hasn't hitboxes, so assign pattern directly.


								; Calculate the initial energy.
			ld b, 0					; BC = 00classID.
			ld hl, .ClassEnergies
			add hl, bc
			add hl, bc
			ld a, (hl)
			ld (ix + Sprite.Energy), a		; Sets the initial energy.
			inc hl
			ld a, (hl)
			ld (ix + Sprite.Energy + 1), a



.EscapeInner:		pop de					; Restore D' = Map ram segment.
			exx

			pop hl					; Restore current map tile address.
			pop de					; Restore current map row address.
			pop bc					; Restore horizontal loop count.
			jp .Continue



.ClassEnergies:		DW	65535				; INITIAL LIFE ENERGY CHART.
			DW	65535				; Elevators.
			DW	65535				; Cubes.
			DW	1024				; Turtles.
			DW	1024				; Bees.
			DW	1024 + 512			; Spiders.
			DW	2048				; Frogs.
			DW	2048				; Guards.
			DW	1024				; Dogs.
			DW	8192				; Wizards.
			DW	4096				; Demons.
			DW	1				; .
			DW	1				; .
			DW	1				; .
			DW	1				; .
			DW	1				; .






SubscribeRecentDeath:	ld l, (ix + Sprite.MapRespawnTx)	; Check if can be subscribe in recent death.
			ld h, (ix + Sprite.MapRespawnTy)

			push hl
			inc l					; Selects the next tile of the respawn tile on its right.
			jr z, .Allowed				; Map right border?, allow if there is no next tile on its right.

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld b, a					; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.

			and MapAttribs.MaskMajor
			jr nz, .Allowed				; Allow if the next tile has encoded another feature not related.

			ld a, b
			and MapAttribs.MaskMinor
			jr z, .Allowed				; There is nothing special, don't respawn again (allow the subscription).

			cp $1E					; It means that is a respawn for multiplayer.
			jr nz, .NormalCountDown
								; It is a multiplayer only flag.
			pop hl
			push hl

			inc l
			inc l
			jr z, .Allowed				; Map right border?, allow if there is no next tile on its right.

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld b, a					; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.

			and MapAttribs.MaskMajor
			jr nz, .Allowed				; Allow if the next of the next tile has encoded another feature not related.

			ld a, b
			and MapAttribs.MaskMinor
			jr z, .Allowed				; There is nothing special, don't respawn again (allow the subscription).

.NormalCountDown:	ld a, b					; It decrements the respawn count.
			sub $02
			ld (hl), a

			pop hl

			ret

.Allowed:		ld hl, RecentDeaths + RecentDeathCount * 2 - 3	; In IX = Sprite character address.
			ld de, RecentDeaths + RecentDeathCount * 2 - 1
			ld bc, RecentDeathCount * 2 - 2
			lddr

			pop hl

			ld (RecentDeaths), hl
			ret






TestForRecentDeath:	ld b, RecentDeathCount			; In DE = respawn map location. OUT ZF = 1, exists a recent death.
			ld hl, RecentDeaths

.Loop:			ld a, e
			cp (hl)
			inc hl
			jr nz, .J1

			ld a, d
			cp (hl)
			inc hl
			ret z

			djnz .Loop

			ld a, 255
			and a

			ret
			
.J1:			inc hl
			djnz .Loop

			ld a, 255
			and a

			ret






BuildUpAllPlayerPatterns:					; It is required when resuming a loaded game.
			ld a, (SpritesCount)
			and a
			ret z

			ld b, a

			ld ix, SpritesPool

.CharacterLoop:		ld a, (ix + Sprite.Class)
			inc a					; Class = 255 = player.
			jp nz, .Next				; Jump is most usual.

			push bc
			push ix
			call BuildUpPlayerPatterns
			pop ix
			pop bc

.Next:			ld de, Sprite.SizeOf
			add ix, de

			djnz .CharacterLoop
			ret






BuildUpPlayerPatterns:	ld b, 11				; IN IX = Player.

.PatternLoop:		push bc					; Player set size = 11.

			; /////// naked player.

			ld e, b					; Compressed patterns 0 up to 11 = naked player.
			dec e

			call .Copy				; IN E = source compressed pattern number.

			; ////// dress with shoes.

			pop bc
			push bc

			ld a, (ix + Sprite.LogicArea + 3)	; + 3 bit 1,0 = 00-11 shoes quality.
			and 3
			jr z, .NoShoes

			dec a

			ld e, a
			add a, a
			add a, a
			add a, a
			add a, e
			add a, e
			add a, e
			add a, b
			dec a
			add a, 11				; Shoes pattern base = 11.

			ld e, a					; IN E = source compressed pattern number.
			call .CopyTIMP
.NoShoes:
			; ////// dress with body armour.

			pop bc
			push bc

			ld a, (ix + Sprite.LogicArea + 3)	; + 3 bit 5,4 = 00-11 body armour quality.

			rra
			rra
			rra
			rra
			and 3
			jr z, .NoBodyHelmet

			dec a

			ld e, a
			add a, a
			add a, a
			add a, a
			add a, e
			add a, e
			add a, e
			add a, b
			dec a
			add a, 44				; Body armour pattern base = 44.

			ld e, a					; IN E = source compressed pattern number.
			call .CopyTIMP
.NoBodyHelmet:
			; ////// dress with helmet.

			pop bc
			push bc

			ld a, (ix + Sprite.LogicArea + 3)	; + 3 bit 7,6 = 00-11 helmet quality.
			rlca
			rlca
			and 3
			jr z, .NoHeadHelmet

			dec a

			ld e, a
			add a, a
			add a, a
			add a, a
			add a, e
			add a, e
			add a, e
			add a, b
			dec a
			add a, 77				; Helmet pattern base = 77.

			ld e, a					; IN E = source compressed pattern number.
			call .CopyTIMP
.NoHeadHelmet:		
			; ////// dress with sword.

			pop bc
			push bc

			ld a, (ix + Sprite.LogicArea + 21)	; + 21 = 0-5 sword quality. 0 = no sword.
			and a
			jr z, .NoSword

			dec a

			ld e, a
			add a, a
			add a, a
			add a, a
			add a, e
			add a, e
			add a, e
			add a, b
			dec a
			add a, 110				; Sword pattern base = 110.

			ld e, a					; IN E = source compressed pattern number.
			call .CopyTIMP
.NoSword:
			; ////// Save pattern in sprite pattern table.

			pop bc
			push bc

			ld a, (ix + Sprite.LogicArea + 6)	
			ld e, a
			add a, a
			add a, a
			add a, a
			add a, e
			add a, e
			add a, e
			add a, b
			dec a

			ld e, a
			call .SaveInSpriteTable			; IN E = Destination pattern number in sprite table.

			pop bc
			dec b
			jp nz, .PatternLoop

IFDEF NO_CACHE

			ret

ELSE
		
			ld a, (ix + Sprite.LogicArea + 6)	; IN IX = Player. Reads the player number.
			ld d, a					; This remove this pattern set from the cache.
			sla a
			rla
			rla
			add a, d
			add a, d
			add a, d				; * 11 (player pattern's set size).
			ld d, a					; D = destination pattern number (start of the player's patternset).
			add a, 11
			ld e, a					; First pattern that not belong to this pattern set.

			ld hl, PatternCachePool			; address ZERO aligned, and not bigger than 255.
			ld b, PatternCacheSize

.CacheLoop:		ld a, (hl)				; Reads the pattern number that is stored in this cache's slot.
			cp d
			jr c, .NoThisSet

			cp e
			jr nc, .NoThisSet

			inc l
			inc l
			ld (hl), $FF				; Marks the pattern as expired.

			inc l
			djnz .CacheLoop
			ret

.NoThisSet:		inc l
			inc l
			inc l
			djnz .CacheLoop
			ret

ENDIF



.Copy:			ld a, (PlayerPatternsSegment)		; E = source compressed pattern number.
.P2_1:			call DOS2.CALL.PUT_P2

			ld l, e
			ld h, $40
			add hl, hl
			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

			ld de, DressBuffer

.StreamLoop1:		ld b, (hl)				; Reads length.
			inc hl
			ld c, (hl)				; Reads value.
			inc hl

			ld a, b
			or c
			ret z

.InflateLoop1:		ld a, c
			ld (de), a
			inc de
			djnz .InflateLoop1
			jp .StreamLoop1



.CopyTIMP:		ld a, (PlayerPatternsSegment)		; E = source compressed pattern number.
.P2_2:			call DOS2.CALL.PUT_P2

			ld l, e
			ld h, $40
			add hl, hl
			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

			ld de, DressBuffer

.StreamLoop2:		ld b, (hl)				; Reads length.
			inc hl
			ld c, (hl)				; Reads value.
			inc hl

			ld a, b
			or c
			ret z

			ld a, c
			and a
			jr z, .Transparent2

.InflateLoop2:		ld (de), a
			inc de
			djnz .InflateLoop2
			jp .StreamLoop2

.Transparent2:		ld a, b					; DE = DE + B.
			add a, e
			ld e, a
			ld a, d
			adc a, 0
			ld d, a
			jp .StreamLoop2



.SaveInSpriteTable:	ld a, e					; E = Destination pattern number in sprite table.
			call .GetAddress			; Out DE = address.
			ld hl, DressBuffer
			ld bc, 256
			ldir

			ret



.GetAddress:		ld d, a					; IN A = Pattern number. OUT DE = address.
			xor a
			rl d
			rla
			rl d
			rla

			rr d
			scf
			rr d
			ld e, 0

			ld b, a
			ld a, (SpritesBaseSegment)
			add a, b
.P2_3:			jp DOS2.CALL.PUT_P2






VdpWait:		ld a, 2
			di
			out ($99),a					; select s#2.
			ld a,15+128
			out ($99),a
			in a,($99)
			rra
			ld a,0						; back to s#0, enable ints.
			out ($99),a
			ld a,15+128
			ei
			out ($99),a					; loop if vdp not ready (CE).
			ret nc

			push hl
			ld hl, (DiagnoseVDPBotleneck)
			inc hl
			ld (DiagnoseVDPBotleneck), hl
			pop hl
			jp VdpWait



; ************************************************************
; * MAP LOW LEVEL INSPECTION (MOST OF IT TILE LEVEL)         *
; ************************************************************






GetMapTileNumber:	ld a, (MapBaseSegment)			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld c, a					; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.

			add hl, hl
			ld a, h
			rlca
			rlca
			and 3
			add a, c

.P2_1:			call DOS2.CALL.PUT_P2

			set 7, h
			res 6, h

			ld c, (hl)
			inc hl
			ld a, (hl)				; AC = Tile number.
			ret






GetTileVisualPosition:	push hl					; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld bc, (MapCurrentTileOffsetX)		; Out h l d ce = top / height / left / width for the given tile.
			ld h, l					; It returns the screen visual position, of the inputed map tile coordinates.
			ld l, 0					; Used to redraw when breaking a block, or in the animate tile feature.
			and a
			sbc hl, bc
			ld a, l
			sra h
			rra
			sra h
			rra
			sra h
			rra
			sra h
			rra

			inc h					; HL = signed visual LEFT of the tile.
			jr z, .NegativeNearX			; No jump is most usual, as this routine isn't called with non in screen tiles.
			dec h
			jp z, .InVisualScopeX			; Jump is most usual.

.InvalidateX:		ld c, 0					; X = out of the screen. Set width = 0 for invalidating it.
			ld e, 0
			pop hl
			ret

.InVisualScopeX:	ld d, a
			cp 256-15				; Tests if it needs to be clipped on the right border or not.
			ld e, 16
			jp c, .CalculateY			; Jump is most usual.
								; Partially hidden on the right border.
			xor a
			sub d					; Calculate the visible width.
			ld e, a
			jp .CalculateY

.NegativeNearX:		cp 256-15				; Checks if totally hidden on the left border.
			jr c, .InvalidateX			; No jump is most usual.
								; It is partially hidden on the left border.
			ld l, a
			xor a
			sub l
			ld l, a					; L = width of hidden part.
			ld a, 16
			sub l
			ld e, a					; Width = width of visible part.
			ld d, 0					; Left = 0.

.CalculateY:		pop hl
			ld l, 0

			ld bc, (MapCurrentTileOffsetY)
			and a
			sbc hl, bc
			ld a, l
			sra h
			rra
			sra h
			rra
			sra h
			rra
			sra h
			rra

			inc h
			jr z, .NegativeNearY			; No jump is most usual, as this routine isn't called with non in screen tiles.
			dec h
			jp z, .InVisualScopeY			; Jump is most usual.

.InvalidateY:		ld l, 0					; HEIGHT = 0.
			ret

.InVisualScopeY:	cp 212
			jr nc, .InvalidateY			; No jump is most usual, as this routine isn't called with non in screen tiles.

			cp 212-15				; Checks if it needs to clip on the bottom border or not.
			jp c, .EnsambleOutput
								; Is partially hidden in the bottom border.
			ld l, a
			ld a, 212
			sub l					; Calculate the visible height.
			ld h, l
			ld l, a					; L = visible height.
			ld c, 0
			ret

.NegativeNearY:		cp 256-15				; Checks if totally hidden on the upper border.
			jr c, .InvalidateY
								; It is partially hidden on the upper border.
			ld l, a
			xor a
			sub l
			ld l, a					; L = height of hidden part.
			ld a, 16
			sub l
			ld l, a					; height = height of visible part.
			ld h, 0					; Top = 0.
			ld c, 0
			ret

.EnsambleOutput:	ld h, a
			ld l, 16
			ld c, 0
			ret






IsWall:			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty), D = Oy, E = Ox (range 0-15<<4, 0-15<<4).
								; OUT A = 0 no, <> 0 wall.
			and MapAttribs.MaskTileNumber
			ld b, a					; BC = TileNumber.
			
			ld hl, TileMap.BitCodedWalls_BaseAddress - 1
			xor a					; No wall.
			sbc hl, bc
			ret nc

			ld hl, TileMap.SolidWalls_StartAddress	; atp CF = 1 == TileMap.SolidWalls_BaseAddr - 1.
			sbc hl, bc
			cpl					; Wall.
			ret c
								; It is a bitcoded wall.
			ld l, c
			ld h, b
			ld bc, TileMap.BitCodedWalls_BaseAddress
			sbc hl, bc				; hl = 00000000 - 0 dr dl ur ul 0 0 0.

			xor a

			rl d
			jr c, .DownHalf
								; .UpHalf.
			rl e
			jr c, .UpRight
								; .UpLeft.
			bit 3,l
			ret z
			dec a
			ret

.UpRight:		bit 4,l
			ret z
			dec a
			ret

.DownHalf:		rl e
			jr c, .DownRight
								; .DownLeft.
			bit 5,l
			ret z
			dec a
			ret

.DownRight:		bit 6,l
			ret z
			dec a
			ret






IsFloor:		call GetMapTileNumber			; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
								; OUT A = 0 no, <> 0 Floor, 127 = only floor if tile aligned.
			and MapAttribs.MaskTileNumber
			ld b, a					; BC = TileNumber.
			
			ld hl, TileMap.StandUpFloor_StartAddress - 1
			xor a					; Sets no floor.
			sbc hl, bc
			ret nc

			ld hl, TileMap.BitCodedWalls_BaseAddress; atp CF = 1 == TileMap.BitCodedWalls_BaseAddr - 1.
			ld a, 127				; Sets floor if tile aligned.
			sbc hl, bc
			ret nc

			ld hl, TileMap.SolidWalls_StartAddress	; atp CF = 1 == TileMap.SolidWalls_BaseAddr - 1.
			sbc hl, bc
			ret c
								; It is a bitcoded solid blocks.
			ld l, c
			ld h, b
			ld bc, TileMap.BitCodedWalls_BaseAddress
			sbc hl, bc				; hl = 00000000 - 0 dr dl ur ul 0 0 0.

			xor a

			rl d
			jr c, .DownHalf
								; .UpHalf.
			rl e
			jr c, .UpRight
								; .UpLeft.
			bit 3,l
			ret z
			dec a
			ret

.UpRight:		bit 4,l
			ret z
			dec a
			ret

.DownHalf:		rl e
			jr c, .DownRight
								; .DownLeft.
			bit 5,l
			ret z
			dec a
			ret

.DownRight:		bit 6,l
			ret z
			dec a
			ret






IsKillingMe:		call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
								; OUT A = 0 no, <> 0 Kill.
			and MapAttribs.MaskTileNumber
			ld b, a					; BC = TileNumber.
			
			ld hl, TileMap.KillTiles_StartAddress - 1
			xor a					; No kill.
			sbc hl, bc
			ret nc

			ld hl, TileMap.KillTiles_StopAddress	; atp CF = 1, so counts as TileMap.KillTiles_StopAddress - 1.
			cpl					; Kill.
			sbc hl, bc
			ret nc

			cpl
			ret






IsOnItem:		call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
								; OUT A = 0 nothing, A <> 0 Item.
			and MapAttribs.MaskTileNumber		; OUT C = Item tile number.
			ld b, a	

			ld hl, TileMap.Items_StopAddress - 1
			xor a
			sbc hl, bc
			ret c

			cpl
			ret






IsOnTeleport:		call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
								; OUT A = 0 nothing, A <> 0 Teleport. H L = Ty Tx, C = Map number.
			and MapAttribs.MaskMajor + MapAttribs.MaskMinor
			cp MapAttribs.Major.Special + MapAttribs.Special.TeleTransport
			ld a, 0
			ret nz

			inc hl 					; HL = tile address +1.
			inc hl
			ld a, (hl)				; Reads the lsnX.
			and MapAttribs.MaskMinor
			rra
			ld e, a

			inc hl
			inc hl
			ld a, (hl)				; Reads the msnX.
			and MapAttribs.MaskMinor
			rla
			rla
			rla
			or e
			ld e, a

			inc hl
			inc hl
			ld a, (hl)				; Reads the lsnY.
			and MapAttribs.MaskMinor
			rra
			ld d, a

			inc hl
			inc hl
			ld a, (hl)				; Reads the msnY.
			and MapAttribs.MaskMinor
			rla
			rla
			rla
			or d
			ld d, a

			inc hl
			inc hl
			ld a, (hl)				; Reads the map number.
			and MapAttribs.MaskMinor
			rra
			ld c, a
			ex de, hl

			ld a, 255
			ret






IsOnLadder:		call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
								; OUT A = 0 nothing, A <> 0 Ladder.
			and MapAttribs.MaskTileNumber
			ld b, a

			ld hl, TileMap.ClimbableLadder_StartAddress - 1
			xor a
			sbc hl, bc
			ret nc

			ld hl, TileMap.ClimbableLadder_StopAddress - 1
			and a
			sbc hl, bc
			ret c

			cpl
			ret






FindsSpecialRoom:	ld hl, $8001				; IN D = room special code, OUT H L = Ty/Tx. CF = 1 not found.
			ld e, 0

			ld a, (MapBaseSegment)
			call DOS2.CALL.PUT_P2
			ld a, (hl)

.Loop:			and MapAttribs.MaskMajor
			cp MapAttribs.Major.Special
			jp nz, .IsNot

			ld a, (hl)
			and MapAttribs.MaskMinor
			cp d

			jr z, .IsIt

.IsNot:			inc hl
			inc hl
			ld a, (hl)

			bit 6, h
			jp z, .Loop

			res 6, h
			inc e
			
			ld a, e
			cp 4					; 4 = Map size in ram segments.
			jr z, .NotFound

			ld bc, (MapBaseSegment)
			add a, c
			call DOS2.CALL.PUT_P2

			ld a, (hl)
			jp .Loop

.NotFound:		scf
			ret

.IsIt:			res 7, h				; Remove the page2 addressing.
			rr h
			rr l					; Was 16bits encoding.

			ld a, e					; ty=ty+segment*32.
			add a,a
			add a,a
			add a,a
			add a,a
			add a,a
			add a, h
			ld h, a

			and a
			ret






RelocatePlayersInRoom:	ld e, 3					; Simple search, nothing about scanning for the 17th column or the 14th row of the visual screen.
								; It is because monster room always will has Offsets X & Y = 0.
			ld a, (MapCurrentTileY)
			ld h, a
			ld b, 12

.LoopRow:		ld a, (MapCurrentTileX)
			ld l, a
			ld c, 16

.LoopColumn:		push bc
			push de
			push hl

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
								; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.
			pop hl
			pop de
			pop bc

			and MapAttribs.MaskAttribs
			cp MapAttribs.Major.Special + MapAttribs.Special.RoomRespawn
			jr z, .PosPlayer

.Next:			inc l
			dec c
			jr nz, .LoopColumn

			inc h
			dec b
			jr nz, .LoopRow

			ret

.PosPlayer:		dec e

			push bc
			push de
			push hl

			ld c, e

			call FindPlayerNumber			; IN C = player number. OUT IX = player character pointer, CF = 1 no present.

			pop hl
			jr c, .NotPresent

			ld (ix + Sprite.MapOx), 0
			ld (ix + Sprite.MapTx), l
			ld (ix + Sprite.MapOy), 0
			ld (ix + Sprite.MapTy), h

.NotPresent:		pop de
			pop bc

			ld a, e
			and a
			ret z

			jp .Next






FindsPlayerStartPoint:	ld hl, $8001				; OUT H L = Ty/Tx.
			ld e, 0

			ld a, (MapBaseSegment)
			call DOS2.CALL.PUT_P2

.Loop:			ld a, (hl)
			and MapAttribs.MaskAttribs
			cp MapAttribs.Major.Special + MapAttribs.Special.PlayerRespawn

			jr z, .IsIt				; No jump most usual.

			inc hl
			inc hl

			bit 6, h
			jp z, .Loop

			res 6, h

			inc e
			ld a, e
			cp 4
			jr z, .NotFound

			ld a, (MapBaseSegment)
			add a, e
			call DOS2.CALL.PUT_P2

			jp .Loop

.NotFound:		ld hl, 0
			ret

.IsIt:			res 7, h				; Remove the page2 addressing.
			rr h
			rr l					; Was 16bits encoding.

			ld a, e					; ty=ty+segment*32.
			add a,a
			add a,a
			add a,a
			add a,a
			add a,a
			add a, h
			ld h, a

			ret



; ************************************************************
; * INPUTS                                                   *
; ************************************************************






Stick:			and a					; IN A = 0 keyboard, 1 = joystick port 1, 2 = joystick port 2.
			jr z, .Keyboard				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.

			ld b, $8F

			dec a
			jr z, .JoyPort1

			ld b, $CF				; JoyPort2.

			di

.JoyPort1:		ld a, 15
			out ($A0), a
			ld a, b
			out ($A1), a
			ld a, 14
			out ($A0), a

			nop

			in a, ($A2)				; bit 7 = right, bit 6 = left, bit 5 = down, bit 4 = up.

			ei

			push af
			and $08
			ld b, a
			pop af

			rla
			
			bit 3, a
			jr z, .NoLeft

			set 0, a

.NoLeft:		and $07
			or b

			ret

.Keyboard:		di

			in a, ($AA)
			and $F0
			or 8
			out ($AA), a

			in a, ($A9)

			ei

			and $F0
			rra
			rra
			rra
			rra

			ret






StrigPrimary:		and a					; IN A = 0 keyboard, 1 = joystick port 1 main button, 2 = joystick port 2 main button.
			jr z, .Keyboard				; OUT A = 0 pressed, <> 0 not pressed.

			ld b, $8F

			dec a
			jr z, .JoyPort1

			ld b, $CF				; JoyPort2.

			di

.JoyPort1:		ld a, 15
			out ($A0), a
			ld a, b
			out ($A1), a

			ld a, 14
			out ($A0), a

			nop

			in a, ($A2)

			ei
			and $10

			ret

.Keyboard:		di

			in a, ($AA)
			and $F0
			or 8
			out ($AA), a

			in a, ($A9)

			ei

			and $01

			ret






StrigSecondary:		and a					; IN A = 0 keyboard secondary button, 1 = joystick port 1 secondary button, 2 = joystick port 2 secondary button.
			jr z, .Keyboard				; OUT A = 0 pressed, <> 0 not pressed.

			ld b, $8F

			dec a
			jr z, .JoyPort1

			ld b, $CF				; JoyPort2.

			di

.JoyPort1:		ld a, 15
			out ($A0), a
			ld a, b
			out ($A1), a

			ld a, 14
			out ($A0), a

			nop

			in a, ($A2)

			ei
			and $20

			ret

.Keyboard:		di

			in a, ($AA)
			and $F0
			or 6
			out ($AA), a

			in a, ($A9)

			ei

			and $04

			ret






IsPausePressed:		di					; A = 0 pressed.

			in a, ($AA)
			and $F0
			or 7
			out ($AA), a

			in a, ($A9)

			ei

			and $10					; Bit 4 = STOP key.

			ret






IsF1Pressed:		di					; A = 0 pressed.

			in a, ($AA)
			and $F0
			or 6
			out ($AA), a

			in a, ($A9)

			ei

			and $20					; Bit 5 = F1 key.

			ret






Is123Pressed:		di					; OUT A = 0 pressed, B = number.

			in a, ($AA)
			and $F0					; Row = 0.
			out ($AA), a

			in a, ($A9)

			ei

			ld b, 1
			bit 1, a
			jr z, .P

			ld b, 2
			bit 2, a
			jr z, .P

			ld b, 3
			bit 3, a
			jr z, .P

			ld a, 255
			ret

.P:			xor a
			ret






IsEscapePressed:	di					; A = 0 pressed.

			in a, ($AA)
			and $F0
			or 7
			out ($AA), a

			in a, ($A9)

			ei

			and $04

			ret






IsShiftPressed:		di					; A = 0 pressed.

			in a, ($AA)
			and $F0
			or 6
			out ($AA), a

			in a, ($A9)

			ei

			and $01

			ret


			
; ************************************************************
; * MISC                                                     *
; ************************************************************






OPL4_SetTimer:		ld a, 2					; Sets the timer count.
			out ($C4), a
			push bc
			pop bc
			ld a, 48				; 47.75 was the real value, round up to 48 (for 60hz).
			out ($C5), a

			ld a, 4
			out ($C4), a
			push bc
			pop bc
			ld a, $01				; Enable timer.
			out ($C5), a
			push bc
			pop bc
			ld a, $80				; But, reset by if a previous /Int was already set.
			out ($C5), a
			ret






OPL4_StopTimer:		ld a, 4
			out ($C4), a
			push bc
			pop bc
			xor a					; Disable timer.
			out ($C5), a
			push bc
			pop bc
			ld a, $80				; It resets any previous /INT.
			out ($C5), a
			ret






ISR:			push af
			in a, ($99)
			rla
			jr nc, .OtherInt
								; It is a VDP interrupt.
			ld a, (VBlankCountInFrame)
			inc a
			ld (VBlankCountInFrame), a

.G2_1:			call DOS2.CALL.GET_P2
			push af

			ld a, (ResourcesSegment)
.P2_1:			call DOS2.CALL.PUT_P2

			push bc
			push de
			push hl
			call SFXPlayer
			pop hl
			pop de
			pop bc

			pop af
.P2_2:			call DOS2.CALL.PUT_P2

			pop af
			ei
			reti

.OtherInt:		in a, ($C4)
			rla
			jr nc, .OtherInt2

			ld a, 4					; It must erase the /INT. The problem is, the opl4 chip will lose track of current FMBank and register.
			out ($C4), a
			push bc
			push de
			push hl
			ld a, $81				; Resets the /INT.
			out ($C5), a
								; Now it requires to restore current FM bank and register number.
			ld a, (FMPlayCurrentFMBank)
			and a
			ld a, (FMPlayCurrentFMReg)

			ld c, $C6
			jr nz, .BankC6
			ld c, $C4

.BankC6:		out (c), a

.VGM:			call VGMPlayer
			pop hl
			pop de
			pop bc

.OtherInt2:		pop af
			ei
			reti






HideScreen:		di
			ld a, (BIOS.VAR.RG1SAV)
			and $BF
			ld (BIOS.VAR.RG1SAV), a
			out ($99), a
			ld a, 1 + 128
			out ($99), a
			ei
			ret






ShowScreen:		di
			ld a, (BIOS.VAR.RG1SAV)
			or $40
			ld (BIOS.VAR.RG1SAV), a
			out ($99), a
			ld a, 1 + 128
			out ($99), a
			ei
			ret






FramesPerSecondControl:	ld a, (IsTurboEnabled)			; Speed control. In B = how many Vblanks per frame?.
			and a
			jr nz, .NoWaitNeeded

			ld a, (VBlankCountInFrame)
			cp b
			jr nc, .NoWaitNeeded

.WaitLoop:		halt

			ld a, (VBlankCountInFrame)
			cp b
			jr c, .WaitLoop

.NoWaitNeeded:		call IsShiftPressed			; A = 0 pressed.
			and a

			ld a, 255
			jr z, .TurboEnabled
			xor a

.TurboEnabled:		ld (IsTurboEnabled), a

			ld a ,(VBlankCountInFrame)
			ld (DiagnoseVBlanksCount), a		; Diagnostic variables for helping the development.

			ld hl, (DiagnoseVBlanksPointer)
			ld (hl), a
			inc hl
			ld (DiagnoseVBlanksPointer), hl
			ld de, DiagnoseVblanksList_End
			and a
			sbc hl, de
			jr c, .NoWrap

			ld hl, DiagnoseVblanksList_Start
			ld (DiagnoseVBlanksPointer), hl

.NoWrap:		xor a					; Resets the vblanks counter.
			ld (VBlankCountInFrame), a

			ld hl, (DiagnoseVDPBotleneck)		; Diagnostic variables for helping the development.
			ld (DiagnoseVDPBotleneckPermanent), hl
			ld hl, 0
			ld (DiagnoseVDPBotleneck), hl
			ret






OpenFirstFrame:		ld a, 1					; It make sure it start with the vdp screen unscrolled for all frames.
			ld (ActiveBuffer), a
			call .J1

			xor a
			ld (ActiveBuffer), a

.J1:			call LoadCurrentBufferVars
			xor a
			ld (ScreenCurrentX), a
			ld (ScreenCurrentY), a
			jp SaveCurrentBufferVars






CloseFrame:		call SetUpVdpScrolls
			call SaveCurrentBufferVars

			ld a, (FramesCount)
			inc a
			ld (FramesCount), a

			ld a, (ActiveBuffer)
			and a

			ld a, $1F				; View first buffer, because the second is the active.
			jr z, .SetViewBuffer

			ld a, $3F				; View second buffer, because the first is the active.

.SetViewBuffer:		di
			out ($99), a
			ld a, 2 + 128
			out ($99), a
			ei
			ret






OpenNextFrame:		ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			jp LoadCurrentBufferVars






SetSingleBufferMode:	xor a
			ld (ActiveBuffer), a

			di
			ld a, $1F				; View first buffer, the same as the active.
			out ($99), a
			ld a, 2 + 128
			out ($99), a

			ei
			ret






LoadCurrentBufferVars:	ld a, (ActiveBuffer)
			and a
			jr nz , .Bf1

			ld hl, (Bf0ScreenCurrentX)
			ld (ScreenCurrentX), hl
			ld hl, (Bf0MapCurrentTileOffsetX)
			ld (MapCurrentTileOffsetX), hl
			ld hl, (Bf0MapCurrentTileOffsetY)
			ld (MapCurrentTileOffsetY), hl
			ret

.Bf1:			ld hl, (Bf1ScreenCurrentX)
			ld (ScreenCurrentX), hl
			ld hl, (Bf1MapCurrentTileOffsetX)
			ld (MapCurrentTileOffsetX), hl
			ld hl, (Bf1MapCurrentTileOffsetY)
			ld (MapCurrentTileOffsetY), hl
			ret






SaveCurrentBufferVars:	ld a, (ActiveBuffer)
			and a
			jr nz , .Bf1

			ld hl, (ScreenCurrentX)
			ld (Bf0ScreenCurrentX), hl
			ld hl, (MapCurrentTileOffsetX)
			ld (Bf0MapCurrentTileOffsetX), hl
			ld hl, (MapCurrentTileOffsetY)
			ld (Bf0MapCurrentTileOffsetY), hl
			ret

.Bf1:			ld hl, (ScreenCurrentX)
			ld (Bf1ScreenCurrentX), hl
			ld hl, (MapCurrentTileOffsetX)
			ld (Bf1MapCurrentTileOffsetX), hl
			ld hl, (MapCurrentTileOffsetY)
			ld (Bf1MapCurrentTileOffsetY), hl
			ret






CreateCoinFromMe:	ld a, r					; IN IX = Me.
			rra
			ret nc

			rra
			and $1F

			ld e, a
			ld d, 0
			ld hl, .ValueData
			add hl, de
			ld a, (hl)

			push ix
			push af
			push ix
			call GetFreeSpriteSlot			; OUT A = ID sprite slot, IX = sprite pointer, CF = 1 error.
			pop iy
			pop bc
			jr c, .Esc

			ld (ix + Sprite.LogicArea + 7), 55	; 35 frames to dissapear.
			ld (ix + Sprite.LogicArea + 8), b	; Coin value.
			ld (ix + Sprite.LogicArea + 9), 0

			ld hl, 0
			exx

			ld l, (iy + Sprite.MapOy)		; Coin start position.
			ld h, (iy + Sprite.MapTy)
			ld e, (iy + Sprite.MapOx)
			ld d, (iy + Sprite.MapTx)
			ld c, $15				; Coin Class.

			call SetSprite				; IN A = ID, H L = Ty/Oy, D E = Tx/Ox, C = Class, H' L' = Ty, Tx respawn ID.

			ld a, r
			rra
			jr c, .Left
			ld (ix + Sprite.LogicArea + 4), $FF	; Set right.

.Left:			and $1F
			cp $18
			jr c, .NoDisplacement

			rla
			rla
			rla
			and $F0
			ld (ix + Sprite.LogicArea + 5), a	; Set displacement left or right force.

.NoDisplacement:	ld (ix + Sprite.LogicArea + 3), $B0	; Set jump force.

			ld a, 1
			call SFXRequest

.Esc:			pop ix
			ret

.ValueData:		DB	1,1,1,1,1,1,3,3,3,3,3,5,5,5,7,7,9,9,9,11,11,12,13,13,13,15,17,17,20,25,40,63






PlayersTeleTransport:	ld c, 3					; IN H L = Ty Tx.

.Loop:			push hl
			push bc
			dec c
			call FindPlayerNumber			; IN C = player number. OUT IX = player character pointer, CF = 1 no present.
			pop bc
			pop hl
			jr c, .NotPresent

			ld (ix + Sprite.MapOx), 0
			ld (ix + Sprite.MapOy), 0
			ld (ix + Sprite.MapTx), l
			ld (ix + Sprite.MapTy), h
			inc l

.NotPresent:		dec c
			jr nz, .Loop

			ret






PlayersRespawn:		ld a, (PlayersCount)			; IN HL = Ty/Tx.
			ld b,a
			and a
			ret z

.PlayerLoop:		push bc

			push bc
			push hl

			call GetFreeSpriteSlot			; Reserve the sprite slot for showing the hand weapon.
			ld (ix + Sprite.Class), SpriteClasses.HandWeaponClass

			call GetFreeSpriteSlot
			pop hl
			pop bc

			ld a, b					; Respawn one player next to the other horizontally.
			dec a
			add a, l

			ld (ix + Sprite.MapTx), a
			ld (ix + Sprite.MapRespawnTx), a
			ld (ix + Sprite.MapTy), h
			ld (ix + Sprite.MapRespawnTy), h
			xor a
			ld (ix + Sprite.MapOx), a
			ld (ix + Sprite.MapOy), a

			ld (ix + Sprite.Class), SpriteClasses.PlayerClass

			push hl					; Reads the current control set for the current player, and configure the sprite logic information.
			ld hl, InputControlPlayer0
			ld a, (PlayersCount)
			sub b
			ld (ix + Sprite.LogicArea + 6), a	; Sets the player number.
			add a, l
			ld l, a
			jr nc, .JJJ2
			inc h
.JJJ2:
			ld a, (hl)
			ld (ix + Sprite.LogicArea + 2), a	; Sets the input control for this player.

			ld a, (ix + Sprite.LogicArea + 6)
			ld hl, Vaults_Start
			add a, h
			ld h, a
			ld (ix + Sprite.LogicArea + 14), l
			ld (ix + Sprite.LogicArea + 15), h

			xor a					; Initial sprite pattern.
			ld (ix + Sprite.PatternFlip), a		; = 0 = No flipped.
			call SetSpritePattern

			ld (ix + Sprite.Energy), 0
			ld (ix + Sprite.Energy + 1), 128	; Initial energy = 32768.

			ld a, (ix + Sprite.LogicArea + 6)
			ld e, a
			ld d, 0
			ld hl, OverlayShowing0
			add hl, de
			ld (hl), 0				; Show next weapon (that initially is empty).

			call UpdateOverlayGrabItem.In

			call BuildUpPlayerPatterns		; IN IX = player. It sets the patterns for this player.

			pop hl
			pop bc

			djnz .PlayerLoop
			ret






BackUpPlayersPosition:	ld c, 3

.Loop:			push bc
			dec c
			call FindPlayerNumber			; IN C = player number. OUT IX = player character pointer, CF = 1 no present.
			pop bc
			jr c, .NotPresent

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld (ix + Sprite.Reserved + 0), l
			ld (ix + Sprite.Reserved + 1), h

			ld l, (ix + Sprite.MapOy)
			ld h, (ix + Sprite.MapTy)
			ld (ix + Sprite.Reserved + 2), l
			ld (ix + Sprite.Reserved + 3), h

			xor a					; As this is used when changing the map location to/from a room, mark the sprite as not painted.
			ld (ix + Sprite.Bf0ScreenWidth), a
			ld (ix + Sprite.Bf1ScreenWidth), a
			ld (ix + Sprite.ScreenWidth), a

.NotPresent:		dec c
			jr nz, .Loop

			ret






RestorePlayersPosition:	ld c, 3

.Loop:			push bc
			dec c
			call FindPlayerNumber			; IN C = player number. OUT IX = player character pointer, CF = 1 no present.
			pop bc
			jr c, .NotPresent

			ld l, (ix + Sprite.Reserved + 0)
			ld h, (ix + Sprite.Reserved + 1)
			ld (ix + Sprite.MapOx), l
			ld (ix + Sprite.MapTx), h

			ld l, (ix + Sprite.Reserved + 2)
			ld h, (ix + Sprite.Reserved + 3)
			ld (ix + Sprite.MapOy), l
			ld (ix + Sprite.MapTy), h

			xor a					; As this is used when changing the map location to/from a room, mark the sprite as not painted.
			ld (ix + Sprite.Bf0ScreenWidth), a
			ld (ix + Sprite.Bf1ScreenWidth), a
			ld (ix + Sprite.ScreenWidth), a

.NotPresent:		dec c
			jr nz, .Loop

			ret






GetBestFrameMapScope:	ld a, h					; In HL = TyTx player respawn starting point. Out HL = TyTx BestFrameMapScope.
			sub 8
			jr nc, .NoTopEdge

			xor a

.NoTopEdge:		ld h, a

			ld a, l
			sub 8
			jr nc, .NoLeftEdge

			xor a

.NoLeftEdge:		ld l, a
			ret






SetThisFrameMapScope:	ld a, h					; In HL = TyTx.
			ld (MapCurrentTileY), a
			ld (IdealMapTileY), a
			ld a, l
			ld (MapCurrentTileX), a
			ld (IdealMapTileX), a
			xor a
			ld (MapCurrentTileOffsetX), a
			ld (IdealMapTileOffsetX), a
			ld (MapCurrentTileOffsetY), a
			ld (IdealMapTileOffsetY), a
			ret






FindPlayerNumber:	ld a, (SpritesCount)			; IN C = player number. OUT IX = player character pointer, CF = 1 no present.
			and a
			scf
			ret z

			ld b, a

			ld ix, SpritesPool

.CharacterLoop:		push bc

			ld a, (ix + Sprite.Class)		; Player class = 255.
			inc a
			jr nz, .NextCharacter

			ld a, (ix + Sprite.LogicArea + 6)
			cp c
			jr nz, .NextCharacter

			pop bc
			ret

.NextCharacter:		ld bc, Sprite.SizeOf
			add ix, bc

			pop bc
			djnz .CharacterLoop

			scf
			ret






DivDE_BC:	        ld a, 16				; DE = DE / BC. HL = rest.
			ld hl, 0

			jp .In

.DivLoop:		add hl, bc				; Restore.

.DivLoop1:		dec a
			ret z

.In:			sla e
			rl d
			adc hl, hl

			sbc hl, bc
			jr c, .DivLoop

			inc e

			jp .DivLoop1






PrintText:		xor a					; Resets the buyed something flag.
			ld (JustBuyedSomething), a		; IN HL = Null terminated string script pointer.
								; IN IX = Optional: Player pointer (if using player related functions).
			ld b, 8					; IN D E = Y/X current screen coordinates for text cursor.

								; IN STRING special chr codes.
								; CHR 1 = Give directly item tile number, next byte: item tile number.
								; CHR 2 = TextBox build for option A, next bytes: X, Y, Width, Height where the option box will draw, item tile number, count, cost.
								; CHR 3 = TextBox build for option B, next bytes: X, Y, Width, Height where the option box will draw, item tile number, count, cost.
								; CHR 4 = TextBox build for Cancel option, next bytes: X, Y, Width, Height, where the cancel box will draw.
								; CHR 5 = Print tile, next word: is tile number.
								; CHR 6 = TextBox build for text, next bytes: X, Y, Width, Height where the box will draw.
								; CHR 7 = TextBox erase, Next bytes: X, Y, Width, Height, where the TextBox was.
								; CHR 8 = Locate, next bytes: X, Y (textbox offsets).
								; CHR 9 = Wait until a button press (reading pointed player controls).
								; CHR 10 = Ignored.
								; CHR 11 = Run option script.
								; CHR 12 = Box cls.
								; CHR 13 = CrCl (in the textbox).
								; CHR 14 = Jump to address if buyed, next word: address.
								; CHR 15 = Escapes if buyed.
								; CHR 16 = TextBox build autosized.
								; CHR 17 = Text auto center.
								; CHR 18 = TextBox auto build for option A, next bytes: item tile number, count, cost.
								; CHR 19 = TextBox auto build for option B, next bytes: item tile number, count, cost.
								; CHR 20 = TextBox auto build for Cancel option.
								; CHR 21 = Cursor backspace 4 pixels.
								; CHR 22 = Cursor up 4 pixels.
								; CHR 23 = Print masked tile, next word: is tile number.

.PrintString:		ld a, (ResourcesSegment)		; Make sure the resource segment is visible, include after printing a tile.
.P2_1:			call DOS2.CALL.PUT_P2

			ld a, (hl)				; IN HL = Null terminated string pointer, IN D E = Y / X screen coordinates.
			cp 24					; B = current text line height.
			jp c, .SpecialCode

			ld a, e					; First, checks if it fits in the current line.
			add a, 8
			ld c, a
			jr c, .CrCl

			ld a, (TextStopMargin)
			cp c
			jp nc, .PrintChar

.CrCl:			ld a, (TextLeftMargin)			; No fits, next line.
			ld e, a

			ld a, d
			add a, b
			ld d, a

			ld b, 8

.PrintChar:		push hl					; Prints the character.
			push de
			push bc

			ld a, b					; Vertical centering.
			cp 10
			jp c, .PrintChar2

			sub 8
			rrca
			and $7F

			add a, d
			ld d, a

.PrintChar2:		ld a, (hl)
			call .PrintChr
			pop bc
			pop de
			pop hl

			inc hl

			ld a, e					; Move the cursor.
			add a, 8
			ld e, a

			ld a, b
			cp 8
			jp nc, .PrintString

			ld b, 8
			jp .PrintString

.PrintChr:		sub ' '					; IN A = char. IN D E = Y / X.
			ret c

.PrintChr_Int:		ld b, a					; In D E = Top / Left. In A = chr.

			ld a, d
			cp 212					; Checks if not hidden in the bottom border.
			ret nc

			call VdpWait

			ld l, b					; Calculates the pattern pointer.
			ld h, 0
			add hl, hl
			add hl, hl
			add hl, hl
			ld bc, CharSetPatterns
			add hl, bc

			ld b, 8

.NextRow:		push hl
			push de
			push bc

			ld a, (hl)				; Reads the pattern line.
			ld b, 8

.PatternLoop:		rla
			jr nc, .NextDot

			push af
			push de
			push bc

			ld b, 255				; Text ForeColor.
			call .DotInt

			pop bc
			pop de
			pop af

.NextDot:		inc e
			jr z, .DotHidRightBorder		; Escapes if the next dot is out of the screen by the right border.
			djnz .PatternLoop

.DotHidRightBorder:	pop bc
			pop de
			pop hl

			inc d					; Escapes if hidden in the bottom border.
			ld a, d
			cp 212
			ret nc

			inc hl
			djnz .NextRow
			ret

.Dot:			call VdpWait				; In D E = Top / Left, B = color.

.DotInt:		di					; In D E = Top / Left, B = color.

			ld a, 36 
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, (ScreenCurrentX)
			add a, e
			out ($9B), a
			xor a
			out ($9B), a				; DX = Left box.

			ld a, (ScreenCurrentY)
			add a, d
			out ($9B), a
			ld a, (ActiveBuffer)
			out ($9B), a				; DY = ActiveBuffer + Top box.

			ld a, 44 
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, b
			out ($9B), a

			xor a
			out ($9B), a				; DIX/DIY Down-Right.

			ld a, $50
			out ($9B), a				; sends HMMV command #46.

			ei
			ret

.Box:			ld a, l					; In D E = Left/Width, H L = Top/Height.
			cp 6					; Checks for minimun size.
			ret c
			ld a, e
			cp 6
			ret c

			ld b, 255				; Draws white borders.
			push hl
			push de
			call  .FillBorder
			pop de
			pop hl

			ld b, 0					; Draws inner black box.
			inc d
			inc h
			dec e
			dec e
			dec l
			dec l

.Fill:			ld a, d					; In D E = Left/Width, H L = Top/Height. IN/OUT B = Color.
			add a, e				; Checks if the box overflows the screen.
			jr z, .NoClipX
			jr nc, .NoClipX

			xor a
			sub d
			ld e, a					; Width = 256 - Left.

.NoClipX:		ld a, h
			add a, l
			jr c, .ClipY

			cp 212					; Is Top + Height < 212?.
			jr c, .NoClipY

.ClipY:			ld a, h					; If Top = out of screen, escapes.
			cp 212
			ret nc

			ld a, 212
			sub h
			ld l, a					; Height = 212 - Top.

.NoClipY:		ld a, (ScreenCurrentX)			; In D E = Left/Width, H L = Top/Height. IN/OUT B = Color.
			add a, d				; Add the scroll offsets.
			ld d, a

			ld a, (ScreenCurrentY)
			add a, h
			ld h, a

			ld a, d					; Test For Split X.
			add a, e
			jr z, .NoSplitX
			jr nc, .NoSplitX
								; Split X.
			xor a
			sub d
			push de
			push hl
			ld e, a
			call .NoSplitX
			pop hl
			pop de

			xor a
			sub d
			ld d, a

			ld a, e
			sub d
			ld e, a
			ld d, 0

.NoSplitX:		ld a, h					; In D E = Left/Width, H L = Top/Height. IN/OUT B = Color.
			add a, l				; Test For Split Y.
			jr z, .NoSplitY
			jr nc, .NoSplitY
								; Split Y.
			xor a
			sub h
			push de
			push hl
			ld l, a
			call .NoSplitY
			pop hl
			pop de

			xor a
			sub h
			ld h, a

			ld a, l
			sub h
			ld l, a
			ld h, 0

.NoSplitY:		call VdpWait				; In D E = Left/Width, H L = Top/Height. IN/OUT B = Color.

			di

			ld a, 36 
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, d
			out ($9B), a
			xor a
			out ($9B), a				; DX = Left box.

			ld a, h
			out ($9B), a
			ld a, (ActiveBuffer)
			out ($9B), a				; DY = ActiveBuffer + Top box.

			ld a, e
			out ($9B), a
			xor a
			out ($9B), a				; NX = Width.

			ld a, l
			out ($9B), a
			xor a
			out ($9B), a				; NY = Height.

			ld a, b
			out ($9B), a

			xor a
			out ($9B), a				; DIX/DIY Down-Right.

			ld a, $C0
			out ($9B), a				; sends HMMV command #46.

			ei
			ret

.FillBorder:		push hl					; In H L = Top / Height, D E = Left / Width. B = Color.

			push de
			inc h
			dec l
			dec l
			push hl
			ld e, 1
			call .Fill				; Left Border.
			pop hl
			pop de
			push de
			ld a, e
			add a, d
			jr z, .FillBorderRightOk
			jr c, .NoRightBorder
.FillBorderRightOk:	dec a
			ld d, a
			ld e, 1
			call .Fill				; Right Border.
.NoRightBorder:		pop de

			pop hl

			push hl
			inc d
			dec e
			dec e
			push de
			ld l, 1
			call .Fill				; Top Border.
			pop de
			pop hl

			ld a, l
			add a, h
			jr z, .FillBorderBottomOk
			ret c					; Escapes if bottom border is out of view.
.FillBorderBottomOk:	dec a
			ld h, a
			ld l, 1
			jp .Fill				; Bottom Border.

.SpecialCode:		push hl					; This push must be poped in the jumped code.
			ld hl, .ChrJumpTable
			add a, a
			add a, l
			ld l, a
			jr nc, .JJJ3
			inc h
.JJJ3:
			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a
			push hl
			ret

.ChrJumpTable:		DW .Null
			DW .GiveItemTileNumber
			DW .BuildOptionA
			DW .BuildOptionB
			DW .BuildCancel
			DW .PrintTile
			DW .BoxBuild
			DW .BoxRestore
			DW .Locate
			DW .WaitForPressButton
			DW .Nothing
			DW .RunOptions
			DW .Cls
			DW .CrClChar
			DW .JumpIfBuyed
			DW .EscIfBuyed
			DW .BoxAutoBuild
			DW .TextAutoCenter
			DW .BoxAutoBuildOptionA
			DW .BoxAutoBuildOptionB
			DW .BoxAutoBuildCancel
			DW .Backspace4
			DW .CursorUp4
			DW .PrintMaskedTile

.Null:			pop hl
			ret

.Backspace4:		pop hl
			inc hl

			ld a, e
			sub 4
			ld e, a

			jp .PrintString

.CursorUp4:		pop hl
			inc hl

			ld a, d
			sub 4
			ld d, a

			jp .PrintString

.GiveItemTileNumber:	pop hl
			inc hl

			push bc
			push de
			push hl
			push ix
			ld c, (hl)
			call SaveItemInVault
			pop ix
			pop hl
			pop de
			pop bc

			inc hl
			jp .PrintString

.BoxAutoBuildOptionA:	pop hl
			inc hl

			push hl
			inc hl
			inc hl
			inc hl
			call .GetStringSize
			pop hl

			ld a, c					; Adds space for box borders.
			add a, 6
			ld c, a
			ld a, b
			add a, 6
			ld b, a

			ld a, 128
			sub c
			jr nc, .OpANoWider

			ld c, 128

.OpANoWider:		ld a, 128
			sub c
			rrca
			and $7F
			ld e, a
			ld (OptionALeft), a

			ld a, 53
			sub b
			jr nc, .OpANoHeighter

			ld b, 53

.OpANoHeighter:		ld a, 53
			sub b
			rrca
			and $7F
			add a, 106
			ld d, a
			ld (OptionATop), a

			ld a, c
			ld (OptionAWidth), a

			ld a, b
			ld (OptionAHeight), a

			ld a, (hl)
			inc hl
			ld (OptionAItemTileNumber), a

			ld a, (hl)
			inc hl
			ld (OptionACount), a

			ld a, (hl)
			inc hl
			ld (OptionACost), a

			jp .BuildBox				; D E = Top / Left. B C = Height / Width.

.BoxAutoBuildOptionB:	pop hl
			inc hl

			push hl
			inc hl
			inc hl
			inc hl
			call .GetStringSize
			pop hl

			ld a, c					; Adds space for box borders.
			add a, 6
			ld c, a
			ld a, b
			add a, 6
			ld b, a

			ld a, 128
			sub c
			jr nc, .OpBNoWider

			ld c, 128

.OpBNoWider:		ld a, 128
			sub c
			rrca
			and $7F
			add a, 128
			ld e, a
			ld (OptionBLeft), a

			ld a, 53
			sub b
			jr nc, .OpBNoHeighter

			ld b, 53

.OpBNoHeighter:		ld a, 53
			sub b
			rrca
			and $7F
			add a, 106
			ld d, a
			ld (OptionBTop), a

			ld a, c
			ld (OptionBWidth), a

			ld a, b
			ld (OptionBHeight), a

			ld a, (hl)
			inc hl
			ld (OptionBItemTileNumber), a

			ld a, (hl)
			inc hl
			ld (OptionBCount), a

			ld a, (hl)
			inc hl
			ld (OptionBCost), a

			jp .BuildBox				; D E = Top / Left. B C = Height / Width.

.BoxAutoBuildCancel:	pop hl
			inc hl

			push hl
			call .GetStringSize
			pop hl

			ld a, c					; Adds space for box borders.
			add a, 6
			ld c, a
			ld a, b
			add a, 6
			ld b, a

			xor a
			sub c
			rrca
			and $7F
			ld e, a
			ld (OptionCancelLeft), a

			ld a, 53
			sub b
			jr nc, .OpCancelNoHeighter

			ld b, 53

.OpCancelNoHeighter:	ld a, 53
			sub b
			rrca
			and $7F
			add a, 106 + 53
			ld d, a
			ld (OptionCancelTop), a

			ld a, c
			ld (OptionCancelWidth), a

			ld a, b
			ld (OptionCancelHeight), a

			jp .BuildBox				; D E = Top / Left. B C = Height / Width.

.BoxAutoBuild:		pop hl
			inc hl

			push hl
			call .GetStringSize
			pop hl

			ld a, c					; Adds space for box borders.
			add a, 6
			ld c, a
			ld a, b
			add a, 6
			ld b, a

			ld a, 212 / 2				; Centers it in the upper half screen.
			sub b

			rrca
			and $7F
			ld d, a

			xor a
			sub c

			rrca
			and $7F
			ld e, a

			jp .BuildBox				; D E = Top / Left. B C = Height / Width.

.GetStringSize:		ld bc, 0				; Current size.
			ld de, $0800				; Current used size in the current line.

.GetStringLoop:		ld a, (hl)
			inc hl
			cp 5
			jr z, .GetStringTile
			cp 23
			jr z, .GetStringTile
			cp 13
			jr z, .GetStringCrCl
			cp 10
			jr z, .GetStringLoop
			cp 17
			jr z, .GetStringLoop
			cp 21
			jr z, .GetStringSpaceBack
			cp 22
			jr z, .GetStringSpaceUp

			cp 32
			jr c, .GetStringStop			; Escapes in the first non printable character.

			ld a, e					; First, check if it fits.
			add a, 8
			jr c, .GetStringCrCl

			cp 240					; Maximun allowed width.
			jr z, .GetStringChrAdvCursor
			jr c, .GetStringChrAdvCursor

.GetStringCrCl:		ld a, e
			cp c
			jr c, .GetStringNoWider
			
			ld c, e

.GetStringNoWider:	ld a, b
			add a, d
			ld b, a

			ld d, 8
			ld e, 0

.GetStringChrAdvCursor: ld a, e
			add a, 8
			ld e, a

			jp .GetStringLoop

.GetStringSpaceBack:	ld a, e
			sub 4
			ld e, a

			jp .GetStringLoop

.GetStringSpaceUp:	ld a, b
			sub 4
			ld b, a

			jp .GetStringLoop

.GetStringStop:		ld a, b
			add a, d
			ld b, a

			ld a, e
			cp c
			ret c

			ld c, e

			ret

.GetStringTile:		inc hl					; Skips the tile number.
			inc hl

			ld a, e					; First, check if it fits.
			add a, 20
			jr c, .GetStringCrCl2

			cp 240
			jr z, .GetStringChrAdvCursor2
			jr c, .GetStringChrAdvCursor2

.GetStringCrCl2:	ld a, e
			cp c
			jr c, .GetStringNoWider2

			ld c, e

.GetStringNoWider2:	ld a, b
			add a, d
			ld b, a

			ld d, 8
			ld e, 0

.GetStringChrAdvCursor2:ld a, e
			add a, 20
			ld e, a

			ld a, d
			cp 20
			jr nc, .GetStringLoop

			ld d, 20
			jp .GetStringLoop

.GetStringSizeSingleLine:
			ld c, 0

.GetStringSingleLineLoop:
			ld a, (hl)
			inc hl
			cp 5
			jr z, .GetStringSingleLineTile
			cp 23
			jr z, .GetStringSingleLineTile
			cp 13
			ret z
			cp 10
			jr z, .GetStringSingleLineLoop
			cp 21
			jr z, .GetStringSingleLineSpaceBack

			cp 32
			ret c

			ld a, c
			add a, 8
			jr c, .GetStringSingleLineNoFits

			cp 240					; Maximun allowed width.
			ld c, a
			ret z
			jr c, .GetStringSingleLineLoop

.GetStringSingleLineNoFits:
			sub 8					; The last one didn't fit, remove it.
			ld c, a
			ret

.GetStringSingleLineSpaceBack:

			ld a, c
			sub 4
			ld c, a

			jr .GetStringSingleLineLoop

.GetStringSingleLineTile:
			inc hl
			inc hl

			ld a, c
			add a, 20
			jr c, .GetStringSingleLineNoFits2

			cp 240					; Maximun allowed width.
			ld c, a
			ret z
			jr c, .GetStringSingleLineLoop

.GetStringSingleLineNoFits2:
			sub 20					; The last one didn't fit, remove it.
			ld c, a
			ret
			
.TextAutoCenter:	pop hl
			inc hl

			push bc
			push hl
			call .GetStringSizeSingleLine		; OUT C = width.
			pop hl

			ld a, (TextLeftMargin)			; Calculates the space for the text within the box.
			ld b, a
			ld a, (TextStopMargin)
			sub b

			cp c
			jr nc, .Centering

			pop bc
			ld a, (TextLeftMargin)
			ld e, a

			jp .PrintString

.Centering:		sub c					; A = BoxTextSpace - TextWidth.

			rrca
			and $7F
			ld e, a

			ld a, (TextLeftMargin)
			add a, e
			ld e, a

			pop bc
			jp .PrintString

.BuildOptionA:		pop hl
			inc hl

			ld a, (hl)
			inc hl
			ld (OptionALeft), a
			ld e, a

			ld a, (hl)
			inc hl
			ld (OptionATop), a
			ld d, a

			ld a, (hl)
			inc hl
			ld (OptionAWidth), a
			ld c, a

			ld a, (hl)
			inc hl
			ld (OptionAHeight), a
			ld b, a

			ld a, (hl)
			inc hl
			ld (OptionAItemTileNumber), a

			ld a, (hl)
			inc hl
			ld (OptionACount), a

			ld a, (hl)
			inc hl
			ld (OptionACost), a

.BuildBox:		ld a, e					; D E = Top / Left. B C = Height / Width.
			ld (CurrentBoxLeft), a
			inc a
			inc a
			inc a
			ld (TextLeftMargin), a

			ld a, e
			add a, c
			dec a
			dec a
			dec a
			ld (TextStopMargin), a

			push hl
			push de
			push bc

			ld h, d
			ld l, b
			ld d, e
			ld e, c

			ld a, h
			inc a
			inc a
			inc a
			ld (TextTopMargin), a
								; It draws the box with its border.
			call .Box				; In D E = Left/Width, H L = Top/Height.

			pop bc
			pop de
			pop hl

			ld a, d
			ld (CurrentBoxTop), a
			ld a, c
			ld (CurrentBoxWidth), a
			ld a, b
			ld (CurrentBoxHeight), a

			inc d
			inc d
			inc d
			inc e
			inc e
			inc e

			ld b, 0
			jp .PrintString

.BuildOptionB:		pop hl
			inc hl

			ld a, (hl)
			inc hl
			ld (OptionBLeft), a
			ld e, a

			ld a, (hl)
			inc hl
			ld (OptionBTop), a
			ld d, a

			ld a, (hl)
			inc hl
			ld (OptionBWidth), a
			ld c, a

			ld a, (hl)
			inc hl
			ld (OptionBHeight), a
			ld b, a

			ld a, (hl)
			inc hl
			ld (OptionBItemTileNumber), a

			ld a, (hl)
			inc hl
			ld (OptionBCount), a

			ld a, (hl)
			inc hl
			ld (OptionBCost), a

			jp .BuildBox

.BuildCancel:		pop hl
			inc hl

			ld a, (hl)
			inc hl
			ld (OptionCancelLeft), a
			ld e, a

			ld a, (hl)
			inc hl
			ld (OptionCancelTop), a
			ld d, a

			ld a, (hl)
			inc hl
			ld (OptionCancelWidth), a
			ld c, a

			ld a, (hl)
			inc hl
			ld (OptionCancelHeight), a
			ld b, a

			jp .BuildBox

.BoxBuild:		pop hl					; Draws a box.
			inc hl

			ld e, (hl)
			inc hl
			ld d, (hl)
			inc hl
			ld c, (hl)
			inc hl
			ld b, (hl)
			inc hl

			jp .BuildBox

.BoxRestore:		pop hl
			inc hl

			ld e, (hl)
			inc hl
			ld d, (hl)
			inc hl
			ld c, (hl)
			inc hl
			ld b, (hl)
			inc hl

			push ix
			push hl						
			push de
			push bc
			ex de, hl
			ld e, c
			ld d, b
			ld c, 0
			call MapRender				; IN H L = Top/Height, D CE = Left/Width, coordinates are in real visual screen (no vdp command coordinates).
			pop bc
			pop de
			pop hl
			pop ix

			jp .PrintString

.Locate:		pop hl
			inc hl

			ld e, (hl)
			inc hl
			ld a, (TextLeftMargin)
			add a, e
			ld e, a

			ld d, (hl)
			inc hl
			ld a, (TextTopMargin)
			add a, d
			ld d, a

			ld b, 0

			jp .PrintString

.WaitForPressButton:	ld a, (ix + Sprite.LogicArea + 2)
			and 3
			push bc
			push de
			call StrigPrimary
			pop de
			pop bc
			and a
			jr z, .WaitForPressButton

.PressLoop:		ld a, (ix + Sprite.LogicArea + 2)
			and 3
			push bc
			push de
			call StrigPrimary
			pop de
			pop bc
			and a
			jr nz, .PressLoop

.Nothing:		pop hl
			inc hl

			jp .PrintString

.CrClChar:		pop hl
			inc hl

			ld a, (TextLeftMargin)
			ld e, a

			ld a, d
			add a, b
			ld d, a

			ld b, 8

			jp .PrintString

.Cls:			pop hl
			inc hl

			ld a, (CurrentBoxLeft)
			ld e, a
			ld a, (CurrentBoxTop)
			ld d, a
			ld a, (CurrentBoxWidth)
			ld c, a
			ld a, (CurrentBoxHeight)
			ld b, a
			jp .BuildBox

.PrintMaskedTile:	ld a, 255
			jp .PrintTileInt

.PrintTile:		xor a
.PrintTileInt:		ld (PrintTextTileMask), a

			pop hl
			inc hl

			ld a, e					; First, checks if it fits in the current line.
			add a, 20
			ld c, a
			jr c, .CrCl2

			ld a, (TextStopMargin)
			cp c
			jp nc, .RenderTile

.CrCl2:			ld a, (TextLeftMargin)			; No fits, next line.
			ld e, a

			ld a, d
			add a, b
			ld d, a

			ld b, 8

.RenderTile:		push bc					; Prints the tile.
			ld c, (hl)
			inc hl
			ld b, (hl)
			inc hl

			push hl
			push de					; E = X, D = Y, BC = TileNumber to print.

			inc e					; 2 pixels Left margin.
			inc e
			inc d					; 2 pixels up margin.
			inc d

			call .PaintTile

			pop de
			pop hl
			pop bc

			ld a, e					; Move the cursor.
			add a, 20
			ld e, a

			ld a, b
			cp 20
			jp nc, .PrintString

			ld b, 20
			jp .PrintString

.PaintTile:		ld h, c					; First set the RAM segment where the tile pattern is.
			ld a, b					; IN BC = Tile number, D E = TOP / LEFT.
			and MapAttribs.MaskTileNumber

			rl h
			rla
			rl h
			rla

			rr h
			scf
			rr h

			ld l, a
			ld a, (TileBaseSegment)
			add a, l
.P2_2:			call DOS2.CALL.PUT_P2

			ld l, 0
								; Ok, HL = tile texture pointer. D E = Top / Left screen coordinates.
			ld a, e					; Checks for clipping.
			cp 256 - 16 + 1
			ld c, 16				; Tile width.

			jr c, .TileNoClipX

			xor a
			sub e
			ld c, a

.TileNoClipX:		ld a, d
			cp 212 - 16 + 1
			ld b, 16				; Tile height.

			jr c, .TileNoClipY

			ld a, 212
			sub d
			ld b, a

.TileNoClipY:		ld a, (ScreenCurrentX)			; Adds the scroll registers.
			add a, e
			ld e, a

			ld a, (ScreenCurrentY)
			add a, d
			ld d, a

			add a, b				; TopScrolled + Height
			jr z, .NoYSplit
			jr nc, .NoYSplit
								; Y Split.
			xor a
			sub d					; 256 - Top = height for first pass.

			push hl

			push bc
			push de
			ld b, a					; New height for upper block.

			call .NoYSplit

			pop de
			pop bc

			pop hl

			xor a
			sub d					; 256 - Top = height in the first pass.
			ld d, a

			add a,a					; Offsets in the pattern texture Y coordinate.
			add a,a
			add a,a
			add a,a
			add a, l
			ld l, a

			ld a, b
			sub d					; Second pass height = Total height - first pass height.
			ld b, a
			ld d, 0					; Top = 0.

.NoYSplit:		ld a, e					; Test for X splits.
			add a, c
			jr z, .NoXSplit
			jr nc, .NoXSplit
								; X Split.
			xor a
			sub e					; 256 - Left = width for first pass.

			push hl

			push bc
			push de
			ld c, a					; New width for left block.

			call .NoXSplit

			pop de
			pop bc

			pop hl

			xor a
			sub e					; 256 - Left = width in the first pass.
			ld e, a

			add a, l				; Offsets in the pattern texture X coordinate.
			ld l, a

			ld a, c
			sub e					; Second pass width = Total width - first pass width.
			ld c, a
			ld e, 0					; Left = 0.

.NoXSplit:		call VdpWait				; HL = tile texture pointer. D E = Top / Left screen coordinates, B C = Height / Width.

			di

			ld a, 36 
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, e
			out ($9B), a
			xor a
			out ($9B), a				; DX = Left box.

			ld a, d
			out ($9B), a
			ld a, (ActiveBuffer)
			out ($9B), a				; DY = ActiveBuffer + Top box.

			ld a, c
			out ($9B), a
			xor a
			out ($9B), a				; NX = Texture Width.

			ld a, b
			out ($9B), a
			xor a
			out ($9B), a				; NY = Texture Height.

			ld a, (PrintTextTileMask)
			and a
			ld a, (hl)
			jr z, .NoTileMask1
			and a
			jr z, .NoTileMask1
			ld a, $92

.NoTileMask1:		out ($9B), a

			xor a
			out ($9B), a				; DIX/DIY Down-Right.

			ld a, $B8
			out ($9B), a				; sends HMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ei
			push hl
			push bc

			inc hl
			jp .LoopIn

.RowLoop:		push hl
			push bc

.DotLoop:		ld a, (PrintTextTileMask)
			and a
			ld a, (hl)
			jr z, .NoTileMask2
			and a
			jr z, .NoTileMask2
			ld a, $92

.NoTileMask2:		inc hl
			out ($9B), a

.LoopIn:		dec c
			jr nz, .DotLoop

			pop bc
			pop hl
			ld a, l
			add a, $10
			ld l, a

			djnz .RowLoop

			ret

.JumpIfBuyed:		pop hl
			inc hl

			inc hl
			inc hl

			ld a, (JustBuyedSomething)
			and a
			jp z, .PrintString

			dec hl
			dec hl

			push de
			ld e, (hl)
			inc hl
			ld d, (hl)
			ex de, hl
			pop de

			jp .PrintString

.EscIfBuyed:		pop hl
			inc hl

			ld a, (JustBuyedSomething)
			and a
			ret nz

			jp .PrintString

.RunOptions:		ld a, (ix + Sprite.LogicArea + 2)
			and 3
			call StrigPrimary
			and a
			jr z, .RunOptions				; Hold until the primary button is released.

			ld iyh, 0					; Start option is cancel box.

.ShowCursor:		call .LoadOption				; It shows the cursor.
			ld iyl, $FF
			call .DrawOptionCursor

.RunOptionHoldLoop:	ld a, (ix + Sprite.LogicArea + 2)		; Test if stick is now moved.
			and 3
			call Stick					; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.
			and $0F
			cp $0F
			jr nz, .RunOptionHoldLoop

.RunOptionWaitLoop:	ld a, (ix + Sprite.LogicArea + 2)		; Test if stick is now moved.
			and 3
			call Stick					; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.

			bit 0, a
			jr z, .Left
			bit 3, a
			jr z, .Right

			ld a, (ix + Sprite.LogicArea + 2)		; Test if button is now pressed.
			and 3
			call StrigPrimary
			and a
			jr nz, .RunOptionWaitLoop
									; Button was pressed.
			call .TestIfCanBuy				; It can buy the item?.
			jr nc, .Buy

			ld a, 2
			call SFXRequestShield

			jr .RunOptionWaitLoop

.Left:			ld a, 3
			call SFXRequestShield

			call .LoadOption				; It erases the cursor.
			ld iyl, $0
			call .DrawOptionCursor
	
			ld a, iyh
			and a
			jr z, .Left2

			dec iyh
			jr .ShowCursor

.Left2:			ld iyh, 2
			jr .ShowCursor

.Right:			ld a, 3
			call SFXRequestShield

			call .LoadOption				; It erases the cursor.
			ld iyl, $0
			call .DrawOptionCursor

			ld a, iyh
			cp 2
			jr z, .Right2

			inc iyh
			jr .ShowCursor

.Right2:		ld iyh, 0
			jr .ShowCursor

.Buy:			pop hl						; It is buy or cancel.
			inc hl

			ld a, iyh					; Just canceled.
			and a
			jr z, .SetCancel

			dec a
			jr z, .BuyA

			ld a, (OptionBCount)				; BuyB.
			ld b, a
			ld a, (OptionBItemTileNumber)
			ld c, a
			jr .BuyCont

.BuyA:			ld a, (OptionACount)
			ld b, a
			ld a, (OptionAItemTileNumber)
			ld c, a

.BuyCont:		push bc

			ld a, 4
			call SFXRequestShield

			pop bc

.BuyLoop:		push bc
			push hl
			push ix
			push iy
			call SaveItemInVault
			pop iy
			pop ix
			pop hl
			pop bc

			djnz .BuyLoop

			push hl
			call .CashCoins
			pop hl

			ld a, $FF
			ld (JustBuyedSomething), a
			jp .PrintString

.SetCancel:		ld a, 17
			call SFXRequestShield

			jp .PrintString

.CashCoins:		ld a, iyh
			dec a

			ld l, (ix + Sprite.LogicArea + 8)		; Cash Coins B.
			ld h, (ix + Sprite.LogicArea + 9)

			ld a, (OptionACost)

			jr z, .CCA

			ld a, (OptionBCost)

.CCA:			ld e, a
			ld d, 0

			and a
			sbc hl, de
			ld (ix + Sprite.LogicArea + 8), l
			ld (ix + Sprite.LogicArea + 9), h
			ret

.TestIfCanBuy:		ld a, iyh					; OUT CF = 1 can't buy it.
			and a
			ret z

			dec a
			jr z, .TCBA

			ld a, (ix + Sprite.LogicArea + 9)		; Test Can Buy B.
			and a
			ret nz

			ld a, (OptionBCost)
			ld l, a
			ld a, (ix + Sprite.LogicArea + 8)
			sub l
			ret

.TCBA:			ld a, (ix + Sprite.LogicArea + 9)		; Test Can Buy A.
			and a
			ret nz

			ld a, (OptionACost)
			ld l, a
			ld a, (ix + Sprite.LogicArea + 8)
			sub l
			ret

.LoadOption:		ld a, iyh					; IN IYH = Option in selection, OUT DEBC = TOP/LEFT/HEIGHT/WIDTH.
			and a
			jr z, .LOCancel

			dec a
			jr z, .LOA

			ld a, (OptionBLeft)				; LOB.
			ld e, a
			ld a, (OptionBTop)
			ld d, a
			ld a, (OptionBWidth)
			ld c, a
			ld a, (OptionBHeight)
			ld b, a
			ret

.LOA:			ld a, (OptionALeft)
			ld e, a
			ld a, (OptionATop)
			ld d, a
			ld a, (OptionAWidth)
			ld c, a
			ld a, (OptionAHeight)
			ld b, a
			ret

.LOCancel:		ld a, (OptionCancelLeft)
			ld e, a
			ld a, (OptionCancelTop)
			ld d, a
			ld a, (OptionCancelWidth)
			ld c, a
			ld a, (OptionCancelHeight)
			ld b, a
			ret

.DrawOptionCursor:	push hl						; IN DEBC = TOP/LEFT/HEIGHT/WIDTH, IYL = Color.
			push de
			push bc

			ld h, d
			ld l, b
			ld d, e
			ld e, c

			inc h
			inc d
			dec l
			dec l
			dec e
			dec e

			ld b, iyl
			call .FillBorder				; In H L = Top / Height, D E = Left / Width. IN/OUT = Color.

			pop bc
			pop de
			pop hl
			ret
















; ************************************************************
; * SENSOR TESTS FOR BEHAVIOUR AND MAP NAVIGATION            *
; ************************************************************






TestIfIsRoomDoor:	ld l, (ix + Sprite.MapTx)		; Test if the player is on a room door. IN IX = player. OUT Z = is on room door, B = Map MSB.
			ld h, (ix + Sprite.MapTy)
			ld a, h
			and 1
			rr l
			rla
			rr l
			rla
			ld l, (ix + Sprite.MapTx)
			ld (RoomDoorSubClass), a		; Save subclass of the door.

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld b, a					; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.

			and MapAttribs.MaskMajor
			cp MapAttribs.Major.RoomDoor
			ret z

			ld a, (ix + Sprite.MapOx)
			add a, $E0
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a
			ld h, (ix + Sprite.MapTy)

			push hl
			ld a, h
			and 1
			rr l
			rla
			rr l
			rla
			ld (RoomDoorSubClass), a		; Save subclass of the door.
			pop hl

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld b, a					; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.

			and MapAttribs.MaskMajor
			cp MapAttribs.Major.RoomDoor
			ret






GetNearestCharacterTrayectory:					; IN IX = who is asking. ZF = 1 no characters present. DE HL = trayectory distance (positive right/bottom) X / Y tttttttt pppp 0000 format.
			ld iy, SpritesPool			; This routine is used to setup the fireball trayectory. Executed one time only per fireball launch.
			ld a, (SpritesCount)

			ld e, (ix + Sprite.MapOx)
			ld d, (ix + Sprite.MapTx)
			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)

			exx
			ld bc, $FFFF
			ld de, 0
			exx

.Loop:			push af

			ld a, (iy + Sprite.Class)
			cp SpriteClasses.CharactersStartClass
			jr c, .Next
			cp SpriteClasses.CharactersStopClass
			jr nc, .Next

			ld l, (iy + Sprite.MapOx)		; It is a character. Not a player, not a weapon, not a ghost.
			ld h, (iy + Sprite.MapTx)

			and a
			sbc hl, de				; hl = characterX - askingX.
			sra h
			rr l

			push hl					; Save the difference.
			exx
			pop hl
			exx

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)

			and a
			sbc hl, bc
			sra h
			rr l

			bit 7, h				; Distance = ABS(diffY) + ABS(diffX).
			jr z, .YPositive

			ld a, l
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

.YPositive:		exx

			bit 7, h
			jr z, .XPositive

			ld a, l
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

.XPositive:		exx

			ld a, l
			exx
			add a, l
			ld l, a
			exx
			ld a, h
			exx
			adc a, h
			ld h, a

			and a
			sbc hl, bc
			jr nc, .FarAway

			add hl, bc				; Restore.

			ld c, l
			ld b, h
			push iy
			pop de					; Save the selected character index.

.FarAway:		exx

.Next:			push bc
			ld bc, Sprite.SizeOf
			add iy, bc
			pop bc

			pop af
			dec a
			jp nz, .Loop

			exx
			ld a, c
			and b
			cp $FF
			ret z

			push de
			pop iy					; Restore pointer of the selected nearest character.

			exx
			ld l, (iy + Sprite.MapOx)		; DE = Sprite who is asking.
			ld h, (iy + Sprite.MapTx)

			and a
			sbc hl, de

			ex de, hl

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)

			and a
			sbc hl, bc

			ld a, 255
			and a
			ret






GetNearestPlayerTrayectory:					; IN IX = who is asking. ZF = 1 no characters present. DE HL = trayectory distance (positive right/bottom) X / Y tttttttt pppp 0000 format.
			ld iy, SpritesPool			; This routine is used to setup the fireball trayectory. Executed one time only per fireball launch.
			ld a, (SpritesCount)

			ld e, (ix + Sprite.MapOx)
			ld d, (ix + Sprite.MapTx)
			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)

			exx
			ld bc, $FFFF
			ld de, 0
			exx

.Loop:			push af

			ld a, (iy + Sprite.Class)
			inc a					; PlayerClass = 255.
			jr nz, .Next

			ld l, (iy + Sprite.MapOx)		; It is a character. Not a player, not a weapon, not a ghost.
			ld h, (iy + Sprite.MapTx)

			and a
			sbc hl, de				; hl = characterX - askingX.
			sra h
			rr l

			push hl					; Save the difference.
			exx
			pop hl
			exx

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)

			and a
			sbc hl, bc
			sra h
			rr l

			bit 7, h				; Distance = ABS(diffY) + ABS(diffX).
			jr z, .YPositive

			ld a, l
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

.YPositive:		exx

			bit 7, h
			jr z, .XPositive

			ld a, l
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

.XPositive:		exx

			ld a, l
			exx
			add a, l
			ld l, a
			exx
			ld a, h
			exx
			adc a, h
			ld h, a

			and a
			sbc hl, bc
			jr nc, .FarAway

			add hl, bc				; Restore.

			ld c, l
			ld b, h
			push iy
			pop de					; Save the selected character index.

.FarAway:		exx

.Next:			push bc
			ld bc, Sprite.SizeOf
			add iy, bc
			pop bc

			pop af
			dec a
			jp nz, .Loop

			exx
			ld a, c
			and b
			cp $FF
			ret z

			push de
			pop iy					; Restore pointer of the selected nearest character.

			exx
			ld l, (iy + Sprite.MapOx)		; DE = Sprite who is asking.
			ld h, (iy + Sprite.MapTx)

			and a
			sbc hl, de

			ex de, hl

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)

			and a
			sbc hl, bc

			ld a, 255
			and a
			ret






TestForAnyCharacterNear:ld iy, SpritesPool			; Except Me, and it must be a character, not a weapon or player. IN IX = character that is asking. H L = relative player position (-) = player is top/left, (+) = player is down/right. ZF = 1 safe, ZF = 0 danger detected.
			ld a, (SpritesCount)			; OUT H L = in in pixel relative position 8 bits signed.
			ld bc, Sprite.SizeOf			; This routine is used only by weapons right now.
			exx

			ld e, (ix + Sprite.MapOx)
			ld d, (ix + Sprite.MapTx)
			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)

.Loop:			push af

			ld a, (iy + Sprite.Class)
			cp SpriteClasses.CharactersStartClass
			jr c, .Next
			cp SpriteClasses.CharactersStopClass
			jr nc, .Next

; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			;ld a, iyl				; As now is only used by weapon, we can neglect this part.
			;cp ixl
			;jp nz, .NotMe

			;ld a, iyh
			;cp ixh
			;jr z, .Next
; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

.NotMe:			ld l, (iy + Sprite.MapOx)		; It is a character.
			ld h, (iy + Sprite.MapTx)

			and a
			sbc hl, de				; hl = characterX - askingX.
			ld a, l
			sra h
			rra
			sra h
			rra

			inc h					; First check MSB. Valid is $00 or $FF.
			jr z, .XNegative			; Usual is 50%/50%.
			dec h
			jr nz, .Next				; Usual is 50%/50%.
								; It is positive.
			cp 20*4
			jp nc, .Next				; Jump is most usual.
			jp .DoY

.XNegative:		cp 256- (16+4-1)*4
			jp c, .Next				; Jumps if far enough. Jump is most usual.

.DoY:			ex af, af'				; Save the X difference.

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)

			and a
			sbc hl, bc				; hl = characterY - askingY.
			ld a, l
			sra h
			rra
			sra h
			rra

			inc h					; First check MSB. Valid is $00 or $FF.
			jr z, .YNegative			; Usual is 50%/50%.
			dec h
			jr nz, .Next				; Usual is 50%/50%.
								; It is positive.
			cp 20*4
			jp nc, .Next
			jp .DoExit

.YNegative:		cp 256- (16+4-1)*4
			jp c, .Next				; Jumps if far enough. Jump is most usual.

.DoExit:		ld h, a
			sra h	; This short division relies on the limit 20*4 = 80 = not using the bit 7, so the bit 7 will be considered the sign.
			sra h
			ex af, af'
			ld l, a
			sra l
			sra l

			pop af

			xor a
			dec a					; ZF = 0.
			ret

.Next:			exx
			add iy, bc
			exx

			pop af
			dec a
			jp nz, .Loop
			ret					; ZF = 1.






TestForPlayerNearLargeBottomScope:
			ld iy, SpritesPool			; IN IX = character that is asking. H L = relative player position (-) = player is top/left, (+) = player is down/right. ZF = 1 safe, ZF = 0 danger detected.
			ld a, (SpritesCount)			; OUT H L = in in pixel relative position 8 bits signed.
			ld bc, Sprite.SizeOf
			exx

			ld e, (ix + Sprite.MapOx)
			ld d, (ix + Sprite.MapTx)
			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)

.Loop:			push af

			ld a, (iy + Sprite.Class)
			inc a					; PlayerClass = 255.
			jp nz, .Next				; Jump is most usual.

			ld l, (iy + Sprite.MapOx)		; It is player class.
			ld h, (iy + Sprite.MapTx)

			sbc hl, de				; hl = playerX - askingX.
			ld a, l
			sra h
			rra
			sra h
			rra

			inc h					; First check MSB. Valid is $00 or $FF.
			jr z, .XNegative			; Usual is 50%/50%.
			dec h
			jr nz, .Next				; Usual is 50%/50%.
								; It is positive.
			cp 20*4
			jp nc, .Next				; Jump is most usual.
			jp .DoY

.XNegative:		cp 256- (16+4-1)*4
			jp c, .Next				; Jumps if far enough. Jump is most usual.

.DoY:			ex af, af'				; Save the X difference.

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)

			and a
			sbc hl, bc				; hl = playerY - askingY.
			ld a, l
			sra h
			rra
			sra h
			rra

			inc h
			dec h
			jp nz, .Next				; Jump is most usual.

			cp 40*4
			jr nc, .Next				; No jump is most usual.

.DoExit:		sra h
			rra
			sra a	; <--- short as MSB will be wasted.
			ld h, a
			ex af, af'
			ld l, a
			sra l
			sra l

			pop af

			xor a
			dec a					; ZF = 0.
			ret

.Next:			exx
			add iy, bc
			exx

			pop af
			dec a
			jr nz, .Loop
			ret					; ZF = 1.






TestForPlayerNear:	ld iy, SpritesPool			; IN IX = character that is asking (can't be a player). H L = relative player position (-) = player is top/left, (+) = player is down/right. ZF = 1 safe, ZF = 0 danger detected.
			ld a, (SpritesCount)			; OUT H L = in in pixel relative position 8 bits signed. OUT IY = player pointer.
			ld bc, Sprite.SizeOf
			exx
	
			ld e, (ix + Sprite.MapOx)
			ld d, (ix + Sprite.MapTx)
			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)

.Loop:			push af

			ld a, (iy + Sprite.Class)
			inc a					; PlayerClass = 255.
			jp nz, .Next				; Jump is most usual.

			ld l, (iy + Sprite.MapOx)		; It is player class.
			ld h, (iy + Sprite.MapTx)

			sbc hl, de				; hl = playerX - askingX.
			ld a, l
			sra h
			rra					; 31/-32 pixels difference in LSB.
			sra h
			rra

			inc h					; First check MSB. Valid is $00 or $FF.
			jr z, .XNegative			; Usual is 50%/50%.
			dec h
			jr nz, .Next				; Usual is 50%/50%.
								; It is positive.
			cp 20*4
			jp nc, .Next				; Jump is most usual.
			jp .DoY

.XNegative:		cp 256- (16+4-1)*4
			jp c, .Next				; Jumps if far enough. Jump is most usual.

.DoY:			ex af, af'				; Save the X difference.

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)

			and a
			sbc hl, bc				; hl = playerY - askingY.
			ld a, l
			sra h
			rra
			sra h
			rra

			inc h					; First check MSB. Valid is $00 or $FF.
			jr z, .YNegative			; Usual is 50%/50%.
			dec h
			jr nz, .Next				; Usual is 50%/50%.
								; It is positive.
			cp 20*4
			jp nc, .Next
			jp .DoExit

.YNegative:		cp 256- (16+4-1)*4
			jp c, .Next				; Jumps if far enough. Jump is most usual.

.DoExit:		ld h, a
			sra h	; This short division relies on the limit 20*4 = 80 = not using the bit 7, so the bit 7 will be considered the sign.
			sra h
			ex af, af'
			ld l, a
			sra l
			sra l

			pop af

			xor a
			dec a					; ZF = 0.
			ret

.Next:			exx
			add iy, bc
			exx

			pop af
			dec a
			jr nz, .Loop
			ret					; ZF = 1.






TestForKillingMe:	ld c, 0					; OUT ZF = 1 ok, ZF = 0 killing me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.

			ld a, (ix + Sprite.MapOy)
			add a, $20
			ld d, a
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a

			call .HorizontalCheck
			ret nz

			ld a, d
			add a, $C0
			ld d, a
			jr nc, .JJJ4
			inc h
.JJJ4:
.HorizontalCheck:	ld a, (ix + Sprite.MapOx)
			add a, $20				; +2 pixels.
			ld e, a
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

			push bc
			push de
			push hl
			call IsKillingMe			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ex af, af'
			ld a, c
			ex af, af'
			pop hl
			pop de
			pop bc
			and a					; OUT A = 0 no, <> 0 kill.
			ret nz

			inc c
			ld a, e
			add a, $C0				; +12 pixels.
			ld e, a
			jr nc, .JJJ5
			inc l
.JJJ5:
			push bc
			push de
			push hl
			call IsKillingMe			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ex af, af'
			ld a, c
			ex af, af'
			pop hl
			pop de
			pop bc
			and a					; OUT A = 0 no, <> 0 kill.
			ret nz

			inc c
			xor a
			ret






TestForFalling:		call .Wrap				; IX = who asks. OUT Z can fall, BC = distance to the floor.
			ret nz

								; Until now, it can fall. ZF = 1 = CAN FALL.

			ld a, (ix + Sprite.Class)		; If who is asking is an elevator, don't checks on boxes or other lifts, because it can crush boxes or others lifts.
			and a
			ret z
			dec a					; Classes 0 or 1 are elevators. It must escape with ZF = 1.
			ret z



			ld hl, SpacingObjectsList

			exx
			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld e, (ix + Sprite.MapOy)
			ld d, (ix + Sprite.MapTy)
			exx

.ObjectLoop:		ld a, (hl)
			ld iyl, a
			inc hl
			or (hl)
			jr z, .FinalResult			; Escape if no more objects to check for spacing.

			ld a, (hl)
			ld iyh, a
			inc hl

			exx					; Hide current result.
			ld l, (iy + Sprite.MapOx)		; Test for X alignment, if -16 > alignment < 16 then it is aligned enought.
			ld h, (iy + Sprite.MapTx)		; As coordinated format are << 4. So test for if -256 > alignment < 256 (map format).
			sbc hl, bc
			ld a, h
			and a
			jr z, .CheckYAxis			; No jump is most usual.
			inc a
			jp nz, .NextObjectExx			; Jump is most usual.
			or l
			jr z, .NextObjectExx			; No jump is most usual. Object not aligned because it is -256, it is completely at his left.

.CheckYAxis:		ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)
			sbc hl, de
			jr z, .NextObjectExx			; Same vertical, this object can't prevent to fall. No jump is most usual.
			ld a, h
			and a
			jr z, .CantFall				; No jump is most usual. If overlap partially below = don't Fall, it is a final result.
			dec a
			jp nz, .NextObjectExx			; Jump is most usual. Don't test on very far objects. Its scope is from 0 to 15 pixels away.

			dec h					; Move coordinate to the bottom logic edge of the sprite box.
			exx

			cp b					; Checks if it is the nearest relevant object.

			exx
			jp c, .SetAsNear			; Jump is most usual.
			jr nz, .NextObjectExx
			ld a, l
			exx

			cp c
			jr nc, .ObjectLoop

			exx
.SetAsNear:		push hl					; It is near relevant.
			exx
			pop bc

			jp .ObjectLoop

.NextObjectExx:		exx
			jp .ObjectLoop

.FinalResult:		ld a, c					; Checks if distance = 0, so don't fall.
			or b
			jr z, .CantFall

			xor a
			ret

.CantFall:		dec a
			ret



.Wrap:			ld d, (ix + Sprite.MapOy)		; OUT ZF = 1 must fall, BC = distance to ground in pixels << 4 (range from 0 up to 16 pixels << 4).
			ld h, (ix + Sprite.MapTy)
			inc h					; +16 pixels. First row of pixels below the sprite.
			bit 7, h
			ret nz					; Escape with ZF = 0 if map overflow.

			call .HorizontalCheck			; OUT A = 0 & Z no floor, <> 0 & NZ Floor, A = 127 & NZ floor if tile aligned.
			ld bc, 0
			jr nz, .CheckIfAlignedRequired
								; Can fall, because there is no floor or not aligned.
.NextScope:		ld a, (ix + Sprite.MapOy)
			ld c, a
			or $70
			sub c
			add a, $10
			ld c, a
			ld b, 0					; BC = space that can fall.

			push bc

			ld a, (ix + Sprite.MapOy)
			add a, $80
			ld d, a
			ld a, (ix + Sprite.MapTy)
			adc a, 1
			ld h, a					; +24 pixels.
			bit 7, h
			jr nz, .Escape				; Escape with ZF = 0 if map overflow.

			call .HorizontalCheck			; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			pop bc
			jr nz, .CanFall				; Even if here is floor, as before was tested some space for fall, it jumps with a "Can Fall".

			ld a, c
			add a, $80
			ld c, a
			jr nc, .JJJ6
			inc b
.JJJ6:
.CanFall:		xor a
			ret

.Escape:		pop bc
			ret

.CheckIfAlignedRequired:cp 127
			ret nz					; Escape if solid floor.

			ld a, (ix + Sprite.MapOy)		; 127 = Aligned to tile if required for floor.
			and $F0
			jr nz, .NextScope			; If not aligned continue with the next scope check.

			dec a					; NZ for indicating no fall because it is aligned.
			ret

.HorizontalCheck:	ld a, (ix + Sprite.MapOx)		; OUT A = 0 & Z no floor, <> 0 & NZ Floor, A = 127 & NZ floor if tile aligned.
			add a, $20				; +2 pixels.
			ld e, a
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

			push de
			push hl
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			pop de
			cp 127
			jr z, .IfTileAlignedFrom1
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret nz

			ld a, e
			add a, $60				; +6 pixels.
			ld e, a
			jr nc, .JJJ7
			inc l
.JJJ7:
			push de
			push hl
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			pop de
			cp 127
			jr z, .IfTileAlignedFrom2
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret nz

			ld a, e
			add a, $50				; +5 pixels, totalling +13 pixels. So, pixels under test is --T- ---- T--- -T-- of the below row.
			ld e, a
			jr nc, .JJJ8
			inc l
.JJJ8:
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret

.IfTileAlignedFrom1:	ld a, e
			add a, $60				; +6 pixels.
			ld e, a
			jr nc, .JJJ9
			inc l
.JJJ9:
			push de
			push hl
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			pop de
			cp 127
			jr z, .IfTileAlignedFrom2
			and a
			ret nz

.IfTileAlignedFrom2:	ld a, e
			add a, $50				; +5 pixels, totalling +13 pixels. So, pixels under test is --T- ---- T--- -T-- of the below row.
			ld e, a
			jr nc, .JJJ10
			inc l
.JJJ10:
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret nz

			ld a, 127				; Previously was known in this line is "floor if tile aligned", so, set it.
			and a
			ret






TestForFallingAccurate:	call .Wrap				; IX = who asks. OUT Z can fall, BC = distance to the floor.
			ret nz

								; Until now, it can fall. ZF = 1 = CAN FALL.

			ld a, (ix + Sprite.Class)		; If who is asking is an elevator, don't checks on boxes or other lifts, because it can crush boxes or others lifts.
			and a
			ret z
			dec a					; Classes 0 or 1 are elevators. It must escape with ZF = 1.
			ret z



			ld hl, SpacingObjectsList

			exx
			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld e, (ix + Sprite.MapOy)
			ld d, (ix + Sprite.MapTy)
			exx

.ObjectLoop:		ld a, (hl)
			ld iyl, a
			inc hl
			or (hl)
			jr z, .FinalResult			; Escape if no more objects to check for spacing.

			ld a, (hl)
			ld iyh, a
			inc hl

			exx					; Hide current result.
			ld l, (iy + Sprite.MapOx)		; Test for X alignment, if -16 > alignment < 16 then it is aligned enought.
			ld h, (iy + Sprite.MapTx)		; As coordinated format are << 4. So test for if -256 > alignment < 256 (map format).
			sbc hl, bc
			ld a, h
			and a
			jr z, .CheckYAxis			; No jump is most usual.
			inc a
			jp nz, .NextObjectExx			; Jump is most usual.
			or l
			jr z, .NextObjectExx			; No jump is most usual. Object not aligned because it is -256, it is completely at his left.

.CheckYAxis:		ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)
			sbc hl, de
			jr z, .NextObjectExx			; Same vertical, this object can't prevent to fall. No jump is most usual.
			ld a, h
			and a
			jr z, .CantFall				; No jump is most usual. If overlap partially below = don't Fall, it is a final result.
			dec a
			jp nz, .NextObjectExx			; Jump is most usual. Don't test on very far objects. Its scope is from 0 to 15 pixels away.

			dec h					; Move coordinate to the bottom logic edge of the sprite box.
			exx

			cp b					; Checks if it is the nearest relevant object.

			exx
			jp c, .SetAsNear			; Jump is most usual.
			jr nz, .NextObjectExx
			ld a, l
			exx

			cp c
			jr nc, .ObjectLoop

			exx
.SetAsNear:		push hl					; It is near relevant.
			exx
			pop bc

			jp .ObjectLoop

.NextObjectExx:		exx
			jp .ObjectLoop

.FinalResult:		ld a, c					; Checks if distance = 0, so don't fall.
			or b
			jr z, .CantFall

			xor a
			ret

.CantFall:		dec a
			ret



.Wrap:			ld d, (ix + Sprite.MapOy)		; OUT ZF = 1 must fall, BC = distance to ground in pixels << 4 (range from 0 up to 16 pixels << 4).
			ld h, (ix + Sprite.MapTy)
			inc h					; +16 pixels. First row of pixels below the sprite.
			bit 7, h
			ret nz					; Escape with ZF = 0 if map overflow.

			call .HorizontalCheck			; OUT A = 0 & Z no floor, <> 0 & NZ Floor, A = 127 & NZ floor if tile aligned.
			ld bc, 0
			jr nz, .CheckIfAlignedRequired
								; Can fall, because there is no floor or not aligned.
.NextScope:		ld a, (ix + Sprite.MapOy)
			ld c, a
			or $70
			sub c
			add a, $10
			ld c, a
			ld b, 0					; BC = space that can fall.

			push bc

			ld a, (ix + Sprite.MapOy)
			add a, $80
			ld d, a
			ld a, (ix + Sprite.MapTy)
			adc a, 1
			ld h, a					; +24 pixels.
			bit 7, h
			jr nz, .Escape				; Escape with ZF = 0 if map overflow.

			call .HorizontalCheck			; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			pop bc
			jr nz, .CanFall				; Even if here is floor, as before was tested some space for fall, it jumps with a "Can Fall".

			ld a, c
			add a, $80
			ld c, a
			jr nc, .JJJ6
			inc b
.JJJ6:
.CanFall:		xor a
			ret

.Escape:		pop bc
			ret

.CheckIfAlignedRequired:cp 127
			ret nz					; Escape if solid floor.

			ld a, (ix + Sprite.MapOy)		; 127 = Aligned to tile if required for floor.
			and $F0
			jr nz, .NextScope			; If not aligned continue with the next scope check.

			dec a					; NZ for indicating no fall because it is aligned.
			ret

.HorizontalCheck:	ld e, (ix + Sprite.MapOx)		; OUT A = 0 & Z no floor, <> 0 & NZ Floor, A = 127 & NZ floor if tile aligned.
			ld l, (ix + Sprite.MapTx)

			push de
			push hl
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			pop de
			cp 127
			jr z, .IfTileAlignedFrom1
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret nz

			ld a, e
			add a, $80				; +6 pixels.
			ld e, a
			jr nc, .JJJ7
			inc l
.JJJ7:
			push de
			push hl
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			pop de
			cp 127
			jr z, .IfTileAlignedFrom2
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret nz

			ld a, e
			add a, $70				; +5 pixels, totalling +15 pixels. So, pixels under test is T--- ---- T--- ---T of the below row.
			ld e, a
			jr nc, .JJJ8
			inc l
.JJJ8:
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret

.IfTileAlignedFrom1:	ld a, e
			add a, $80				; +6 pixels.
			ld e, a
			jr nc, .JJJ9
			inc l
.JJJ9:
			push de
			push hl
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			pop de
			cp 127
			jr z, .IfTileAlignedFrom2
			and a
			ret nz

.IfTileAlignedFrom2:	ld a, e
			add a, $70				; +5 pixels, totalling +13 pixels. So, pixels under test is --T- ---- T--- -T-- of the below row.
			ld e, a
			jr nc, .JJJ10
			inc l
.JJJ10:
			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 no, <> 0 Floor, = 127 floor if tile aligned.
			ret nz

			ld a, 127				; Previously was known in this line is "floor if tile aligned", so, set it.
			and a
			ret






TestForNextFallingLeft:	ld d, (ix + Sprite.MapOy)		; OUT ZF = 1 will fall.
			ld h, (ix + Sprite.MapTy)
			inc h					; +16 pixels. First row of pixels below the sprite.
			bit 7, h
			jr nz, .MapOverflow			; Escape with ZF = 0 if map overflow.

			ld a, (ix + Sprite.MapOx)
			sub a, $40				; +2 pixels.
			ld e, a
			ld a, (ix + Sprite.MapTx)
			sbc a, 0
			ld l, a
			jr c, .MapOverflow

			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 no, <> 0 Floor.
			ret

.MapOverflow:		xor a
			ret






TestForNextFallingRight:ld d, (ix + Sprite.MapOy)		; OUT ZF = 1 will fall.
			ld h, (ix + Sprite.MapTy)
			inc h					; +16 pixels. First row of pixels below the sprite.
			bit 7, h
			jr nz, .MapOverflow

			ld a, (ix + Sprite.MapOx)
			add a, $40				; +2 pixels.
			ld e, a
			ld a, (ix + Sprite.MapTx)
			adc a, 1
			ld l, a
			jr c, .MapOverflow

			call IsFloor				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 no, <> 0 Floor.
			ret

.MapOverflow:		xor a
			ret






TestForClimbUp:		ld a, (ix + Sprite.MapOy)		; OUT ZF = 1 can climb.
			sub $20					; = Climb speed.
			ld a, (ix + Sprite.MapTy)
			sbc a, 0
			ld h, a
			ret c					; If CF = 1 then ZF = 0.

			ld a, (ix + Sprite.MapOx)
			add a, $20
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a
			ret c					; If CF = 1 then ZF = 0.

			push hl
			call IsOnLadder				; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			and a					; OUT A = 0 nothing, A <> 0 Ladder.
			jr z, .NoLadder

			ld a, (ix + Sprite.MapOx)
			add a, $D0
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a
			ret c					; If CF = 1 then ZF = 0.

			call IsOnLadder				; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 nothing, A <> 0 Ladder.
			jr z, .NoLadder

			xor a
			ret

.NoLadder:		xor a
			dec a
			ret






TestForClimbDown:	ld a, (ix + Sprite.MapOy)		; OUT ZF = 1 can climb.
			add a, $10
			ld a, (ix + Sprite.MapTy)
			adc a, 1
			ld h, a
			ret c					; If CF = 1 then ZF = 0.
			bit 7, h
			ret nz

			ld a, (ix + Sprite.MapOx)
			add a, $20
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a
			ret c					; If CF = 1 then ZF = 0.

			push hl
			call IsOnLadder				; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			and a					; OUT A = 0 nothing, A <> 0 Ladder.
			jr z, .NoLadder

			ld a, (ix + Sprite.MapOx)
			add a, $D0
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a
			ret c					; If CF = 1 then ZF = 0.

			call IsOnLadder				; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 nothing, A <> 0 Ladder.
			jr z, .NoLadder

			xor a
			ret

.NoLadder:		xor a
			dec a
			ret






TestForBreakableFloor:	ld h, (ix + Sprite.MapTy)		; OUT ZF = 1 breakable. E = 1 by just walking on.
			inc h					; +16 pixels. First row of pixels below the sprite.
			bit 7, h
			ret nz

			ld a, (ix + Sprite.MapOx)
			add a, $80				; +8 pixels X, just below.
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld b, a

			and MapAttribs.MaskMajor		; OUT AC = Tile number.
			cp MapAttribs.Major.AutoBreakable	; It is breakable by just walking on?.
			ld e, 1
			ret z

			cp MapAttribs.Major.BreakableToBlack	; It is breakable by hitting it with the sword?.
			ld e, 0
			ret z
			cp MapAttribs.Major.BreakableToBlue	; It is breakable by hitting it with the sword?.
			ret






BreakingFloor:		ld h, (ix + Sprite.MapTy)		; IN B = power of the hit (ignored if autobreakable). OUT ZF = 1 just broke.
			inc h					; +16 pixels. First row of pixels below the sprite.
			bit 7, h
			ret nz

			ld a, (ix + Sprite.MapOx)
			add a, $80				; +8 pixels X, just below.
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

.GetIn:			push hl					; IN HL = tile position in the map.
			exx
			pop hl					; Backup needed to know later where to redraw the tile.
			exx

			push bc
			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty). Out hl = tile address in the map +1.
			ld b, a

			and MapAttribs.MaskMajor		; OUT AC = Tile number.
			cp MapAttribs.Major.AutoBreakable	; It is breakable by just walking on?.
			jp z, .AutoBreakable
			cp MapAttribs.Major.BreakableToBlack	; It is breakable by hitting it with the sword?.
			jr z, .BreakableToBlack
			cp MapAttribs.Major.BreakableToBlue	; It is breakable by hitting it with the sword?.
			jp nz, .Escape

			ld a, b					; Breakable to empty blue. Tile 324.
								; Restore msb.
			and MapAttribs.MaskMinor		; $1E.
			rra
			pop bc

			sub b					; Durability - hit power.
			jr z, .BreakedToBlue 
			jr c, .BreakedToBlue

			add a,a					; << 1.
			or MapAttribs.Major.BreakableToBlue	; Not breaked yet.
			ld b, a
			ld a, (hl)
			and 1
			or b
			ld (hl), a				; Adjust the durability.
			ret

.BreakedToBlue:		ld de, TileMap.BlueBackgroundTile_Address
								; The block just breaks to blue.
			ld (hl), d
			dec hl
			ld (hl), e

			exx
			call GetTileVisualPosition
			push ix
			push bc
			push de
			push hl
			call MapRender
			pop hl
			pop de
			pop bc
			pop ix

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			push ix
			call MapRender
			pop ix

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			xor a
			ret

.BreakableToBlack:	ld a, b					; Breakable to empty black. Tile 320.
								; Restore msb.
			and MapAttribs.MaskMinor		; $1E.
			rra
			pop bc

			sub b					; Durability - hit power.
			jr z, .BreakedToBlack
			jr c, .BreakedToBlack

			add a,a					; << 1.
			or MapAttribs.Major.BreakableToBlack	; Not breaked yet.
			ld b, a
			ld a, (hl)
			and 1
			or b
			ld (hl), a				; Adjust the durability.
			ret

.BreakedToBlack:	ld de, TileMap.BlackBackgroundTile_Address
								; The block just breaks to black.
			ld (hl), d
			dec hl
			ld (hl), e

			exx
			call GetTileVisualPosition
			push ix
			push bc
			push de
			push hl
			call MapRender
			pop hl
			pop de
			pop bc
			pop ix

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			push ix
			call MapRender
			pop ix

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			xor a
			ret

.Escape:		pop bc
			ret

.AutoBreakable:		ld a, b					; Breakable to custom feature.
			and MapAttribs.MaskMinor
			ld de, TileMap.BlackBackgroundTile_Address
			jr z, .SetTile

			cp $02
			ld de, TileMap.BlueBackgroundTile_Address
			jr z, .SetTile

			cp $04
			jr nz, .Cont1

			ld a, (FramesCount)
			and $1E					; There is only up to 15 items (fruits and some bonus).
			ld e, a
			ld d, 0
			cp 30
			jr c, .SetTile
			dec e
			dec e
			jr .SetTile

.Cont1:			cp $06
			jr nz, .Cont2

			ld a, (FramesCount)
			and $1E					; There is only up to 15 items (fruits and some bonus).
			or $01					; On blue background.
			ld e, a
			ld d, 0
			cp 30
			jr c, .SetTile
			dec e
			dec e
			jr .SetTile

.Cont2:			cp $10
			jr nc, .Cont3
									; Custom Feature: range $08/$0A/$0C/$0E = keys.
			add a, TileMap.Keys_StartAddress - $08
			ld e, a						; Only black background possible.
			ld d, 0
			jr .SetTile

.Cont3:			cp $1C
			jr nc, .Cont4

			add a, TileMap.Weapons_StartAddress - $10	; Custom Feature: range $10/$12/$14/$16/$18/$1A = weapons.
			ld e, a
			ld d, 0
			jr .SetTile

.Cont4:			jr nz, .Cont5
									; Custom Feature: $1C.
			ld de, TileMap.KillNeddlesDownBlack_Address
			jr .SetTile

.Cont5:			ld de, TileMap.KillNeddlesDownBlue_Address	; Custom Feature: $1E.

.SetTile:		ld (hl), d					; Write the new tile value in the map.
			dec hl
			ld (hl), e

			exx
			call GetTileVisualPosition
			push ix
			push bc
			push de
			push hl
			call MapRender
			pop hl
			pop de
			pop bc
			pop ix

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			push ix
			call MapRender
			pop ix

			ld a, (ActiveBuffer)
			inc a
			and 1
			ld (ActiveBuffer), a

			pop bc

			xor a
			ret






TestForBreakableCeiling:ld a, (ix + Sprite.MapOy)		; OUT ZF = 1 breakable.
			sub $10
			ld a, (ix + Sprite.MapTy)
			sbc a, 0
			ld h, a
			ret c

			ld a, (ix + Sprite.MapOx)
			add a, $80				; +8 pixels X, just below.
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).

			and MapAttribs.MaskMajor		; OUT AC = Tile number.
			cp MapAttribs.Major.AutoBreakable
			ret z
			cp MapAttribs.Major.BreakableToBlack
			ret z
			cp MapAttribs.Major.BreakableToBlue
			ret






BreakingCeiling:	ld a, (ix + Sprite.MapOy)		; IN B = hit force.
			sub $10
			ld a, (ix + Sprite.MapTy)
			sbc a, 0
			ld h, a
			ret c

			ld a, (ix + Sprite.MapOx)
			add a, $80				; +8 pixels X, just below.
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

			jp BreakingFloor.GetIn			; IN HL = tile position in the map. IN B = hit force.






TestForBreakableLeft:	ld a, (ix + Sprite.MapOx)		; OUT ZF = 1 breakable.
			sub a, $20
			ld a, (ix + Sprite.MapTx)
			sbc a, 0
			ld l, a			
			ret c

			ld a, (ix + Sprite.MapOy)
			add a, $80				; +8 pixels Y, just in the middle height of the body.
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).

			and MapAttribs.MaskMajor		; OUT AC = Tile number.
			cp MapAttribs.Major.AutoBreakable
			ret z
			cp MapAttribs.Major.BreakableToBlack	; It is breakable by hitting it with the sword?.
			ret z
			cp MapAttribs.Major.BreakableToBlue	; It is breakable by hitting it with the sword?.
			ret






BreakingLeft:		ld a, (ix + Sprite.MapOx)		; IN B = hit force.
			sub a, $20
			ld a, (ix + Sprite.MapTx)
			sbc a, 0
			ld l, a			
			ret c

			ld a, (ix + Sprite.MapOy)
			add a, $80				; +8 pixels Y, just in the middle height of the body.
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a

			jp BreakingFloor.GetIn			; IN HL = tile position in the map. IN B = hit force.






TestForBreakableRight:	ld a, (ix + Sprite.MapOx)		; OUT ZF = 1 breakable.
			add a, $20
			ld a, (ix + Sprite.MapTx)
			adc a, 1
			ld l, a			
			ret c

			ld a, (ix + Sprite.MapOy)
			add a, $80				; +8 pixels Y, just in the middle height of the body.
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).

			and MapAttribs.MaskMajor		; OUT AC = Tile number.
			cp MapAttribs.Major.AutoBreakable
			ret z
			cp MapAttribs.Major.BreakableToBlack	; It is breakable by hitting it with the sword?.
			ret z
			cp MapAttribs.Major.BreakableToBlue	; It is breakable by hitting it with the sword?.
			ret






BreakingRight:		ld a, (ix + Sprite.MapOx)		; IN B = hit force.
			add a, $20
			ld a, (ix + Sprite.MapTx)
			adc a, 1
			ld l, a			
			ret c

			ld a, (ix + Sprite.MapOy)
			add a, $80				; +8 pixels Y, just in the middle height of the body.
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a

			jp BreakingFloor.GetIn			; IN HL = tile position in the map. IN B = hit force.






TestForJumping:		call .Wrap				; OUT ZF = 1 can jump. BC = distance to the ceiling.
			ret nz

								; Until now, it can jump. ZF = 1 = CAN JUMP.

			ld a, (ix + Sprite.Class)		; If who is asking is an elevator, don't checks on boxes or other lifts, because it can crush boxes or others lifts.
			and a
			ret z
			dec a					; Classes 0 or 1 are elevators. It must escape with ZF = 1.
			ret z


								; Until now, it can jump.
			ld hl, SpacingObjectsList

			exx
			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld e, (ix + Sprite.MapOy)
			ld d, (ix + Sprite.MapTy)
			exx

.ObjectLoop:		ld a, (hl)
			ld iyl, a
			inc hl
			or (hl)
			jr z, .FinalResult			; Escape if no more objects to check for spacing.

			ld a, (hl)
			ld iyh, a
			inc hl

			exx					; Hide current result.
			ld l, (iy + Sprite.MapOx)		; Test for X alignment, if -16 > alignment < 16 then it is aligned enought.
			ld h, (iy + Sprite.MapTx)		; As coordinated format are << 4. So test for if -256 > alignment < 256 (map format).
			sbc hl, bc
			ld a, h
			and a
			jr z, .CheckYAxis			; No jump is most usual.
			inc a
			jp nz, .NextObjectExx			; Jump is most usual.
			or l
			jr z, .NextObjectExx			; No jump is most usual. Object not aligned because it is -256, it is completely at his left.

.CheckYAxis:		ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)
			sbc hl, de
			jr nc, .NextObjectExx			; Same vertical or avobe, this object can't prevent to jump. No jump is most usual.
			ld a, h
			inc a
			jr z, .CantJump				; No jump is most usual. If overlap partially below = don't jump, it is a final result.
			inc a
			jp nz, .NextObjectExx			; Jump is most usual. Don't test on very far objects. Its scope is from 0 to 15 pixels away.

			ld a, l					; NEG().
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

			dec h
			ld a, h
			exx

			cp b					; Checks if it is the nearest relevant object.

			exx
			jp c, .SetAsNear			; Jump is most usual.
			jr nz, .NextObjectExx
			ld a, l
			exx

			cp c
			jr nc, .ObjectLoop

			exx
.SetAsNear:		push hl					; It is near relevant.
			exx
			pop bc

			jp .ObjectLoop

.NextObjectExx:		exx
			jp .ObjectLoop

.FinalResult:		ld a, c					; Checks if distance = 0, so don't fall.
			or b
			jr z, .CantJump

			xor a
			ret

.CantJump:		dec a
			ret



.Wrap:			ld a, (ix + Sprite.MapOy)		; OUT ZF = 1 can jump, BC = distance to ceiling in pixels << 4.
			sub $10
			ld d, a
			ld a, (ix + Sprite.MapTy)
			sbc a, 0
			ld h, a					; -1 pixels. First row of pixels on top of the sprite.
			ret c					; In this circunstance, ZF = 0.

			call .HorizontalCheck
			ld bc, 0
			ret nz					; Escapes if can't jump.

			ld a, (ix + Sprite.MapOy)
			sub $10					; Again -1.

			and $70
			add a, $10
			ld c, a
			ld b, 0

			push bc

			ld a, (ix + Sprite.MapOy)
			sub a, $90
			ld d, a
			ld a, (ix + Sprite.MapTy)
			sbc a, 0
			ld h, a					; +9 pixels.
			jr c, .Escape				; In this circunstance, ZF = 0.

			call .HorizontalCheck
			pop bc
			jr nz, .CanJump				; Even if here is ceiling, as before was tested some space for jump, it jumps with a "Can jump".

			ld a, c
			add a, $80
			ld c, a
			jr nc, .JJJ11
			inc b
.JJJ11:
.CanJump:		xor a
			ret

.Escape:		pop bc
			ret

.HorizontalCheck:	ld a, (ix + Sprite.MapOx)
			add a, $20				; +2 pixels.
			ld e, a
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

			push de
			push hl
			call IsWall				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			pop de
			and a					; OUT A = 0 no, <> 0 Floor.
			ret nz

			ld a, e
			add a, $60				; +6 pixels.
			ld e, a
			jr nc, .JJJ12
			inc l
.JJJ12:
			push de
			push hl
			call IsWall				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			pop hl
			pop de
			and a					; OUT A = 0 no, <> 0 Floor.
			ret nz

			ld a, e
			add a, $50				; +5 pixels, totalling +13 pixels. So, pixels under test is --T- ---- T--- -T-- of the below row.
			ld e, a
			jr nc, .JJJ13
			inc l
.JJJ13:
			call IsWall				; IN D = Oy, E = Ox ((0-15)<<4, (0-15)<<4), H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 no, <> 0 Floor.
			ret






MoveOnGravity:		ld e, (ix + Sprite.MapOy)		; It moves down the character by the gravity factor. IN HL = falling step, IN BC = Maximun ammount that can fall.
			ld d, (ix + Sprite.MapTy)

			and a
			sbc hl, bc
			jr nc, .LimitHit

			add hl, bc				; Restores.
			add hl, de

			ret c
			bit 7, h				; Valid range for Ty = 0-127.
			ret nz

			ld (ix + Sprite.MapOy), l
			ld (ix + Sprite.MapTy), h
			ret

.LimitHit:		ld l, c
			ld h, b
			add hl, de

			ret c
			bit 7, h
			ret nz

			ld (ix + Sprite.MapOy), l
			ld (ix + Sprite.MapTy), h
			ret






MoveOnJump:		ld e, (ix + Sprite.MapOy)		; It moves up the character by the input factor. IN HL = jump step, IN BC = Maximun ammount that can jump.
			ld d, (ix + Sprite.MapTy)

			and a
			sbc hl, bc
			jr nc, .LimitHit

			add hl, bc				; Restores.
			ex de, hl
			and a
			sbc hl, de
			ret c

			ld (ix + Sprite.MapOy), l
			ld (ix + Sprite.MapTy), h
			ret

.LimitHit:		ld l, c
			ld h, b
			ex de, hl
			sbc hl, de
			ret c

			ld (ix + Sprite.MapOy), l
			ld (ix + Sprite.MapTy), h
			ret






WalkToTheLeft:		ld e, (ix + Sprite.MapOx)		; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.
			ld d, (ix + Sprite.MapTx)

			and a
			sbc hl, bc
			jr nc, .LimitHit

			add hl, bc				; Restores.
			and a
			ex de, hl
			sbc hl, de

			ret c					; Escapes if map edge.

			ld (ix + Sprite.MapOx), l
			ld (ix + Sprite.MapTx), h
			ret

.LimitHit:		ld l, c
			ld h, b
			ex de, hl
			sbc hl, de

			ret c

			ld (ix + Sprite.MapOx), l
			ld (ix + Sprite.MapTx), h
			ret






WalkToTheRight:		ld e, (ix + Sprite.MapOx)		; It moves to the right a character. IN HL = step. IN BC = Maximun ammount that can walk.
			ld d, (ix + Sprite.MapTx)

			and a
			sbc hl, bc
			jr nc, .LimitHit

			add hl, bc				; Restores.
			add hl, de

			ret c					; Escapes if map edge.

			ld (ix + Sprite.MapOx), l
			ld (ix + Sprite.MapTx), h
			ret

.LimitHit:		ld l, c
			ld h, b
			add hl, de

			ret c

			ld (ix + Sprite.MapOx), l
			ld (ix + Sprite.MapTx), h
			ret






TestForWallOnHisLeft:	call .Wrap				; OUT ZF = 1 can walk. BC = distance to the wall.
			ret nz


								; Until now, it can walk. ZF = 1 = CAN WALK.
			ld hl, SpacingObjectsList

			exx
			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld e, (ix + Sprite.MapOy)
			ld d, (ix + Sprite.MapTy)
			exx

.ObjectLoop:		ld a, (hl)
			ld iyl, a
			inc hl
			or (hl)
			jr z, .FinalResult			; Escape if no more objects to check for spacing.

			ld a, (hl)
			ld iyh, a
			inc hl

			exx					; Hide current result.
			ld l, (iy + Sprite.MapOy)		; Test for Y alignment, if -16 > alignment < 16 then it is aligned enought.
			ld h, (iy + Sprite.MapTy)		; As coordinated format are << 4. So test for if -256 > alignment < 256 (map format).
			sbc hl, de
			ld a, h
			and a
			jr z, .CheckXAxis			; No jump is most usual.
			inc a
			jp nz, .NextObjectExx			; Jump is most usual.
			or l
			jr z, .NextObjectExx			; No jump is most usual. Object not aligned because it is -256, it is completely at his top.

.CheckXAxis:		ld l, (iy + Sprite.MapOx)
			ld h, (iy + Sprite.MapTx)
			sbc hl, bc
			jr nc, .NextObjectExx			; Same horizontal or on his right, this object can't prevent to walk. No jump is most usual.
			ld a, h
			inc a
			jr z, .CantWalk2			; No jump is most usual. If overlap partially on his left = don't walk, it is a final result.
			inc a
			jp nz, .NextObjectExx			; Jump is most usual. Don't test on very far objects. Its scope is from 0 to 15 pixels away.

			ld a, l					; NEG().
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

			dec h
			ld a, h
			exx

			cp b					; Checks if it is the nearest relevant object.

			exx
			jp c, .SetAsNear			; Jump is most usual.
			jr nz, .NextObjectExx
			ld a, l
			exx

			cp c
			jr nc, .ObjectLoop

			exx
.SetAsNear:		push hl					; It is near relevant.
			exx
			pop bc
			ld (SelectedObjectWalking), iy

			jp .ObjectLoop

.NextObjectExx:		exx
			jp .ObjectLoop

.FinalResult:		ld a, c					; Checks if distance = 0, so don't walk.
			or b
			jr z, .CantWalk

			xor a
			ret

.CantWalk:		ld iy, (SelectedObjectWalking)		; If can't walk is only because there is something blocking it.

.CantWalk2:		ld a, (ix + Sprite.Class)		; Only signals to the box if not being pushed by another box (the player or anybody can't push two boxes together, BASIC RULE FROM THE CASTLE).
			cp SpriteClasses.BoxesClass
			jr nz, .CanPushBox

			dec a					; ZF = 0.
			ret

.CanPushBox:		ld a, (PushingObjectWalking)
			inc a
			ret nz

			set 0, (iy + Sprite.LogicArea + 2)	; Signals to the box that is being pushed to the left.
			dec a					; ZF = 0.
			ret



.Wrap:			ld a, (ix + Sprite.MapOx)		; OUT ZF = 1 can walk, BC = space before the wall.
			sub $10
			ld e, a
			ld a, (ix + Sprite.MapTx)
			sbc a, 0
			ld l, a					; -1 pixel on his left.
			ret c					; Escape if map left edge. In this circunstances ZF = 0.

			call .VerticalCheck
			ld bc, 0
			ret nz

			ld a, (ix + Sprite.MapOx)
			sub $10					; Again -1.

			and $70
			add a, $10
			ld c, a
			ld b, 0

			push bc

			ld a, (ix + Sprite.MapOx)
			sub a, $90
			ld e, a
			ld a, (ix + Sprite.MapTx)
			sbc a, 0
			ld l, a					; +9 pixels.
			jr c, .Escape				; Escape if map left edge. In this circunstances ZF = 0.

			call .VerticalCheck
			pop bc
			jr nz, .CanWalk				; Even if here is a wall, as before was tested some space for walk, it jumps with a "Can walk".

			ld a, c
			add a, $80
			ld c, a
			jr nc, .JJJ14
			inc b
.JJJ14:
.CanWalk:		xor a
			ret

.Escape:		pop bc
			ret

.VerticalCheck:		ld a, (ix + Sprite.MapOy)		; OUT ZF = 0 wall.
			add a, $20
			ld d, a
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a					; Y += 2 pixels.

			push de
			push hl
			call IsWall				; IN H = ty, L = tx (range 0-255tx, 0-127ty), D = Oy, E = Ox (range 0-15<<4, 0-15<<4).
								; OUT A = 0 no, <> 0 wall.
			pop hl
			pop de
			and a
			ret nz

			ld a, d
			add a, $60				; Y += 6 pixels.
			ld d, a
			jr nc, .JJJ15
			inc h
.JJJ15:
			push de
			push hl
			call IsWall				; IN H = ty, L = tx (range 0-255tx, 0-127ty), D = Oy, E = Ox (range 0-15<<4, 0-15<<4).
								; OUT A = 0 no, <> 0 wall.
			pop hl
			pop de
			and a
			ret nz

			ld a, d
			add a, $50
			ld d, a
			jr nc, .JJJ16
			inc h
.JJJ16:
			call IsWall				; IN H = ty, L = tx (range 0-255tx, 0-127ty), D = Oy, E = Ox (range 0-15<<4, 0-15<<4).
								; OUT A = 0 no, <> 0 wall.
			and a
			ret






TestForWallOnHisRight:	call .Wrap				; ZF = 1 can walk. BC = distance to the wall.
			ret nz



			ld hl, SpacingObjectsList

			exx
			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld e, (ix + Sprite.MapOy)
			ld d, (ix + Sprite.MapTy)
			exx

.ObjectLoop:		ld a, (hl)
			ld iyl, a
			inc hl
			or (hl)
			jr z, .FinalResult			; Escape if no more objects to check for spacing.

			ld a, (hl)
			ld iyh, a
			inc hl

			exx					; Hide current result.
			ld l, (iy + Sprite.MapOy)		; Test for Y alignment, if -16 > alignment < 16 then it is aligned enought.
			ld h, (iy + Sprite.MapTy)		; As coordinated format are << 4. So test for if -256 > alignment < 256 (map format).
			sbc hl, de
			ld a, h
			and a
			jr z, .CheckXAxis			; No jump is most usual.
			inc a
			jp nz, .NextObjectExx			; Jump is most usual.
			or l
			jr z, .NextObjectExx			; No jump is most usual. Object not aligned because it is -256, it is completely at his top.

.CheckXAxis:		ld l, (iy + Sprite.MapOx)
			ld h, (iy + Sprite.MapTx)
			sbc hl, bc
			jr z, .NextObjectExx			; Same horizontal, this object can't prevent to walk. No jump is most usual.
			ld a, h
			and a
			jr z, .CantWalk2			; No jump is most usual. If overlap partially on his right = don't walk, it is a final result.
			dec a
			jp nz, .NextObjectExx			; Jump is most usual. Don't test on very far objects. Its scope is from 0 to 15 pixels away.

			dec h					; Move coordinate to the right logic edge of the sprite box.
			exx

			cp b					; Checks if it is the nearest relevant object.

			exx
			jp c, .SetAsNear			; Jump is most usual.
			jr nz, .NextObjectExx
			ld a, l
			exx

			cp c
			jr nc, .ObjectLoop

			exx
.SetAsNear:		push hl					; It is near relevant.
			exx
			pop bc
			ld (SelectedObjectWalking), iy

			jp .ObjectLoop

.NextObjectExx:		exx
			jp .ObjectLoop

.FinalResult:		ld a, c					; Checks if distance = 0, so don't walk.
			or b
			jr z, .CantWalk

			xor a
			ret

.CantWalk:		ld iy, (SelectedObjectWalking)		; If can't walk is only because there is something blocking it.

.CantWalk2:		ld a, (ix + Sprite.Class)		; Only signals to the box if not being pushed by another box (the player or anybody can't push two boxes together, BASIC RULE FROM THE CASTLE).
			cp SpriteClasses.BoxesClass
			jr nz, .CanPushBox

			dec a					; ZF = 0.
			ret

.CanPushBox:		ld a, (PushingObjectWalking)
			inc a
			ret nz

			set 1, (iy + Sprite.LogicArea + 2)	; Signals to the box that is being pushed to the right.
			dec a
			ret



.Wrap:			ld e, (ix + Sprite.MapOx)		; OUT ZF = 1 can walk, BC = distance to the wall in pixels << 4.
			ld l, (ix + Sprite.MapTx)
			inc l					; +16 pixels. First column of pixels on its right.
			jr z, .MapOverflow

			call TestForWallOnHisLeft.VerticalCheck
			ld bc, 0
			ret nz					; Escapes if can't walk.

			ld a, (ix + Sprite.MapOx)
			ld c, a
			or $70
			sub c
			add a, $10
			ld c, a
			ld b, 0					; BC = space that can walk.

			push bc

			ld a, (ix + Sprite.MapOx)
			add a, $80
			ld e, a
			ld a, (ix + Sprite.MapTx)
			adc a, 1
			ld l, a					; +24 pixels.
			jr c, .MapOverflow2

			call TestForWallOnHisLeft.VerticalCheck
			pop bc
			jr nz, .CanWalk				; Even if here is a wall, as before was tested some space for walk, it jumps with a "Can walk".

			ld a, c
			add a, $80
			ld c, a
			jr nc, .JJJ17
			inc b
.JJJ17:
.CanWalk:		xor a
			ret

.MapOverflow2:		pop bc
.MapOverflow:		ld a, 255
			and a
			ret






TestForDoorOnLeft:	ld a, (ix + Sprite.MapOx)
			sub $10
			ld a, (ix + Sprite.MapTx)
			sbc a, 0
			ld l, a					; -1 pixel on his left.
			ret c					; Escape if map left edge.

			ld a, (ix + Sprite.MapOy)
			add a, $40
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a
			jp TestForDoorOnRight.In






TestForTransportation:	ld h, (ix + Sprite.MapTy)		; OUT ZF = 0 no transportation. ZF = 1, transport, A = 0 left.
			inc h					; +16 pixels. First row of pixels below the sprite.
			bit 7, h
			ret nz

			ld a, (ix + Sprite.MapOx)
			add a, $80
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
								; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.
			and MapAttribs.MaskTileNumber
			dec a
			ret nz
								; Tile number => 256.
			ld a, c
			cp TileMap.BeltTransportLeft_StartAddress - 256
			ret c
			cp TileMap.BeltTransportLeft_StopAddress - 256
			jr c, .Left
			cp TileMap.BeltTransportRight_StopAddress - 256
			jr nc, .No

			xor a					; Right.
			ld a, 255
			ret

.Left:			xor a
			ret

.No:			ld a, 255
			and a
			ret






TestForDoorOnRight:	ld l, (ix + Sprite.MapTx)
			inc l
			ret z					; Escapes if map overflow.

			ld a, (ix + Sprite.MapOy)
			add a, $40
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a

.In:			push hl					; Save the tile position in the map.

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld b, a					; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.
			ex de, hl				; DE = tile address in the map + 1.

			ld hl, TileMap.Door_StartAddress - 1
			sbc hl, bc
			jr nc, .Esc1

			ld hl, TileMap.Door_StopAddress		; CF = 1 so, = TileMap.Door_StopAddress - 1.
			sbc hl, bc
			jr c, .Esc1
								; It is a door.
			ld l, c
			ld h, b
			ld bc, TileMap.Door_StartAddress
			sbc hl, bc				; HL = door ordinal number.

			add hl, hl
			ld bc, TileMap.Keys_StartAddress
			add hl, bc				; HL = key needed. Not sensitive to the black/blue background.
			ld c, l					; C = key item.

			dec de
			push de					; Save the tiledoor address in the map.

			call UseAItem				; IN C = item. IX = Player. OUT ZF = 1 found and removed. ZF = 0 not present.
			jr nz, .NoKey
								; The player has the key, and was already removed from the vault.
			pop hl
			ld de, TileMap.DoorOpenTile_Address	; Setup the open door tile.
			ld (hl), e
			inc hl
			ld (hl), d

			pop hl
			push hl
			call UpdateTileVisuals
			pop hl
								; Ok, the door was opened logically and visually. But the upper tile can be a tile part of the door that must be converted too.
			ld a, h
			and a
			ret z					; Escape if map overflow.

			dec h					; Move up in the map.

			push hl

			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			ld b, a					; OUT AC = Tile number. CF = 0. OUT HL = tile address +1.
			ex de, hl

			ld hl, TileMap.UpDoor_StartAddress - 1
			sbc hl, bc
			jr nc, .Esc1

			ld hl, TileMap.UpDoor_StopAddress	; CF = 1, so counts as TileMap.UpDoor_StopAddress - 1.
			sbc hl, bc
			jr c, .Esc1

			ld l, c
			ld h, b
			ld bc, TileMap.UpDoor_StartAddress
			sbc hl, bc
								; HL = UpDoor tile number.
			ld a, l					; Only is interesting if it is bricks or rock background.
			and 1
			ld l, a
			ld h, 0

			ld bc, TileMap.UpDoorOpen_BaseAddress
			add hl, bc				; HL = tile number of open door.
			ex de, hl

			ld (hl), d
			dec hl
			ld (hl), e

			pop hl
			jp UpdateTileVisuals

.NoKey:			pop de
.Esc1:			pop hl
			ret






TestForCheckPoint:	ld a, (SpritesCount)
			and a
			ret z

			ld b, a

			ld ix, SpritesPool

.CharacterLoop:		push bc

			ld a, (ix + Sprite.Class)
			inc a					; Class = 255 = player.
			call z, .Player

.Next:			ld bc, Sprite.SizeOf
			add ix, bc

			pop bc
			djnz .CharacterLoop
			ret

.Player:		ld h, (ix + Sprite.MapTy)		; IN IX = me.
			ld l, (ix + Sprite.MapTx)
			push hl
			call GetMapTileNumber			; IN H = ty, L = tx (range 0-255tx, 0-127ty). OUT AC = Tile number. CF = 0. OUT HL = tile address +1.
			pop hl
			ld b, a

			and MapAttribs.MaskMajor + MapAttribs.MaskMinor
			cp MapAttribs.Major.Special + MapAttribs.Special.PlayerCheckPoint
			ret nz
								; It is a checkpoint.
			ld a, (LastCheckPointTx)		; Checks anti multi-reaction.
			cp l
			jr nz, .ProcessIt

			ld a, (LastCheckPointTy)
			cp h
			ret z

.ProcessIt:		ld a, (LastCheckPointWait)		; It avoids a save/error infinite cycle.
			and a
			jr z, .ProcessIt2

			dec a
			ld (LastCheckPointWait), a
			ret

.ProcessIt2:		ld a, l					; It early marks the checkpoint "as done" because it needs to save the savestate with the checkpoint marked as done, otherwise when loading from that checkpoint it will react for saving.
			ld (LastCheckPointTx), a
			ld a, h
			ld (LastCheckPointTy), a
			ld a, 255
			ld (LastCheckPointWait), a

			push hl
			call ISRUnHooker			; It mute chips aswell.
			pop hl

			xor a					; Determine the new current song number.
			rr h
			rla
			rr l
			rla
			inc a

			call LoadSong

			push ix
			ld de, .SaveName			; Save instant savegame, with new song number already set (this is important).
			call SaveGame
			pop ix

			push af
			call ISRHooker
			pop af

			ret nc
								; Error. Unmark the checkpoint allowing another try later.
			xor a
			ld (LastCheckPointTx), a
			ld (LastCheckPointTy), a
			ret

.SaveName:		DB "CHECKPOI.SAV", 0



; ************************************************************
; * IA ENGINE                                                *
; ************************************************************






IACharacters:		ld a, (SpritesCount)
			and a
			ret z

			ld b, a

			ld ix, SpritesPool

.CharacterLoopEle:	push bc

			ld a, (ix + Sprite.Class)
			cp SpriteClasses.LiftsStopAddress	; Elevators has priority as them can push/hold all characters and boxes.
			jr nc, .NextCharacterEle

			call ElevatorClass

.SpriteRenderEle:	ld a, (ix + Sprite.Class)		; It continue existing?.
			cp SpriteClasses.EmptySlotClass
			jr z, .NextCharacterEle			; No jump, is most usual.

			call RenderOneSprite
			call .BackUpSpriteScreenVars

.NextCharacterEle:	ld bc, Sprite.SizeOf
			add ix, bc

			pop bc
			djnz .CharacterLoopEle

			ld a, (SpritesCount)
			and a
			ret z

			ld b, a

			dec a					; this is the "Sprite size formula", must positioning IX on the last sprite.
			ld l, a
			ld h, 0					; The sprite pool is executed in reverse, because the players must be run at last, allowing positioning the players after all boxes and lifts was render and reposition, so the user will not see in the screen that the player is one pixel avobe the box top when the box or the lift is going down.
			add hl, hl				; Running players at last also ensure the best Z priority for the player sprite.
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, hl				; * 64.
			ex de, hl			
			ld ix, SpritesPool
			add ix, de

.CharacterLoop:		push bc					; Scans the sprite pool in reverse for guarantee the players runs on last, and that way to solve some logistic problems with boxes and elevators.

			ld a, (ix + Sprite.Class)
			cp SpriteClasses.EmptySlotClass
			jr z, .NextCharacter			; No jump, is most usual.

			cp SpriteClasses.LiftsStopAddress	; Don't process elevators as they are already done.
			jr c, .NextCharacter			; No jump, is most usual.

			cp SpriteClasses.PlayerClass
			jp z, .PlayerClass

			and $1F
			add a, a
			ld e, a
			ld d, 0
			ld hl, .ClassesPool
			add hl, de

			cp SpriteClasses.CharactersStopClass << 1
			push hl					; It is a character. it is the "transportation belt" feature?.
			call c, .TransportBelt
			pop hl

			call .Exec

.SpriteRender:		ld a, (ix + Sprite.Class)		; Continue existing?.
			cp SpriteClasses.EmptySlotClass
			jr z, .NextCharacter			; No jump, most usual.

			call RenderOneSprite

.SpecialReturnHack:	call .BackUpSpriteScreenVars

.NextCharacter:		ld a, ixl
			sub Sprite.SizeOf
			ld ixl, a
			jr nc, .NextCharacterSub
			dec ixh
.NextCharacterSub:
			pop bc
			dec b
			jp nz, .CharacterLoop

			ret

.PlayerClass:		call .TransportBelt

			call PlayerClass

			jp .SpriteRender

.Exec:			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

			jp (hl)

.TransportBelt:		call TestForTransportation		; It is on the "Transport belt" feature?.
			ret nz					; OUT ZF = 0 no transportation. ZF = 1, transport, A = 0 left.

			and a
			jr nz, .TransportRight
								; Transport Left.
			call TestForWallOnHisLeft		; ZF = 0 wall. BC = distance that can walk if ZF = 1.
			ret nz

			ld hl, $0020
			jp WalkToTheLeft			; In BC = walk limit.

.TransportRight:	call TestForWallOnHisRight		; ZF = 0 wall. BC = distance that can walk if ZF = 1.
			ret nz

			ld hl, $0020
			jp WalkToTheRight			; In BC = walk limit.

.BackUpSpriteScreenVars:ld a, (ActiveBuffer)			; After the sprite render, it needs to backup the screen render coordinates to the correct backup variables.
			and a
			jr nz, .Bf1Ele
								; Bf0Ele.
			ld a, (ix + Sprite.ScreenX)
			ld (ix + Sprite.Bf0ScreenX), a
			ld a, (ix + Sprite.ScreenY)
			ld (ix + Sprite.Bf0ScreenY), a
			ld a, (ix + Sprite.ScreenWidth)
			ld (ix + Sprite.Bf0ScreenWidth), a
			ld a, (ix + Sprite.ScreenHeight)
			ld (ix + Sprite.Bf0ScreenHeight), a
			ret

.Bf1Ele:		ld a, (ix + Sprite.ScreenX)
			ld (ix + Sprite.Bf1ScreenX), a
			ld a, (ix + Sprite.ScreenY)
			ld (ix + Sprite.Bf1ScreenY), a
			ld a, (ix + Sprite.ScreenWidth)
			ld (ix + Sprite.Bf1ScreenWidth), a
			ld a, (ix + Sprite.ScreenHeight)
			ld (ix + Sprite.Bf1ScreenHeight), a
			ret

.ClassesPool:		DW ElevatorClass			; Character classes (Creatable from respawn points).
			DW ElevatorClass
			DW CubeClass
			DW TurtleClass
			DW BeesClass
			DW SpiderClass
			DW FrogClass
			DW GuardClass
			DW DogClass
			DW WizardClass
			DW DemonClass
			DW Monster1Class
			DW Monster2Class
			DW Monster3Class
			DW Monster4Class
			DW Monster5Class

			DW BombClass				; Features Classes. (Not creatable from respawn points).
			DW TwisterClass
			DW FireBallClass
			DW ThunderClass
			DW KnifeClass
			DW CoinClass
			DW TurtleClass				; Unused class id.
			DW TurtleClass				; Unused class id.
			DW TurtleClass				; Unused class id.
			DW TurtleClass				; Unused class id.
			DW TurtleClass				; Unused class id.
			DW RemoveMeClass
			DW CrushedClass
			DW ShieldClass
			DW HandWeaponClass
			DW BornClass






AttackLogic:		ld a, e					; IN IX = Who is attacking.
			or d					; IN (AttackPower) = power of the attack (unsigned 16bits).
			ret z					; IN (IgnorePlayers) <> 0 true.
								; Test if the attack area is null.
			exx
			ld a, e					; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
			or d					; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.
			ret z

			ex de, hl
			add hl, bc
			ex de, hl				; Y_Stop = DE = Height + MapTy/Oy.
			jr nc, .NoHeightClip

			ld de, $FFFF

.NoHeightClip:		exx
			ex de, hl
			add hl, bc
			ex de, hl				; X_Stop = DE = Width + MapTx/Ox.
			jr nc, .NoWidthClip

			ld de, $FFFF

.NoWidthClip:		ld a, (SpritesCount)
			ld iy, SpritesPool

.CharacterLoop:		push af					; Save sprite countdown.

			ld a, (iy + Sprite.Class)

			inc a					; PlayerClass = 255.
			jp z, .PlayerClass
			dec a

			cp SpriteClasses.CharactersStopClass
			jp nc, .NextCharacter			; Only characters and boxes can be kill.

.ProcessIt:		ld a, ixh				; Don't attack itself.
			cp iyh
			jr nz, .NotTheSameCharacter

			ld a, ixl
			cp iyl
			jp z, .NextCharacter

.NotTheSameCharacter:						; Test Character's high damage hitbox.
								; Test X axis of it.

			ld a, (iy + Sprite.MapOx)		; first calculate the real in map X coordinate of the hitbox.
			add a, (iy + Sprite.HighHitBoxLeft)
			ld l, a
			ld a, (iy + Sprite.MapTx)
			adc a, 0
			ld h, a
			jr c, .NextHitBox			; If it escapes the map size, escape the test with this hitbox.

			sbc hl, de				; Compare the X coordinate of the character's hitbox with the right border of the attacked area.
			jr nc, .NextHitBox
			add hl, de				; Restore.
			
			ld a, (iy + Sprite.HighHitBoxWidth)	; Calculate the real in map X coordinate for the right border of this hitbox.
			add a,a					; Width is << 3, not the usual << 4.
			jr nc, .J1
			inc h
			jr z, .Clip1

.J1:			add a, l
			ld l, a
			ld a, h
			adc a, 0
			ld h, a
			jr nc, .NoClip1

.Clip1:			and a					; Clips the character's hitbox with to the right border of the map.
			ld hl, $FFFF

.NoClip1:		sbc hl, bc				; Compare the X coordinate for the right border of character's hitbox, with the x coordinate of tha attack area.
			jr z, .NextHitBox
			jr c, .NextHitBox

			exx					; Test Y axis of it.

			ld a, (iy + Sprite.MapOy)		; first calculate the real in map Y coordinate of the hitbox.
			add a, (iy + Sprite.HighHitBoxTop)
			ld l, a
			ld a, (iy + Sprite.MapTy)
			adc a, 0
			ld h, a
			jr c, .NextHitBoxExx			; If it escapes the map size, escape the test with this hitbox.

			sbc hl, de				; Compare the Y coordinate of the character's hitbox with the bottom border of the attacked area.
			jr nc, .NextHitBoxExx
			add hl, de				; Restore.
			
			ld a, (iy + Sprite.HighHitBoxHeight)	; Calculate the real in map Y coordinate for the bottom border of this hitbox.
			add a,a					; Width is << 3, not the usual << 4.
			jr nc, .J2
			inc h
			jr z, .Clip2

.J2:			add a, l
			ld l, a
			ld a, h
			adc a, 0
			ld h, a
			jr nc, .NoClip2

.Clip2:			and a					; Clips the character's hitbox with to the bottom border of the map.
			ld hl, $FFFF

.NoClip2:		sbc hl, bc				; Compare the Y coordinate for the bottom border of character's hitbox, with the Y coordinate of tha attack area.
			jr z, .NextHitBoxExx
			jr c, .NextHitBoxExx

			ld hl, (AttackPower)
			xor a					; High damage hitbox flag.
			call .Attack				; Ok this character is attacked.

			jp .NextCharacterExx			; Test Character's low damage hitbox.
					
.NextHitBoxExx:		exx

.NextHitBox:							; Test X axis of it.

			ld a, (iy + Sprite.MapOx)		; first calculate the real in map X coordinate of the hitbox.
			add a, (iy + Sprite.LowHitBoxLeft)
			ld l, a
			ld a, (iy + Sprite.MapTx)
			adc a, 0
			ld h, a
			jr c, .NextCharacter			; If it escapes the map size, escape the test with this character.

			sbc hl, de				; Compare the X coordinate of the character's hitbox with the right border of the attacked area.
			jr nc, .NextCharacter
			add hl, de				; Restore.
			
			ld a, (iy + Sprite.LowHitBoxWidth)	; Calculate the real in map X coordinate for the right border of this hitbox.
			add a,a					; Width is << 3, not the usual << 4.
			jr nc, .J3
			inc h
			jr z, .Clip3

.J3:			add a, l
			ld l, a
			ld a, h
			adc a, 0
			ld h, a
			jr nc, .NoClip3

.Clip3:			and a					; Clips the character's hitbox with to the right border of the map.
			ld hl, $FFFF

.NoClip3:		sbc hl, bc				; Compare the X coordinate for the right border of character's hitbox, with the x coordinate of tha attack area.
			jr z, .NextCharacter
			jr c, .NextCharacter

			exx					; Test Y axis of it.

			ld a, (iy + Sprite.MapOy)		; first calculate the real in map Y coordinate of the hitbox.
			add a, (iy + Sprite.LowHitBoxTop)
			ld l, a
			ld a, (iy + Sprite.MapTy)
			adc a, 0
			ld h, a
			jr c, .NextCharacterExx			; If it escapes the map size, escape the test with this character.

			sbc hl, de				; Compare the Y coordinate of the character's hitbox with the bottom border of the attacked area.
			jr nc, .NextCharacterExx
			add hl, de				; Restore.
			
			ld a, (iy + Sprite.LowHitBoxHeight)	; Calculate the real in map Y coordinate for the bottom border of this hitbox.
			add a,a					; Width is << 3, not the usual << 4.
			jr nc, .J4
			inc h
			jr z, .Clip4

.J4:			add a, l
			ld l, a
			ld a, h
			adc a, 0
			ld h, a
			jr nc, .NoClip4

.Clip4:			and a					; Clips the character's hitbox with to the bottom border of the map.
			ld hl, $FFFF

.NoClip4:		sbc hl, bc				; Compare the Y coordinate for the bottom border of character's hitbox, with the Y coordinate of tha attack area.
			jr z, .NextCharacterExx
			jr c, .NextCharacterExx

			ld hl, (AttackPower)
			srl h					; Half damage.
			rr l
			ld a, 255				; Low damage hitbox flag.
			call .Attack				; Ok this character is attacked.

.NextCharacterExx:	exx
			
.NextCharacter:		push bc
			ld bc, Sprite.SizeOf
			add iy, bc
			pop bc
			pop af
			dec a
			jp nz, .CharacterLoop

			xor a
			ld (IgnorePlayers), a			; Erase the flag, so it is a one time flag.
			ret

.PlayerClass:		ld a, (IgnorePlayers)
			and a
			jr nz, .NextCharacter
			jp .ProcessIt

.Attack:		ex af, af'				; Backup hitbox flag.

			ld a, (iy + Sprite.Class)		; IX = who is attacking, IY = who is being attacked.
								; HL = Damage.
			cp SpriteClasses.PlayerClass
			jr z, .PlayerClassAttacked

			cp SpriteClasses.CharactersStopClass	; This check must be present because this routine can be called internally to the .Attack sub directly.
			ret nc

			push bc
			push de
			exx 
			push bc
			push de
			exx

			ld c, l
			ld b, h					; BC = Damage.

			add a, a
			ld e, a
			ld d, 0
			ld hl, .ClassesPool
			add hl, de

			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

			push ix
			push iy

			push ix					; Swap IX / IY.
			push iy
			pop ix
			pop iy

			ex af, af'				; Restore hitbox flag.
			call .Exec				; IN IX = attacked sprite. IY = Who is attacking. BC = damage received.
								; IN A = 0 high damage hitbox. <> 0 low damage hitbox.
			pop iy
			pop ix

			pop de
			pop bc
			exx
			pop de
			pop bc

			ret

.PlayerClassAttacked:	push bc
			push de
			exx 
			push bc
			push de
			exx

			ld c, l
			ld b, h					; BC = Damage.

			push ix
			push iy

			push ix					; Swap IX / IY.
			push iy
			pop ix
			pop iy

			ex af, af'				; Restore hitbox flag.
			call PlayerClass.Attacked		; IN IX = attacked sprite. IY = Who is attacking. BC = damage received.
								; IN A = 0 high damage hitbox. <> 0 low damage hitbox.
			pop iy
			pop ix

			pop de
			pop bc
			exx
			pop de
			pop bc

			ret

.Exec:			jp (hl)

.ClassesPool:		DW ElevatorClass.Attacked		; Character classes (Creatable from respawn points).
			DW ElevatorClass.Attacked
			DW CubeClass.Attacked
			DW TurtleClass.Attacked
			DW BeesClass.Attacked
			DW SpiderClass.Attacked
			DW FrogClass.Attacked
			DW GuardClass.Attacked
			DW DogClass.Attacked
			DW WizardClass.Attacked
			DW DemonClass.Attacked
			DW Monster1Class.Attacked
			DW Monster2Class.Attacked
			DW Monster3Class.Attacked
			DW Monster4Class.Attacked
			DW Monster5Class.Attacked



; ************************************************************
; * CHARACTER CLASSES                                        *
; ************************************************************






; USAGE for LogicArea: PLAYER
; + 0 = countdown, Is recently damaged?.			; = 0 NO. If recently damaged can't produce damage.

; + 1 bit 0 = 1 initialized.
; + 1 bit 5 = 1 climbing a ladder mode.
; + 1 bit 6 = 1 attack secondary button was pressed.
; + 1 bit 7 = 1 attack primary button was pressed.

; + 2 bit 1-0 = Input control.
; + 2 bit 2 = current/last direction. 0 = left.
; + 2 bit 3 = 1 Fast speed.					; If the player pumps the direction a second time, it will speed up. If he changes the direction, will slowdown.
; + 2 bit 4 = 1 pause detected.					; Used to detect a direction pump.
; + 2 bit 5 = Extra jump.
; + 2 bit 6 = Was running/walking when jumped or not?.
; + 2 bit 7 = Was falling just before before be on the ground?.	; Needed to detect when breaking breakable blocks.

; + 3 bit 1,0 = 00-11 shoes quality.
; + 3 bit 3,2 = unused (before sword quality).
; + 3 bit 5,4 = 00-11 body helmet quality.
; + 3 bit 7,6 = 00-11 Head helmet quality.

; + 4 Current jump TakeOff force.

; + 5 bit 2-0 countdown for going attack again.
; + 5 bit 4-3 Animation pattern sequencer.
; + 5 bit 7-5 count down for detecting short walking pauses.

; + 6 Player number (0, 1, 2 ...).
; + 7 Head helmet lifetime.					; Each hit to it will decrease its lifetime and then dissapear as broken. Lifetime = 8, 16, 24 hits based on quality.
; + 8 Coins LSB.
; + 9 Coins MSB.

; + 10 Current pattern offset (Action + animation).
; + 11 Pattern base number.

; + 12 Hold from falling countdown.				; When attack it can hoover a bit, just like WBIML.
; + 13 Body helmet lifetime.					; Each hit to it will decrease its lifetime and then dissapear as broken. Lifetime = 8, 16, 24 hits based on quality.
; + 14 Vault pointer LSB.
; + 15 Vault pointer MSB.
; + 16 Shoes lifetime.						; Its lifetime is counted in jumps. lifetime = 127, 191, 255 jumps.
; + 17 Score LSB.
; + 18 Score.
; + 19 Score MSB.

; + 21 0-5 sword quality.					; 0 = no sword.
; + 22 Horizontal inertia LSB.					; Used when the player is hit. Negative for LEFT.
; + 23 Horizontal inertia MSB.

PlayerClass:		bit 0, (ix + Sprite.LogicArea + 1)	; It was already initialized?.
			jr nz, .AlreadyInitialized

			ld a, (ix + Sprite.LogicArea + 6)	; Setup the pattern set to use, which depends on the player number (0, 1, 2).
			ld e, a
			add a, a
			ld d, a
			add a, a
			add a, a
			add a, d
			add a, e
			ld (ix + Sprite.LogicArea + 11), a



.AlreadyInitialized:	ld a, 255				; Signals that a player is present. So, avoid the GAME OVER screen.
			ld (IsPlayerPresent), a



			bit 5, (ix + Sprite.LogicArea + 1)	; The player is climbing a ladder?.
			jp z, .NoClimbLadder			; Jump is most usual.
								; When climbing a ladder 3 actions can be done as all hands are busy.
								; Climb up, climb down, or jump out of the ladder.

			ld a, (ix + Sprite.LogicArea + 2)	; So first test if want to climb up.
			and 3
			call Stick				; IN A = 0 keyboard, 1 = joystick port 1, 2 = joystick port 2.
			bit 1, a				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.
			jr nz, .NoClimbUp
								; Test if can ClimbUp more.
			call TestForClimbUp			; OUT ZF = 1 can climb.
			ret nz

.ClimbUpNow:		ld hl, $0020
			push hl					; hl = upward movement.
			call TestForJumping			; OUT BC = distance to the ceiling.
			pop hl
			call z, MoveOnJump			; In BC = space for jumping.

			jp .ClimbAnimation



.NoClimbUp:		bit 2, a
			jr nz, .NoClimbDown
								; Test if can ClimbDown more.
			call TestForClimbDown			; OUT ZF = 1 can climb.
			ret nz

.ClimbDownNow:		ld hl, $0020
			push hl
			call TestForFalling			; OUT ZF = 1 can fall. BC = distance to the ground.
			pop hl
			call z, MoveOnGravity			; IN BC = limit distance to fall.



.ClimbAnimation:	ld a, (ix + Sprite.LogicArea + 5)
			add a, $08
			and $18
			ld b, a
			ld a, (ix + Sprite.LogicArea + 5)
			and $E7
			or b
			ld (ix + Sprite.LogicArea + 5), a
			and $10
			rra
			rra
			ld (ix + Sprite.PatternFlip), a
			ld (ix + Sprite.LogicArea + 10), 10
			jp CommonClass.Pattern



.NoClimbDown:		bit 0, a
			jr nz, .NoEscapeLeft
								; Want to escape left.
			res 5, (ix + Sprite.LogicArea + 1)	; Turn off the climb mode.
			res 3, (ix + Sprite.LogicArea + 2)	; Escape with slow speed.

			ld (ix + Sprite.LogicArea + 4), 10	; Setup a little jump to the left.
			set 6, (ix + Sprite.LogicArea + 2)
			res 2, (ix + Sprite.LogicArea + 2)

			ld (ix + Sprite.LogicArea + 10), 9	; Setup the initial pattern for the escape.
			ld (ix + Sprite.PatternFlip), 4
			jp CommonClass.Pattern



.NoEscapeLeft:		bit 3, a
			ret nz
								; Want to escape right.
			res 5, (ix + Sprite.LogicArea + 1)	; Turn off the climb mode.
			res 3, (ix + Sprite.LogicArea + 2)	; Escape with slow speed.

			ld (ix + Sprite.LogicArea + 4), 10	; Setup a little jump to the right.
			set 6, (ix + Sprite.LogicArea + 2)
			set 2, (ix + Sprite.LogicArea + 2)

			ld (ix + Sprite.LogicArea + 10), 9	; Setup the initial pattern for the escape.
			ld (ix + Sprite.PatternFlip), 0
			jp CommonClass.Pattern


								; Currently is not climbing a ladder, but check if want to take a ladder.
.NoClimbLadder:		ld a, (ix + Sprite.LogicArea + 2)	; So first test if want to climb up.
			and 3
			call Stick				; IN A = 0 keyboard, 1 = joystick port 1, 2 = joystick port 2.
			bit 1, a				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.
			jr nz, .NoTakeLadderUp
								; Test if can Climb.
			call TestForClimbUp			; OUT ZF = 1 can climb.
			jr nz, .NoTakeLadder

			set 5, (ix + Sprite.LogicArea + 1)	; Flags that it want to take the ladder.

			ld a, (ix + Sprite.MapOx)		; Now align the player with the ladder.
			add a, $20
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld (ix + Sprite.MapTx), a
			ld (ix + Sprite.MapOx), 0

			jp .ClimbUpNow



.NoTakeLadderUp:	bit 2, a
			jr nz, .NoTakeLadder
								; Test if can Climb.
			call TestForClimbDown			; OUT ZF = 1 can climb.
			jr nz, .NoTakeLadder

			set 5, (ix + Sprite.LogicArea + 1)	; Flags that it want to take the ladder.

			ld a, (ix + Sprite.MapOx)		; Now align the player with the ladder.
			add a, $20
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld (ix + Sprite.MapTx), a
			ld (ix + Sprite.MapOx), 0

			jp .ClimbDownNow



.NoTakeLadder:		ld a, (ix + Sprite.LogicArea + 2)	; The player wants to launch a secondary weapon?.
			and 3
			call Stick				; IN A = 0 keyboard, 1 = joystick port 1, 2 = joystick port 2.
			and $04					; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.
			jr nz, .NotSecondaryWeapon
								; DOWN arrow pressed.
			bit 6, (ix + Sprite.LogicArea + 1)	; Test if DOWN key was JUST pressed.
			jr nz, .SecondaryWeaponEscape
								; Ok, just pressed.
			set 6, (ix + Sprite.LogicArea + 1)	; Remember that it is pressed.


								; Check if there is a next weapon.
			call WhatIsNextWeapon			; IN IX = player. OUT C = Weapon item, = 0 no.			
			ld a, c
			and a
			jr z, .SecondaryWeaponEscape		; Escape if no next weapon.

								; Ok, there is a secondary weapon to shot.
			ld b, TileMap.Weapons_StartAddress	; Decode/recode the weapon class. Until Now, the weapon was just the tile number of the weapon texture.
			sub b
			rra
			add a, SpriteClasses.SecondaryWeaponStartAddress
			ld c, a					; C = weapon class for the Sprite.Class.

			push ix					; Save in IY this player's sprite/instance pointer.
			pop iy

			push ix
			push bc
								; Asks for a new sprite slot.
			call GetFreeSpriteSlot			; OUT A = ID sprite slot, IX = sprite pointer, CF = 1 error.
			jr c, .NoSpaceForNewWeapon

			pop bc
								; And there is space for start a secondary weapon.
			ld h, (iy + Sprite.MapTy)		; Reads this player position.
			ld l, (iy + Sprite.MapOy)
			ld d, (iy + Sprite.MapTx)
			ld e, (iy + Sprite.MapOx)

			call SetSprite				; IN A = ID, H L = Ty/Oy, D E = Tx/Ox, C = Class, H' L' = Ty, Tx respawn ID.

			ld a, (iy + Sprite.LogicArea + 2)
			and $0C
			ld (ix + Sprite.LogicArea + 2), a	; Sets the direction and speed used for some weapons.

			pop ix					; Restore this own player Sprite/instance pointer.

			call UseNextWeapon			; Remove the weapon from the vault, as it has been used.

			ld a, (ix + Sprite.LogicArea + 6)
			call UpdateOverlayGrabItem.In

			ld a, 5
			call SFXRequest				; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.

			jp .SecondaryWeaponEscape

.NoSpaceForNewWeapon:	pop bc
			pop ix

			jp .SecondaryWeaponEscape

.NotSecondaryWeapon:	res 6, (ix + Sprite.LogicArea + 1)	; Marks that the DOWN key was released. So, the secondary weapon only launch on pumping the DOWN key.
.SecondaryWeaponEscape:


								; Is the player on damaging tiles?.
			call TestForKillingMe			; OUT ZF = 1 ok, ZF = 0 killing me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.
			jp z, .SafeFromTiles

			ld a, c					; Test if damaging from the upper half part of the sprite?.
			cp 2
			ld a, 0					; A = 0, Cancel any current jump for when damaging from the upper half part.
			jr c, .DamagingFromUpperHalf
								; Is damaging from the bottom half part.
			ld a, 14				; A = 14, set up a little jump for the player, to help escape the trap.

.DamagingFromUpperHalf:	ld (ix + Sprite.LogicArea + 4), a

.CalculateDamage:	ex af, af'				; The neddles is the first thing that kills.
			cp TileMap.KillNeddles_StopAddress	; Tile number that did the damage.

			ld e, 6					; SFX for neddles & others.
			ld bc, $1000				; 1 / 16 damage, Needles things.
			jr c, .DoDamageTile			; Jumps if neddles.

			cp TileMap.KillLava_StopAddress
								; Misc things.
			ld bc, $1000				; 1 / 16 damage.
			jr nc, .DoDamageTile
								; It is Lava.
			ld e, 7					; SFX lava.
			ld bc, 24576				; 1 / 8 + 1 / 4.

.DoDamageTile:		push bc
			ld a, e
			call SFXRequest				; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.
			pop bc



.DoDamage:		res 5, (ix + Sprite.LogicArea + 1)	; Turn off the climb mode.

			ld l, (ix + Sprite.Energy)		; IN BC = damage.
			ld h, (ix + Sprite.Energy + 1)		; Reduce the energy.
			and a
			sbc hl, bc
			jr c, .Death
			jr z, .Death

			ld (ix + Sprite.Energy), l
			ld (ix + Sprite.Energy + 1), h		; Save the new reduced energy.

			ld a, (ix + Sprite.LogicArea + 6)
			call UpdateOverlayGrabItem.In

			jp .SafeFromTiles

.Death:			ld c, TileMap.Potion_Address		; Checks if he has the revival potion.
			call UseAItem				; IN C = item. IX = Player. OUT ZF = 1 found and removed. ZF = 0 not present.
			jr nz, .ReallyHeIsDead
								; He has the revival potion and used it!.
			ld hl, 32768 + 16384
			ld (ix + Sprite.Energy), l
			ld (ix + Sprite.Energy + 1), h

			ld c, TileMap.Potion_Address		; Shows the potion in the overlay.
			call UpdateOverlayGrabItem		; IN IX = Player. C = recently grab item.

			ld a, 18
			call SFXRequest

			jp .SafeFromTiles

.ReallyHeIsDead:	ld (ix + Sprite.Energy), 0
			ld (ix + Sprite.Energy + 1), 0

			ld (ix + Sprite.LogicArea + 10), 3
			ld (ix + Sprite.PatternFlip), 0
			call CommonClass.Pattern

			ld (ix + Sprite.LogicArea + 0), 0
			ld (ix + Sprite.Class), SpriteClasses.CrushedClass

			ld a, (ix + Sprite.LogicArea + 6)
			call RemoveOverlay

			ld a, 19
			jp SFXRequest



.SafeFromTiles:		ld l, (ix + Sprite.LogicArea + 22)	; Checks if the player has some inertia from a hit.
			ld h, (ix + Sprite.LogicArea + 23)

			ld a, h
			or l
			jp z, .NoInertia

			ld a, h
			and a
			jr z, .InertiaToRight
								; Inertia to the left.
			ld de, $0010				; reduce the inertia force.
			add hl, de

			ld (ix + Sprite.LogicArea + 22), l
			ld (ix + Sprite.LogicArea + 23), h

			and a
			sbc hl, de				; Back to the actual value.

			ld a, l					; To positive sign.
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

			push hl
			call TestForWallOnHisLeft		; OUT ZF = 1 no wall. BC = distance to the wall.
			pop hl
			call z, WalkToTheLeft			; IN HL = step. IN BC = Maximun ammount that can walk.
		
			jp .NoInertia



.InertiaToRight:	ld de, $0010				; Reduce the inertia force.
			sbc hl, de

			ld (ix + Sprite.LogicArea + 22), l
			ld (ix + Sprite.LogicArea + 23), h

			add hl, de				; Back to the actual value.

			push hl
			call TestForWallOnHisRight		; OUT ZF = 1 no wall. BC = distance to the wall.
			pop hl
			call z, WalkToTheRight			; IN HL = step. IN BC = Maximun ammount that can walk.



.NoInertia:		ld a, (ix + Sprite.LogicArea + 5)	; Check if can attack again, or isn't allowed because a previous attack cycle still incomplete.
			and $07
			jr z, .CanAttack

			dec a					; Can't attack so fast, countdown for next attack.
			ld b, a					; Save the countdown value.

			cp 2
			jr nc, .CountDownAndOthersThings 

			ld a, (ix + Sprite.LogicArea + 10)	; Is currently showing the attack pattern?.
			cp 2
			jr nz, .CountDownAndOthersThings

			ld (ix + Sprite.LogicArea + 10), 6	; Remove the attack pattern only if was showing that one.

			bit 2, (ix + Sprite.LogicArea + 2)
			ld (ix + Sprite.PatternFlip), $00
			jr nz, .NoFlipThisTime
			ld (ix + Sprite.PatternFlip), $04
.NoFlipThisTime:
			push bc
			call CommonClass.Pattern
			pop bc



.CountDownAndOthersThings:
			ld a, (ix + Sprite.LogicArea + 5)	; Save the updated countdown for next attack.
			and $F8
			or b
			ld (ix + Sprite.LogicArea + 5), a

			jp .OtherActions



.CanAttack:		ld a, (ix + Sprite.LogicArea + 2)	; Check if the player hits the primary button.
			and 3
			call StrigPrimary			; OUT A = 0 pressed.
			and a
			jp nz, .PButtonWasOff

			bit 7, (ix + Sprite.LogicArea + 1)	; Checks that the player press off/on the button.
			jp nz, .OtherActions

			set 7, (ix + Sprite.LogicArea + 1)

			bit 2, (ix + Sprite.LogicArea + 2)
			ld (ix + Sprite.PatternFlip), $04
			jr z, .AttackPatternLeft
			ld (ix + Sprite.PatternFlip), $00

.AttackPatternLeft:	ld (ix + Sprite.LogicArea + 10), 2	; = 2 Attack pattern offset.
			call CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 21)	; SFX depends on what has on his hand.
			add a, 11				; use SFX = 11 up to 16.
			call SFXRequest				; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.

			ld a, (ix + Sprite.LogicArea + 21)	; Hoover depends on the handweapon.
			inc a
			sra a					; Frames to hoover = 0,1,1,2,2,3 for (no weapon, dagger, axe, knife, rambo knife, sword).
			ld (ix + Sprite.LogicArea + 12), a	; set, how many frames to hoover.

			and a					; /// ATTACK POWER DATA.
			ld hl, 0				; No damage for no having a handweapon.
			jr z, .SetAttackPower

			dec a
			ld hl, 512				; Damage for dagger.
			jr z, .SetAttackPower

			dec a
			ld hl, 1024				; Damage for axe.
			jr z, .SetAttackPower

			dec a
			ld hl, 2048				; Damage for knife.
			jr z, .SetAttackPower

			dec a
			ld hl, 4096				; Damage for rambo knife.
			jr z, .SetAttackPower

			ld hl, 6144				; Damage for sword.

.SetAttackPower:	ld (AttackPower), hl

			ld a, (ix + Sprite.LogicArea + 5)	; Sets countdown for next attack. And it also depends on the handweapon.
			and $F8
			ld c, a
			ld a, 6
			sub (ix + Sprite.LogicArea + 21)
			or c					; Frames to wait for allow next attack = 6,5,4,3,2,1 for (no weapon, dagger, axe, knife, rambo knife, sword).
			ld (ix + Sprite.LogicArea + 5), a

			xor a
			ld (IgnorePlayers), a			; FRIENDLY FIRE = TRUE ;).

			bit 2, (ix + Sprite.LogicArea + 2)	; The hitbox size also depends on the sword. The direction depends on the last walk direction.
			jr z, .LeftHitBoxDraw


								; Checks if on his right is a breakable block that can be attacked and if so, attack it.
			call TestForBreakableRight		; IN IX = who.
			push af

			ld a, (ix + Sprite.LogicArea + 21)	; Retrieve handweapon level. It will hit with block force (0,1,2,3,3,3).
			cp 4
			jr c, .BlockPowerLimit
			ld a, 3
.BlockPowerLimit:
			ld b, a

			pop af
			call z, BreakingRight			; IN IX = who, B = hit force in breakables scale.

			ld a, (ix + Sprite.LogicArea + 21)
			ld e, a
			add a, a
			add a, e
			ld e, a
			ld d, 0
			ld hl, .AttackAreaData
			add hl, de

			ld a, (hl)				; Reads the box top.
			inc hl

			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)

			add a, c
			ld c, a
			jr nc, .Add16_8_J1
			inc b
.Add16_8_J1:
			ld e, (hl)				; Reads the box height.
			inc hl
			ld d, 0

			push hl
			exx
			pop hl

			ld e, (hl)				; Reads the box width.
			ld d, 0

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			inc b					; X+16 = attack box LEFT.

			jp AttackLogic				; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.

.AttackAreaData:	DB $70, $10, $10			; Nothing. AttackBox offset TOP / HEIGHT / WIDTH.
			DB $70, $20, $50			; Dagger.
			DB $40, $70, $50			; Axe.
			DB $70, $20, $70			; Knife.
			DB $60, $40, $80			; Rambo knife.
			DB $60, $30, $D0			; Sword.



.LeftHitboxDraw:						; Checks if on his left is a breakable block that can be attacked and if so, attack it.
			call TestForBreakableLeft		; IN IX = who.
			push af

			ld a, (ix + Sprite.LogicArea + 21)	; Retrieve handweapon level. It will hit with block force (0,1,2,3,3,3).
			cp 4
			jr c, .BlockPowerLimit2
			ld a, 3
.BlockPowerLimit2:
			ld b, a

			pop af
			call z, BreakingLeft

			ld a, (ix + Sprite.LogicArea + 21)
			ld e, a
			add a, a
			add a, e
			ld e, a
			ld d, 0

			ld hl, .AttackAreaData
			add hl, de

			ld a, (hl)				; Reads the box top.
			inc hl

			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)

			add a, c
			ld c, a
			jr nc, .Add16_8_J2
			inc b
.Add16_8_J2:
			ld e, (hl)				; Reads the box height.
			inc hl
			ld d, 0

			push hl
			exx
			pop hl

			ld e, (hl)				; Reads the box width.
			ld d, 0

			ld a, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			sub e
			ld c, a
			jr nc, .Sub16_8_J1
			dec b
.Sub16_8_J1:
			jp AttackLogic				; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.

.PButtonWasOff:		res 7, (ix + Sprite.LogicArea + 1)




.OtherActions:		ld a, (ix + Sprite.MapOx)		; The player is on a teleport?.
			add a, $80
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a
			jr c, .NoTelePort

			ld a, (ix + Sprite.MapOy)
			add a, $80
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a
			jr c, .NoTelePort

			call IsOnTeleport			; IN H = ty, L = tx (range 0-255tx, 0-127ty).
			and a					; OUT A = 0 nothing, A <> 0 Teleport. H L = Ty Tx, C = Map number.
			jr z, .NoTelePort
								; The player is on a teleport.
			ld (TeleportDestination), hl		; Setup the teleport variables.
			ld a, c
			ld (TeleportMapDestination), a
			ld a, 255				; Flags the teleport method, which must be done outside the player object.
			ld (TeleportEnable), a

.NoTelePort:		ld a, (ix + Sprite.MapOx)		; The player is overlapping an item?.
			add a, $80
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld l, a
			jr c, .NoGrab

			ld a, (ix + Sprite.MapOy)
			add a, $80
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld h, a
			jr c, .NoGrab

			push hl
			call IsOnItem
			pop hl
			and a
			jr z, .NoGrab
								; Grab an item.
			push hl					; IN C = Item that it grabs. H L = Ty Tx where is the item.

			push bc
			call GetMapTileNumber			; OUT HL = tile MSB address, and RAM segment settled.
			pop bc

			ld a, c
			and 1
			ld de, TileMap.BlackBackgroundTile_Address
			jr z, .Black
			ld de, TileMap.BlueBackgroundTile_Address
.Black:
			ld (hl), d				; Erase the item from the map.
			dec hl
			ld (hl), e
								; Save the item in the vault.
			call SaveItemInVault			; IN C = item.

			pop hl
			call UpdateTileVisuals			; Render the tile which was holding the item just grabbed.
.NoGrab:



			ld a, (ix + Sprite.LogicArea + 4)	; It is currently goin up in a jump?. CF = 0.
			and a
			jr z, .NoReduceJumpForce
			dec a					; Reduce the jump force factor.
			ld (ix + Sprite.LogicArea + 4), a
.NoReduceJumpForce:
			ld l, a
			ld h, 0
			add hl,hl
			add hl,hl
			add hl,hl
			add hl,hl				; format: tttttttt pppp 0000.

			ld de, (GravityIntensity)
			sbc hl, de
			jr c, .GoingDown


								; Going up or steady when jump.
			push hl					; hl = compensated upward movement.
			call TestForJumping			; OUT BC = distance to the ceiling.
			pop hl
			push af
			call z, MoveOnJump			; In BC = space for jumping.
			pop af
			jr z, .WentUp


								; It hits some ceiling. Test for breakable blocks.
			call TestForBreakableCeiling
			jr nz, .WentUp
								; It is brakable.
			ld a, (ix + Sprite.LogicArea + 3)	; Calculates the breaking force.
			and $C0					; It depends on the helmet.
			rlca
			rlca
			inc a
			ld b, a

			call BreakingCeiling

			ld (ix + Sprite.LogicArea + 4), 0	; Cancel the jump force if hit a breakable block. It is for no damaging several times the block in a single jump.



.WentUp:		bit 6, (ix + Sprite.LogicArea + 2)
			jr z, .WasNotWalkingWhenJumpedUp

			call .GetPlayerWalkSpeed		; Out HL = step.

			bit 2, (ix + Sprite.LogicArea + 2)
			jr z, .JumpingLeftDirection
								; Jumping right direction.
			push hl
			call TestForWallOnHisRight
			pop hl
			call z, WalkToTheRight

			ld (ix + Sprite.LogicArea + 10), 9
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern



.JumpingLeftDirection:	push hl
			call TestForWallOnHisLeft
			pop hl
			call z, WalkToTheLeft

			ld (ix + Sprite.LogicArea + 10), 9
			ld (ix + Sprite.PatternFlip), $04
			jp CommonClass.Pattern



.WasNotWalkingWhenJumpedUp:
			ld (ix + Sprite.LogicArea + 10), 5
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern



.GoingDown:		add hl, de				; Restores.

			ld a, (ix + Sprite.LogicArea + 12)
			and a
			jr z, .AllowFalling

			dec (ix + Sprite.LogicArea + 12)
			ret



.AllowFalling:		ex de, hl
			and a
			sbc hl, de				; HL = gravity compensated.

			push hl
			call TestForFalling			; OUT ZF = 1 can fall. BC = distance to the ground.
			pop hl
			jp nz, .NoFall

			set 7, (ix + Sprite.LogicArea + 2)	; Marks it as falling.

			call MoveOnGravity			; The character is falling. IN HL = falling step, IN BC = limit distance to fall.

			bit 6, (ix + Sprite.LogicArea + 2)
			jp z, .WasNotWalkingWhenJumped
								; Was walking when jumped.
			call .GetPlayerWalkSpeed		; OUT HL = step.

			bit 2, (ix + Sprite.LogicArea + 2)
			jr z, .FallingLeftDirection


								; Falling right direction.
			ld (ix + Sprite.PatternFlip), $00
			ld (ix + Sprite.LogicArea + 10), 9

			ld a, (ix + Sprite.LogicArea + 2)	; When falling the player can control a bit the fall.
			and 3
			push hl
			call Stick				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.

			bit 0, a
			jr nz, .NoBrakesToTheRight

			pop hl
			ld de, $0010				; Brakes a bit.
			and a
			sbc hl, de
			push hl

			ld (ix + Sprite.LogicArea + 10), 4

.NoBrakesToTheRight:	call TestForWallOnHisRight
			pop hl
			call z, WalkToTheRight

			jp CommonClass.Pattern



.FallingLeftDirection:	ld (ix + Sprite.PatternFlip), $04	; IN HL = Jump X displacement.
			ld (ix + Sprite.LogicArea + 10), 9

			ld a, (ix + Sprite.LogicArea + 2)	; When falling the player can control a bit the fall.
			and 3
			push hl
			call Stick				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.

			bit 3, a
			jr nz, .NoBrakesToTheLeft

			pop hl
			ld de, $0010				; Brakes a bit.
			and a
			sbc hl, de
			push hl

			ld (ix + Sprite.LogicArea + 10), 4

.NoBrakesToTheLeft:	call TestForWallOnHisLeft
			pop hl
			call z, WalkToTheLeft

			jp CommonClass.Pattern



.WasNotWalkingWhenJumped:ld a, (ix + Sprite.LogicArea + 2)	; When falling the player can control a bit the fall to the left or right because it jumped straight up.
			and 3
			call Stick				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.

			ld hl, 0

			bit 0, a
			jr nz, .NoLeft

			call TestForWallOnHisLeft
			ld hl, $0010
			call z, WalkToTheLeft

			ld (ix + Sprite.LogicArea + 10), 9
			ld (ix + Sprite.PatternFlip), $04
			jp CommonClass.Pattern

.NoLeft:		bit 3, a
			jr nz, .NoRight

			call TestForWallOnHisRight
			ld hl, $0010
			call z, WalkToTheRight

			ld (ix + Sprite.LogicArea + 10), 9
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern

.NoRight:		ld (ix + Sprite.LogicArea + 10), 4
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern


								; The player is now on the floor. Some floor breaks when landing on it, other floor can break when just walking on it.
.NoFall:		call TestForBreakableFloor		; OUT ZF = 1 breakable. E = 1 by just walking on.
			jr nz, .NoBreakable

			bit 7, (ix + Sprite.LogicArea + 2)	; Was falling just before? In others words, he just landed?.
			jr nz, .BreakingOnLanding
								; No, he don't landed.
			bit 0, e				; Break by just walking on it?.
			jr z, .NoBroke
								; It breaks by just walking on it.
			ld b, 0					; Sets break power = 0.
			jp .FloorBreaking



.BreakingOnLanding:	ld a, (ix + Sprite.LogicArea + 3)	; The break power is related to the shoes he is wearing.
			and 3
			inc a
			ld b, a



.FloorBreaking:		call BreakingFloor			; IN B = hit force.  OUT ZF = 1 if just broke.
			jr nz, .NoBroke


								; It just broke.
			ld a, (ix + Sprite.LogicArea + 2)	; Give the player the chance of jumping when just broke the floor.
			and 3
			call StrigSecondary			; IN A = 0 keyboard secondary button, 1 = joystick port 1 secondary button, 2 = joystick port 2 secondary button.
			and a					; OUT A = 0 pressed, <> 0 not pressed.
			jr z, .JumpOnFloorJustBroke



.NoBroke:							; The player is on the floor.
.NoBreakable:		bit 7, (ix + Sprite.LogicArea + 2)	; Just landed or not?.
			res 7, (ix + Sprite.LogicArea + 2)	; Reset that flag.
			jr z, .WasntLanding
								; Was just landing.
			ld (ix + Sprite.LogicArea + 4), 0	; Resets any remaining jump force.

			ld (ix + Sprite.LogicArea + 10), 3	; Set impact pattern. And don't let to do anything else on this frame.
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern



.WasntLanding:		res 6, (ix + Sprite.LogicArea + 2)	; Resets the "jumped when running" aswell.

			ld a, (ix + Sprite.LogicArea + 2)	; Want to jump?.
			and 3
			call StrigSecondary			; IN A = 0 keyboard secondary button, 1 = joystick port 1 secondary button, 2 = joystick port 2 secondary button.
			and a					; OUT A = 0 pressed, <> 0 not pressed.
			jr nz, .NoWantToJump



.JumpOnFloorJustBroke:						; He want to jump.
			ld a, (ix + Sprite.LogicArea + 2)	; Check if want to jump forward, in the direction that he is facing.
			and 3
			call Stick
			bit 0, a
			jr nz, .StickNoLeft
								; Stick on the left.
			bit 2, (ix + Sprite.LogicArea + 2)
			jr nz, .NoMatch

.Match:			set 6, (ix + Sprite.LogicArea + 2)	; Matchs, set the "jump when running" flag.

.NoMatch:		ld a, (ix + Sprite.LogicArea + 3)	; The jump force is related to the shoes quality.
			and 3
			add a, 15				; Adjust jump force for all shoes.
			ld (ix + Sprite.LogicArea + 4), a	; Set up the initial jump force.

			ld (ix + Sprite.LogicArea + 10), 3	; pattern taking force for jump.
			ld (ix + Sprite.PatternFlip), $00



			ld a, (ix + Sprite.LogicArea + 16)	; Reads and reduce the shoes lifetime.
			and a
			jp z, CommonClass.Pattern

			dec a
			ld (ix + Sprite.LogicArea+ 16), a
			jp nz, CommonClass.Pattern


								; The shoes lifetime just expired.
			ld a, (ix + Sprite.LogicArea + 3)	; Remove the shoes.
			and $FC
			ld (ix + Sprite.LogicArea + 3), a

			push ix					; Regenerate this player's patterns set.
			call BuildUpPlayerPatterns
			pop ix

			jp CommonClass.Pattern



.StickNoLeft:		bit 3, a
			jr nz, .NoMatch
								; Stick on the right.
			bit 2, (ix + Sprite.LogicArea + 2)
			jr z, .NoMatch
			jp .Match



.NoWantToJump:		bit 2, (ix + Sprite.LogicArea + 2)	; Which direction was walking?.
			jp z, .WasWalkingLeftDirection


								; Was walking right direction.
			ld a, (ix + Sprite.LogicArea + 2)
			and 3
			call Stick				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.
			bit 0, a
			jr z, .DirectionChangeToLeft
			bit 3, a
			jr nz, .PauseWalkingRight

			call .GetPlayerWalkSpeed		; Continue walking to the right.
			push hl
			ld a, 255
			ld (PushingObjectWalking), a		; Flags that it is a player and is on the floor when walking.
			call TestForWallOnHisRight
			ld a, 0
			ld (PushingObjectWalking), a
			pop hl
			push af
			call z, WalkToTheRight
			pop af
			call nz, TestForDoorOnRight		; If there is a door, it opens the door if the player has the key.

			ld a, (ix + Sprite.LogicArea + 5)	; Check if was a short pause and if so, speed up.
			and $E0
			jr z, .NoSpeedUp1

			cp $60					; Pause threshold.
			jr nc, .NoSpeedUp1

			set 3, (ix + Sprite.LogicArea + 2)

.NoSpeedUp1:		ld a, (ix + Sprite.LogicArea + 5)
			and $1F					; Resets pause countup.
			ld (ix + Sprite.LogicArea + 5), a

			ld a, (ix + Sprite.LogicArea + 5)	; Animation sequence.
			add a, $08
			and $18
			ld b, a

			ld a, (ix + Sprite.LogicArea + 5)
			and $E7
			or b
			ld (ix + Sprite.LogicArea + 5), a

			and $18
			cp $18
			jr nz, .NormalSequence

			res 4, a

.NormalSequence:	rrca
			rrca
			rrca
			add a, 6
			ld (ix + Sprite.LogicArea + 10), a
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern

.PauseWalkingRight:	res 3, (ix + Sprite.LogicArea + 2)	; Maybe is a short pause for pump and speed up.

			ld a, (ix + Sprite.LogicArea + 5)
			add a, $20
			ret z

			ld (ix + Sprite.LogicArea + 5), a
			jp .TestForDoorKnock



.DirectionChangeToLeft: res 2, (ix + Sprite.LogicArea + 2)	; Changes the direction to the left and slowdown.
			res 3, (ix + Sprite.LogicArea + 2)

			ld a, (ix + Sprite.LogicArea + 5)
			and $1F					; Resets pause countup.
			ld (ix + Sprite.LogicArea + 5), a

			ld (ix + Sprite.LogicArea + 10), 4
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern



.WasWalkingLeftDirection:ld a, (ix + Sprite.LogicArea + 2)
			and 3
			call Stick				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.
			bit 3, a
			jr z, .DirectionChangeToRight
			bit 0, a
			jr nz, .PauseWalkingLeft

			call .GetPlayerWalkSpeed		; Continue walking to the left.
			push hl
			ld a, 255
			ld (PushingObjectWalking), a		; Flags that it is a player and is on the floor when walking.
			call TestForWallOnHisLeft
			ld a, 0
			ld (PushingObjectWalking), a
			pop hl
			push af
			call z, WalkToTheLeft
			pop af
			call nz, TestForDoorOnLeft		; If there is a door, it opens the door if the player has the key.

			ld a, (ix + Sprite.LogicArea + 5)	; Check if was a short pause and if so, speed up.
			and $E0
			jr z, .NoSpeedUp2

			cp $60					; Pause threshold.
			jr nc, .NoSpeedUp2

			set 3, (ix + Sprite.LogicArea + 2)

.NoSpeedUp2:		ld a, (ix + Sprite.LogicArea + 5)
			and $1F					; Resets pause countup.
			ld (ix + Sprite.LogicArea + 5), a

			ld a, (ix + Sprite.LogicArea + 5)	; Animation sequence.
			add a, $08
			and $18
			ld b, a

			ld a, (ix + Sprite.LogicArea + 5)
			and $E7
			or b
			ld (ix + Sprite.LogicArea + 5), a

			and $18
			cp $18
			jr nz, .NormalSequence2

			res 4, a

.NormalSequence2:	rrca
			rrca
			rrca
			add a, 6
			ld (ix + Sprite.LogicArea + 10), a
			ld (ix + Sprite.PatternFlip), $04
			jp CommonClass.Pattern

.PauseWalkingLeft:	res 3, (ix + Sprite.LogicArea + 2)

			ld a, (ix + Sprite.LogicArea + 5)	; Maybe is a short pause for pump and speed up.
			add a, $20
			ret z

			ld (ix + Sprite.LogicArea + 5), a
			jp .TestForDoorKnock



.DirectionChangeToRight:set 2, (ix + Sprite.LogicArea + 2)	; Changes the direction to the right and slowdown.
			res 3, (ix + Sprite.LogicArea + 2)

			ld a, (ix + Sprite.LogicArea + 5)
			and $1F					; Resets pause countup.
			ld (ix + Sprite.LogicArea + 5), a

			ld (ix + Sprite.LogicArea + 10), 4
			ld (ix + Sprite.PatternFlip), $00
			jp CommonClass.Pattern



.TestForDoorKnock:	ld a, (ix + Sprite.LogicArea + 2)
			and 3
			call Stick				; OUT A = bit 3 right, bit 2 down, bit 1 up, bit 0 left.
			bit 1, a
			ret nz

			call TestIfIsRoomDoor			; Test if the player is on a room door. IN IX = player. OUT Z = is on room door, B = Map MSB.
			ret nz

			ld a, b
			and MapAttribs.MaskMinor

			cp MapAttribs.RoomDoor.Cocktails
			ret c					; Ignore invalid codes for RoomDoor.

			ld (RoomDoorType), a

			ld (PlayerThatEnter), ix		; Points who enter in the room.

			ld a, 255
			ld (SwitchToRoom), a

			ld a, 20
			jp SFXRequest



.GetPlayerWalkSpeed:	ld a, (ix + Sprite.LogicArea + 3)	; Out HL = speed.
			and 3					; Reads the shoes quality.
			add a, a
			add a, .WalkSpeedData mod 256
			ld l, a
			ld h, .WalkSpeedData / 256
			jr nc, .AdrOk

			inc h

.AdrOk:			bit 3, (ix + Sprite.LogicArea + 2)
			jr z, .NoSpeedUp

			inc hl

.NoSpeedUp:		ld l, (hl)
			ld h, 0
			ret

.WalkSpeedData:		DB $10, $20				; No shoes.
			DB $10, $30				; Violet shoes.
			DB $20, $30				; Yellow shoes.
			DB $20, $40				; White shoes.



.Attacked:							; IN IX = attacked sprite. IY = Who is attacking. BC = damage received.
								; IN A = 0 high damage hitbox. <> 0 low damage hitbox.

			ld hl, (GravityIntensity)		; Makes a little jump backwards (to my lefT).
			ld de, $0030
			add hl, de				; 3 + 2 + 1 = 6 pixels jump.
			sra h
			rr l
			sra h
			rr l
			sra h
			rr l
			sra h
			rr l
			ld (ix + Sprite.LogicArea + 4), l

			res 3, (ix + Sprite.LogicArea + 2)	; Signals slow.

			ld l, (iy + Sprite.MapOx)		; Check if who is attacking me is at my right.
			ld h, (iy + Sprite.MapTx)

			ld e, (ix + Sprite.MapOx)
			ld d, (ix + Sprite.MapTx)

			and a
			sbc hl, de

			ld hl, $FFC0
			jr nc, .EnemyAtMyRight
			ld hl, $0040
.EnemyAtMyRight:
			ld e, (ix + Sprite.LogicArea + 22)
			ld d, (ix + Sprite.LogicArea + 23)

			add hl, de				; Accumulative inertia.

			ld (ix + Sprite.LogicArea + 22), l
			ld (ix + Sprite.LogicArea + 23), h

			and a
			jr nz, .AttackedBody
								; Was attacked in the head.
			ld a, (ix + Sprite.LogicArea + 7)	; Reads the head-helmet lifetime.
			and a
			jr z, .HeadHelmetOk			; It don't has head-helmet.

			dec a
			ld (ix + Sprite.LogicArea + 7), a
			jr nz, .HeadHelmetOk

			ld a, (ix + Sprite.LogicArea + 3)	; Clears the head-helmet attribute.
			and $3F
			ld (ix + Sprite.LogicArea + 3), a

			push iy
			push ix
			push bc
			call BuildUpPlayerPatterns		; Now must undress this player patterns. IN IX = Player.
			pop bc
			pop ix
			pop iy

.HeadHelmetOk:		ld a, (ix + Sprite.LogicArea + 3)	; Reads the head helmet level.
			and $C0
			ld l, c
			ld h, b
			ld c, a
			ld b, 0
			rl c
			rl b
			rl c
			rl b
			sbc hl, bc				; Reduce the damage received. From 0 up to 768.
			jr nc, .CarryDamage1
			ld hl, 0
.CarryDamage1:
			ld c, l
			ld b, h
			
			ld a, 21
			call SFXRequestShield

			jp .DoDamage				; IN BC = damage.			

.AttackedBody:		ld a, (ix + Sprite.LogicArea + 13)	; Reads the body-helmet lifetime.
			and a
			jr z, .BodyHelmetOk			; It don't has body-helmet.

			dec a
			ld (ix + Sprite.LogicArea + 13), a
			jr nz, .BodyHelmetOk

			ld a, (ix + Sprite.LogicArea + 3)	; Clears the armour attribute.
			and $CF
			ld (ix + Sprite.LogicArea + 3), a

			push iy
			push ix
			push bc
			call BuildUpPlayerPatterns		; Now must undress this player patterns. IN IX = Player.
			pop bc
			pop ix
			pop iy

.BodyHelmetOk:		ld a, (ix + Sprite.LogicArea + 3)	; Reads the body helmet level.
			and $30
			add a,a
			add a,a
			ld l, c
			ld h, b
			ld c, a
			ld b, 0
			rl c
			rl b
			sbc hl, bc				; Reduce the damage received. From 0 up to 384.
			jr nc, .CarryDamage2
			ld hl, 0
.CarryDamage2:
			ld c, l
			ld b, h

			ld a, 22
			call SFXRequestShield

			jp .DoDamage				; IN BC = damage.






HandWeaponClass:	ld a, ixl
			add a, Sprite.SizeOf
			ld iyl, a
			ld a, ixh
			jr nc, .Noiyh
			inc a
.Noiyh:			ld iyh, a

			ld a, (iy + Sprite.LogicArea + 10)	; Check if the player is showing the attack pattern or not.
			cp 2
			jr nz, .NoShow

			ld a, (iy + Sprite.MapOy)		; Same Y as related player.
			ld (ix + Sprite.MapOy), a
			ld a, (iy + Sprite.MapTy)
			ld (ix + Sprite.MapTy), a

			ld a, (iy + Sprite.LogicArea + 21)	; Shows the correct pattern.
			add a, 33
			ld (ix + Sprite.Pattern), a

			ld a, (iy + Sprite.PatternFlip)		; Check which direction is attacking.
			and 4
			ld (ix + Sprite.PatternFlip), a			
			jr z, .Right
								; .Left.
			ld a, (iy + Sprite.MapOx)
			sub $D0
			ld (ix + Sprite.MapOx), a
			ld a, (iy + Sprite.MapTx)
			sbc a, 0
			ld (ix + Sprite.MapTx), a
			ret

.Right:			ld a, (iy + Sprite.MapOx)
			add a, $D0
			ld (ix + Sprite.MapOx), a
			ld a, (iy + Sprite.MapTx)
			adc a, 0
			ld (ix + Sprite.MapTx), a
			ret

.NoShow:		ld (ix + Sprite.ScreenWidth), 0		; Special hack code for skipping the "RenderOneSprite" routine, and marks it as no rendered, so it don't trigger a background restore.
			pop bc
			jp IACharacters.SpecialReturnHack






; + 1 bit 0 = 0 Going up, = 1 Going down.
; + 3 = wait before start in another direction.

ElevatorClass:		ld a, (ix + Sprite.LogicArea + 3)
			and a
			jr z, .Normal

			dec (ix + Sprite.LogicArea + 3)
			ret

.Normal:		ld a, (ix + Sprite.Class)		; Checks if it is left/right or up/down.
			and a					; Is left/right?.
			jp nz, .LeftRight


								; It is up/down.
			ld a, (ix + Sprite.MapRespawnTy)	; Chose the correct elevator's pattern for this elevator.
			and 1
			ld b, a
			ld a, (ix + Sprite.MapRespawnTx)
			and 1
			add a, a
			add a, b
			add a, 194				; Elevator class patterns (4 differents patterns for up/down).
			ld (ix + Sprite.Pattern), a 		; This class don't has a hitbox, so it uses fast assignament.

			bit 0, (ix + Sprite.LogicArea + 1)	; Which current direction up/down?.
			jr z, .Up


								; Down.
			call TestForFalling
			push af
			push bc
			call z, .DownTree
			pop bc
			pop af
			jr nz, .StopDown

			ld hl, $0010
			call MoveOnGravity

			ld a, (ix + Sprite.MapOy)
			and $70
			jp nz, .Attack

			ld a, (IsTurboEnabled)
			and a
			jp nz, .Attack

			ld (ix + Sprite.LogicArea + 3), 8

.Attack:		ld a, (ix + Sprite.MapOy)		; Kill anything crushed.
			add a, $80
			ld c, a
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld b, a
			ld de, $0080
			exx

			ld a, (ix + Sprite.MapOx)
			add a, $10				; Don't kill on the right/left border overlap.
			ld c, a
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld b, a
			
			ld de, $00E0

			ld hl, 65535
			ld (AttackPower), hl

			xor a
			ld (IgnorePlayers), a
			jp AttackLogic				; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.
.StopDown:		res 0, (ix + Sprite.LogicArea + 1)

			ld a, (IsTurboEnabled)
			and a
			jp nz, .Attack

			ld (ix + Sprite.LogicArea + 3), 8
			jp .Attack



.Up:			call TestForJumping			; OUT BC = distance to the ceiling.
			push af
			push bc
			call z, .UpTree
			pop bc
			pop af
			jr nz, .StopUp

			ld hl, $0010
			call MoveOnJump				; In BC = space for jumping.

			ld a, (ix + Sprite.MapOy)
			and $70
			jp nz, .Attack

			ld a, (IsTurboEnabled)
			and a
			jp nz, .Attack

			ld (ix + Sprite.LogicArea + 3), 8

			jp .Attack



.StopUp:		set 0, (ix + Sprite.LogicArea + 1)

			ld a, (IsTurboEnabled)
			and a
			jp nz, .Attack

			ld (ix + Sprite.LogicArea + 3), 8
			jp .Attack



.LeftRight:		ld a, (ix + Sprite.MapRespawnTy)	; Chose the correct elevator's pattern for this elevator.
			and 1
			ld b, a
			ld a, (ix + Sprite.MapRespawnTx)
			and 1
			add a, a
			add a, b
			add a, 198				; Elevator class patterns (4 differents patterns for left/right).
			ld (ix + Sprite.Pattern), a 		; This class don't has a hitbox, so it uses fast assignament.

			bit 0, (ix + Sprite.LogicArea + 1)	; Which current direction left/right?.
			jr z, .MLeft


								; Right.
			call TestForWallOnHisRight		; OUT Z can walk, BC = space before the wall.
			jr nz, .StopRight

			ld hl, $0010
			call WalkToTheRight			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.
			jp .Attack



.StopRight:		res 0, (ix + Sprite.LogicArea + 1)
			jp .Attack



.MLeft:			call TestForWallOnHisLeft		; OUT Z can walk, BC = space before the wall.
			jr nz, .StopLeft

			ld hl, $0010
			call WalkToTheLeft			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.
			jp .Attack



.StopLeft:		set 0, (ix + Sprite.LogicArea + 1)
			jp .Attack



.UpTree:		ld a, (SpritesCount)			; IN IX = who is elevating/holding things on his top.
			ld b, a



			ld iy, SpritesPool

.Loop:			push bc

			ld a, (iy + Sprite.Class)
			cp SpriteClasses.EmptySlotClass
			jp z, .Next

.DoUpCharacter:		ld c, (ix + Sprite.MapOx)		; Test for X alignment.
			ld b, (ix + Sprite.MapTx)
			ld l, (iy + Sprite.MapOx)
			ld h, (iy + Sprite.MapTx)

			and a
			sbc hl, bc
			jr c, .Left

			ld a, h
			and a
			jp nz, .Next
								; If H = 0 means distance is 15 pixels or less.
.TestY:			ld l, (ix + Sprite.MapOy)		; Test if Y is on his top.
			ld h, (ix + Sprite.MapTy)
			ld c, (iy + Sprite.MapOy)
			ld b, (iy + Sprite.MapTy)

			and a
			sbc hl, bc
								; Only interested in boxes at his top.
			ld a, h
			dec a
			jr nz, .Next				; Not interested if not on his Top (16 pixels range on his top).
			or l
			jr nz, .Next

.OnTop:			push ix					; Test for crushing.
			push iy
			push iy
			pop ix
			call TestForJumping
			pop iy		
			pop ix
			jr nz, .Crushed

			push iy
			push ix

			push iy
			pop ix
			ld a, (ix + Sprite.Class)
			cp SpriteClasses.SpacingClassesStopAddress	; Is it a box or another elevator, as it has the property for holding others things...
			call c, .UpTree				; Call this routine recursively on that object.

			pop ix
			pop iy

			ld a, (iy + Sprite.MapOy)		; Push that class up!.
			sub $10
			ld (iy + Sprite.MapOy), a
			jp nc, .Next
			dec (iy + Sprite.MapTy)
			jp .Next

.Crushed:		push hl

			xor a
			ld hl, 65535				; Kill the object crushed: IY only.
			call AttackLogic.Attack 		; A = hitbox flag, IX = who is attacking, IY = who is being attacked.
								; HL = Damage.
			pop hl
			jp .Next

.Left:			ld a, h
			inc a
			jr nz, .Next
								; If H = $FF means distance is 16 pixels or less.
			or l
			jp nz, .TestY				; Only interact if is 15 pixels or less.

.Next:			ld bc, Sprite.SizeOf
			add iy, bc
			pop bc
			dec b
			jp nz, .Loop
			ret



.DownTree:		ld a, (SpritesCount)			; IN IX = who is elevating/holding things on his top.
			ld b, a

			ld iy, SpritesPool

.Loop2:			push bc

			ld a, (iy + Sprite.Class)
			cp SpriteClasses.EmptySlotClass
			jp z, .Next2

.DoDownCharacter:	ld c, (ix + Sprite.MapOx)		; Test for Y alignment.
			ld b, (ix + Sprite.MapTx)
			ld l, (iy + Sprite.MapOx)
			ld h, (iy + Sprite.MapTx)

			and a
			sbc hl, bc
			jr c, .Left2

			ld a, h
			and a
			jr nz, .Next2
								; If H = 0 means distance is 15 pixels or less.
.TestY2:		ld l, (ix + Sprite.MapOy)		; Test if Y is on his top.
			ld h, (ix + Sprite.MapTy)
			ld c, (iy + Sprite.MapOy)
			ld b, (iy + Sprite.MapTy)

			and a
			sbc hl, bc
								; Only interested in boxes at his top (16 pixels away on his top).
			ld a, h
			dec a
			jr nz, .Next2

			ld a, l
			and a
			jr nz, .Next2

.OnTop2:		push iy
			push ix

			push iy
			pop ix
			ld a, (ix + Sprite.Class)
			cp SpriteClasses.SpacingClassesStopAddress	; Is it a box or another elevator, as it has the property for holding others things...
			call c, .DownTree			; Call this routine recursively on that object.

			pop ix
			pop iy

			ld a, (ix + Sprite.MapTy)		; Remove the object from the way, to see if this object falls.
			push af

			ld (ix + Sprite.MapTy), $FE		; sets Illegal position and away from everything.

			push iy
			push ix
			push iy
			pop ix
			call TestForFalling
			pop ix
			pop iy
			jr nz, .NoFreeFall

			pop af
			ld (ix + Sprite.MapTy), a		; Restore the original position.

			ld a, (iy + Sprite.MapOy)		; Lets that class move down.
			add a, $10
			ld (iy + Sprite.MapOy), a
			ld a, (iy + Sprite.MapTy)
			adc a, 0
			ld (iy + Sprite.MapTy), a
			jp .Next2

.NoFreeFall:		pop af
			ld (ix + Sprite.MapTy), a
			jp .Next2		

.Left2:			ld a, h
			inc a
			jr nz, .Next2
								; If H = $FF means distance is 16 pixels or less.
			ld a, l
			and a
			jr nz, .TestY2				; Only interact if is 15 pixels or less.

.Next2:			ld bc, Sprite.SizeOf
			add iy, bc
			pop bc
			dec b
			jp nz, .Loop2

.Attacked:		ret






; + 2 bit 0 = 1 signals that somebody is pushing it to the left.
; + 2 bit 1 = 1 signals that somebody is pushing it to the right.

CubeClass:		ld a,  (ix + Sprite.LogicArea + 2)	; Cubes must be pushed if a player is pushing them, and it can be pushed, if not must push back the player.
			res 0, (ix + Sprite.LogicArea + 2)	; Cubes must maintain away others characters.
			res 1, (ix + Sprite.LogicArea + 2)	; Cubes must maintain away weapons if them overlaps with it.
			cp 1
			jr z, .PushedLeft
			cp 2
			jr nz, .DoY
								; Pushed right.
			call TestForWallOnHisRight		; OUT ZF = 1 can walk, BC = space before the wall.
			jr nz, .DoY

			ld hl, $0020
			call WalkToTheRight
			jp .DoY

.PushedLeft:		call TestForWallOnHisLeft		; OUT ZF = 1 can walk, BC = space before the wall.
			jr nz, .DoY

			ld hl, $0020
			call WalkToTheLeft

.DoY:			call TestForFallingAccurate		; Cubes must fall, and kill anybody when falling.
			ld hl, (GravityIntensity)
			call z, MoveOnGravity

			ld a, (ix + Sprite.MapRespawnTy)	; Chose the correct cube pattern for this cube.
			and 1
			ld b, a
			ld a, (ix + Sprite.MapRespawnTx)
			and 1
			add a, a
			add a, b
			add a, 202				; Cube class patterns (4 differents patterns).
			call SetSpritePattern

			ld a, (ix + Sprite.MapOy)		; Kill anything that is overlapping the box.
			add a, $20
			ld c, a
			ld a, (ix + Sprite.MapTy)
			adc a, 0
			ld b, a
			ld de, $00E0
			exx

			ld a, (ix + Sprite.MapOx)
			add a, $20
			ld c, a
			ld a, (ix + Sprite.MapTx)
			adc a, 0
			ld b, a
			ld de, $00C0

			ld hl, 65535				; Crush attack.
			ld (AttackPower), hl

			xor a
			ld (IgnorePlayers), a

			jp AttackLogic				; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.

.Attacked:		ld hl, $7FFF				; IN IX = attacked sprite. IY = Who is attacking. BC = damage received.
								; IN A = 0 high damage hitbox. <> 0 low damage hitbox.
			and a
			sbc hl, bc
			jr z, .Crush
			ld a, b					; Checks if it is a crush from an elevator or box.
			cp $80
			ret c

.Crush:			ld e, ixl				; Remove from the spacing object list.
			ld d, ixh

			call SpacingListUnsubscribe		; IN DE = Object address.

			ld (ix + Sprite.LogicArea + 0), 0	; Was damage = >=32767, so, it is from a crush.
			ld (ix + Sprite.Class), SpriteClasses.CrushedClass	; Set the CrushedClass for this box.
			ret






; USAGE for LogicArea: TURTLES
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk		; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 Initialized?.
; + 10 Current pattern offset.
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.				; 0 = no.

TurtleClass:		ld a, (ix + Sprite.LogicArea + 9)	; Is already initialized?.
			and a
			jr nz, .AlreadyInit

			ld (ix + Sprite.LogicArea + 9), $FF	; Initializes.
			ld (ix + Sprite.LogicArea + 11), 155 	; character base pattern number.

			ld a, r
			res 0, (ix + Sprite.LogicArea + 2)
			and 1
			jr z, .AlreadyInit
			set 0, (ix + Sprite.LogicArea + 2)

.AlreadyInit:		call CommonClass.IsTileKillingMe	; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.
			ld a, 23
			jp nz, SFXRequest

			call CommonClass.TestIfAttacked		; OUT [NZ] just attacked.
			jp nz, CommonClass.Pattern

			call CommonClass.WillingToAttack3	; Check if can attack?. OUT [Z] can attack.
			jr nz, .OthersActions

			ld (ix + Sprite.LogicArea + 6), 9	; Set delay for next attack.

			bit 0,  (ix + Sprite.LogicArea + 2)	; Attack.
			jr z, .AttackLeft			; Build up an attacked area based on wich side the spider is facing.
								; Attack right.
			ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0010
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0050
			exx

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld bc, $00D0
			add hl, bc				; The attack zone is the last 3 pixels of the pattern.
			ld c, l
			ld b, h
			ld de, $0030

.DoAttack:		xor a
			ld (IgnorePlayers), a

			ld hl, 1024
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.			
			ld (ix + Sprite.LogicArea + 10), 2	; Attack pattern offset number.

			ld a, 24
			call SFXRequest

			jp CommonClass.Pattern

.AttackLeft:		ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0010
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0050
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0030

			jr .DoAttack

.OthersActions:		call CommonClass.ApplyGravityAndJump	; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			jp z, CommonClass.Pattern
			and a					; Only walk if is in the floor, not in the ceiling.
			jp z, CommonClass.Pattern

			call CommonClass.TestIfJustFalled	; OUT [NZ] just falled.
			jp nz, CommonClass.Pattern

			ld hl, $0010		
			call CommonClass.Walk			; IN [HL] = Step << 4, OUT [NZ] no walk.
			jp z, CommonClass.Pattern
								; Didn't walk because hit something.
			call CommonClass.SwitchWalkDirection
			jp CommonClass.Pattern

.Attacked:		call CommonClass.Attacked

			ld a, (ix + Sprite.Class)		; Check if this character is turtle. Elsecase is crushed class.
			cp SpriteClasses.TurtleCharacterClass	; Class = 3 = Turtle.
			jp nz, CommonClass.Pattern

			ld (ix + Sprite.LogicArea + 0), 12	; Extend the attack shock over the standard.
			jp CommonClass.Pattern






; USAGE for LogicArea: BeesClass.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk/fly	; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 
; + 10 Current pattern offset (Action + animation).
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.				; 0 = no.
; + 14 Flying direction.					; (range 0 - 15 for 0-360º).

; + 15 Character behaviour stage.

; + 17, 16 Ty/Oy of the victim player.
; + 19, 18 Ty/Oy for return to the guard area.

; + 20 Flying change direction delay in frames count.

BeesClass:		ld (ix + Sprite.LogicArea + 11), 185	; Bee base sprite pattern number.

			call CommonClass.TestIfAttacked
			jp nz, CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 15)	; What is doing?.
			and a
			jr z, .WasJustFlying
			dec a
			jr z, .WasDroppingForAttack
			dec a
			jp z, .WasHoldingTheAttackPattern
			dec a
			jp z, .WasReturningToGuardArea



.WasJustFlying:		ld (ix + Sprite.PatternFlip), 0

			ld a, (ix + Sprite.LogicArea + 20)
			inc a
			and 1
			ld (ix + Sprite.LogicArea + 20), a
			jr nz, .NoChangeDir

			ld a, (ix + Sprite.LogicArea + 14)
			inc a
			and $0F
			ld (ix + Sprite.LogicArea + 14), a

.NoChangeDir:		ld b, 3
			call CommonClass.Fly			; IN B = Speed (range 0-5).

			call CommonClass.WillingToAttackLB	; Check if can attack and want?. OUT [Z] can attack.
			jp nz, CommonClass.Pattern		; H L = relative player position (-) = top/left, (+) = down/right. ZF = 1 safe, ZF = 0 danger detected

			ld a, h					; Only if the player is under this character and aligned enough.
			cp 40
			jp nc, CommonClass.Pattern

			ld a, l
			cp 12
			jr c, .SetDrop

			cp 256-11
			jp c, CommonClass.Pattern

.SetDrop:		inc (ix + Sprite.LogicArea + 15)	; Sets dropping mode.

			bit 7, l
			ld a, 0
			jr z, .ToRight
			ld a, 4
.ToRight:		ld (ix + Sprite.PatternFlip), a

			ld a, (iy + Sprite.MapTy)		; Now, must set the target Y.
			ld (ix + Sprite.LogicArea + 17), a
			ld a, (iy + Sprite.MapOy)
			ld (ix + Sprite.LogicArea + 16), a

			ld a, (ix + Sprite.MapTy)		; Now, must set the return to guard Y.
			ld (ix + Sprite.LogicArea + 19), a
			ld a, (ix + Sprite.MapOy)
			ld (ix + Sprite.LogicArea + 18), a

			jp CommonClass.Pattern



.WasDroppingForAttack:	ld e, (ix + Sprite.LogicArea + 16)	; Calculate the distance to the target.
			ld d, (ix + Sprite.LogicArea + 17)
			ld l, (ix + Sprite.MapOy)
			ld h, (ix + Sprite.MapTy)
			sbc hl, de
			jr nc, .Attack				; The target is reached?.

			call TestForFalling			; OUT BC = distance to the floor.
			ld hl, $0080				; HL = Drop speed.
			push af
			call z, MoveOnGravity
			pop af
			jr nz, .FloorCrash

			ld a, (ix + Sprite.LogicArea + 7)	; Animate the drop.
			inc a
			and 1
			ld (ix + Sprite.LogicArea + 7), a

			add a, 4
			ld (ix + Sprite.LogicArea + 10), a	; Sets the drop pattern.

			jp CommonClass.Pattern

.FloorCrash:		ld (ix + Sprite.LogicArea + 10), 3	; Sets the crash with floor pattern.

			inc (ix + Sprite.LogicArea + 15)	; Sets Returning to guard mode.
			inc (ix + Sprite.LogicArea + 15)

			jp CommonClass.Pattern

.Attack:		ld a, (ix + Sprite.MapOy)		; Sets the attack area.
			add a, $80
			ld c, a
			ld b, (ix + Sprite.MapTy)
			jr nc, .JJJ20
			inc b
.JJJ20:
			ld de, $0080
			exx

			ld a, (ix + Sprite.MapOx)
			add a, $20
			ld c, a
			ld b, (ix + Sprite.MapTx)
			jr nc, .JJJ21
			inc b
.JJJ21:
			ld de, $00C0

			ld hl, 2048 
			ld (AttackPower), hl

			xor a
			ld (IgnorePlayers), a
			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.

			ld (ix + Sprite.LogicArea + 10), 2	; Sets the attack pattern.

			inc (ix + Sprite.LogicArea + 15)	; Sets Returning to guard mode.

			ld (ix + Sprite.LogicArea + 7), 3	; Reset the hold count (3 frames).

			jp CommonClass.Pattern



.WasHoldingTheAttackPattern:
			dec (ix + Sprite.LogicArea + 7)
			ret nz

			inc (ix + Sprite.LogicArea + 15)
			ret



.WasReturningToGuardArea:
			ld e, (ix + Sprite.LogicArea + 18)	; Calculate the distance to the guard position.
			ld d, (ix + Sprite.LogicArea + 19)
			ld l, (ix + Sprite.MapOy)
			ld h, (ix + Sprite.MapTy)
			sbc hl, de
			jr z, .SetGuardMode
			jr c, .SetGuardMode			; The guard area is reached?.

			call TestForJumping			; OUT BC = distance to the ceiling.
			ld hl, $0030				; Fly climbing speed.
			jr nz, .SetGuardMode

			call MoveOnJump				; In BC = space for jumping.
			call CommonClass.WalkPattern
			jp CommonClass.Pattern

.SetGuardMode:		ld (ix + Sprite.LogicArea + 15), 0	; Sets guard mode.

			jp CommonClass.WalkPattern

.Attacked:		jp CommonClass.Attacked






; USAGE for LogicArea: Spiders.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk		; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 
; + 10 Current pattern offset.
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.				; 0 = no.

SpiderClass:		ld (ix + Sprite.LogicArea + 11), 165 	; character base pattern number.

			call CommonClass.IsTileKillingMe	; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.

			ld a, 25
			jp nz, SFXRequest

			call CommonClass.TestIfAttacked		; OUT [NZ] just attacked.
			jp nz, CommonClass.Pattern

			call CommonClass.WillingToAttack3	; Check if can attack?. OUT [Z] can attack.
			jr nz, .OthersActions

			ld (ix + Sprite.LogicArea + 6), 6	; Set delay for next attack.

			bit 0,  (ix + Sprite.LogicArea + 2)	; Attack.
			jr z, .AttackLeft			; Build up an attacked area based on wich side the spider is facing.
								; Attack right.
			ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0050
			exx

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld bc, $00D0
			add hl, bc				; The attack zone is the last 3 pixels of the pattern.
			ld c, l
			ld b, h
			ld de, $0030

.DoAttack:		xor a
			ld (IgnorePlayers), a

			ld hl, 3072
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.			
			ld (ix + Sprite.LogicArea + 10), 2	; Attack pattern offset number.

			ld a, 26
			call SFXRequest

			jp CommonClass.Pattern

.AttackLeft:		ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0050
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0030

			jr .DoAttack

.OthersActions:		call CommonClass.ApplyGravityAndJump	; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			jp z, CommonClass.Pattern
			and a					; Only walk if is in the floor, not in the ceiling.
			jp z, CommonClass.Pattern

			call CommonClass.TestIfJustFalled	; OUT [NZ] just falled.
			jp nz, CommonClass.Pattern

			ld hl, $0040		
			call CommonClass.Walk			; IN [HL] = Step << 4, OUT [NZ] no walk.
			jp z, CommonClass.Pattern
								; Didn't walk because hit something.
			ld a, r
			bit 1, a
			jr nz, .DecideToJump

			call CommonClass.SwitchWalkDirection
			jp CommonClass.Pattern

.DecideToJump:		ld hl, $00C0
			ld de, $0020
			call CommonClass.Jump			; IN [HL] = Jump force, [DE] = X displacement.
			jp CommonClass.Pattern

.Attacked:		call CommonClass.Attacked
			jp CommonClass.Pattern






; USAGE for LogicArea: Frog.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk		; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 Floor flag.						; <> 0 Flag indicating that the frog is in the air, not in the floor.
; + 10 Current pattern offset.
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.		
; + 14 Countdown for next jump.

FrogClass:		ld (ix + Sprite.LogicArea + 11), 175 	; character base pattern number.

			call CommonClass.IsTileKillingMe	; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.

			ld a, 27
			jp nz, SFXRequest

			call CommonClass.TestIfAttacked		; OUT [NZ] just attacked.
			jp nz, CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 9)
			and a
			jr z, .CanFlipIfAttack

			call CommonClass.WillingToAttack2	; Check if can attack?. OUT [Z] can attack.
			jr nz, .OthersActions
			jr .Attack

.CanFlipIfAttack:	call CommonClass.WillingToAttack3	; Check if can attack?. OUT [Z] can attack.

			jr nz, .OthersActions

.Attack:		ld (ix + Sprite.LogicArea + 6), 6	; Set delay for next attack.

			bit 0,  (ix + Sprite.LogicArea + 2)	; Attack.
			jr z, .AttackLeft			; Build up an attacked area based on wich side the spider is facing.
								; Attack right.
			ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0050
			exx

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld bc, $00D0
			add hl, bc				; The attack zone is the last 3 pixels of the pattern.
			ld c, l
			ld b, h
			ld de, $0030

.DoAttack:		xor a
			ld (IgnorePlayers), a

			ld hl, 1024
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.			
			ld (ix + Sprite.LogicArea + 10), 2	; Attack pattern offset number.

			ld a, 28
			call SFXRequest

			jp CommonClass.Pattern

.AttackLeft:		ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0050
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0030

			jr .DoAttack

.OthersActions:		ld (ix + Sprite.LogicArea + 9), $FF

			call CommonClass.ApplyGravityAndJump	; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			jp z, CommonClass.Pattern
			and a					; Only escape if is in the ceiling, not in the floor.
			jp z, CommonClass.Pattern
								; Stopped by the floor.
			ld (ix + Sprite.LogicArea + 9), 0
			ld (ix + Sprite.LogicArea + 8), 0

			ld a, r
			and 3
			cp 3
			jr z, .P1

			add a, 6
			ld (ix + Sprite.LogicArea + 10), a

.P1:			bit 0, (ix + Sprite.LogicArea + 2)
			ld (ix + Sprite.PatternFlip), 4
			jr z, .P2
			ld (ix + Sprite.PatternFlip), 0

.P2:			ld a, (ix + Sprite.LogicArea + 14)
			and a
			jr z, .Jump

			dec a
			ld (ix + Sprite.LogicArea + 14), a
			jp CommonClass.Pattern

.Jump:			ld hl, $00C0
			ld de, $0040
			call CommonClass.Jump			; IN [HL] = Jump force, [DE] = X displacement.
			ld a, r
			rra
			and $3f
			ld (ix + Sprite.LogicArea + 14), a
			jp CommonClass.Pattern

.Attacked:		call CommonClass.Attacked
			jp CommonClass.Pattern






; USAGE for LogicArea: GuardClass.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk		; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 Floor flag.						; <> 0 Flag indicating that is in the air, not in the floor.
; + 10 Current pattern offset.
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.		
; + 14 Countdown for next jump.

GuardClass:		ld (ix + Sprite.LogicArea + 11), 206 	; character base pattern number.

			call CommonClass.IsTileKillingMe	; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.

			ld a, 29
			jp nz, SFXRequest

			call CommonClass.TestIfAttacked		; OUT [NZ] just attacked.
			jp nz, CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 9)
			and a
			jr z, .CanFlipIfAttack

			call CommonClass.WillingToAttack2	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
			jp nz, .OthersActions

			ld a, l
			cp 12
			jr c, .Attack

			cp 256-12+1
			jr nc, .Attack

			jp .OthersActions

.CanFlipIfAttack:	call CommonClass.WillingToAttack3	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
			jr nz, .OthersActions

			ld a, l
			cp 12
			jr c, .Attack

			cp 256-12+1
			jr nc, .Attack

			jp .OthersActions

.Attack:		ld (ix + Sprite.LogicArea + 6), 6	; Set delay for next attack.

			bit 0,  (ix + Sprite.LogicArea + 2)	; Attack.
			jr z, .AttackLeft			; Build up an attacked area based on wich side the spider is facing.
								; Attack right.
			ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld bc, $00C0
			add hl, bc				; The attack zone is the last 3 pixels of the pattern.
			ld c, l
			ld b, h
			ld de, $0040

.DoAttack:		xor a
			ld (IgnorePlayers), a

			ld hl, 2048
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.			
			ld (ix + Sprite.LogicArea + 10), 2	; Attack pattern offset number.

			ld a, (ix + Sprite.LogicArea + 9)
			and a
			jr nz, .DoAttack_J1			; After attack, jump!.
			
			ld hl, $00C0
			ld de, $0020
			call CommonClass.Jump			; IN [HL] = Jump force, [DE] = X displacement.

.DoAttack_J1:		ld a, 30
			call SFXRequest

			jp CommonClass.Pattern

.AttackLeft:		ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0040

			jr .DoAttack

.OthersActions:		ld (ix + Sprite.LogicArea + 9), $FF

			call CommonClass.ApplyGravityAndJump	; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			jp z, CommonClass.Pattern
			and a					; Only escape if is in the ceiling, not in the floor.
			jp z, CommonClass.Pattern
								; Stopped by the floor.
			ld (ix + Sprite.LogicArea + 9), 0
			ld (ix + Sprite.LogicArea + 8), 0

			call CommonClass.TestIfJustFalled	; OUT [NZ] just falled.
			jp nz, CommonClass.Pattern

			ld hl, $0020		
			call CommonClass.Walk			; IN [HL] = Step << 4, OUT [NZ] no walk.
			jp z, CommonClass.Pattern
								; Didn't walk because hit something.
			call CommonClass.SwitchWalkDirection
			jp CommonClass.Pattern

.Attacked:		call CommonClass.Attacked
			jp CommonClass.Pattern






; USAGE for LogicArea: DogClass.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk		; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 Floor flag.						; <> 0 Flag indicating that is in the air, not in the floor.
; + 10 Current pattern offset.
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.		
; + 14 Countdown for next jump.
; + 15 Stay quiet count down.

DogClass:		ld (ix + Sprite.LogicArea + 11), 216 	; character base pattern number.

			call CommonClass.IsTileKillingMe	; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.
			ld a, 31
			jp nz, SFXRequest

			call CommonClass.TestIfAttacked		; OUT [NZ] just attacked.
			jp nz, CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 9)
			and a
			jr z, .CanFlipIfAttack

			call CommonClass.WillingToAttack2	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
			jp nz, .OthersActions

			ld a, l
			cp 12
			jr c, .Attack

			cp 256-12+1
			jr nc, .Attack

			jp .OthersActions

.CanFlipIfAttack:	call CommonClass.WillingToAttack3	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
			jr nz, .OthersActions

			ld a, l
			cp 12
			jr c, .Attack

			cp 256-12+1
			jr nc, .Attack

			jp .OthersActions

.Attack:		ld (ix + Sprite.LogicArea + 6), 4	; Set delay for next attack.

			bit 0,  (ix + Sprite.LogicArea + 2)	; Attack.
			jr z, .AttackLeft			; Build up an attacked area based on wich side the spider is facing.
								; Attack right.
			ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld bc, $00C0
			add hl, bc				; The attack zone is the last 3 pixels of the pattern.
			ld c, l
			ld b, h
			ld de, $0040

.DoAttack:		xor a
			ld (IgnorePlayers), a

			ld hl, 2048 + 1024
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.			
			ld (ix + Sprite.LogicArea + 10), 2	; Attack pattern offset number.

			ld a, (ix + Sprite.LogicArea + 9)
			and a
			jr nz, .DoAttack_J1			; After attack, jump!.
			
			ld hl, $00C0
			ld de, $0020
			call CommonClass.Jump			; IN [HL] = Jump force, [DE] = X displacement.

.DoAttack_J1:		ld a, 32
			call SFXRequest

			jp CommonClass.Pattern

.AttackLeft:		ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0040

			jr .DoAttack

.OthersActions:		ld (ix + Sprite.LogicArea + 9), $FF

			call CommonClass.ApplyGravityAndJump	; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			jp z, CommonClass.Pattern
			and a					; Only escape if is in the ceiling, not in the floor.
			jp z, CommonClass.Pattern
								; Stopped by the floor.
			ld (ix + Sprite.LogicArea + 9), 0
			ld (ix + Sprite.LogicArea + 8), 0

			call CommonClass.TestIfJustFalled	; OUT [NZ] just falled.
			jp nz, CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 15)	; Checks if must stay quiet.
			and a
			jr z, .NoStayQuiet

			dec a
			ld (ix + Sprite.LogicArea + 15), a

			ld (ix + Sprite.LogicArea + 10), 3 
			jp CommonClass.Pattern

.NoStayQuiet:		ld a, r
			cp 5
			jr nc, .Walk

			add a, a
			add a, a
			add a, a
			add a, a
			ld (ix + Sprite.LogicArea + 15), a

.Walk:			ld hl, $0030		
			call CommonClass.WalkPlatform		; IN [HL] = Step << 4, OUT [NZ] no walk.
			jp z, CommonClass.Pattern
								; Didn't walk because hit something.
			call CommonClass.SwitchWalkDirection
			jp CommonClass.Pattern

.Attacked:		call CommonClass.Attacked

			ld a, (ix + Sprite.Class)
			cp SpriteClasses.DogCharacterClass	; Still a dog?.
			jp nz, CommonClass.Pattern

			ld (ix + Sprite.LogicArea + 0), 3	; Custom shock if attacked.
			jp CommonClass.Pattern






; USAGE for LogicArea: WizardClass.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk		; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 Floor flag.						; <> 0 Flag indicating that is in the air, not in the floor.
; + 10 Current pattern offset.
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.		
; + 14 Countdown for next jump.
; + 15 Stay quiet count down.

WizardClass:		ld (ix + Sprite.LogicArea + 11), 226 	; character base pattern number.

			call CommonClass.IsTileKillingMe	; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.

			ld a, 33
			jp nz, SFXRequest

			call CommonClass.TestIfAttacked		; OUT [NZ] just attacked.
			jp nz, CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 9)
			and a
			jr z, .CanFlipIfAttack

			call CommonClass.WillingToAttack2	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
			jp .Attack2

.CanFlipIfAttack:	call CommonClass.WillingToAttack3	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
.Attack2:		jp nz, .OthersActions

.Attack:		ld (ix + Sprite.LogicArea + 6), 10	; Set delay for next attack.

			bit 0,  (ix + Sprite.LogicArea + 2)	; Attack.
			jr z, .AttackLeft			; Build up an attacked area based on wich side the spider is facing.
								; Attack right.
			ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld bc, $00C0
			add hl, bc				; The attack zone is the last 3 pixels of the pattern.
			ld c, l
			ld b, h
			ld de, $0040

.DoAttack:		xor a
			ld (IgnorePlayers), a

			ld hl, 8192
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.			
.ThrowFireball:		ld (ix + Sprite.LogicArea + 10), 2	; Attack pattern offset number.

			push ix					; Save Me.
			push ix
			pop iy
								; Asks for a new sprite slot.
			call GetFreeSpriteSlot			; OUT A = ID sprite slot, IX = sprite pointer, CF = 1 error.
			jr c, .AttackCompleted
								; And there is space for start a secondary weapon.
			ld h, (iy + Sprite.MapTy)		; Reads this player position.
			ld l, (iy + Sprite.MapOy)
			ld d, (iy + Sprite.MapTx)
			ld e, (iy + Sprite.MapOx)
			ld c, 16 				; Bomb class.

			call SetSprite				; IN A = ID, H L = Ty/Oy, D E = Tx/Ox, C = Class, H' L' = Ty, Tx respawn ID.

			ld a, (iy + Sprite.LogicArea + 2)
			rla
			rla
			and $0C
			ld (ix + Sprite.LogicArea + 2), a	; Sets the direction and speed used for some weapons.

			ld (ix + Sprite.LogicArea + 15), 255

			ld a, 34
			call SFXRequest

.AttackCompleted:	pop ix					; Restore this own player Sprite/instance pointer.

			jp CommonClass.Pattern

.AttackLeft:		ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0040

			jr .DoAttack

.OthersActions:		ld (ix + Sprite.LogicArea + 9), $FF

			ld a, r
			cp 4
			jp c, .ThrowFireball

			call CommonClass.ApplyGravityAndJump	; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			jp z, CommonClass.Pattern
			and a					; Only escape if is in the ceiling, not in the floor.
			jp z, CommonClass.Pattern
								; Stopped by the floor.
			ld (ix + Sprite.LogicArea + 9), 0
			ld (ix + Sprite.LogicArea + 8), 0

			call CommonClass.TestIfJustFalled	; OUT [NZ] just falled.
			jp nz, CommonClass.Pattern

			ld hl, $0010		
			call CommonClass.WalkPlatform		; IN [HL] = Step << 4, OUT [NZ] no walk.
			jp z, CommonClass.Pattern
								; Didn't walk because hit something.
			call CommonClass.SwitchWalkDirection
			jp CommonClass.Pattern

.Attacked:		call CommonClass.Attacked
			jp CommonClass.Pattern






; USAGE for LogicArea: DemonClass.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk		; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 Floor flag.						; <> 0 Flag indicating that is in the air, not in the floor.
; + 10 Current pattern offset.
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.		
; + 14 Countdown for next jump.
; + 15 Stay quiet count down.

DemonClass:		ld (ix + Sprite.LogicArea + 11), 145 	; character base pattern number.

			call CommonClass.IsTileKillingMe	; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.

			ld a, 35
			jp nz, SFXRequest

			call CommonClass.TestIfAttacked		; OUT [NZ] just attacked.
			jp nz, CommonClass.Pattern

			ld a, (ix + Sprite.LogicArea + 9)
			and a
			jr z, .CanFlipIfAttack

			call CommonClass.WillingToAttack2	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
			jp .Attack2

.CanFlipIfAttack:	call CommonClass.WillingToAttack3	; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
.Attack2:		jp nz, .OthersActions

.Attack:		ld (ix + Sprite.LogicArea + 6), 10	; Set delay for next attack.

			bit 0,  (ix + Sprite.LogicArea + 2)	; Attack.
			jr z, .AttackLeft			; Build up an attacked area based on wich side the spider is facing.
								; Attack right.
			ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld l, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)
			ld bc, $00C0
			add hl, bc				; The attack zone is the last 3 pixels of the pattern.
			ld c, l
			ld b, h
			ld de, $0040

.DoAttack:		xor a
			ld (IgnorePlayers), a

			ld hl, 4096
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.			
.ThrowFireball:		ld (ix + Sprite.LogicArea + 10), 2	; Attack pattern offset number.

			push ix					; Save Me.
			push ix
			pop iy
								; Asks for a new sprite slot.
			call GetFreeSpriteSlot			; OUT A = ID sprite slot, IX = sprite pointer, CF = 1 error.
			jr c, .AttackCompleted
								; And there is space for start a secondary weapon.
			ld h, (iy + Sprite.MapTy)		; Reads this player position.
			ld l, (iy + Sprite.MapOy)
			ld d, (iy + Sprite.MapTx)
			ld e, (iy + Sprite.MapOx)
			ld c, 18				; Fireball class.

			call SetSprite				; IN A = ID, H L = Ty/Oy, D E = Tx/Ox, C = Class, H' L' = Ty, Tx respawn ID.

			ld a, (iy + Sprite.LogicArea + 2)
			rla
			rla
			and $0C
			ld (ix + Sprite.LogicArea + 2), a	; Sets the direction and speed used for some weapons.

			ld (ix + Sprite.LogicArea + 15), 255

			ld a, 36
			call SFXRequest

.AttackCompleted:	pop ix					; Restore this own player Sprite/instance pointer.

			jp CommonClass.Pattern

.AttackLeft:		ld l, (ix + Sprite.MapOy)		; Build an attack zone.
			ld h, (ix + Sprite.MapTy)
			ld bc, $0060
			add hl, bc
			ld c, l
			ld b, h
			ld de, $0040
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0040

			jr .DoAttack

.OthersActions:		ld (ix + Sprite.LogicArea + 9), $FF

			ld a, r
			cp 3
			jp c, .ThrowFireball

			call CommonClass.ApplyGravityAndJump	; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			jp z, CommonClass.Pattern
			and a					; Only escape if is in the ceiling, not in the floor.
			jp z, CommonClass.Pattern
								; Stopped by the floor.
			ld (ix + Sprite.LogicArea + 9), 0
			ld (ix + Sprite.LogicArea + 8), 0

			call CommonClass.TestIfJustFalled	; OUT [NZ] just falled.
			jp nz, CommonClass.Pattern

			ld hl, $0010		
			call CommonClass.WalkPlatform		; IN [HL] = Step << 4, OUT [NZ] no walk.
			jp z, CommonClass.Pattern
								; Didn't walk because hit something.
			call CommonClass.SwitchWalkDirection
			jp CommonClass.Pattern

.Attacked:		call CommonClass.Attacked
			jp CommonClass.Pattern






Monster1Class:		ld a, 255				; This flags that there is the monster still living.
			ld (IsMainMonsterPresent), a

			ld a, (ix + Sprite.LogicArea + 0)	; Checks for initialization.
			and a
			jr nz, .AlreadyInit

			; Do all the initial setup of things here.

			; HINT: you can create new instances of secondary classes for enlarging the sprite or create any character or things to complicate the scene.
			; HINT: there is unused classID to create new classes and code linked to it. (see the end of IACharacters routine, and SpriteClasses declaration for find unused class id).
			; HINT: see how the DemonClass create and setup fireballclass as a model.

			ld a, 255
			ld (ix + Sprite.LogicArea + 0), a

.AlreadyInit:							; Normal action code.

			; HINT: you can use CommonClass movements and model see any avobe class code to see how, or hardcode non standard movements directly.



.Attacked:		ret






Monster2Class:
.Attacked:

Monster3Class:
.Attacked:

Monster4Class:
.Attacked:

Monster5Class:
.Attacked:






; USAGE for LogicArea: CommonClass.
;
; + 0 = countdown, Is recently attacked?.			; = 0 No.
; + 1 
; + 2 bit 0 = Current direction. 0 = left.
; + 3 countdown for jump again.					; If recently jump, this countdown will avoid it to jump again. Used in characters that jumps.
; + 4, 5 Current jump TakeOff force.
; + 6 countdown for going attack again.				; If already attacked, it needs to wait to attack again.
; + 7 Current animation pattern incrementer for walk/fly	; 0 = first pattern of what is it doing.
; + 8 Was just falling before?.
; + 9 
; + 10 Current pattern offset (Action + animation).
; + 11 Pattern base number.
; + 12, 13 X Displacement when jumping.				; 0 = no.
; + 14 Flying direction.					; (range 0 - 15 for 0-360º).



CommonClass:

.IsTileKillingMe:	call TestForKillingMe			; OUT [Z] ok safe, ZF = 0 killed me. C = block quarter that does the damage (or atleast the first one detected). A' = Tile number LSB detected to create damage.
			ret z

			ld (ix + Sprite.LogicArea + 0), 0
			ld (ix + Sprite.Class), SpriteClasses.CrushedClass
			ret

.TestIfAttacked:	ld a, (ix + Sprite.LogicArea + 0)	; OUT [NZ] just attacked.
			and a
			ret z

			dec a
			ld (ix + Sprite.LogicArea + 0), a
			inc a
			and 1
			ld (ix + Sprite.LogicArea + 10), a	; = 0 , = 1, attacked pattern offset numbers.
			ld (ix + Sprite.PatternFlip), 0
			ld a, 255
			and a
			ret




.TestIfJustFalled:	ld a, (ix + Sprite.LogicArea + 8)	; OUT [NZ] just falled.
			and a					; Just falled?.
			ret z

			ld (ix + Sprite.LogicArea + 8), 0	; Reset flag.
			ld (ix + Sprite.LogicArea + 10), 3	; Sets landed pattern offset number.
			ld (ix + Sprite.PatternFlip), 0
			ret



.WillingToAttack1:	ld a, (ix + Sprite.LogicArea + 6)	; Check if can attack?. OUT [Z] can attack.
			and a
			ret z

			dec a
			ld (ix + Sprite.LogicArea + 6), a
			inc a
			ret



.WillingToAttack2:	call .WillingToAttack1			; Check if can attack?. OUT [Z] can attack. H L = relative player position (-) = top/left, (+) = down/right.
			ret nz

			call TestForPlayerNear			; IN IX = character that is asking. H L = relative player position (-) = top/left, (+) = down/right. ZF = 1 safe, ZF = 0 danger detected.
			jr nz, .WillingToAttack2_1

			ld a, 255				; Is safe, don't want to attack.
			and a
			ret

.WillingToAttack2_1:	xor a
			ret



.WillingToAttackLB:	call .WillingToAttack1			; Check if can attack?. OUT [Z] can attack.  H L = relative player position (-) = top/left, (+) = down/right.
			ret nz

			call TestForPlayerNearLargeBottomScope	; IN IX = character that is asking. H L = relative player position (-) = top/left, (+) = down/right. ZF = 1 safe, ZF = 0 danger detected.
			jr nz, .WillingToAttackLB_1

			ld a, 255				; Is safe, don't want to attack.
			and a
			ret

.WillingToAttackLB_1:	xor a
			ret



.WillingToAttack3:	call .WillingToAttack2			; Check if can attack?. OUT [Z] can attack.  H L = relative player position (-) = top/left, (+) = down/right.
			ret nz

			bit 0, (ix + Sprite.LogicArea + 2)	; Checks if the character is facing the direction of the danger detected.
			jr nz, .FacingRight

			bit 7, l
			jr nz, .IsFacingCorrectly

			call .SwitchWalkDirection
			ld a, 255
			and a
			ret

.IsFacingCorrectly:	xor a
			ret

.FacingRight:		bit 7, l
			ret z
		
			call .SwitchWalkDirection
			ld a, 255
			and a
			ret



.WalkPlatform:		bit 0, (ix + Sprite.LogicArea + 2)	; IN [HL] = Step << 4, OUT [NZ] no walked.
			jr nz, .WalkPRight

.WalkPLeft:		push hl
			call TestForNextFallingLeft		; OUT ZF = 1 will fall.
			pop hl
			jr nz, .PLeftNoFall

			xor a
			dec a
			ret

.PLeftNoFall:		push hl					; IN [HL] = Step << 4, OUT [NZ] no walked.
			call TestForWallOnHisLeft
			pop hl
			ret nz
			call WalkToTheLeft
			ld (ix + Sprite.PatternFlip), 4
			call .WalkPattern
			xor a
			ret

.WalkPRight:		push hl
			call TestForNextFallingRight		; OUT ZF = 1 will fall.
			pop hl
			jr nz, .PRightNoFall

			xor a
			dec a
			ret

.PRightNoFall:		push hl					; IN [HL] = Step << 4, OUT [NZ] no walked.
			call TestForWallOnHisRight
			pop hl
			ret nz
			call WalkToTheRight
			ld (ix + Sprite.PatternFlip), 0
			call .WalkPattern
			xor a
			ret



.Walk:			bit 0, (ix + Sprite.LogicArea + 2)	; IN [HL] = Step << 4, OUT [NZ] no walked.
			jr nz, .WalkRight

.WalkLeft:		push hl					; IN [HL] = Step << 4, OUT [NZ] no walked.
			call TestForWallOnHisLeft
			pop hl
			ret nz
			call WalkToTheLeft
			ld (ix + Sprite.PatternFlip), 4
			call .WalkPattern
			xor a
			ret

.WalkRight:		push hl					; IN [HL] = Step << 4, OUT [NZ] no walked.
			call TestForWallOnHisRight
			pop hl
			ret nz
			call WalkToTheRight
			ld (ix + Sprite.PatternFlip), 0
			call .WalkPattern
			xor a
			ret

.WalkPattern:		ld a, 6					; Sprite walk base pattern offset number.
			add a, (ix + Sprite.LogicArea + 7)
			ld (ix + Sprite.LogicArea + 10), a

			ld a, (ix + Sprite.LogicArea + 7)
			inc a
			cp 3
			jr c, .NoPatternLoop

			xor a

.NoPatternLoop:		ld (ix + Sprite.LogicArea + 7), a
			ret



.SwitchWalkDirection:	bit 0, (ix + Sprite.LogicArea + 2)
			jr nz, .WasRight
								; Was left.
			set 0, (ix + Sprite.LogicArea + 2)
			ld (ix + Sprite.PatternFlip), 0
			ld (ix + Sprite.LogicArea + 10), 3
			ret			

.WasRight:		res 0, (ix + Sprite.LogicArea + 2)
			ld (ix + Sprite.PatternFlip), 0
			ld (ix + Sprite.LogicArea + 10), 3
			ret



.Jump:			ld (ix + Sprite.LogicArea + 4), l	; IN [HL] = Jump force, [DE] = X displacement.
			ld (ix + Sprite.LogicArea + 5), h
			ld (ix + Sprite.LogicArea + 12), e
			ld (ix + Sprite.LogicArea + 13), d

			ld (ix + Sprite.LogicArea + 10), 3
			ld (ix + Sprite.PatternFlip), 0
			ret



.ApplyGravityAndJump:	ld hl, (GravityIntensity)		; OUT [Z] moved up/down, A = 0 up, <> 0 down.
			ld e, (ix + Sprite.LogicArea + 4)
			ld d, (ix + Sprite.LogicArea + 5)

			sbc hl, de
			jr nc, .WillFall
								; Is jumping.
			ex de, hl				; Negate sign.
			ld hl, 0
			sbc hl, de

			push hl
			call TestForJumping			; OUT BC = distance to the ceiling.
			pop hl
			push af
			call z, MoveOnJump			; In BC = space for jumping.
			call .UpgradeJumpForce

			ld l, (ix + Sprite.LogicArea + 12)
			ld h, (ix + Sprite.LogicArea + 13)
			ld a, l
			or h
			jr z, .Up

			call .Walk
			ld (ix + Sprite.LogicArea + 10), 9
			pop af
			ld a, 0
			ret

.Up:			ld (ix + Sprite.LogicArea + 10), 4	; 4 = Jump pattern number offset.
			ld (ix + Sprite.PatternFlip), 0
			pop af
			ld a, 0
			ret

.WillFall:		push hl
			call TestForFalling			; OUT ZF = 1 Falled.
			push af
			push bc
			call .UpgradeJumpForce
			pop bc
			pop af
			pop hl
			jr nz, .TestIfJustHitTheFloor
								; Is in the air.
			ld (ix + Sprite.LogicArea + 8), $FF	; Flags that it just falled.

			call z, MoveOnGravity			; The character is falling. IN HL = falling step, IN BC = limit distance to fall.

			ld l, (ix + Sprite.LogicArea + 12)
			ld h, (ix + Sprite.LogicArea + 13)
			ld a, l
			or h
			jr z, .FallDown

			call .Walk
			jr z, .WalkedFalling
								; No walked.
			ld (ix + Sprite.LogicArea + 12), 0
			ld (ix + Sprite.LogicArea + 13), 0
			jr .FallDown

.TestIfJustHitTheFloor:	ld a, (ix + Sprite.LogicArea + 8)
			and a
			jr z, .EndNZ

			ld a, r
			and 1

			ld (ix + Sprite.PatternFlip), 0
			ld (ix + Sprite.LogicArea + 10), 3

			set 0, (ix + Sprite.LogicArea + 2)
			jr z, .EndNZ
			res 0, (ix + Sprite.LogicArea + 2)

.EndNZ:			ld a, 255
			and a
			ret

.WalkedFalling:		ld (ix + Sprite.LogicArea + 10), 9
			xor a
			ld a, 255
			ret

.FallDown:		ld (ix + Sprite.LogicArea + 10), 4	; 4 = Jump pattern number offset.
			ld (ix + Sprite.PatternFlip), 0

			xor a
			ld a, 255
			ret

.UpgradeJumpForce:	ld l, (ix + Sprite.LogicArea + 4)
			ld h, (ix + Sprite.LogicArea + 5)
			ld bc, $0010
			and a
			sbc hl, bc
			jr c, .ZeroJumpForce

			ld (ix + Sprite.LogicArea + 4), l
			ld (ix + Sprite.LogicArea + 5), h
			ret

.ZeroJumpForce:		ld (ix + Sprite.LogicArea + 4), 0
			ld (ix + Sprite.LogicArea + 5), 0
			ret



.Pattern:		ld a, (ix + Sprite.LogicArea + 11)	; Pattern = Character base pattern number + action pattern offset number.
			ld b, (ix + Sprite.LogicArea + 10)
			add a, b
			jp SetSpritePattern			; IN A = Pattern number. MODs: AF, DE, HL.



.Fly:			push bc					; IN B = Speed (range 0-5).
			ld a, (ix + Sprite.LogicArea + 14)
			and $0F
			add a,a
			add a,a
			ld l, a
			ld h, 0
			ld de, .FlyData
			add hl, de
								; HL = address to fly X step.
			ld e, (hl)
			inc hl
			ld d, (hl)
			inc hl
			ld c, (hl)
			inc hl
			ld b, (hl)

			pop hl

			ld a, 5
			sub h
			jr z, .FlyNoDiv

.FlyDivLoop:		sra d
			rr e
			sra b
			rr c

			dec a
			jr nz, .FlyDivLoop

.FlyNoDiv:		push bc

			bit 7, d
			jr z, .FlyRight
								; It is .FlyLeft.
			ld a, e
			cpl
			ld e, a
			ld a, d
			cpl
			ld d, a
			inc de

			ex de, hl
			push hl					; IN [HL] = Step << 4, OUT [NZ] no walked.
			call TestForWallOnHisLeft
			pop hl
			call z, WalkToTheLeft

			jp .FlyYAxis

.FlyRight:		ex de, hl
			push hl					; IN [HL] = Step << 4, OUT [NZ] no walked.
			call TestForWallOnHisRight
			pop hl
			call z, WalkToTheRight

.FlyYAxis:		pop hl
			bit 7, h
			jr z, .FlyDown
								; It is .FlyUp.
			ld a, l
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

			push hl
			call TestForJumping			; OUT BC = distance to the ceiling.
			pop hl
			call z, MoveOnJump			; In BC = space for jumping.
			jp .WalkPattern				; /// In characters that only fly it using the same offset space of the walking animation, mixted character must extend the + 10 into a new offset space after calling CommonClass.Fly routine.

.FlyDown:		push hl
			call TestForFalling			; OUT ZF = 1 Falled.
			pop hl
			call z, MoveOnGravity			; The character is falling. IN HL = falling step, IN BC = limit distance to fall.
			jp .WalkPattern				; /// In characters that only fly it using the same offset space of the walking animation, mixted character must extend the + 10 into a new offset space after calling CommonClass.Fly routine.

.FlyData:		DW $0080, $0000
			DW $0060, $FFE0
			DW $0040, $FFC0
			DW $0020, $FFA0
			DW $0000, $FF80
			DW $FFE0, $FFA0
			DW $FFC0, $FFC0
			DW $FFA0, $FFE0
			DW $FF80, $0000
			DW $FFA0, $0020
			DW $FFC0, $0040
			DW $FFE0, $0060
			DW $0000, $0080
			DW $0020, $0060
			DW $0040, $0040
			DW $0060, $0020



.Attacked:		ld (ix + Sprite.LogicArea + 0), 4	; IN IX = attacked sprite. IY = Who is attacking. BC = damage received.
								; IN A = 0 high damage hitbox. <> 0 low damage hitbox.
			ld (ix + Sprite.LogicArea + 10), 0	; = 0 Pattern offset for attacked.
			ld (ix + Sprite.PatternFlip), 0

			ld l, (ix + Sprite.Energy)
			ld h, (ix + Sprite.Energy + 1)
			and a
			sbc hl, bc
			jr z, .Death
			jr c, .Death

			ld (ix + Sprite.Energy), l
			ld (ix + Sprite.Energy + 1), h

			ld a, 37
			jp SFXRequest

.Death:			ld hl, $7FFF
			and a
			sbc hl, bc
			jr z, .Crush
			ld a, b					; Checks if it is a crush from an elevator or box.
			cp $80
			jr c, .NoCrush

.Crush:			ld (ix + Sprite.LogicArea + 0), 0	; Was damage = >=32767, so, it is from a crush.
			ld (ix + Sprite.Class), SpriteClasses.CrushedClass	; Set the CrushedClass for this box.
			ret

.NoCrush:		call SubscribeRecentDeath		; Register the character for not respawming anymore in the recent time.

			ld a, (iy + Sprite.Class)
			cp SpriteClasses.PlayerClass
			jr nz, .NoPlayerAttacking

			push ix

			ld a, (ix + Sprite.Class)		; Read this class.
			add a, a
			ld e, a
			ld d, 0
			ld hl, .Points
			add hl, de

			ld e, (hl)
			inc hl
			ld d, (hl)

			push iy
			pop ix

			call ScoreUp				; IN IX = Player, DE = Score adder.

			pop ix

.NoPlayerAttacking:	ld (ix + Sprite.LogicArea + 0), 0
			ld (ix + Sprite.Class), SpriteClasses.CrushedClass

			jp CreateCoinFromMe

.Points:		DW 0
			DW 0
			DW 0
			Dw 100					; Turtle.
			DW 250					; Bee.
			DW 1000					; Spider.
			DW 750					; Frog.
			DW 2500					; Guard.
			DW 1500					; Dog.
			DW 5000					; Wizard.
			DW 7500					; Demon.
			DW 15000				; Monster1.
			DW 20000				; Monster2.
			DW 30000				; Monster3.
			DW 35000				; Monster4.
			DW 50000				; Monster5.






; + 2 bit 0 = 0 unitialized.
; + 2 bit 1 = 1 already explode.
; + 2 bit 2 = current/last direction. 0 = left.
; + 2 bit 3 = 1 Fast speed.					; If the player pumps the direction a second time, it will speed up. If he changes the direction, will slowdown.
; + 3 bit 0 = animation sequence.
; + 4 = ticking countdown.
; + 5 = X displacement per frame.
; + 6 = Jump force.
; + 7 = Jump force backup.

BombClass:		bit 0, (ix + Sprite.LogicArea + 2)
			jr nz, .NoInit
			set 0, (ix + Sprite.LogicArea + 2)

			ld (ix + Sprite.LogicArea + 4), 20	; frames countdown for explosion.

			bit 3, (ix + Sprite.LogicArea + 2)
			ld a, $30
			jr z, .Slow
			ld a, $50
.Slow:
			ld (ix + Sprite.LogicArea + 5), a

			ld (ix + Sprite.LogicArea + 6), 13
			ld (ix + Sprite.LogicArea + 7), 13

.NoInit:		ld a, (ix + Sprite.LogicArea + 4)	; Ticking.
			and a
			jp z, .Explosion

			dec a
			ld (ix + Sprite.LogicArea + 4), a

			bit 2, (ix + Sprite.LogicArea + 2)
			jr nz, .Right
								; .Left.
			call TestForWallOnHisLeft		; OUT ZF = 1 can walk, BC = space before the wall.

			ld l, (ix + Sprite.LogicArea + 5)
			ld h, 0
			push af
			call z, WalkToTheLeft			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.
			pop af
			jr z, .DoY

			set 2, (ix + Sprite.LogicArea + 2)

			ld a, 38
			call SFXRequestShield

			jp .DoY			

.Right:			call TestForWallOnHisRight		; OUT ZF = 1 can walk, BC = space before the wall.

			ld l, (ix + Sprite.LogicArea + 5)
			ld h, 0
			push af
			call z, WalkToTheRight
			pop af
			jr z, .DoY

			res 2, (ix + Sprite.LogicArea + 2)

			ld a, 38
			call SFXRequestShield

.DoY:			ld a, (ix + Sprite.LogicArea + 6)	; It is currently goin up in a jump?.
			and a
			jr z, .NoReduceJumpForce

			dec a					; Reduce the jump force factor.
			ld (ix + Sprite.LogicArea + 6), a

.NoReduceJumpForce:	ld l, a
			ld h, 0
			add hl,hl
			add hl,hl
			add hl,hl
			add hl,hl				; format: tttttttt pppp 0000.

			ld de, (GravityIntensity)
			sbc hl, de
			jr c, .GoingDown
								; Going up or steady when jump.
			push hl					; hl = compensated upward movement.
			call TestForJumping			; OUT BC = distance to the ceiling.
			pop hl
			push af
			call z, MoveOnJump			; In BC = space for jumping.
			pop af
			jr z, .Pattern

			ld (ix + Sprite.LogicArea + 6), 0	; Cancel the jump force.
			jp .Pattern

.GoingDown:		add hl, de				; Restores.

			ex de, hl
			and a
			sbc hl, de				; HL = gravity compensated.

			push hl
			call TestForFalling			; OUT ZF = 1 can fall. BC = distance to the ground.
			pop hl
			push af
			call z, MoveOnGravity
			pop af
			jr z, .Pattern

			ld a, 38
			call SFXRequestShield

			ld a, (ix + Sprite.LogicArea + 7)
			sub 2
			jr nc, .BounceSave

.BounceZero:		xor a
.BounceSave:		ld (ix + Sprite.LogicArea + 6), a
			ld (ix + Sprite.LogicArea + 7), a

.Pattern:		ld a, (ix + Sprite.LogicArea + 3)	; Patterns animation.
			inc a
			and 1
			ld (ix + Sprite.LogicArea + 3), a
			add a, 236
			ld (ix + Sprite.Pattern), a		; This class don't has a hitbox, so use normal assignament.
			ret

.Explosion:		bit 1, (ix + Sprite.LogicArea + 2)
			jp nz, RemoveMe

			set 1, (ix + Sprite.LogicArea + 2)

			ld a, (ix + Sprite.MapOy)
			sub $80
			ld c, a
			ld a, (ix + Sprite.MapTy)
			sbc a, 0
			ld b, a
			jr nc, .YOk

			ld bc, 0

.YOk:			ld de, $0200				; attack box is 32*32 pixels with the bomb centered. PERFECT.
			exx

			ld a, (ix + Sprite.MapOx)
			sub $80
			ld c, a
			ld a, (ix + Sprite.MapTx)
			sbc a, 0
			ld b, a
			jr nc, .XOk

			ld bc, 0

.XOk:			ld de, $0200

			xor a
			ld (IgnorePlayers), a

			ld hl, 2048
			ld (AttackPower), hl

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.

			ld (ix + Sprite.Pattern), 238		; This class don't has a hitbox, so use normal assignament.

			ld a, 39
			jp SFXRequestShield






; + 2 bit 0 = 0 unitialized.
; + 2 bit 2 = current/last direction. 0 = left.
; + 3 bits 0 = animation sequence.
; + 1 = Attack Countdown.					; Make sure that it only damage once per place.
; + 4 = Extinguished countdown.

TwisterClass:		bit 0, (ix + Sprite.LogicArea + 2)
			jr nz, .NoInit

			set 0, (ix + Sprite.LogicArea + 2)

			ld (ix + Sprite.LogicArea + 4), 90

.NoInit:		ld a, 9
			call TestIfSFXIsPlaying			; IN A = Effect to test. ZF = 1 is playing.
			jr z, .NoReplay

			ld a, 9
			call SFXRequest

.NoReplay:		bit 2, (ix + Sprite.LogicArea + 2)
			jr nz, .Right
								; Left.
			call TestForWallOnHisLeft		; OUT ZF = 1 can walk, BC = space before the wall.
			jr z, .WL

			set 2, (ix + Sprite.LogicArea + 2)	; Change direction.
			call .LowerLifeTime
			jp .DoY

.WL:			ld hl, $0060
			call WalkToTheLeft			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.
			jp .DoY

.Right:			call TestForWallOnHisRight		; OUT ZF = 1 can walk, BC = space before the wall.
			jr z, .WR

			res 2, (ix + Sprite.LogicArea + 2)	; Change direction.
			call .LowerLifeTime
			jp .DoY

.WR:			ld hl, $0060
			call WalkToTheRight			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.

.DoY:			call TestForFalling			; OUT ZF = 1 can fall. BC = distance to the ground.
			ld hl, (GravityIntensity)
			call z, MoveOnGravity

			call TestForAnyCharacterNear		; Except Me, and it must be a character, not a weapon or player. IN IX = character that is asking. H L = relative character position (-) = character is top/left, (+) = character is down/right. ZF = 1 no other character, ZF = 0 near.
			jr z, .Pattern
								; Check how near it is.
			ld a, h
			cp 8
			jr c, .NearEnough1

			cp 256-8
			jr c, .Pattern

.NearEnough1:		ld a, l
			cp 8
			jr c, .NearEnough2

			cp 256-8
			jr c, .Pattern

.NearEnough2:		ld a, (ix + Sprite.LogicArea + 1)	; Check if can attack.
			and a
			jr nz, .Pattern

			ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)
			ld de, $0100
			exx

			ld c, (ix + Sprite.MapOx)		; Can attack.
			ld b, (ix + Sprite.MapTx)
			ld de, $0100

			ld hl, 1024
			ld (AttackPower), hl
			ld a, 255
			ld (IgnorePlayers), a

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.
			ld (ix + Sprite.LogicArea + 1), 3

			call .LowerLifeTime

			bit 2, (ix + Sprite.LogicArea + 2)
			res 2, (ix + Sprite.LogicArea + 2)
			jr nz, .Pattern

			set 2, (ix + Sprite.LogicArea + 2)

.Pattern:		ld a, (ix + Sprite.LogicArea + 3)
			inc a
			and 1
			ld (ix + Sprite.LogicArea + 3), a

			ld (ix + Sprite.PatternFlip), 0
			add a, 239
			ld (ix + Sprite.Pattern), a		; This class don't has a hitbox, so use normal assignament.

			ld a, (ix + Sprite.LogicArea + 1)
			and a
			jr z, .LifeTime

			dec a
			ld (ix + Sprite.LogicArea + 1), a

.LifeTime:		ld a, (ix + Sprite.LogicArea + 4)
			and a
			jp z, RemoveMe

			dec a
			ld (ix + Sprite.LogicArea + 4), a
			ret

.LowerLifeTime:		ld a, (ix + Sprite.LogicArea + 4)
			sub 9
			jr c, .FastDissapearing
			jr z, .FastDissapearing
			ld (ix + Sprite.LogicArea + 4), a
			ret

.FastDissapearing:	pop hl					; Removes the call.
			jp RemoveMe






; + 2 bit 0 = 0 unitialized.
; + 3 = animation sequence.
; + 4 = Extinguished countdown.
; + 6 = X trayectory MSB.
; + 8 = Y trayectory MSB.
; + 9 <> 0 X is bigger than Y.
; + 10 = Smaller fraction. Format 0000,ffff.
; + 14 = Delay.
; + 15 = Aiming to player.

FireBallClass:		bit 0, (ix + Sprite.LogicArea + 2)
			jr nz, .NoInit

			set 0, (ix + Sprite.LogicArea + 2)
			ld (ix + Sprite.LogicArea + 14), 3

			ld a, 8					; Firewall launch.
			call SFXRequest				; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.

			ld a, (ix + Sprite.LogicArea + 15)
			and a
			push af
			call z, GetNearestCharacterTrayectory	; IN IX = who is asking. ZF = 1 no players present. DE HL = trayectory distance (positive right/bottom) X / Y tttttttt pppp ffff format.
			pop af
			call nz, GetNearestPlayerTrayectory	; IN IX = who is asking. ZF = 1 no players present. DE HL = trayectory distance (positive right/bottom) X / Y tttttttt pppp ffff format.

			ld (ix + Sprite.LogicArea + 6), d
			ld (ix + Sprite.LogicArea + 8), h

			bit 7, d
			jr z, .XPositive

			ld a, e
			cpl
			ld e, a
			ld a, d
			cpl
			ld d, a
			inc de

.XPositive:		bit 7, h
			jr z, .YPositive

			ld a, l
			cpl
			ld l, a
			ld a, h
			cpl
			ld h, a
			inc hl

.YPositive:		ld c, l
			ld b, h

			and a
			sbc hl, de
			jr nc, .YBigger
								; Y = bigger.
			ld (ix + Sprite.LogicArea + 9), 255	; Points that X = Bigger.

			ld l, e
			ld h, d
			ld e, c
			ld d, b
			ld c, l
			ld b, h					; swap DE BC.

.YBigger:		ex de, hl
			add hl, hl
			add hl, hl
			add hl, hl
			ex de, hl				; Bigger must be 8 magnitudes bigger.

			call DivDE_BC				; DE = DE / BC. HL = rest.

			rl e
			rl e
			rl e
			and $70
			ld (ix + Sprite.LogicArea + 10), e	; DE = Smaller / Bigger. Trayectory relation. 0fff 0000.

.NoInit:		bit 0, (ix + Sprite.LogicArea + 9)
			ld a, $80				; Step 8 pixels = the unit.
			jr nz, .Ok1

			ld a, (ix + Sprite.LogicArea + 10)

.Ok1:			ld l, a
			ld a, (ix + Sprite.MapOx)
			ld h, (ix + Sprite.MapTx)

			bit 7, (ix + Sprite.LogicArea + 6)
			jr z, .Right
								; Left.
			sub l
			ld l, a
			jr nc, .JJJ102
			dec h
.JJJ102:
			jp .XSave

.Right:			add a, l
			ld l, a
			jr nc, .JJJ22
			inc h
.JJJ22:
.XSave:			ld (ix + Sprite.MapOx), l
			ld (ix + Sprite.MapTx), h

			bit 0, (ix + Sprite.LogicArea + 9)	; Now handle the Y coordinate.
			ld a, $80
			jr z, .Ok2

			ld a, (ix + Sprite.LogicArea + 10)

.Ok2:			ld l, a
			ld a, (ix + Sprite.MapOy)
			ld h, (ix + Sprite.MapTy)

			bit 7, (ix + Sprite.LogicArea + 8)
			jr z, .Bottom

			sub l
			ld l, a
			jr nc, .JJJ103
			dec h
.JJJ103:
			jp .YSave

.Bottom:		add a, l
			ld l, a
			jr nc, .JJJ23
			inc h
.JJJ23:
.YSave:			ld (ix + Sprite.MapOy), l
			ld (ix + Sprite.MapTy), h

			ld a, (ix + Sprite.LogicArea + 14)	; Delay before start attacking.
			and a
			jr z, .CheckToAttack

			dec a
			ld (ix + Sprite.LogicArea + 14), a
			jp .Pattern

.CheckToAttack:		ld a, (ix + Sprite.LogicArea + 15)	; Thrown by a wizard?. Test when attack.
			and a
			jr z, .AttackToCharacter
								; Attack to a player.

			call TestForPlayerNear			; IN IX = character that is asking (can't be a player). H L = relative player position (-) = player is top/left, (+) = player is down/right. ZF = 1 safe, ZF = 0 danger detected.
			jp .AttackToCharacter2			; OUT H L = in in pixel relative position 8 bits signed. OUT IY = player pointer.

.AttackToCharacter:	call TestForAnyCharacterNear		; Except Me, and it must be a character, not a weapon or player. IN IX = character that is asking. H L = relative character position (-) = character is top/left, (+) = character is down/right. ZF = 1 no other character, ZF = 0 near.
.AttackToCharacter2:	jr z, .Pattern
								; Check how near it is.
			ld a, h
			cp 8
			jr c, .NearEnough1

			cp 256-8
			jr c, .Pattern

.NearEnough1:		ld a, l
			cp 8
			jr c, .NearEnough2

			cp 256-8
			jr c, .Pattern

.NearEnough2:		ld c, (ix + Sprite.MapOy)
			ld b, (ix + Sprite.MapTy)
			ld de, $0100
			exx

			ld c, (ix + Sprite.MapOx)		; Can attack.
			ld b, (ix + Sprite.MapTx)
			ld de, $0100

			ld hl, 512
			ld (AttackPower), hl

			ld a, (ix + Sprite.LogicArea + 15)	; Thrown by a wizard?.
			and a
			ld a, 255
			jr z, .SetEnemy

			inc a

.SetEnemy:		ld (IgnorePlayers), a

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.
.Pattern:		ld a, (ix + Sprite.LogicArea + 3)
			inc a
			and 3
			ld (ix + Sprite.LogicArea + 3), a
								; Patterns 32 al 35.
			add a, 241
			ld (ix + Sprite.Pattern), a		; This class don't has a hitbox, so use normal assignament.

			ret






; + 1 = Thunder stage.

ThunderClass:		ld (ix + Sprite.Pattern), 252

			ld a, (ix + Sprite.LogicArea + 1)
			and a
			jr z, .LightOn

			dec a
			jr z, .Attack

			di					; Light off and remove.
			ld a, (BIOS.VAR.RG7SAV)			; Black borders.
			out ($99), a
			ld a,7+128
			out ($99), a

			ld a, (BIOS.VAR.RG18SAV)
			out ($99), a
			ld a, 18+128
			out ($99), a
			ei

			jp RemoveMe

.LightOn:		di
			ld a, $FF
			out ($99), a
			ld a,7+128
			out ($99), a

			ld a, (BIOS.VAR.RG18SAV)
			add a, $88
			out ($99), a
			ld a, 18+128
			out ($99), a
			ei

			inc (ix + Sprite.LogicArea + 1)

			ld a, 10				; Thunder SFX.
			jp SFXRequest				; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.

.Attack:		ld bc, (MapCurrentTileOffsetY)
			ld de, $1000
			exx

			ld bc, (MapCurrentTileOffsetX)
			ld de, $0D40

			ld hl, 8192
			ld (AttackPower), hl

			ld a, 255
			ld (IgnorePlayers), a

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.
			inc (ix + Sprite.LogicArea + 1)

			ret






; + 2 bit 0 = 0 unitialized.

KnifeClass:		bit 2, (ix + Sprite.LogicArea + 2)
			jr nz, .Right
								; Left.
			ld (ix + Sprite.PatternFlip), $04
			ld (ix + Sprite.Pattern), 245		; This class don't has a hitbox for this, so use the normal asignament.

			call TestForWallOnHisLeft		; OUT ZF = 1 can walk, BC = space before the wall.
			jp nz, RemoveMe

			ld hl, $00A0
			call WalkToTheLeft			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.
			jp .DoY

.Right:			ld (ix + Sprite.Pattern), 37		; This class don't has a hitbox for this, so use the normal asignament.

			call TestForWallOnHisRight		; OUT ZF = 1 can walk, BC = space before the wall.
			jp nz, RemoveMe

			ld hl, $00A0
			call WalkToTheRight			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.

.DoY:			call TestForAnyCharacterNear		; Except Me, and it must be a character, not a weapon or player. IN IX = character that is asking. H L = relative character position (-) = character is top/left, (+) = character is down/right. ZF = 1 no other character, ZF = 0 near.
			ret z
								; Check how near it is.
			ld a, h
			cp 8
			jr c, .NearEnough1

			cp 256-8
			ret c

.NearEnough1:		ld a, l
			cp 8
			jr c, .NearEnough2

			cp 256-8
			ret c

.NearEnough2:		ld c, (ix + Sprite.MapOy)		; Can attack.
			ld b, (ix + Sprite.MapTy)
			ld de, $0100
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0100
			
			ld hl, 1536
			ld (AttackPower), hl 
			ld a, 255
			ld (IgnorePlayers), a

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.
			jp RemoveMe






; + 2 Coin accumulated fall.
; + 3 Coin Jump Force.
; + 4 Coin wich direction displace.				; 0 = left.
; + 5 Coin horizontal displacement.
; + 6 Animation pattern sequence.
; + 7 TimeOut.
; + 8 Coins value LSB.
; + 9 Coins value MSB.

CoinClass:		dec (ix + Sprite.LogicArea + 7)
			jp z, RemoveMe

			ld a, (ix + Sprite.LogicArea + 5)
			and a
			jr z, .DoVertical

			bit 0, (ix + Sprite.LogicArea + 4)
			jr z, .Left
								; Right.
			call TestForWallOnHisRight		; OUT ZF = 1 can walk, BC = space before the wall.
			jr nz, .DoVertical

			ld h, 0
			ld l, (ix + Sprite.LogicArea + 5)
			call WalkToTheRight			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.

			ld a, (ix + Sprite.LogicArea + 5)
			sub $10
			ld (ix + Sprite.LogicArea + 5), a
			jr .DoVertical

.Left:			call TestForWallOnHisLeft		; OUT ZF = 1 can walk, BC = space before the wall.
			jr nz, .DoVertical

			ld h, 0
			ld l, (ix + Sprite.LogicArea + 5)
			call WalkToTheLeft			; It moves to the left a character. IN HL = step. IN BC = Maximun ammount that can walk.

			ld a, (ix + Sprite.LogicArea + 5)
			sub $10
			ld (ix + Sprite.LogicArea + 5), a

.DoVertical:		ld hl, (GravityIntensity)
			ld e, (ix + Sprite.LogicArea + 3)
			ld d, 0
			and a
			sbc hl, de
			jr c, .GoingUp
								; Going Down.
			push hl
			call TestForFalling			; OUT ZF = 1 can fall. BC = distance to the ground.
			pop hl
			jr z, .CanFall

			ld a, (ix + Sprite.LogicArea + 2)
			and $F0
			ld (ix + Sprite.LogicArea + 3), a		
			ld (ix + Sprite.LogicArea + 2), 0
			jr .Pattern

.CanFall:		call MoveOnGravity			; The character is falling. IN HL = falling step, IN BC = limit distance to fall.

			ld a, (ix + Sprite.LogicArea + 2)
			add a, $18
			ld (ix + Sprite.LogicArea + 2), a

			ld a, (ix + Sprite.LogicArea + 3)
			and a
			jr z, .Pattern

			sub $10
			ld (ix + Sprite.LogicArea + 3), a

			jr .Pattern

.GoingUp:		ld a, h
			cpl
			ld h, a
			ld a, l
			cpl
			ld l, a
			inc hl

			push hl
			call TestForJumping			; OUT BC = distance to the ceiling.
			pop hl
			jr z, .CanGoUp
			
			ld (ix + Sprite.LogicArea + 3), 0	; Bounce on ceiling.
			jr .Pattern

.CanGoUp:		call MoveOnJump				; In BC = space for jumping.

			ld a, (ix + Sprite.LogicArea + 3)
			and a
			jr z, .Pattern

			sub $10
			ld (ix + Sprite.LogicArea + 3), a

.Pattern:		ld a, (ix + Sprite.LogicArea + 6)
			inc a
			cp 5
			jr c, .POk

			xor a

.POk:			ld (ix + Sprite.LogicArea + 6), a
			add a,a 

			ld e, a
			ld d, 0
			ld hl, .PatternData
			add hl, de

			ld e, (hl)
			inc hl
			ld d, (hl)

			ld (ix + Sprite.PatternFlip), d
			ld (ix + Sprite.Pattern), e		; This class don't has a hitbox for this, so use the normal asignament.

								; Test if a player is on it.
			call TestForPlayerNear			; IN IX = character that is asking. H L = relative player position (-) = top/left, (+) = down/right. ZF = 1 safe, ZF = 0 danger detected.
			ret z					; OUT IY = player pointer.

			ld a, h
			bit 7, a
			jr z, .Z1
			cpl
			inc a

.Z1:			cp 12
			ret nc

			ld a, l
			bit 7, a
			jr z, .Z2
			cpl
			inc a

.Z2:			cp 12
			ret nc

			ld e, (ix + Sprite.LogicArea + 8)	; A player is grabbing it.
			ld d, (ix + Sprite.LogicArea + 9)	; + 8 Coins LSB.
			ld l, (iy + Sprite.LogicArea + 8)	; + 9 Coins MSB.
			ld h, (iy + Sprite.LogicArea + 9)

			add hl, de
			ret c

			ld (iy + Sprite.LogicArea + 8), l
			ld (iy + Sprite.LogicArea + 9), h

			ld c, TileMap.Coin_Address

			push ix

			push iy					; Sets that player as who grabs this coin.
			pop ix
			call UpdateOverlayGrabItem		; IN IX = Player. C = recently grab item.

			pop ix

			ld a, 40
			call SFXRequest

			jp RemoveMe

.PatternData:		DB 246, 0, 247, 0, 248, 0, 247, 4, 246, 4






; + 0 = not initialized.
; + 1 = frame countdown.

RemoveMeClass:		ld a, (ix + Sprite.LogicArea + 0)
			and a
			jr nz, .AlreadyInitialized

			dec (ix + Sprite.LogicArea + 0)

			ld (ix + Sprite.LogicArea + 1), 2

.AlreadyInitialized:	ld (ix + Sprite.ScreenWidth), 0		; Make sure it don't trigger a background restoration.

			dec (ix + Sprite.LogicArea + 1)

			pop bc
			jp nz, IACharacters.SpecialReturnHack	; It saves the render of an empty pattern.
			push bc

			push ix					; Free the sprite slot.
			call FreeOffScreenSpriteSlot
			call UpdateSpriteCount
			pop ix

			ret






; + 0 = pattern animation count.				; Must be initialized with 0 before switch to this class.

CrushedClass:		ld a, (ix + Sprite.LogicArea + 0)	; It is a class for showing an explosion when some box is crushed.
			inc a
			ld (ix + Sprite.LogicArea + 0), a
			add a, 249
			ld (ix + Sprite.Pattern), a		; This class don't has a hitbox, so it uses normal assignament.
			cp 249 + 3
			ret c

			jp RemoveMe






; + 0 <> 0 already initialized.
; + 14, 15 = related played address.
; + 16 = Sine step (value 0 - 15). It is preset when creating this class.
; + 17-18 = lifetime countdown.

ShieldClass:		ld a, (ix + Sprite.LogicArea + 0)
			and a
			jr nz, .AlreadyInitialized

			ld hl, $300
			ld (ix + Sprite.LogicArea + 17), l
			ld (ix + Sprite.LogicArea + 18), h

			ld a, 255
			ld (ix + Sprite.LogicArea + 0), a

.AlreadyInitialized:	ld a, r
			and 3
			add a, 39
			ld (ix + Sprite.Pattern), a		; No hitbox, use direct assignament.

			ld a, (ix + Sprite.LogicArea + 14)
			ld iyl, a
			ld a, (ix + Sprite.LogicArea + 15)
			ld iyh, a

			ld a, (ix + Sprite.LogicArea + 16)
			add a, a
			add a, a
			ld e, a
			ld d, 0
			ld hl, CommonClass.FlyData
			add hl, de

			ld c, (hl)
			inc hl
			ld b, (hl)
			inc hl
			push hl

			ld l, (iy + Sprite.MapOx)
			ld h, (iy + Sprite.MapTx)
			add hl, bc
			add hl, bc
			add hl, bc
			ld (ix + Sprite.MapOx), l
			ld (ix + Sprite.MapTx), h

			pop hl
			ld c, (hl)
			inc hl
			ld b, (hl)

			ld l, (iy + Sprite.MapOy)
			ld h, (iy + Sprite.MapTy)
			add hl, bc
			add hl, bc
			add hl, bc
			ld (ix + Sprite.MapOy), l
			ld (ix + Sprite.MapTy), h

			ld a, (ix + Sprite.LogicArea + 16)
			inc a
			and $0F
			ld (ix + Sprite.LogicArea + 16), a

			ld c, (ix + Sprite.MapOy)		; Attack.
			ld b, (ix + Sprite.MapTy)
			ld de, $0100
			exx

			ld c, (ix + Sprite.MapOx)
			ld b, (ix + Sprite.MapTx)
			ld de, $0100
			
			ld hl, 256
			ld (AttackPower), hl 
			ld a, 255
			ld (IgnorePlayers), a

			call AttackLogic			; IN IX = Who is attacking.
								; IN (AttackPower) = power of the attack (unsigned 16bits).
								; IN (IgnorePlayers) <> 0 true.
								; IN BC = MapTx/Ox, DE = Width in pixels << 4 of the attack area.
								; IN BC'= MapTy/Oy, DE'= Height in pixels << 4 of the attack area.
			ld l, (ix + Sprite.LogicArea + 17)
			ld h, (ix + Sprite.LogicArea + 18)
			dec hl
			ld (ix + Sprite.LogicArea + 17), l
			ld (ix + Sprite.LogicArea + 18), h

			ld a, l
			or h
			ret nz

			jp RemoveMe






BornClass:		ld a, (ix + Sprite.LogicArea + 3)
			add a, 252
			ld (ix + Sprite.PatternFlip), 0
			ld (ix + Sprite.Pattern), a		; This class don't has a hitbox, so it uses normal assignament.

			inc (ix + Sprite.LogicArea + 3)			

			and a
			ret nz

			ld a, (ix + Sprite.LogicArea + 2)
			ld (ix + Sprite.Class), a
			cp 3

			ld e, ixl				; Only subscribe boxes and lifts.
			ld d, ixh
			call c, SpacingListSubscribe		; IN DE = Object address.
		
			ld (ix + Sprite.LogicArea + 2), 0
			ld (ix + Sprite.LogicArea + 3), 0

			ld a, 255
			ld (ix + Sprite.Pattern), a		; This class don't has a hitbox, so it uses normal assignament.

			ret



; ************************************************************
; * VAULT HANDLING FUNCTIONS                                 *
; ************************************************************






VaultsReset:		ld hl, Vaults_Start
			ld de, Vaults_Start + 1
			ld bc, Vaults_End - Vaults_Start - 1
			ld (hl), $FF				; $FF = free slot.
			ldir
			ret






SaveItemInVault:	ld a, c
			and $FE					; Removes the background color that now doesn't matter, that was even = black, odd = blue background.
			ld c, a

			cp TileMap.Fruits_StopAddress
			jr nc, .IsNotFruit			; Is it a fruit?.
								; It is a fruit.
			ld e, c
			ld d, 0
			inc de
			inc de
			ex de, hl
			add hl, hl
			add hl, hl
			add hl, hl
			ex de, hl
			
			call ScoreUpNoUpdate

			call CheckIfItemIsPresent		; Out ZF = 1 if already have it.
			jp z, UpdateOverlayGrabItem

			call .AddIt				; IN C = add the item in the vault.

			ld c, TileMap.Fruits_StopAddress - 2	; He collected all the fruits?.

.FruitLoop:		push bc
			call CheckIfItemIsPresent
			pop bc
			ret nz					; Escapes if it haven't this fruit.
			dec c
			dec c
			jr nz, .FruitLoop
			call CheckIfItemIsPresent		; Checks fruit tile number = 0.
			ret nz					; Escapes if it haven't this fruit.
								; All the fruits are collected.
			ld b, 5					; It must create 5 ShieldClass sprites.
.ShieldCreateLoop:	push bc

			push ix					; Save in IY this player's sprite/instance pointer.
			pop iy

			push ix
								; Asks for a new sprite slot.
			push bc
			call GetFreeSpriteSlot			; OUT A = ID sprite slot, IX = sprite pointer, CF = 1 error.
			pop bc
			jr c, .NoShieldCreated

			ld c, SpriteClasses.ShieldClass

			push bc
			call SetSprite				; IN A = ID, H L = Ty/Oy, D E = Tx/Ox, C = Class, H' L' = Ty, Tx respawn ID.
			pop bc

			ld a, iyl
			ld (ix + Sprite.LogicArea + 14), a
			ld a, iyh
			ld (ix + Sprite.LogicArea + 15), a

			ld a, b
			dec a

			ld e, a
			ld d, 0
			ld hl, .ShieldFlyData
			add hl, de

			ld a, (hl)
			ld (ix + Sprite.LogicArea + 16), a

.NoShieldCreated:	pop ix					; Restore this own player Sprite/instance pointer.

			pop bc
			djnz .ShieldCreateLoop

			ld de, 40000
			call ScoreUpNoUpdate

			ld c, TileMap.Fruits_StopAddress - 2	; Remove all fruits.

.FruitLoopErase:	push bc
			call UseAItem
			pop bc
			dec c
			dec c
			jr nz, .FruitLoopErase
			call UseAItem

			ld c, TileMap.Potion_Address		; Add the potion.
			jp SaveItemInVault

.IsNotFruit:		cp TileMap.Bonus_Address
			jr z, .ScoreUp				; ScoreUp upgrades the overlay.

			cp TileMap.Gold_Address
			jr nz, .IsNotJustPointsOrGold

			ld de, 50
.GoldUp:		call GoldUp
								; C already has the item grabbed.
			call UpdateOverlayGrabItem		; IN IX = Player. C = recently grab item.

			ld a, 41
			jp SFXRequest

.ScoreUp:		ld de, 150
			call ScoreUpNoUpdate
								; C already has the item grabbed.
			call UpdateOverlayGrabItem		; IN IX = Player. C = recently grab item.

			ld a, 42
			jp SFXRequest

.IsNotJustPointsOrGold:	cp TileMap.Weapons_StopAddress
			jp c, .AddIt				; Jumps if weapons.

			cp TileMap.Potion_Address
			jr nz, .IsNotPotion
								; It is the potion.
			call CheckIfItemIsPresent		; Out ZF = 1 if already have it.
			ret z

			call .AddIt				; IN C = add the item in the vault.

			ld a, 43
			jp SFXRequest

.IsNotPotion:		cp TileMap.Keys_StopAddress
			jp c, .AddIt				; Jumps if keys.

.IsNotKeys:		cp TileMap.Shoes_StopAddress
			jr nc, .IsNotShoes
								; Is a some shoes.
			sub TileMap.Shoes_StartAddress
			rra
			inc a
			ld b, a

			ld a, (ix + Sprite.LogicArea + 3)
			and $FC
			or b
			ld (ix + Sprite.LogicArea + 3), a

			ld a, b
			rrca
			rrca
			add a, 63
			ld (ix + Sprite.LogicArea + 16), a	; Shoes lifetime = 127, 191, 255 jumps.

			jp BuildUpPlayerPatterns

.IsNotShoes:		cp TileMap.Armour_StopAddress
			jr nc, .IsNotArmour
								; Is armour.
			sub TileMap.Armour_StartAddress
			rra
			inc a
			rla
			rla
			rla
			rla
			ld b, a

			ld a, (ix + Sprite.LogicArea + 3)
			and $CF
			or b
			ld (ix + Sprite.LogicArea + 3), a

			rr b					; Lifetime = 8, 16, 24 hits based on quality.
			ld (ix + Sprite.LogicArea + 13), b

			jp BuildUpPlayerPatterns

.IsNotArmour:		cp TileMap.HandWeapon_StopAddress
			jr nc, .IsNotHandWeapon
								; Is a hand weapon.
			sub TileMap.HandWeapon_StartAddress
			rra
			inc a
			and $07
			ld (ix + Sprite.LogicArea + 21), a

			jp BuildUpPlayerPatterns

.IsNotHandWeapon:	cp TileMap.Helmet_StopAddress
			jr nc, .IsNotHelmet
								; Is a helmet.
			sub TileMap.Helmet_StartAddress
			rra
			inc a
			rrca
			rrca
			ld b, a

			ld a, (ix + Sprite.LogicArea + 3)
			and $3F
			or b
			ld (ix + Sprite.LogicArea + 3), a

			rr b
			rr b
			rr b					; Lifetime = 8, 16, 24 hits based on quality.
			ld (ix + Sprite.LogicArea + 7), b

			jp BuildUpPlayerPatterns

.IsNotHelmet:		cp TileMap.Items_StopAddress
			ret nc

			ld de, 2048
			cp TileMap.Beer_Address
			jr z, .HealthUp
			cp TileMap.Cocktail_Address
			jr z, .HealthUp

			ld de, 4096
			cp TileMap.Jim_Address
			jr z, .HealthUp

			ld de, 2048 + 1024
			cp TileMap.Vodka_Address
			jr nz, .NoDrinks

.HealthUp:		call HealthUp				; IN IX = Player, DE = Health adder. Mod HL.
			call UpdateOverlayGrabItem		; IN IX = Player. C = recently grab item.

			ld a, 44
			jp SFXRequest

.NoDrinks:		; // add more things here.




			cp TileMap.Coin_Address
			ld de, 10
			jp z, .GoldUp

			ret

.AddIt:			ld l, (ix + Sprite.LogicArea + 14)
			ld h, (ix + Sprite.LogicArea + 15)

			ld e, c
			ld a, $FF				; Looking for a free slot.
			ld bc, 256				; Vault size.
			cpir
			ld c, e
			jp nz, UpdateOverlayGrabItem		; Escape if not free slot in the vault.

			dec hl
			ld (hl), e				; Found a free slot, store the item.
								; IN IX = Player. C = recently grab item.
			jp UpdateOverlayGrabItem

.ShieldFlyData:		DB 0, 4, 8, 10, 14






CheckIfItemIsPresent:	ld l, (ix + Sprite.LogicArea + 14)	; IN C = Item. IN IX = PLAYER CHARACTER. OUT ZF = 1, it has.
			ld h, (ix + Sprite.LogicArea + 15)	; Reads the address of the player's vault.

			ld a, c
			ld bc, 256
			cpir
			ld c, a
			ret






CountHowManyThisItem:	ld l, (ix + Sprite.LogicArea + 14)	; IN C = Item. IN IX = PLAYER CHARACTER. OUT DE = count.
			ld h, (ix + Sprite.LogicArea + 15)	; Reads the address of the player's vault.
			ld de, 0
			ld b, e
			ld a, c

.Loop:			cp (hl)

			jp nz, .Next
			inc de

.Next:			inc hl
			djnz .Loop
			ret






WhatIsNextWeapon:	ld l, (ix + Sprite.LogicArea + 14)	; IN IX = player. OUT C = Weapon item, = 0 no.			
			ld h, (ix + Sprite.LogicArea + 15)
			inc h
			dec hl					; Last item position of the vault.
			ld b, 0

.Loop:			ld a, (hl)
			cp TileMap.Weapons_StartAddress
			jr c, .Next

			cp TileMap.Weapons_StopAddress
			jr c, .Weapon

.Next:			dec hl
			djnz .Loop

			ld c, 0
			ret

.Weapon:		ld c, a
			ret






UseNextWeapon:		call WhatIsNextWeapon			; IN IX = player. OUT C = weapon. C = 0 no weapon.
			ld a, c
			and a
			ret z					; Escape if no weapon.

.In:			push bc
			push hl					; HL = address of the weapon.

			ld e, (ix + Sprite.LogicArea + 14)
			ld d, (ix + Sprite.LogicArea + 15)
			inc d					; DE = stop address for the vault.

			ex de, hl
			sbc hl, de
			ld c, l
			ld b, h

			pop hl

			ld e, l
			ld d, h
			inc hl
			dec bc
			ld a, b
			or c
			jr z, .J1
			ldir
.J1:
			ld (hl), $FF				; The last slot is freed.

			pop bc
			ret






UseAItem:		ld l, (ix + Sprite.LogicArea + 14)	; IN C = item. IX = Player. OUT ZF = 1 found and removed. ZF = 0 not present.
			ld h, (ix + Sprite.LogicArea + 15)
			ld b, 0
			ld a, c

.Loop:			cp (hl)
			jr z, .Found

			inc hl
			djnz .Loop

			ld a, 255
			and a
			ret

.Found:			call UseNextWeapon.In
			xor a
			ret



; ************************************************************
; * SCORE & HEALTH & GOLD HANDLING FUNCTIONS.                *
; ************************************************************






HealthUp:		ld l, (ix + Sprite.Energy)		; IN IX = Player, DE = Health adder. Mod HL.
			ld h, (ix + Sprite.Energy + 1)

			add hl, de
			jr c, .Max

.Save:			ld (ix + Sprite.Energy), l
			ld (ix + Sprite.Energy + 1), h

			ret

.Max:			ld hl, 65535
			jr .Save






GoldUp:			ld l, (ix + Sprite.LogicArea + 8)	; IN IX = Player, DE = coin adder. MOD HL.
			ld h, (ix + Sprite.LogicArea + 9)	; + 9 Coins MSB.

			add hl, de
			jr c, .Max

.Save:			ld (ix + Sprite.LogicArea + 8), l
			ld (ix + Sprite.LogicArea + 9), h

			ret

.Max:			ld hl, 65535
			jr .Save






ScoreUpNoUpdate:	ld l, (ix + Sprite.LogicArea + 17)	; IN IX = Player, DE = Score adder. MOD BHL.
			ld h, (ix + Sprite.LogicArea + 18)
			ld b, (ix + Sprite.LogicArea + 19)

			add hl, de
			jr nc, .NoMSBUp

			inc b
			jr z, .Max
			
.NoMSBUp:		ld (ix + Sprite.LogicArea + 17), l
			ld (ix + Sprite.LogicArea + 18), h
			ld (ix + Sprite.LogicArea + 19), b
			ret

.Max:			ld hl, 65535
			ld b, 255
			jr .NoMSBUp






ScoreUp:		call ScoreUpNoUpdate			; IN IX = Player, DE = Score adder.

			ld a, (ix + Sprite.LogicArea + 6)	; Reads the player number.
			jp UpdateOverlayGrabItem.In		; IN IX = player, A = player number.



; ************************************************************
; * OVERLAY ENGINE.                                          *
; ************************************************************






UpdateOverlayGrabItem:	ld a, (ix + Sprite.LogicArea + 6)	; IN IX = Player. C = recently grab item.

			ld hl, OverlayShowing0
			ld e, a
			ld d, 0
			add hl, de
			ld (hl), 40				; How many frames will show the last item grab.

			ld hl, OverlayLastItem0
			add hl, de
			ld (hl), c				; Save the last item grabbed.

.In:			ld de, OverlayBufferSize		; IN IX = Player. A = player number.
			ld hl, OverlayBuffer0

			and a
			jr z, .J1

.L1:			add hl, de
			dec a
			jr nz, .L1

.J1:			push hl					; HL = Overlay buffer address.

			ld bc, OverlayBufferSize		; Copy the base overlay outline.
			ex de, hl
			ld hl, .Data
			ldir

			ld a, (ix + Sprite.LogicArea + 6)	; Test if it were showing some recently grabbed item.
			ld e, a
			ld d, 0
			ld hl, OverlayShowing0
			add hl, de
			ld a, (hl)
			and a
			jr nz, .ShowGrabItem

			call WhatIsNextWeapon			; IN IX = player. OUT C = Weapon item, = 0 no.
			ld a, c
			and a
			jr nz, .GotAWeaponItem
								; Here is nothing to show right now.
			jp .Score

.ShowGrabItem:		ld hl, OverlayLastItem0
			add hl, de
			ld c, (hl)				; Reads the last grabbed item.

.GotAWeaponItem:	pop hl					; Restore HL = Overlay buffer address.
			push bc					; C = item number.
			push hl

			ld b, c					; Calculates the tile texture address for the item to show.
			xor a
			rl b
			rla
			rl b
			rla
			rr b
			scf
			rr b

			ld c, a
			ld a, (TileBaseSegment)
			add a, c
.P2_1:			call DOS2.CALL.PUT_P2

			ld c, 0					; BC = addres in RAM of the item's texture.

			pop hl					; Grab layout buffer base address.
			push hl
			ld de, 4 + (OverlayBufferWidth*5)	; Centers the texture in the overlay.
			add hl, de

			ld e, c
			ld d, b
			ex de, hl

			ld b, 8					; Tile texture height.

.L2:			push bc

			ldi
			inc hl
			ldi
			inc hl
			ldi
			inc hl
			ldi
			inc hl
			ldi
			inc hl
			ldi
			inc hl
			ldi
			inc hl
			ldi
			inc hl

			ld a, e
			add a, 16				; Difference between OVerlayBufferWidth and tile texture width / 2.
			ld e, a
			jr nc, .JJJ24
			inc d
.JJJ24:
			ld a, l
			add a, 16
			ld l, a
			jr nc, .JJJ25
			inc h
.JJJ25:
			pop bc

			djnz .L2

			pop de					; DE = layout buffer base address.
			pop bc					; C = item number.
								; If item is a key must show the count on it.
			ld a, c
			cp TileMap.Coin_Address
			jr z, .CoinItem

			cp TIleMap.Gold_Address
			jr z, .CoinItem

			cp TileMap.Keys_StartAddress
			jr c, .Score2

			cp TileMap.Keys_StopAddress
			jr nc, .Score2

			push de					; Save DE = layout buffer base address.
			call CountHowManyThisItem		; OUT DE = count.

			pop hl
			push hl					; save HL = layout buffer base address.

			ld bc, OverlayBufferWidth * 7 - 4 - 5	; 5 = number width. 4 = right alignment.
			add hl, bc				; Layout buffer position for the first digit (most right).
			ex de, hl
								; DE = layout buffer. HL = number.

.L3:			ld a, l					; Escape if zero (means no more digits left).
			or h
			jr z, .Score

			call .HLDiv10				; IN/OUT HL = number. HL = HL / 10. A = rest.

			push de
			push hl
			call .PrintDigit
			pop hl
			pop de

			ld a, e
			sub 3
			ld e, a
			jp nc, .L3
			dec d
			jp .L3

.CoinItem:		ld l, (ix + Sprite.LogicArea + 8)	; In DE = layout buffer base address.
			ld h, (ix + Sprite.LogicArea + 9)
			push de					; save DE = layout buffer base address.

			ex de, hl
			ld bc, OverlayBufferWidth * 7 - 4 - 5	; 5 = number width. 4 = right alignment.
			add hl, bc
			ex de, hl

			jr .L3

.Score2:		push de
.Score:			pop hl					; Restore HL = layout buffer base address.
			push hl
			ld bc, OverlayBufferWidth - 5
			add hl, bc
			ex de, hl

			ld l, (ix + Sprite.LogicArea + 17)	; Reads the score.
			ld h, (ix + Sprite.LogicArea + 18)
			ld b, (ix + Sprite.LogicArea + 19)

			ld a, l
			or h
			or b
			jr z, .Score0Case

.L4:			ld a, l					; Score loop.
			or h
			or b
			jr z, .EnergyBar

			push de
			call .BHLDiv10				; IN/OUT HL = number. BHL = BHL / 10. A = rest.
			pop de

			push bc
			push de
			push hl
			call .PrintDigit
			pop hl
			pop de
			pop bc

			ld a, e
			sub 3
			ld e, a
			jp nc, .L4
			dec d
			jp .L4

.Score0Case:		xor a
			call .PrintDigit

.EnergyBar:		pop bc					; BC = Overlay buffer address.

			ld a, (ix + Sprite.Energy + 1)
			and $F8
			rra					; A = MSB energy / 8.
			rra
			rra
			adc a, 0				; A = Round up.

			and $3F					; Here range can be from 0 - 32.
			cp $20
			jr c, .NoMaximunValue

			ld a, $1F				; Maximun value.

.NoMaximunValue:	ld hl, .CoordEnergy			; Now, A = range 0 - 31.

			and a
			ret z

.EnergyLoop:		ld e, (hl)
			inc hl
			ld d, (hl)
			inc hl

			ex de, hl
			add hl, bc
			ld (hl), $1C
			ex de, hl

			dec a
			jr nz, .EnergyLoop
			ret

.PrintDigit:		ld l, a
			ld h, 0
			add hl, hl
			ld c, l
			ld b, h
			add hl, hl
			add hl, hl
			add hl, hl
			add hl, bc				; HL * (3*6).

			ld bc, .DataNumbers
			add hl, bc

			ld b, 6

.PDVLoop:		push bc
			push de

			ld b, 3

.PDHLoop:		ld a, (hl)
			and a
			jr z, .Transparent

			ld (de), a

.Transparent:		inc hl
			inc de

			djnz .PDHLoop

			pop de
			pop bc

			ld a, e
			add a, OverlayBufferWidth
			ld e, a
			jr nc, .JJJ26
			inc d
.JJJ26:
			djnz .PDVLoop
			ret

.BHLDiv10:		ld c, 10				; IN BHL = number, OUT BHL = result, A = rest.
			ld d, $15
			xor a
			add hl,hl
			rl b
			rla
			add hl,hl
			rl b
			rla
			add hl,hl
			rl b
			rla

.BHLDiv10Loop:		add hl,hl
			rl b
			rla
			cp c
			jr c, .BHLDiv10Next

			sub c
			inc l

.BHLDiv10Next:		dec d
			jr nz, .BHLDiv10Loop
			ret

.HLDiv10:		ld bc,$0D0A				; IN HL = number. OUT HL = result, A = rest.
			xor a
			add hl,hl
			rla
			add hl,hl
			rla
			add hl,hl
			rla

.HLDiv10Loop:		add hl,hl
			rla
			cp c
			jr c, .HLDiv10Next

			sub c
			inc l

.HLDiv10Next:		djnz .HLDiv10Loop
			ret

.Data:			DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
			DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
			DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
			DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
			DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
			DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
			DB $00,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$04,$00
			DB $04,$ff,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$04,$ff,$04
			DB $04,$ff,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$04,$ff,$04
			DB $04,$ff,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$04,$ff,$04
			DB $04,$ff,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$04,$ff,$04
			DB $04,$ff,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$04,$ff,$04
			DB $04,$ff,$04,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$04,$ff,$04
			DB $00,$04,$ff,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$ff,$04,$00
			DB $00,$00,$04,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$04,$00,$00
			DB $00,$00,$00,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$00,$00,$00

.CoordEnergy:		DW 7*OverlayBufferWidth+1, 8*OverlayBufferWidth+1, 9*OverlayBufferWidth+1, 10*OverlayBufferWidth+1, 11*OverlayBufferWidth+1, 12*OverlayBufferWidth+1
			DW 13*OverlayBufferWidth+2
			DW 14*OverlayBufferWidth+3, 14*OverlayBufferWidth+4, 14*OverlayBufferWidth+5, 14*OverlayBufferWidth+6, 14*OverlayBufferWidth+7, 14*OverlayBufferWidth+8, 14*OverlayBufferWidth+9, 14*OverlayBufferWidth+10, 14*OverlayBufferWidth+11, 14*OverlayBufferWidth+12, 14*OverlayBufferWidth+13, 14*OverlayBufferWidth+14, 14*OverlayBufferWidth+15, 14*OverlayBufferWidth+16, 14*OverlayBufferWidth+17, 14*OverlayBufferWidth+18, 14*OverlayBufferWidth+19, 14*OverlayBufferWidth+20
			DW 13*OverlayBufferWidth+21
			DW 12*OverlayBufferWidth+22, 11*OverlayBufferWidth+22, 10*OverlayBufferWidth+22, 9*OverlayBufferWidth+22, 8*OverlayBufferWidth+22, 7*OverlayBufferWidth+22

.DataNumbers:		DB $00,$ff,$00
			DB $ff,$00,$ff
			DB $ff,$04,$ff
			DB $ff,$04,$ff
			DB $ff,$04,$ff
			DB $00,$ff,$04

			DB $00,$ff,$00
			DB $ff,$ff,$04
			DB $00,$ff,$04
			DB $00,$ff,$04
			DB $00,$ff,$04
			DB $00,$ff,$04

			DB $00,$ff,$00
			DB $ff,$00,$ff
			DB $00,$00,$ff
			DB $00,$ff,$04
			DB $ff,$04,$00
			DB $ff,$ff,$ff

			DB $ff,$ff,$00
			DB $00,$00,$ff
			DB $00,$ff,$ff
			DB $00,$00,$ff
			DB $ff,$00,$ff
			DB $00,$ff,$04

			DB $ff,$00,$ff
			DB $ff,$04,$ff
			DB $ff,$ff,$ff
			DB $00,$00,$ff
			DB $00,$00,$ff
			DB $00,$00,$ff

			DB $ff,$ff,$ff
			DB $ff,$04,$00
			DB $ff,$ff,$04
			DB $00,$00,$ff
			DB $ff,$04,$ff
			DB $00,$ff,$ff

			DB $00,$ff,$ff
			DB $ff,$04,$00
			DB $ff,$ff,$04
			DB $ff,$04,$ff
			DB $ff,$04,$ff
			DB $00,$ff,$04

			DB $ff,$ff,$ff
			DB $00,$00,$ff
			DB $00,$ff,$ff
			DB $00,$00,$ff
			DB $00,$00,$ff
			DB $00,$00,$ff

			DB $00,$ff,$00
			DB $ff,$04,$ff
			DB $00,$ff,$04
			DB $ff,$04,$ff
			DB $ff,$04,$ff
			DB $00,$ff,$04

			DB $00,$ff,$00
			DB $ff,$04,$ff
			DB $00,$ff,$ff
			DB $00,$00,$ff
			DB $00,$00,$ff
			DB $00,$00,$ff

			DB $00,$ff,$ff
			DB $ff,$ff,$04
			DB $00,$ff,$ff
			DB $00,$ff,$ff
			DB $ff,$ff,$ff
			DB $00,$ff,$04






EraseOverlay:		ld a, (SpritesCount)
			ld b, a
			ld ix, SpritesPool

.Loop:			ld a, (ix + Sprite.Class)
			inc a						; PlayerClass = 255.
			jr nz, .Next

			push bc						; It is a player class.
			call .EraseOverlay
			pop bc

.Next:			push bc
			ld bc, Sprite.SizeOf
			add ix, bc
			pop bc
			djnz .Loop
			ret

.EraseOverlay:		ld a, (ix + Sprite.LogicArea + 6)		; Reads the player number.
.In:			rrca
			rrca
			add a, 32
			ld d, a
			ld h, 8
			ld e, OverlayBufferWidth
			ld l, OverlayBufferHeight
			ld c, 0
			push ix
			call MapRender
			pop ix
			ret






RemoveOverlay:		push af					; Makes sure the overlay is erased from the other screen buffer aswell (in the current screen buffer was already erased).
			call SaveCurrentBufferVars		; This is required, because if it doesn't exists anymore, the normal gameplay routine don't know about it anymore, and will not erase it, seeing it as a "ghost overlay image".

			ld a, (ActiveBuffer)			; Activate the other buffer.
			inc a
			and 1
			ld (ActiveBuffer), a
			call LoadCurrentBufferVars

			pop af

			call EraseOverlay.In

			call SaveCurrentBufferVars

			ld a, (ActiveBuffer)			; Go back to the actual buffer.
			inc a
			and 1
			ld (ActiveBuffer), a
			jp LoadCurrentBufferVars






PaintOverlay:		ld a, (SpritesCount)
			ld b, a
			ld ix, SpritesPool

.Loop:			push bc
			ld a, (ix + Sprite.Class)
			inc a					; PlayerClass = 255.

			call z, .PrintOverlay			; It is a player class.

.Next:			ld bc, Sprite.SizeOf
			add ix, bc
			pop bc
			djnz .Loop
			ret

.PrintOverlay:		ld a, (ix + Sprite.LogicArea + 6)
			ld e, a
			ld d, 0
			ld hl, OverlayShowing0
			add hl, de
			ld a, (hl)
			and a
			jr z, .NoUpdate

			dec a
			ld (hl), a
			jr nz, .NoUpdate

			ld a, (ix + Sprite.LogicArea + 6)		; It updates the overlay's buffer.
			call UpdateOverlayGrabItem.In

.NoUpdate:		ld a, (ix + Sprite.LogicArea + 6)		; Reads the player number.
			ld de, OverlayBufferSize
			ld hl, OverlayBuffer0

			and a
			jr z, .BufferFound

.L1:			add hl, de
			dec a
			jr nz, .L1

.BufferFound:		ex de, hl					; IN DE = SY / SX in RAM.
			ld a, (ix + Sprite.LogicArea + 6)		; Reads the player number.
			rrca
			rrca
			add a, 32
			ld l, a						; L = DX.

			ld a, (ScreenCurrentX)				; Now must check if it need to split the render horizontally, for horizontal wrap on the real bitmap right edge.
			add a, l
			add a, OverlayBufferWidth
			jr nc, .NoHorizontalWrap
			jr z, .NoHorizontalWrap

			push af						; First fase (horizontal split).
									; A = how many pixels columns overflows the bitmap on its logical right edge.
			ld c, a
			ld a, OverlayBufferWidth
			sub c
			push af
			ld c, a						; C = new width to render.

			push de
			call .CheckForVerticalWrap
			pop de
			pop af

			add a, e
			ld e, a
			jr nc, .JJJ27
			inc d
.JJJ27:
			pop af
			ld c, a					; C = new width to render.

			ld a, (ScreenCurrentX)			; Adjust new visual LEFT.
			ld l, a
			xor a
			sub l
			ld l, a					; When the .DrawArea executes it will do DX = L + (ScreenCurrentX), so we adjust here for a result DX = 0.

			jp .CheckForVerticalWrap

.NoHorizontalWrap:	ld c, OverlayBufferWidth

.CheckForVerticalWrap:	ld h, 8					; IN D E = Pattern address / PY-PX, L = Screen visual LEFT, C = Width.
			ld a, (ScreenCurrentY)
			add a, h
			add a, OverlayBufferHeight
			jr nc, .NoVerticalWrap
			jr z, .NoVerticalWrap

			push af					; FirstFase (vertical split).
								; A = how many pixels rows overflows the bitmap on its logical bottom edge.
			ld b, a
			ld a, OverlayBufferHeight
			sub b
			push af
			ld b, a					; B = new height to render.
			ld h, 8

			push hl
			push de
			push bc
			call .DrawArea
			pop bc
			pop de
			pop hl
			pop af

			push hl

			push de

			add a,a
			add a,a
			add a,a
			ld e, a
			ld d, 0
			ld l, e
			ld h, d
			add hl,hl
			add hl,de				; HL = A * 24. Must be ajusted if OverlayBufferWidth is not 24.

			pop de
			add hl, de				; Adjust PY.
			ex de, hl

			pop hl

			pop af
			ld b, a					; B = new height to render.

			ld a, (ScreenCurrentY)			; Adjust new visual LEFT.
			ld h, a
			xor a
			sub h
			ld h, a					; When the .DrawArea executes it will do DY = H + (ScreenCurrentY), so we adjust here for a result DY = 0.

			jp .DrawArea

.NoVerticalWrap:	ld b, OverlayBufferHeight

.DrawArea:		call VdpWait				; IN D E = Pattern address / PY-PX. H L = Screen visual TOP / LEFT. IN B C = Height / Width.

			di

			ld a, 36
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			ld a, (ScreenCurrentX)
			add a, l
			out ($9B), a
			xor a
			out ($9B), a				; DX.

			ld a, (ScreenCurrentY)
			add a, h
			out ($9B), a
			ld a, (ActiveBuffer)
			out ($9B), a				; DY.

			ld a, c
			out ($9B), a
			xor a
			out ($9B), a				; NX.

			ld a, b
			out ($9B), a
			xor a
			out ($9B), a				; NY.

			ex de, hl				; Now H L = buffer layout area address / PY-PX.

			push bc

			ld c, $9B
			ld e, l
			ld d, h					; DE = backup area address.
			outi

			xor a
			out ($9B), a

			ld a, $B8				; LMMC / TIMP.
			out ($9B), a				; sends HMMC command #46.

			ld a, 44 + 128				; Disable indirect register write auto-increment. And fix it to write in the register #44 always.
			out ($99), a
			ld a, 17 + 128
			out ($99), a

			pop bc
			push bc
			ld b, c
			dec b
			jr z, .GetIn2
			jp .GetIn

.YLoop:			push bc					; B = NY.

			ld b, c					; Now, B = C = NX.

			ld a, b					; If don't splits horizontally use the faster outi's routine.
			cp 24					; Customized for OverlayBufferWidth = 24, change it if you change that.
			jp z, .Outis

.GetIn:			ld c, $9B
			otir

.GetIn2:		pop bc

			ld a, OverlayBufferWidth
			add a, e
			ld e, a
			jr nc, .JJJ28
			inc d
.JJJ28:
			ld h, d
			ld l, e

			djnz .YLoop

			ei
			ret

.Outis:			ld c, $9B

			outi					; Customized for OverlayBufferWidth = 24, change it if you change that.
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			outi
			outi
			outi
			outi

			pop bc

			ld a, OverlayBufferWidth
			add a, e
			ld e, a
			jr nc, .JJJ29
			inc d
.JJJ29:
			ld h, d
			ld l, e

			djnz .YLoop

			ei
			ret



; ************************************************************
; * MAIN GAMEPLAY ROUTINE.                                   *
; ************************************************************






GameEngine:		call ISRHooker

			ld hl, DiagnoseVblanksList_Start
			ld (DiagnoseVBlanksPointer), hl

.AfterGameOver:		ld a, 1					; Sets the first map as current.
			ld (CurrentMapNumber), a

			call ISRUnHooker

			call LoadMap				; Reestablece el mapa.
			ret c
			call LoadTileSet
			ret c
			call LoadSpriteSet
			ret c
			ld a, 5
			call LoadSong				; In A = song number.

			call ISRHooker

			call .MainScreen

			ld hl, DiagnoseVblanksList_Start
			ld (DiagnoseVBlanksPointer), hl
			ld de, DiagnoseVblanksList_Start + 1
			ld bc, DiagnoseVblanksList_End - DiagnoseVblanksList_Start - 1
			ld (hl), 0
			ldir

			call VaultsReset			; Erase the contents of all players vaults (where players stores all the collected items).
			ld hl, $0060
			ld (GravityIntensity), hl		; Setup some fisics factors.

			call InitSpriteSystem

			call ISRUnHooker			; Loads the initial FM song.
			ld a, 1
			call LoadSong				; In A = song number.
			call ISRHooker

			call HideScreen

			call FindsPlayerStartPoint		; OUT H L = Ty/Tx. It scan the map looking for the starting point.
			call PlayersRespawn			; IN/OUT H L = Ty/Tx. It creates and fills with unique related properties the player's characters in the sprites pool.
								; (It also set IN RAM his overlays and initial dressable patterns set).
			push hl
			call .ClearRecentDeaths
			pop hl

.BackFromTeleTransport:	call GetBestFrameMapScope		; In H/L = Ty/Tx player respawn starting point. Out H/L = Ty/Tx BestFrameMapScope.

.BackToGamePlay:	push hl					; In H/L = Ty/Tx Best frame map scope.
			call OpenFirstFrame			; It make sure it start with the vdp screen unscrolled for all frames.

IFDEF NO_CACHE
ELSE
				call TestIfCacheIsNowInvalid	; Flushs the sprite cache if invalid.
ENDIF
				pop hl
				push hl
				call SetThisFrameMapScope	; In HL = TyTx.
				call ScreenMapRedraw

			call CloseFrame

			call ShowScreen

			call OpenNextFrame

IFDEF NO_CACHE
ELSE
				call TestIfCacheIsNowInvalid	; Flushs the sprite cache if invalid.
ENDIF
				pop hl
				call SetThisFrameMapScope	; IN H L = Ty/Tx. Again for this frame too, matching the first frame.
				call ScreenMapRedraw

			call CloseFrame

			xor a					; Resets the vblanks counter.
			ld (VBlankCountInFrame), a

.GamePlayLoop:		xor a
			ld (IsPlayerPresent), a

			call OpenNextFrame

				call EraseOverlay
				call SpriteRestoreBackground

IFDEF NO_CACHE
ELSE
				call UpdateOldnessInCache	; Sprite pattern oldness property at VRAM buffers.
ENDIF

				call ScrollsOnDemand

IFDEF NO_CACHE
ELSE
				call TestIfCacheIsNowInvalid	; After scrolling is required to test if now VRAM sprite patterns cache is invalid.
ENDIF

				ld a, (FramesCount)		; Two consecutive frames (odd + even) each 8 frames must run the tile animation feature.
				and $07
				jp nz, .TANotEvenFrame
								; Tile Animation Even Frame.
				ld a, (AnimatedTilesSequencer)
				inc a
				and 3
				ld (AnimatedTilesSequencer), a
				call AnimateTiles		; Animate tiles, respawn enemies.
				jp .NoTAFrame

.TANotEvenFrame:		dec a				; Checks for Tile Animation Odd Frame.
				call z, AnimateTiles		; Animate tiles, respawn enemies.
.NoTAFrame:

				ld a, (FramesCount)		; In each 64th frame is scanned looking for characters respawn.
				and $3F
				cp $3F
				call z, ScanForCharactersRespawn

				call IACharacters		; Logic & movement & printing of characters.

				call PaintOverlay

			call CloseFrame

			ld b, 3					; /// FPS control ///.
			call FramesPerSecondControl		; Speed control. In B = how many Vblanks per frame?.

			call TestForCheckPoint			; Checks if a player is on the checkpoint.

			call IsPausePressed
			jp z, .PauseScreen

			call IsF1Pressed
			jp z, .GameOverScreen

			ld a, (IsPlayerPresent)
			and a
			jp z, .GameOverScreenDelay

			ld a, 32
			ld (AfterNoPlayersCountDown), a

			ld a, (TeleportEnable)
			and a
			jp nz, .TeleTransportation

.GameOverScreenDelayBack:
			ld a, (SwitchToRoom)
			and a
			jp z, .GamePlayLoop
			; ////////////////////// END OF NORMAL GAME PLAY ROUTINE /////////////////.
								; Needs to switch to a deal room or monster room.
			; ////////////////////// DEAL OR MONSTER ROOM GAME PLAY //////////////////.

			xor a
			ld (SwitchToRoom), a

			ld a, (RoomDoorType)			; RoomDoor code matchs with scenario code.
			ld d, a

			call FindsSpecialRoom			; IN D = room code. OUT H/L = Ty/Tx of the top/left corner of the room.
			jp c, .GamePlayLoop			; Early escapes if the room is not found.

			ld de,(IdealMapTileOffsetY)		; First save the current map position, for later resume the gameplay.
			push de
			ld de,(IdealMapTileOffsetX)
			push de

			push hl					; H/L = Ty/Tx of the top/left corner of the room.

			call HideScreen

			call OpenFirstFrame			; It make sure it start with the vdp screen unscrolled for all frames.

IFDEF NO_CACHE
ELSE
				call TestIfCacheIsNowInvalid	; Flushs the sprite cache if invalid.
ENDIF
				pop hl
				push hl
				call SetThisFrameMapScope	; In HL = TyTx.
				call ScreenMapRedraw

			call CloseFrame

			call ShowScreen

			call OpenNextFrame

IFDEF NO_CACHE
ELSE
				call TestIfCacheIsNowInvalid	; Flushs the sprite cache if invalid.
ENDIF
				pop hl
				call SetThisFrameMapScope	; IN H L = Ty/Tx. Again for this frame too, matching the first frame.
				call ScreenMapRedraw

			call CloseFrame

			ld a, (RoomDoorType)			; Is a deal or a monster fight?.
			cp MapAttribs.RoomDoor.Monster1
			jp nc, .MonsterFight

			; ////////////////////// DEAL ROOM GAME PLAY //////////////////.

			push af					; It is a deal, how we will use PrintText function here requires Single Buffer Mode.

			call SetSingleBufferMode

			ld a, (ResourcesSegment)
			call DOS2.CALL.PUT_P2

			pop af

			sub 4
			ld e, a
			ld d, 0
			ld hl, ScriptsRoomsIndex
			add hl, de
			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

.LooksForScript:	ld a, (RoomDoorSubClass)
			add a, a
			ld e, a
			ld d, 0
			add hl, de
			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

.RunScript:		ld ix, (PlayerThatEnter)
			call PrintText				; IN HL = null terminated string pointer. IN IX = Player sprite pointer.

			call HideScreen

			pop de
			ld l, d
			pop de
			ld h, d
			jp .BackToGamePlay

			; ////////////////////// MONSTER ROOM GAME PLAY //////////////////.
.MonsterFight:		ld a, 64 + 128				; 128 frames delay to exit, for when main monster is dead. The first 64 frames will be spend waiting for the main monster to spawn.
			ld (MonsterRoomExitDelay), a

			xor a
			ld (FramesCount), a

			call BackUpPlayersPosition

			call RelocatePlayersInRoom

			call ISRUnHooker
			ld a, 8
			call LoadSong				; In A = song number.
			call ISRHooker

			; //// Must kill enemy characters that remains from the previous action. Or let it do it automatically by the farness test?.

			xor a					; Resets the vblanks counter.
			ld (VBlankCountInFrame), a

.MonsterLoop:		xor a					; Runs the monster gameplay loop.
			ld (IsPlayerPresent), a
			ld (IsMainMonsterPresent), a

			call OpenNextFrame

				call EraseOverlay
				call SpriteRestoreBackground

IFDEF NO_CACHE
ELSE
				call UpdateOldnessInCache	; Sprite pattern oldness property at VRAM buffers.
ENDIF

				ld a, (FramesCount)		; Two consecutive frames (odd + even) each 8 frames must run the tile animation feature.
				and $07
				jp nz, .MonsterTANotEvenFrame
								; Tile Animation Even Frame.
				ld a, (AnimatedTilesSequencer)
				inc a
				and 3
				ld (AnimatedTilesSequencer), a
				call AnimateTiles		; Animate tiles, respawn enemies.
				jp .MonsterNoTAFrame

.MonsterTANotEvenFrame:		dec a				; Checks for Tile Animation Odd Frame.
				call z, AnimateTiles		; Animate tiles, respawn enemies.
.MonsterNoTAFrame:
				ld a, (FramesCount)		; In each 64th frame is scanned looking for characters respawn.
				and $3F
				cp $3F
				call z, ScanForCharactersRespawn

				call IACharacters		; Logic & movement & printing of characters.

				call PaintOverlay

			call CloseFrame

			ld b, 3					; /// FPS control ///.
			call FramesPerSecondControl		; Speed control. In B = how many Vblanks per frame?.

			ld a, (IsPlayerPresent)
			and a
			jp z, .GameOverScreenMDelay

			ld a, 32
			ld (AfterNoPlayersCountDown), a

.MonsterGODelayed:	ld a, (IsMainMonsterPresent)
			and a
			jp nz, .MonsterLoop

			ld a, (MonsterRoomExitDelay)		; Delays the exit X frames.
			dec a
			ld (MonsterRoomExitDelay),a
			jp nz, .MonsterLoop

			call RestorePlayersPosition

			pop de
			ld l, d
			pop de
			ld h, d
			jp .BackToGamePlay

			; ////////////////////// GAME OVER SCREEN //////////////////.

.GameOverScreenDelay:	ld a, (AfterNoPlayersCountDown)
			and a
			jr z, .GameOverScreen

			dec a
			ld (AfterNoPlayersCountDown), a

			jp .GameOverScreenDelayBack

.GameOverScreenMDelay:	ld a, (AfterNoPlayersCountDown)
			and a
			jr z, .GameOverScreenPops

			dec a
			ld (AfterNoPlayersCountDown), a
			jp .MonsterGODelayed

.GameOverScreenPops:	pop de
			pop de

.GameOverScreen:	call ISRUnHooker
			ld a, 7
			call LoadSong				; In A = song number.
			call ISRHooker

			call OpenNextFrame

				call EraseOverlay
				
				ld hl, .GameOverData
				call PrintText

			call CloseFrame

.GameOverStuck:		xor a					; This avoid an accidentally answer to continue.
			call StrigPrimary			; IN A = 0 keyboard, 1 = joystick port 1 main button, 2 = joystick port 2 main button.
			and a					; OUT A = 0 pressed, <> 0 not pressed.
			jr z, .GameOverStuck

			ld e, 1

.GameOverHalt2:		ld b, 0

.GameOverHalt:		halt

			push de
			xor a
			push bc
			call StrigPrimary			; IN A = 0 keyboard, 1 = joystick port 1 main button, 2 = joystick port 2 main button.
			pop bc
			and a					; OUT A = 0 pressed, <> 0 not pressed.
			pop de
			jr z, .Continue

			ld a, (FMPlay)				; Check if there is music playing.
			and a
			jr z, .NoContinueMusic

			ld a, (FMPlayMusicLoopedFlag)
			and a
			jr z, .GameOverHalt

			jp .GameOverOver

.NoContinueMusic:	djnz .GameOverHalt
			dec e
			jr nz, .GameOverHalt2

			jp .GameOverOver

.GameOverData:		DB 16, 17, "GAME OVER?", 13, 17, "PRESS SPACE to continue!.", 0

.GameOverData2:		DB 16, 17, "GAME OVER", 13, 17, "Sorry folks!.", 0

.Continue:		call ISRUnHooker			; It already mutes.

			ld de, TestForCheckPoint.SaveName
			call LoadGame
			push af
			ld a, (FMCurrentSongNumber)
			call nc, LoadSong			; Loads the current SaveGamed song only if it sucessfully loaded the game.
			call ISRHooker
			pop af
			jr c, .GameOverOVer

			call HideScreen

			call BuildUpAllPlayerPatterns		; Redraw all player patterns.

			ld a,(IdealMapTileY)
			ld h, a
			ld a,(IdealMapTileX)
			ld l, a

			jp .BackToGamePlay

.GameOverOVer:		call ISRUnHooker			; It already mutes.
			ld a, 6
			call LoadSong
			call ISRHooker

			call OpenNextFrame

				call EraseOverlay
				
				ld hl, .GameOverData2
				call PrintText

			call CloseFrame

			ld a, (FMPlay)				; Check if there is music playing.
			and a
			jr z, .NoGameOverMusic

.GameOverMusicHaltLoop:	halt

			ld a, (FMPlayMusicLoopedFlag)
			and a
			jr z, .GameOverMusicHaltLoop

			jp .AfterGameOver

.NoGameOverMusic:	ld b, 255

.GameOverOverLoop:	halt
			djnz .GameOverOverLoop

			jp .AfterGameOver

			; ////////////////////// PAUSE SCREEN /////////////////////.

.PauseScreen:		call OpenNextFrame

				ld de, DressBuffer
				ld hl, .PauseBox
				call .PauseAddText

				xor a

.PausePlayerLoop:		push af

				ld c, a
				push de
				call FindPlayerNumber		; IN C = player number. OUT IX = player character pointer, CF = 1 no present.
				pop de
				jp c, .PauseNextPlayer

				ld hl, .PauseTextPl
				call .PauseAddText

				ld a, (ix + Sprite.LogicArea + 6)

				add a, '1'
				ld (de), a
				inc de

				ld hl, .PauseTextSc
				call .PauseAddText

				ld b, 8

.PausePlayerScoreLoopSpace:	ld a, 32
				ld (de), a
				inc de
				djnz .PausePlayerScoreLoopSpace

				push de

				ld l, (ix + Sprite.LogicArea + 17)	; Reads the score.
				ld h, (ix + Sprite.LogicArea + 18)
				ld b, (ix + Sprite.LogicArea + 19)

				dec de
				ld a, l
				or h
				or b
				jr z, .PausePlayerScore0

.PausePlayerScoreLoop:		ld a, l
				or h
				or b
				jr z, .PauseScoreDone

				push de				
				call UpdateOverlayGrabItem.BHLDiv10	; IN BHL = number, OUT BHL = result, A = rest.
				pop de

				add a, '0'
				ld (de), a
				dec de

				jr .PausePlayerScoreLoop

.PausePlayerScore0:		ld a, '0'
				ld (de), a

.PauseScoreDone:		pop de

				ld c, TileMap.Potion_Address		; Revival formula.
				call CheckIfItemIsPresent		; IN C = Item. IN IX = PLAYER CHARACTER. OUT ZF = 1, it has.
				ld a, 5
				jr z, .PauseHasRevival
				ld a, 23

.PauseHasRevival:		ld (de), a
				inc de
				ld a, c
				ld (de), a
				inc de
				xor a
				ld (de), a
				inc de
				ld a, 22
				ld (de), a
				inc de
				ld a, 22
				ld (de), a
				inc de
				ld a, 22
				ld (de), a
				inc de

				ld a, 13
				ld (de), a
				inc de

				ld b, (ix + Sprite.Energy + 1)		; Reads the MSB of health.
				call .PauseBar

				ld hl, .PauseTextHl
				call .PauseAddText

				ld b, 5

.PausePlayerGoldLoopSpace:	ld a, 32
				ld (de), a
				inc de
				djnz .PausePlayerGoldLoopSpace

				push de

				ld l, (ix + Sprite.LogicArea + 8)	; Reads the gold.
				ld h, (ix + Sprite.LogicArea + 9)
				ld b, 0

				dec de
				ld a, l
				or h
				or b
				jr z, .PausePlayerGold0

.PausePlayerGoldLoop:		ld a, l
				or h
				or b
				jr z, .PauseGoldDone

				push de				
				call UpdateOverlayGrabItem.BHLDiv10	; IN BHL = number, OUT BHL = result, A = rest.
				pop de

				add a, '0'
				ld (de), a
				dec de

				jr .PausePlayerGoldLoop

.PausePlayerGold0:		ld a, '0'
				ld (de), a

.PauseGoldDone:			pop de

				ld a, 13
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, (ix + Sprite.LogicArea + 7)	; Reads the head helmet, range from 0 to 24.
				add a, a
				ld b, a
				add a, a
				add a, a
				add a, b
				ld b, a
				call .PauseBar

				ld hl, .PauseTextHH
				call .PauseAddText

				ld c, TileMap.Keys_StartAddress
				call .PauseAddKey

				ld c, TileMap.Keys_StartAddress + 2
				call .PauseAddKey

				ld c, TileMap.Keys_StartAddress + 4
				call .PauseAddKey

				ld a, 13
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, (ix + Sprite.LogicArea + 13)	; Reads the body helmet. range from 0 to 24.
				add a, a
				ld b, a
				add a, a
				add a, a
				add a, b
				ld b, a
				call .PauseBar

				ld hl, .PauseTextBH
				call .PauseAddText

				ld c, TileMap.Keys_StartAddress + 6
				call .PauseAddKey

				ld c, TileMap.Keys_StartAddress + 8
				call .PauseAddKey

				ld c, TileMap.Keys_StartAddress + 10
				call .PauseAddKey

				ld a, 13
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld b, (ix + Sprite.LogicArea + 16)	; Reads the body helmet. range from 0 to 255.
				call .PauseBar

				ld hl, .PauseTextSH
				call .PauseAddText

				ld c, TileMap.Keys_StartAddress + 12
				call .PauseAddKey

				ld a, 13
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

				ld a, 17
				ld (de), a
				inc de

				ld c, 0

.PauseFruitLoop:		push bc
				push de
				call CheckIfItemIsPresent		; IN C = Item. IN IX = PLAYER CHARACTER. OUT ZF = 1, it has.
				pop de
				pop bc
				ld a, 5
				jr z, .PauseFruitPoke
				ld a, 23

.PauseFruitPoke:		ld (de), a
				inc de
				ld a, c
				ld (de), a
				inc de
				xor a
				ld (de), a
				inc de
				ld a, 21
				ld (de), a
				inc de

				inc c
				inc c
				ld a, c
				cp TileMap.Fruits_StopAddress
				jr c, .PauseFruitLoop

				ld a, 13
				ld (de), a
				inc de

				ld a, 13
				ld (de), a
				inc de

				ld a, 22
				ld (de), a
				inc de

.PauseNextPlayer:		pop af

				inc a
				cp 3
				jp c, .PausePlayerLoop

				xor a
				ld (de), a			; Must be null terminated.

				ld hl, DressBuffer 
				call PrintText

			call CloseFrame

.PauseHold:		call IsPausePressed
			jp nz, .PauseHold

			jp .BenchmarkScreen

.PauseBar:		ld l, 4				; In B = ammount. In DE = PrintText's buffer.

.PauseBarLoop:		ld a, b				; Averages 60, 44, 28, 10. So if B = 255 the full bar will be show.
			cp 60
			ld c, 187
			jr nc, .PauseBarPrint

			cp 44
			ld c, 186
			jr nc, .PauseBarPrint

			cp 28
			ld c, 185
			jr nc, .PauseBarPrint

			cp 10
			ld c, 184
			jr nc, .PauseBarPrint
			ld c, 183

.PauseBarPrint:		ld a, 5
			ld (de), a
			inc de
			ld a, c
			ld (de), a
			inc de
			xor a
			ld (de), a
			inc de
			ld a, 21
			ld (de), a
			inc de

			ld a, b
			sub 64
			jr nc, .PauseBarNoZero

			xor a

.PauseBarNoZero:	ld b, a

			dec l
			jr nz, .PauseBarLoop
			ret

.PauseAddText:		ld a, (hl)
			and a
			ret z

			ld (de), a
			inc hl
			inc de
			jp .PauseAddText

.PauseAddKey:		ld a, 21
			ld (de), a
			inc de

			ld a, 5				; IN C = KEY item (its tile number).
			ld (de), a
			inc de
			ld a, c
			ld (de), a
			inc de
			xor a
			ld (de), a
			inc de

			push de
			call CountHowManyThisItem	; IN C = Item. IN IX = PLAYER CHARACTER. OUT DE = count.

			ld hl, 99
			and a
			sbc hl, de
			jr nc, .PauseKeyNoMax
			ld de, 99

.PauseKeyNoMax:		ld l, e
			pop de

			ld h, 0
			ld b, h

			push de				
			call UpdateOverlayGrabItem.BHLDiv10	; IN BHL = number, OUT BHL = result, A = rest.
			pop de
			
			push af				; Save the unit digit.
			push de				
			call UpdateOverlayGrabItem.BHLDiv10	; IN BHL = number, OUT BHL = result, A = rest.
			pop de
							; A = second digit.
			pop bc
			ld c, a

			ld a, 21
			ld (de), a
			inc de

			ld a, 21
			ld (de), a
			inc de

			ld a, 21
			ld (de), a
			inc de

			ld a, c
			and a
			jr z, .PauseKeyNo00
			add a, '0'
			jr .PauseKeyPrintDigit

.PauseKeyNo00:		ld a, ' '

.PauseKeyPrintDigit:	ld (de), a
			inc de

			ld a, b
			add a, '0'
			ld (de), a
			inc de

			ret

.PauseBox:		DB 6, 13, 2, 238, 208, 0
.PauseTextPl:		DB "Player ", 0
.PauseTextSc:		DB " Score ", 0
.PauseTextHl:		DB " Health.  Gold ", 0
.PauseTextHH:		DB " Helmet.     ", 0
.PauseTextBH:		DB " Body armour.", 0
.PauseTextSH:		DB " Shoes.      ", 0

.BenchmarkScreen:	call OpenNextFrame
			call CloseFrame

			call OpenNextFrame

				ld de, DressBuffer
				ld hl, .BenchBox
				call .PauseAddText

				ld a, (BIOS.VAR.RG09SAV)
				and 2
				ld hl, .BenchPalText
				jr nz, .BenchNorm
				ld hl, .BenchNTSCText
.BenchNorm:
				call .PauseAddText

				xor a
				ld (de), a

				ld hl, DressBuffer 
				call PrintText

				ld hl, (DiagnoseVBlanksPointer)
				inc hl
				ld b, 20

.BenchSkip:			push bc
				call .BenchRead
				pop bc
				djnz .BenchSkip

				ld d, 10		; Start X coordinate.
				ld e, 1			; Width of the bar.

.BenchLoop:			call .BenchRead
				push hl

				and a
				jr z, .BenchZero

				add a, a
				add a, a
				ld l, a

				cp 140			; Peak limit.
				jr c, .NoPeak
				ld l, 140		; 140 pixels = Maximun size.
.NoPeak:
				ld a, 180		; Bottom coordinate.
				sub l
				ld h, a
				
				push de

				ld a, l
				cp 13
				ld b, $80		; Green.
				jr c, .BenchColor
				cp 29
				ld b, $B4		; Yellow.
				jr c, .BenchColor
				ld b, $98		; Red.

.BenchColor:			call PrintText.Fill	; In D E = Left/Width, H L = Top/Height. IN/OUT B = Color.
				pop de

.BenchZero:			inc d

				pop hl

				ld bc, (DiagnoseVBlanksPointer)
				and a
				sbc hl, bc
				jr z, .BenchDone
				add hl, bc

				jp .BenchLoop

.BenchDone:		ld hl, 256 * (180 - 4*3) + 1	; Normal Mark.
			ld de, 256 * 10 + 235
			ld b, $FF
			call PrintText.Fill

			call CloseFrame

.BenchHold:		call IsPausePressed
			jr nz, .BenchHold

			call OpenNextFrame
			call CloseFrame

			call OpenNextFrame
				call ScreenMapRedraw
			call CloseFrame

			xor a					; Resets the vblanks counter.
			ld (VBlankCountInFrame), a

			jp .GamePlayLoop

.BenchRead:		ld a, (hl)				; Out A = VBlank count.
			inc hl

			ld bc, DiagnoseVblanksList_End
			and a
			sbc hl, bc
			jr z, .BenchBufferWrap

			add hl, bc
			ret

.BenchBufferWrap:	ld hl, DiagnoseVblanksList_Start
			ret		

.BenchBox:		DB 6, 8, 30, 240, 154, 0

.BenchPalText:		DB 17, "The line is 16.6 fps.", 0

.BenchNTSCText:		DB 17, "The line is 20 fps.", 0

.TeleTransportation:	xor a
			ld (TeleportEnable), a			; Erase the flag.

			ld a, (TeleportMapDestination)		; Test if it is for the same map or another map.
			and a
			jr z, .CurrentMap
								; Must load the other map.
			ld (CurrentMapNumber), a		; Save the new map number.

			call ISRUnHooker

			call LoadMap				; Reestablece el mapa.
			ret c
			call LoadTileSet
			ret c
			call LoadSpriteSet
			ret c

			call ISRHooker

			call .ClearRecentDeaths

.CurrentMap:		call HideScreen

			ld hl, (TeleportDestination)
			push hl
			call PlayersTeleTransport		; IN H L = Ty Tx.
			pop hl

			jp .BackFromTeleTransport

			; ////////////////////// MAIN SCREEN /////////////////////.

.MainScreen:		call HideScreen

			xor a
			ld (FramesCount), a

			xor a
			ld (CurrentBoxLeft), a 			; Parameters for the PrintText function.
			ld (CurrentBoxTop), a
			dec a
			ld (CurrentBoxWidth), a
			ld a, 211
			ld (CurrentBoxHeight), a
			ld a, 12
			ld (TextLeftMargin), a
			ld a, 5
			ld (TextTopMargin), a
			ld a, 250
			ld (TextStopMargin), a

			ld hl, .MainScreenMessageTable
			ld (MainScreenCurrentMessage), hl

			ld hl, $0030				; Main screen in the map position.
			push hl

			call OpenFirstFrame			; It make sure it start with the vdp screen unscrolled for all frames.

				pop hl
				push hl
				call SetThisFrameMapScope	; In HL = TyTx.
				call ScreenMapRedraw

			call CloseFrame

			call ShowScreen

			call OpenNextFrame

				pop hl
				call SetThisFrameMapScope	; IN H L = Ty/Tx. Again for this frame too, matching the first frame.
				call ScreenMapRedraw

			call CloseFrame

.MainScreenLoop:	call OpenNextFrame

				ld a, (FramesCount)		; Two consecutive frames (odd + even) each 8 frames must run the tile animation feature.
				and $07
				jp nz, .MainScreenTANotEvenFrame
								; Tile Animation Even Frame.
				ld a, (AnimatedTilesSequencer)
				inc a
				and 3
				ld (AnimatedTilesSequencer), a
				call AnimateTiles		; Animate tiles, respawn enemies.
				jp .MainScreenNoTAFrame

.MainScreenTANotEvenFrame:	dec a				; Checks for Tile Animation Odd Frame.
				call z, AnimateTiles		; Animate tiles, respawn enemies.
.MainScreenNoTAFrame:
				ld a, (FramesCount)
				and $1F
				cp $1D
				call z, .MainScreenMessage

			call CloseFrame

			xor a
			call StrigPrimary			; IN A = 0 keyboard, 1 = joystick port 1 main button, 2 = joystick port 2 main button.
			jr z, .MainScreenPlayerSelect		; OUT A = 0 pressed, <> 0 not pressed.

			ld a, 1
			call StrigPrimary			; IN A = 0 keyboard, 1 = joystick port 1 main button, 2 = joystick port 2 main button.
			jr z, .MainScreenPlayerSelect		; OUT A = 0 pressed, <> 0 not pressed.

			ld a, 2
			call StrigPrimary			; IN A = 0 keyboard, 1 = joystick port 1 main button, 2 = joystick port 2 main button.
			jr z, .MainScreenPlayerSelect		; OUT A = 0 pressed, <> 0 not pressed.

			call IsEscapePressed			; A = 0 pressed.
			and a
			jp z, .MainScreenSFXTest

			ld b, 3					; /// FPS control ///.
			call FramesPerSecondControl		; Speed control. In B = how many Vblanks per frame?.
			jr .MainScreenLoop

.MainScreenMessage:	ld hl, (MainScreenCurrentMessage)
			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

			push hl
			call PrintText
			call CloseFrame
			call OpenNextFrame
			pop hl
			call PrintText

			ld hl, (MainScreenCurrentMessage)
			inc hl
			inc hl
			ld (MainScreenCurrentMessage), hl

			ld a, (hl)
			inc hl
			or (hl)
			ret nz

			ld hl, .MainScreenMessageTable
			ld (MainScreenCurrentMessage), hl
			ret

.MainScreenPlayerSelect:
			call OpenNextFrame

				ld hl, .MSPS
				call PrintText

			call CloseFrame

.MainScreenPSLoop:	call Is123Pressed			; OUT A = 0 pressed, B = number.
			and a
			jr nz, .MainScreenPSLoop

			ld a, b
			ld (PlayersCount), a
			cp 3
			jr z, .KeyboardSelected

			call OpenNextFrame

				ld hl, .MSPS2
				call PrintText

			call CloseFrame

.MainScreenCSLoop:	call Is123Pressed			; OUT A = 0 pressed, B = number.
			and a
			jr nz, .MainScreenCSLoop

			ld a, b
			cp 3
			jr z, .MainScreenCSLoop

			cp 2
			jr z, .JoysticksSelected

.KeyboardSelected:	xor a
			ld (InputControlPlayer0), a
			inc a
			ld (InputControlPlayer1), a
			inc a
			ld (InputControlPlayer2), a
			ret

.JoysticksSelected:	ld a, 1			
			ld (InputControlPlayer0), a
			inc a
			ld (InputControlPlayer1), a
			xor a
			ld (InputControlPlayer2), a
			ret

.MainScreenSFXTest:	ld hl, .MSPS3
			call PrintText

			ld e, 1

.MainScreenSFXTestLoop:	push de

			ld l, e
			ld h, 0
			ld b, h

			call UpdateOverlayGrabItem.BHLDiv10	; IN BHL = number, OUT BHL = result, A = rest.
			add a, '0'

			ld (.MSPS4 + 8), a

			call UpdateOverlayGrabItem.BHLDiv10	; IN BHL = number, OUT BHL = result, A = rest.
			add a, '0'

			ld (.MSPS4 + 7), a

			call UpdateOverlayGrabItem.BHLDiv10	; IN BHL = number, OUT BHL = result, A = rest.
			add a, '0'

			ld (.MSPS4 + 6), a

			ld hl, .MSPS4
			call PrintText
			pop de

			push de
			ld b, 8					; /// FPS control ///.
			call FramesPerSecondControl		; Speed control. In B = how many Vblanks per frame?.
			call IsEscapePressed			; A = 0 pressed.
			pop de
			and a
			jp z, .MainScreen

			push de
			call Is123Pressed			; OUT A = 0 pressed, B = number.
			pop de
			and a
			jr z, .Pressed123

			jr .MainScreenSFXTestLoop

.Pressed123:		ld a, b
			dec a
			jr z, .PlaySFX
			dec a
			jr z, .Inc
								; Dec.
			dec e
			jp .MainScreenSFXTestLoop

.Inc:			inc e
			jp .MainScreenSFXTestLoop

.PlaySFX:		ld a, e
			push de
			call SFXRequest				; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.
			pop de
			jp .MainScreenSFXTestLoop

.ClearRecentDeaths:	ld b, RecentDeathCount
			ld hl, RecentDeaths

.ClearRecents:		ld (hl), 0
			inc hl
			ld (hl), 0
			inc hl
			djnz .ClearRecents

			ret
.MainScreenMessageTable:
			DW .MSM1, .MSM2, .MSM3, .MSM4
			DW 0

.MSM1:			DB 8, 0, 90, 17, "a FlulpyCrea production...", 0

.MSM2:			DB 8, 0, 102, 17, "a long term development...", 0

.MSM3:			DB 8, 0, 138, 17, "... SUPER CASTLE ...", 0

.MSM4:			DB 8, 0, 162, 17, "a flyguille's original", 0



.MSPS:			DB 6, 12, 85, 232, 100, "Press '1' for one player.", 13, "Press '2' for two players.", 13, "Press '3' for three players.", 13, 0
.MSPS2:			DB 6, 12, 85, 232, 100, "Press '1' for keyboard.", 13, "Press '2' for joysticks.", 13, 0
.MSPS3:			DB 6, 12, 85, 232, 100, "'1' Play current SFX.", 13, "'2' Next SFX.", 13, "'3' Previous SFX.", 13, "'ESC' Exit.", 13, 0
.MSPS4:			DB 6, 180,150, 50, 15, 17, "   ", 0



; ************************************************************
; * SFX ENGINE (PSG)                                         *
; ************************************************************






TestIfSFXIsPlaying:	ld c, a					; IN A = Effect to test. ZF = 1 is playing.
			ld a, (SFXCurrentlyPlaying1)
			cp c
			ret z
			ld a, (SFXCurrentlyPlaying2)
			cp c
			ret z
			ld a, (SFXCurrentlyPlaying3)
			cp c
			ret






SFXRequestShield:	push bc					; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.
			push de
			push hl
			call SFXRequest
			pop hl
			pop de
			pop bc
			ret






SFXRequest:		push af
			ld a, (ResourcesSegment)
.P2_1:			call DOS2.CALL.PUT_P2
			pop af

			ld c, a					; IN A = Effect requested. It ignores if no channels left and is playing the same stream in other channel. It don't checks if it is a valid Effect.
			ld l, a
			ld h, 0
			add hl, hl
			ld de, EffectsTable
			add hl, de

			ld b, 3
			ld de, SFXCurrentlyPlaying1

.Loop:			ld a, (de)
			and a
			jr nz, .Next

.Overlap:		di

			inc de					; Set the pointer to the stream.
			ld a, (hl)
			inc hl
			ld (de), a
			inc de

			ld a, (hl)
			ld (de), a

			ei

			dec de
			dec de
			ld a, c
			ld (de), a

			ret

.Next:			inc de
			inc de
			inc de
			djnz .Loop

			ld a, (SFXCurrentlyPlaying1)		; Test if it is already playing the same stream type.
			cp c
			ret z
			ld a, (SFXCurrentlyPlaying2)
			cp c
			ret z
			ld a, (SFXCurrentlyPlaying3)
			cp c
			ret z

			ld a, (SFXLastOverlappedChannel)	; No free channel. Is prefferable to overlap a preexistent stream.
			inc a
			cp 3
			jr c, .CycleOverlap

			xor a

.CycleOverlap:		ld (SFXLastOverlappedChannel), a

			ld de, SFXCurrentlyPlaying1
			and a
			jr z, .Overlap
			ld de, SFXCurrentlyPlaying2
			dec a
			jr z, .Overlap
			ld de, SFXCurrentlyPlaying3
			jp .Overlap






SFXPlayer:		ld hl, SFXCurrentlyPlaying1
			ld b, 3

.MainLoop:		ld a, (hl)
			inc hl
			and a
			jr z, .MainNext

			ld e, (hl)
			inc hl
			ld d, (hl)

.ChannelLoop:		push hl
			call .Play				; IN DE = stream address. IN B = channel in reverse, so SFXCurrentlyPlaying1 = PSG channel 3.
			pop hl
			jr nc, .ChannelLoop
			and a
			jr z, .StreamEnds

			ld (hl), d
			dec hl
			ld (hl), e
			inc hl
			inc hl

			djnz .MainLoop
			ret

.MainNext:		inc hl
			inc hl
			djnz .MainLoop
			ret

.StreamEnds:		dec hl
			dec hl
			ld (hl), 0
			inc hl
			inc hl
			inc hl
			djnz .MainLoop
			ret

.Play:			ld a, (de)				; Read data type. Valid values 0, 2, 4, 6...
			inc de

			ld hl, .PlayFunctionsTable		; WARNING!!! no protections, bad streams can crash the game.
			add a, l
			ld l, a
			jr nc, .JJJ30
			inc h
.JJJ30:
			ld a, (hl)
			inc hl
			ld h, (hl)
			ld l, a

			jp (hl)

.PlayFunctionsTable:	DW .PlayTone				; 0, t, t.
			DW .PlayVolume				; 2, v.
			DW .ToneEnable				; 4.
			DW .ToneDisable				; 6.
			DW .NoiseEnable				; 8.
			DW .NoiseDisable			; 10.
			DW .NoisePeriod				; 12, p.
			DW .Instrument				; 14, i.
			DW .InstrumentPeriod			; 16, p, p.
			DW .EndPeriod				; 18.
			DW .EndStream				; 20.

.PlayTone:		ld a, b
			dec a
			add a, a
			ld c, a

			out ($A0), a

			ld a, (de)
			inc de
			out ($A1), a

			ld a, c
			inc a
			out ($A0), a

			ld a, (de)
			inc de
			out ($A1), a

			ret

.PlayVolume:		ld a, b
			add a, 7

			out ($A0), a

			ld a, (de)
			inc de
			out ($A1), a
			ret

.ToneEnable:		ld a, 7
			out ($A0), a

			ld a, b
			dec a
			ld c, $FE
			jr z, .ToneEnableWr
			dec a
			ld c, $FD
			jr z, .ToneEnableWr
			ld c, $FB

.ToneEnableWr:		in a, ($A2)
			and c
			out ($A1), a
			ret

.ToneDisable:		ld a, 7
			out ($A0), a

			ld a, b
			dec a
			ld c, 1
			jr z, .ToneDisableWr
			dec a
			ld c, 2
			jr z, .ToneDisableWr
			ld c, 4

.ToneDisableWr:		in a, ($A2)
			or c
			out ($A1), a
			ret

.NoiseEnable:		ld a, 7
			out ($A0), a

			ld a, b
			dec a
			ld c, $F7
			jr z, .NoiseEnableWr
			dec a
			ld c, $EF
			jr z, .NoiseEnableWr
			ld c, $DF

.NoiseEnableWr:		in a, ($A2)
			and c
			out ($A1), a
			ret

.NoiseDisable:		ld a, 7
			out ($A0), a

			ld a, b
			dec a
			ld c, $08
			jr z, .NoiseDisableWr
			dec a
			ld c, $10
			jr z, .NoiseDisableWr
			ld c, $20

.NoiseDisableWr:	in a, ($A2)
			or c
			out ($A1), a
			ret

.NoisePeriod:		ld a, 6
			out ($A0), a

			ld a, (de)
			inc de
			out ($A1), a
			and a
			ret	

.Instrument:		ld a, 13
			out ($A0), a

			ld a, (de)
			inc de
			out ($A1), a
			and a
			ret

.InstrumentPeriod:	ld a, 11
			out ($A0), a

			ld a, (de)
			inc de
			out ($A1), a

			ld a, 12
			out ($A0), a

			ld a, (de)
			inc de
			out ($A1), a
			and a
			ret

.EndPeriod:		ld a, 255
			scf
			ret

.EndStream:		call .NoiseDisable
			call .ToneDisable

			xor a
			scf
			ret






PSG_Stop:		ld a, 7
			out ($A0), a

			in a, ($A2)					; Disable all channels.
			or $3F
			out ($A1), a

			xor a
			ld (SFXCurrentlyPlaying1), a
			ld (SFXCurrentlyPlaying2), a
			ld (SFXCurrentlyPlaying3), a

			ret






Opl4_Start:		ld a, (FMCurrentSongNumber)			; Checks if there is a song loaded.
			and a
			scf
			ret z

			ld hl, $8000
			ld (FMPlayAddress), hl
			ld a, (FMSongBaseSegment)
			ld (FMPlayAddressSegment), a
			xor a
			ld (FMPlayMusicLoopedFlag), a

			ld a, $FF
			ld (FMPlay),a					; Order to start playing.
			ret






Opl4_Stop:		ld a, $C0
			ld b, 9

.FMLoop:		push af

			out ($C4), a
			push bc
			pop bc
			xor a
			out ($C5), a

			pop af
			push af

			out ($C6), a
			push bc
			pop bc
			xor a
			out ($C5), a

			pop af
			inc a
			djnz .FMLoop

			ld a, $68
			ld b, 24

.WaveLoop:		push af

			out ($7E), a
			push bc
			pop bc
			ld a, 8
			out ($7F), a

			pop af
			inc a
			djnz .WaveLoop

			xor a
			ld (FMPlay),a					; Order to stop playing.

			ret






VGMPlayer:		ld a, (FMPlay)					; is playing?.
			and a
			ret z

			call .GetAddress

.Z80_357_Loop:		ld a, (hl)
			inc a						; 0xFF.
			jp z, .VdpFrame
			inc a						; 0xFE.
			jr z, .SetBankC6
			inc a						; 0xFD.
			jr z, .SetBankC4
			inc a						; 0xFC.
			jp z, .OutData
			inc a						; 0xFB.
			jr z, .SetBank7E
			inc a						; 0xFA.
			jr z, .Finish
									; It is a reg write, which one, depends on FMPlayCurrentBank.
			ld a, (FMPlayCurrentBank)
			and a
			jr z, .Z80_357_WaveAddress
			dec a
			jr z, .Z80_357_FMAddress1
			jp .Z80_357_FMAddress2

.OutData:		ld a, (FMPlayCurrentBank)
			and a
			jr z, .Z80_357_WaveData
			jp .Z80_357_FMData

.Finish:		ld hl, $8000					; It loops all the song.
			ld a, (FMSongBaseSegment)
			ld (FMPlayAddressSegment), a
			ld a, 255
			ld (FMPlayMusicLoopedFlag), a			; It marks that it already looped.

			jp .SaveAddressAndRestore

.SetBankC6:		ld a, 2
			ld (FMPlayCurrentBank), a
			call .NextAddress
			jp .Z80_357_Loop

.SetBankC4:		ld a, 1
			ld (FMPlayCurrentBank), a
			call .NextAddress
			jp .Z80_357_Loop

.SetBank7E:		xor a
			ld (FMPlayCurrentBank), a
			call .NextAddress
			jp .Z80_357_Loop

.Z80_357_WaveAddress:	ld a, (hl)
			out ($7E), a
.Z80_357_WaveData:	call .NextAddress
			ld a, (hl)
			out ($7F), a
			call .NextAddress
			jp .Z80_357_Loop

.Z80_357_FMAddress1:	ld a, (hl)
			out ($C4), a
			ld (FMPlayCurrentFMReg), a
			xor a
			ld (FMPlayCurrentFMBank), a

.Z80_357_FMData:	call .NextAddress
			ld a, (hl)
			out ($C5), a
			call .NextAddress
			jp .Z80_357_Loop

.Z80_357_FMAddress2:	ld a, (hl)
			out ($C6), a
			ld (FMPlayCurrentFMReg), a
			ld a, $FF
			ld (FMPlayCurrentFMBank), a

			call .NextAddress
			ld a, (hl)
			out ($C5), a
			call .NextAddress
			jp .Z80_357_Loop

.VdpFrame:		call .NextAddress

.SaveAddressAndRestore:	ld (FMPlayAddress), hl

			ld a, (FMPlaySegmentBackUp)
.P2_3:			jp DOS2.CALL.PUT_P2

.GetAddress:
.G2_1:			call DOS2.CALL.GET_P2
			ld (FMPlaySegmentBackUp), a

			ld a, (FMPlayAddressSegment)
.P2_2:			call DOS2.CALL.PUT_P2
			ld hl, (FMPlayAddress)
			ret

.NextAddress:		inc hl
			bit 6, h
			ret z

			res 6, h
			ld a, (FMPlayAddressSegment)
			inc a
			ld (FMPlayAddressSegment), a
.P2_1:			jp DOS2.CALL.PUT_P2



; ************************************************************
; * RESOURCES                                                *
; ************************************************************






FNAME "resource.bin"
ORG $8000






CharSetPatterns:	DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000100 b
			DB 00001110 b
			DB 00001110 b
			DB 00001110 b
			DB 00000100 b
			DB 00000000 b
			DB 00000100 b

			DB 00000000 b
			DB 01100110 b
			DB 01100110 b
			DB 11001100 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00100100 b
			DB 01111110 b
			DB 00100100 b
			DB 00100100 b
			DB 01111110 b
			DB 00100100 b
			DB 00000000 b

			DB 00001000 b
			DB 00111110 b
			DB 01011000 b
			DB 01011000 b
			DB 00111100 b
			DB 00010110 b
			DB 00011010 b
			DB 01111100 b

			DB 00000000 b
			DB 01000010 b
			DB 10100100 b
			DB 01001000 b
			DB 00010000 b
			DB 00100100 b
			DB 01001010 b
			DB 10000100 b

			DB 00000000 b
			DB 01110000 b
			DB 10001000 b
			DB 10001000 b
			DB 01010010 b  
			DB 00100100 b
			DB 01011000 b
			DB 00100110 b

			DB 01100000 b
			DB 01100000 b
			DB 01100000 b
			DB 00100000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000100 b
			DB 00001000 b
			DB 00010000 b
			DB 00010000 b
			DB 00010000 b
			DB 00001000 b
			DB 00000100 b

			DB 00000000 b
			DB 00010000 b
			DB 00001000 b
			DB 00000100 b
			DB 00000100 b
			DB 00000100 b
			DB 00001000 b
			DB 00010000 b

			DB 00001000 b
			DB 00101010 b
			DB 00011100 b
			DB 00001000 b 
			DB 00011100 b
			DB 00101010 b
			DB 00001000 b
			DB 00000000 b

			DB 00000000 b
			DB 00001000 b
			DB 00001000 b
			DB 00111110 b
			DB 00001000 b
			DB 00001000 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00010000 b
			DB 00111000 b
			DB 00111000 b
			DB 01100000 b

			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00111110 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00010000 b
			DB 00111000 b
			DB 00010000 b

			DB 00000000 b
			DB 00000010 b
			DB 00000100 b
			DB 00001000 b
			DB 00010000 b
			DB 00100000 b
			DB 01000000 b
			DB 00000000 b

			DB 00011000 b
			DB 00100100 b
			DB 01000110 b
			DB 01001010 b
			DB 01010010 b
			DB 00100100 b
			DB 00011000 b
			DB 00000000 b

			DB 00001000 b
			DB 00111000 b
			DB 00001000 b
			DB 00001000 b
			DB 00001000 b
			DB 00001000 b
			DB 00011100 b
			DB 00000000 b

			DB 00111000 b
			DB 01000100 b
			DB 00000010 b
			DB 00000100 b
			DB 00011000 b
			DB 00100000 b
			DB 01111110 b
			DB 00000000 b

			DB 00111100 b
			DB 01000010 b
			DB 00000010 b
			DB 00001100 b
			DB 00000010 b
			DB 01000010 b
			DB 00111100 b
			DB 00000000 b

			DB 01000010 b
			DB 01000010 b
			DB 01000010 b
			DB 01111110 b
			DB 00000010 b
			DB 00000010 b
			DB 00000010 b
			DB 00000000 b

			DB 01111110 b
			DB 01000000 b
			DB 01111000 b
			DB 00000100 b
			DB 00000010 b
			DB 01000010 b
			DB 00111100 b
			DB 00000000 b

			DB 00011110 b
			DB 00100000 b
			DB 01000000 b
			DB 01111100 b
			DB 01000010 b
			DB 01000010 b
			DB 00111100 b
			DB 00000000 b

			DB 01111110 b
			DB 00000010 b
			DB 00000010 b
			DB 00001110 b
			DB 00000010 b
			DB 00000010 b
			DB 00000010 b
			DB 00000000 b

			DB 00111100 b
			DB 01000010 b
			DB 01000010 b
			DB 00111100 b
			DB 01000010 b
			DB 01000010 b
			DB 00111100 b
			DB 00000000 b

			DB 00111100 b
			DB 01000010 b
			DB 01000010 b
			DB 00111110 b
			DB 00000010 b
			DB 00000010 b
			DB 00111100 b
			DB 00000000 b

			DB 00000000 b
			DB 00011000 b
			DB 00011000 b
			DB 00000000 b
			DB 00000000 b
			DB 00011000 b
			DB 00011000 b
			DB 00000000 b

			DB 00000000 b
			DB 00011000 b
			DB 00011000 b
			DB 00000000 b
			DB 00000000 b
			DB 00011000 b
			DB 00011000 b
			DB 00110000 b

			DB 00000000 b
			DB 00001100 b
			DB 00011000 b
			DB 00110000 b
			DB 01100000 b
			DB 00110000 b
			DB 00011000 b
			DB 00001100 b

			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00111110 b
			DB 00000000 b
			DB 00111110 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00110000 b
			DB 00011000 b
			DB 00001100 b
			DB 00000110 b
			DB 00001100 b
			DB 00011000 b
			DB 00110000 b

			DB 00000000 b
			DB 00111100 b
			DB 01100110 b
			DB 00000100 b
			DB 00011000 b
			DB 00000000 b
			DB 00011000 b
			DB 00011000 b

			DB 00000000 b
			DB 00011100 b
			DB 00100010 b
			DB 01001010 b
			DB 01010110 b
			DB 01011000 b
			DB 01000010 b
			DB 00111100 b

			DB 00111000 b
			DB 01000100 b
			DB 10000010 b
			DB 10000010 b
			DB 11111110 b
			DB 10000010 b
			DB 10000010 b
			DB 00000000 b

			DB 11111000 b
			DB 10000100 b
			DB 10000100 b
			DB 11111100 b
			DB 10000110 b
			DB 10000010 b
			DB 11111100 b
			DB 00000000 b

			DB 00111100 b
			DB 01000010 b
			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 01000010 b
			DB 00111100 b
			DB 00000000 b

			DB 11111000 b
			DB 10000100 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000100 b
			DB 11111000 b
			DB 00000000 b

			DB 11111110 b
			DB 10000000 b
			DB 10000000 b
			DB 11110000 b
			DB 10000000 b
			DB 10000000 b
			DB 11111110 b
			DB 00000000 b

			DB 11111110 b
			DB 10000000 b
			DB 10000000 b
			DB 11110000 b
			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 00000000 b

			DB 00111100 b
			DB 01000010 b
			DB 10000000 b
			DB 10001100 b
			DB 10000010 b
			DB 10000010 b
			DB 01111100 b
			DB 00000000 b

			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 11111110 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 00000000 b

			DB 00011100 b
			DB 00001000 b
			DB 00001000 b
			DB 00001000 b
			DB 00001000 b
			DB 00001000 b
			DB 00011100 b
			DB 00000000 b

			DB 00011100 b
			DB 00001000 b
			DB 00001000 b
			DB 00001000 b
			DB 01001000 b
			DB 01001000 b
			DB 00110000 b
			DB 00000000 b

			DB 01000100 b
			DB 01001000 b
			DB 01010000 b
			DB 01100000 b
			DB 01010000 b
			DB 01001000 b
			DB 01000100 b
			DB 00000000 b

			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 11111110 b
			DB 00000000 b

			DB 10000010 b
			DB 11000110 b
			DB 10101010 b
			DB 10010010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 00000000 b

			DB 11000010 b
			DB 10100010 b
			DB 10010010 b
			DB 10001010 b
			DB 10000110 b
			DB 10000010 b
			DB 10000010 b
			DB 00000000 b

			DB 01111100 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 01111100 b
			DB 00000000 b

			DB 11111100 b
			DB 10000010 b
			DB 10000010 b
			DB 11111100 b
			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 00000000 b

			DB 00111100 b
			DB 01000010 b
			DB 01000010 b
			DB 01000010 b
			DB 01010100 b
			DB 01001000 b
			DB 00110100 b
			DB 00000010 b

			DB 11111100 b
			DB 10000010 b
			DB 10000010 b
			DB 11111100 b
			DB 10010000 b
			DB 10001000 b
			DB 10000100 b
			DB 00000000 b

			DB 01111110 b
			DB 10000000 b
			DB 10000000 b
			DB 01111100 b
			DB 00000010 b
			DB 00000010 b
			DB 11111100 b
			DB 00000000 b

			DB 11111110 b
			DB 00010000 b
			DB 00010000 b
			DB 00010000 b
			DB 00010000 b
			DB 00010000 b
			DB 00010000 b
			DB 00000000 b

			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 01111100 b
			DB 00000000 b

			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 01000100 b
			DB 00101000 b
			DB 00010000 b
			DB 00000000 b

			DB 10000010 b
			DB 10000010 b
			DB 10000010 b
			DB 10010010 b
			DB 10010010 b
			DB 01010100 b
			DB 00101000 b
			DB 00000000 b

			DB 10000010 b
			DB 01000100 b
			DB 00101000 b
			DB 00010000 b
			DB 00101000 b
			DB 01000100 b
			DB 10000010 b
			DB 00000000 b

			DB 10000010 b
			DB 01000100 b
			DB 00101000 b
			DB 00010000 b
			DB 00100000 b
			DB 01000000 b
			DB 10000000 b
			DB 00000000 b

			DB 11111110 b
			DB 00000100 b
			DB 00001000 b
			DB 00010000 b
			DB 00100000 b
			DB 01000000 b
			DB 11111110 b
			DB 00000000 b

			DB 00000000 b
			DB 00111100 b
			DB 00100000 b
			DB 00100000 b
			DB 00100000 b
			DB 00100000 b
			DB 00111100 b
			DB 00000000 b

			DB 00000000 b
			DB 01000000 b
			DB 00100000 b
			DB 00010000 b
			DB 00001000 b
			DB 00000100 b
			DB 00000010 b
			DB 00000000 b

			DB 00000000 b
			DB 00111100 b
			DB 00000100 b
			DB 00000100 b
			DB 00000100 b
			DB 00000100 b
			DB 00111100 b
			DB 00000000 b

			DB 00000000 b
			DB 00001000 b
			DB 00010100 b
			DB 00100010 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 01111110 b
			DB 00000000 b

			DB 00000000 b
			DB 00010000 b
			DB 00001000 b
			DB 00000100 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 01110000 b
			DB 00001000 b
			DB 01111000 b
			DB 10001000 b
			DB 01111000 b
			DB 00000000 b

			DB 10000000 b
			DB 10000000 b
			DB 10110000 b
			DB 11001000 b
			DB 10001000 b
			DB 11001000 b
			DB 10110000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 01110000 b
			DB 10001000 b
			DB 10000000 b
			DB 10001000 b
			DB 01110000 b
			DB 00000000 b

			DB 00001000 b
			DB 00001000 b
			DB 01101000 b
			DB 10011000 b
			DB 10001000 b
			DB 10011000 b
			DB 01101000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 01110000 b
			DB 10001000 b
			DB 11111000 b
			DB 10000000 b
			DB 01110000 b
			DB 00000000 b

			DB 00010000 b
			DB 00101000 b
			DB 00100000 b
			DB 11111000 b
			DB 00100000 b
			DB 00100000 b
			DB 00100000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 01101000 b
			DB 10011000 b
			DB 10011000 b
			DB 01101000 b
			DB 00001000 b
			DB 01110000 b

			DB 10000000 b
			DB 10000000 b
			DB 11110000 b
			DB 10001000 b
			DB 10001000 b
			DB 10001000 b
			DB 10001000 b
			DB 00000000 b

			DB 00100000 b
			DB 00000000 b
			DB 01100000 b
			DB 00100000 b
			DB 00100000 b
			DB 00100000 b
			DB 01110000 b
			DB 00000000 b

			DB 00010000 b
			DB 00000000 b
			DB 00110000 b
			DB 00010000 b
			DB 00010000 b
			DB 00010000 b
			DB 10010000 b
			DB 01100000 b

			DB 01000000 b
			DB 01000000 b
			DB 01001000 b
			DB 01010000 b
			DB 01100000 b
			DB 01010000 b
			DB 01001000 b
			DB 00000000 b

			DB 01100000 b
			DB 00100000 b
			DB 00100000 b
			DB 00100000 b
			DB 00100000 b
			DB 00100000 b
			DB 01110000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 11010000 b
			DB 10101000 b
			DB 10101000 b
			DB 10101000 b
			DB 10101000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 10110000 b
			DB 11001000 b
			DB 10001000 b
			DB 10001000 b
			DB 10001000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 01110000 b
			DB 10001000 b
			DB 10001000 b
			DB 10001000 b
			DB 01110000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 10110000 b
			DB 11001000 b
			DB 11001000 b
			DB 10110000 b
			DB 10000000 b
			DB 10000000 b

			DB 00000000 b
			DB 00000000 b
			DB 01101000 b
			DB 10011000 b
			DB 10011000 b
			DB 01101000 b
			DB 00001000 b
			DB 00001000 b

			DB 00000000 b
			DB 00000000 b
			DB 10110000 b
			DB 11001000 b
			DB 10000000 b
			DB 10000000 b
			DB 10000000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 01111000 b
			DB 10000000 b
			DB 11110000 b
			DB 00001000 b
			DB 11110000 b
			DB 00000000 b

			DB 01000000 b
			DB 01000000 b
			DB 11110000 b
			DB 01000000 b
			DB 01000000 b
			DB 01001000 b
			DB 00110000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 10010000 b
			DB 10010000 b
			DB 10010000 b
			DB 10010000 b
			DB 01101000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 10001000 b
			DB 10001000 b
			DB 10001000 b
			DB 01010000 b
			DB 00100000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 10001000 b
			DB 10101000 b
			DB 10101000 b
			DB 10101000 b
			DB 01010000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 10001000 b
			DB 01010000 b
			DB 00100000 b
			DB 01010000 b
			DB 10001000 b
			DB 00000000 b

			DB 00000000 b
			DB 00000000 b
			DB 10001000 b
			DB 10001000 b
			DB 10011000 b
			DB 01101000 b
			DB 00001000 b
			DB 01110000 b

			DB 00000000 b
			DB 00000000 b
			DB 11111000 b
			DB 00010000 b
			DB 00100000 b
			DB 01000000 b
			DB 11111000 b
			DB 00000000 b






ScriptsRoomsIndex:	DW .CocktailsIndex
			DW .WeaponsIndex
			DW .FruitsIndex
			DW .UpgradesIndex
			DW .SpecialDealsIndex
			DW .HospitalIndex

.CocktailsIndex:	DW .Cocktails0
			DW .Cocktails1
			DW .Cocktails2
			DW .Cocktails3
			DW .Cocktails4
			DW .Cocktails5
			DW .Cocktails6
			DW .Cocktails7

.WeaponsIndex:		DW .Weapons0
			DW .Weapons1
			DW .Weapons2
			DW .Weapons3
			DW .Weapons4
			DW .Weapons5
			DW .Weapons6
			DW .Weapons7

.FruitsIndex:		DW .Fruits0
			DW .Fruits1
			DW .Fruits2
			DW .Fruits3
			DW .Fruits4
			DW .Fruits5
			DW .Fruits6
			DW .Fruits7

.UpgradesIndex:		DW .Upgrades0
			DW .Upgrades1
			DW .Upgrades2
			DW .Upgrades3
			DW .Upgrades4
			DW .Upgrades5
			DW .Upgrades6
			DW .Upgrades7

.SpecialDealsIndex:	DW .Special0
			DW .Special1
			DW .Special2
			DW .Special3
			DW .Special4
			DW .Special5
			DW .Special6
			DW .Special7

.HospitalIndex:		DW .Hospital0
			DW .Hospital1
			DW .Hospital2
			DW .Hospital3
			DW .Hospital4
			DW .Hospital5
			DW .Hospital6
			DW .Hospital7

INCLUDE "Speechs.asm"






INCLUDE "sfx.asm"






HitBoxesTable:		

INCLUDE "hitboxes.asm"



; ************************************************************
; * VARIABLES                                                *
; ************************************************************






ORG $C000



VAR_START:

IFDEF NO_CACHE
ELSE

PatternCachePool:	RB	PatternCacheSize * PatternCache.SizeOf	; Ammount of patterns that can be in the cache in VRAM. Too large can reduce the performance.

ENDIF

DOS2.CALL:
	.BDOS:		EQU	5

	.ALL_SEG:	RB	3					; DOS2 jump table.
	.FRE_SEG:	RB	3

	.RD_SEG:	RB	3
	.WR_SEG:	RB	3
	.CAL_SEG:	RB	3
	.CALLS:		RB	3
	.PUT_PH:	RB	3
	.GET_PH:	RB	3

	.PUT_P0:	RB	3
	.GET_P0:	RB	3

	.PUT_P1:	RB	3
	.GET_P1:	RB	3

	.PUT_P2:	RB	3
	.GET_P2:	RB	3

	.PUT_P3:	RB	3
	.GET_P3:	RB	3

HookBackUp:		RB	3

CPUDetected:		RB	1					; 0 = z80 3.57mhz, 1 = z80 7mhz, 2 = R800.

FileHandle:		RB	1

TileBaseSegment:	RB	1					; 128K.
MapBaseSegment:		RB	1					; 64K Configured in the loading procedure.
SpritesBaseSegment:	RB	1					; 64K (256 sprite patterns).
PlayerPatternsSegment:	RB	1					; Where is all the player patterns (in compressed form). See compress.bas utility.
FMSongBaseSegment:	RB	1					; Where the current song must be load.
ResourcesSegment:	RB	1

FMPlay:			RB	1					; is playing?.
FMPlayAddress:		RW	1
FMPlayAddressSegment:	RB	1
FMPlayCurrentBank:	RB	1
FMPlayCurrentFMBank:	RB	1
FMPlayCurrentFMReg:	RB	1
FMPlaySegmentBackUp:	RB	1
FMPlayMusicLoopedFlag:	RB	1

CurrentTileSetLoaded:	RB	1					; = 255 = Is loaded the global tileset file, = 1~9 loaded specific map tileset.
CurrentSpriteSetLoaded:	RB	1					; = 255 = Is loaded the global spriteset file, = 1~9 loaded specific map spriteset.

VarSaveState_Start:

GravityIntensity:	RW	1					; How many pixels it will falls if not supported per frame? format tttt tttt pppp 0000.

FMCurrentSongNumber:	RB	1

CurrentMapNumber:	RB	1

IdealMapTileOffsetX:	RB	1					; IDEAL MAP POSITION where the "ScrollsOnDemand" routine wants to go.
IdealMapTileX:		RB	1
IdealMapTileOffsetY:	RB	1
IdealMapTileY:		RB	1

ScreenCurrentX:		RB	1					; HARDWARE SCROLLS REGISTERS SETUP.
ScreenCurrentY:		RB	1					; This is the current top/left position in the screen's bitmap that the VDP is currently showing.

									; ACTUAL BUFFER: What is showing at the top/left screen position?.
MapCurrentTileOffsetX:	RB	1					; xxxx0000 pixel offset for the current left tile, that is showing at the most left pixel of the screen.
MapCurrentTileX:	RB	1					; Current left tile position that is showing in the most left of the screen.
MapCurrentTileOffsetY:	RB	1					; yyyy0000 pixel offset for the current top tile, that is showing at the most top pixel of the screen.
MapCurrentTileY:	RB	1					; Current top tile position that is showing in the most top of the screen.

Bf0ScreenCurrentX:	RB	1					; This is the current top/left position in the screen's bitmap that the VDP is currently showing.
Bf0ScreenCurrentY:	RB	1
									; BACKUP FOR BUFFER 0: What is showing at the top/left screen position?.
Bf0MapCurrentTileOffsetX:RB	1					; xxxx0000 pixel offset for the current left tile, that is showing at the most left pixel of the screen.
Bf0MapCurrentTileX:	RB	1					; Current left tile position that is showing in the most left of the screen.
Bf0MapCurrentTileOffsetY:RB	1					; yyyy0000 pixel offset for the current top tile, that is showing at the most top pixel of the screen.
Bf0MapCurrentTileY:	RB	1					; Current top tile position that is showing in the most top of the screen.

Bf1ScreenCurrentX:	RB	1					; This is the current top/left position in the screen's bitmap that the VDP is currently showing.
Bf1ScreenCurrentY:	RB	1
									; BACKUP FOR BUFFER 1: What is showing at the top/left screen position?.
Bf1MapCurrentTileOffsetX:RB	1					; xxxx0000 pixel offset for the current left tile, that is showing at the most left pixel of the screen.
Bf1MapCurrentTileX:	RB	1					; Current left tile position that is showing in the most left of the screen.
Bf1MapCurrentTileOffsetY:RB	1					; yyyy0000 pixel offset for the current top tile, that is showing at the most top pixel of the screen.
Bf1MapCurrentTileY:	RB	1					; Current top tile position that is showing in the most top of the screen.

AnimatedTilesSequencer:	RB	1					; Range (0-3).

PlayersCount:		RB	1
InputControlPlayer0:	RB	1
InputControlPlayer1:	RB	1
InputControlPlayer2:	RB	1

ActiveBuffer:		RB	1					; = 0 writing on page 0, = 1 writing on page 1. Viewing the other.
FramesCount:		RB	1					; counts the frames actually rendered.
VBlankCountInFrame:	RB	1					; counts the vblanks detected within one frame rendered.

IFDEF NO_CACHE
ELSE

Bf0PatternCacheY:	RB	1					; Where is the pattern cache holded?.
Bf1PatternCacheY:	RB	1
Bf0PatternCacheRow0Enabled:	RB	1				; the enabled avoids to use the row that are passing the Y=255/0 barrier.
Bf0PatternCacheRow1Enabled:	RB	1
Bf1PatternCacheRow0Enabled:	RB	1
Bf1PatternCacheRow1Enabled:	RB	1

ENDIF

SpritesCount:		RB	1					; It shorts the sprite bucles for not inspecting all the SpritesPoolSize sprites slot.
SpritesPool:		RB	SpritesPoolSize * Sprite.SizeOf		; Number of characters that can be in the screen at the same time. Too large will be a waste of time and memory.

SpacingObjectsList:	RW	SpritesPoolSize

Vaults_Start:
VaultPlayer0:		RB	256
VaultPlayer1:		RB	256
VaultPlayer2:		RB	256
Vaults_End:

LastCheckPointTx:	RB	1
LastCheckPointTy:	RB	1
LastCheckPointWait:	RB	1

RecentDeathCount:	EQU	64
RecentDeaths:		RW	RecentDeathCount			; Used to know wich characters died recently.

OverlayBufferWidth:	EQU	24
OverlayBufferHeight:	EQU	16
OverlayBufferSize:	EQU	OverlayBufferWidth * OverlayBufferHeight
OverlayBuffer0:		RB	OverlayBufferSize
OverlayBuffer1:		RB	OverlayBufferSize
OverlayBuffer2:		RB	OverlayBufferSize

OverlayShowing0:	RB	1					; What to show? 0 = next weapon, <> 0 last grab item. If <> 0 is countdown.
OverlayShowing1:	RB	1
OverlayShowing2:	RB	1

OverlayLastItem0:	RB	1					; It stores what was the last item that the player grabs.
OverlayLastItem1:	RB	1
OverlayLastItem2:	RB	1

VarSaveState_End:

TeleportDestination:	RW	1					; LSB Tx, MSB Ty.
TeleportMapDestination:	RB	1
TeleportEnable:		RB	1

PlayerThatEnter:	RW	1					; Player who enter pointer (Sprite structure).
RoomDoorType:		RB	1					; Wich deals type?. = roomdoor code.
RoomDoorSubClass:	RB	1
SwitchToRoom:		RB	1					; <> 0 the gameplay must switch to the room defined by the roomdor code. Do the deal, and then restore it back to the original gameplay player positions.

CurrentBoxLeft:		RB	1					; Sets when build up the textbox.
CurrentBoxTop:		RB	1
CurrentBoxWidth:	RB	1
CurrentBoxHeight:	RB	1
TextLeftMargin:		RB	1
TextTopMargin:		RB	1
TextStopMargin:		RB	1
PrintTextTileMask:	RB	1

OptionALeft:		RB	1
OptionATop:		RB	1
OptionAWidth:		RB	1
OptionAHeight:		RB	1
OptionACost:		RB	1
OptionACount:		RB	1
OptionAItemTileNumber:	RB	1

OptionBLeft:		RB	1
OptionBTop:		RB	1
OptionBWidth:		RB	1
OptionBHeight:		RB	1
OptionBCost:		RB	1					; Cost is in coins.
OptionBCount:		RB	1
OptionBItemTileNumber:	RB	1

OptionCancelLeft:	RB	1
OptionCancelTop:	RB	1
OptionCancelWidth:	RB	1
OptionCancelHeight:	RB	1

JustBuyedSomething:	RB	1					; Used in the script to flag that the user just buyed.

SelectedObjectWalking:	RW	1					; Used in TestForWalk routines for pushing boxes.
PushingObjectWalking:	RB	1					; It flags when it can push a box, like only if the player is in the floor.

IgnorePlayers:		RB	1					; Flag used in the AttackLogic, for maintain the players safe against they own weapons.
AttackPower:		RW	1					; Power for the attack in the AttackLogic.

SFXCurrentlyPlaying1:	RB	1
SFXPointer1:		RW	1

SFXCurrentlyPlaying2:	RB	1
SFXPointer2:		RW	1

SFXCurrentlyPlaying3:	RB	1
SFXPointer3:		RW	1

SFXLastOverlappedChannel:	RB	1

IsTurboEnabled:		RB	1
IsPlayerPresent:	RB	1					; Signals that in this frame, a player was present.
AfterNoPlayersCountDown:	RB	1

IsMainMonsterPresent:	RB	1
MonsterRoomExitDelay:	RB	1

DressBuffer:		RB	1024					; Used in the merge of player pattern and in the MapRender for storing tile textures base addresses.

MainScreenCurrentMessage:	RW	1

; ************************************************************
; * DEVELOPMENT DIAGNOSTICS                                  *
; ************************************************************

DiagnoseVDPBotleneck:	RW	1
DiagnoseVDPBotleneckPermanent:	RW	1
DiagnoseVBlanksCount:	RB	1

DiagnoseVBlanksPointer:	RW	1

DiagnoseVblanksList_Start:
DiagnoseVBlanksList:	RB	256
DiagnoseVblanksList_End:

VAR_END:
