'
' NeoLib - Sprite Module
'
' Features:
'  14 subs
'  3 functions
' For a total of: 17 routines
'
' Specially designed and coded for AAP's QBCPC
' Official Library of the QuickBASIC Caliber Programming Compo (Summer & Autumn 2003)
'

DECLARE FUNCTION neoSpriteSize% (StartX AS INTEGER, StartY AS INTEGER, EndX AS INTEGER, EndY AS INTEGER)
DECLARE FUNCTION neoSpriteCollide% (X1 AS INTEGER, Y1 AS INTEGER, BufferSeg1 AS INTEGER, BufferOff1 AS INTEGER, X2 AS INTEGER, Y2 AS INTEGER, BufferSeg2 AS INTEGER, BufferOff2 AS INTEGER, TransCol AS INTEGER)
DECLARE FUNCTION neoSpriteCollideOnLayer% (Layer AS INTEGER, X1 AS INTEGER, Y1 AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER)

DECLARE SUB neoSpriteInitLayers ()
DECLARE SUB neoSpriteSetClipBox (X1 AS INTEGER, Y1 AS INTEGER, X2 AS INTEGER, Y2 AS INTEGER)
DECLARE SUB neoSpriteGet (Layer AS INTEGER, StartX AS INTEGER, StartY AS INTEGER, EndX AS INTEGER, EndY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER)
DECLARE SUB neoSpritePut (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER)
DECLARE SUB neoSpritePutTrans (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER)
DECLARE SUB neoSpritePutColor (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, PutColor AS INTEGER)
DECLARE SUB neoSpritePutRotate (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, Angle AS INTEGER)
DECLARE SUB neoSpritePutBlend (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, StdPal AS STRING, Neg AS INTEGER)
DECLARE SUB neoSpritePutForward (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, CheckCol AS INTEGER)
DECLARE SUB neoSpritePutBit (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, BitMode AS INTEGER)
DECLARE SUB neoSpriteMirror (BufferSeg AS INTEGER, BufferOff AS INTEGER, HorVerFlags AS INTEGER)
DECLARE SUB neoSpriteScale (SpriteArray() AS INTEGER, ZoomFactor AS SINGLE)
DECLARE SUB neoSpriteLighting (BufferSeg AS INTEGER, BufferOff AS INTEGER, LightFactor AS INTEGER, StdPal AS STRING)
DECLARE SUB neoSpriteConvert (BufferSeg AS INTEGER, BufferOff AS INTEGER, DoNotConvert AS INTEGER, ConvertTo AS INTEGER)

DEFINT A-Z
'$DYNAMIC
'$INCLUDE: 'QB.BI'
'$INCLUDE: 'neoLAYER.bi'
'$INCLUDE: 'neoEMS.bi'

CONST XORBITS = 1, ORBITS = 2, ANDBITS = 4

DIM SHARED SpriteHandle AS INTEGER
DIM SHARED sClipBox(4) AS INTEGER

'/////////////////////////////////////////////////////////////////////////
' FUNCTIONS
'/////////////////////////////////////////////////////////////////////////
FUNCTION neoSpriteSize (StartX AS INTEGER, StartY AS INTEGER, EndX AS INTEGER, EndY AS INTEGER)
	'returns the amount of bytes needed to store a sprite
	'- StartX: the upperleft x-coordinate of the sprite
	'- StartY: the upperleft y-coordinate of the sprite
	'- EndX: the lowerright x-coordinate of the sprite
	'- EndY: the lowerright y-coordinate of the sprite

	Wid% = EndX - StartX + 1
	Hei% = EndY - StartY + 1

	neoSpriteSize = Wid% * Hei% + 4
END FUNCTION

FUNCTION neoSpriteCollide (X1 AS INTEGER, Y1 AS INTEGER, BufferSeg1 AS INTEGER, BufferOff1 AS INTEGER, X2 AS INTEGER, Y2 AS INTEGER, BufferSeg2 AS INTEGER, BufferOff2 AS INTEGER, TransCol AS INTEGER)
	'returns the first pixel with which the two specified sprites collide eachother
	'- X1: the x-position of the first sprite
	'- Y1: the y-position of the first sprite
	'- BufferSeg1: the buffer segment of the first sprite
	'- BufferOff1: the buffer offset of the first sprite
	'- X2: the x-position of the second sprite
	'- Y2: the y-position of the second sprite
	'- BufferSeg2: the buffer segment of the second sprite
	'- BufferOff2: the buffer offset of the second sprite
	'- TransCol: the transparent color of both sprites

        DEF SEG = BufferSeg1
        Wid1% = PEEK(BufferOff1) \ 8 + PEEK(BufferOff1 + 1) * 32
        Hei1% = PEEK(BufferOff1 + 2)

        DEF SEG = BufferSeg2
        Wid2% = PEEK(BufferOff2) \ 8 + PEEK(BufferOff2 + 1) * 32
        Hei2% = PEEK(BufferOff2 + 2)

        FOR tryX = 0 TO Wid1% - 1
        	FOR tryY = 0 TO Hei1% - 1
                        realX = tryX + X1
                        realY = tryY + Y1
                        otherX = realX - X2
                        otherY = realY - Y2

                        DEF SEG = BufferSeg1
                        value1% = PEEK(tryY * Wid1% + tryX + 4 + BufferOff1)

                        IF otherX >= 0 AND otherX <= Wid2% - 1 AND otherY >= 0 AND otherY <= Hei2% - 1 AND value1% <> TransCol THEN
                        	DEF SEG = BufferSeg2
                                value2% = PEEK(otherY * Wid2% + otherX + 4 + BufferOff2)

				IF value2% <> TransCol THEN neoSpriteCollide = value2%: EXIT FUNCTION
                        END IF
        	NEXT tryY
        NEXT tryX

        neoSpriteCollide = TransCol
END FUNCTION

FUNCTION neoSpriteCollideOnLayer (Layer AS INTEGER, X1 AS INTEGER, Y1 AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER)
	'returns the first pixel the sprite collides with on the layer
        '- Layer: the layer to check collision on
        '- X1: the x-position of the sprite
        '- Y1: the y-position of the sprite
        '- BufferSeg: the sprite segment
        '- BufferOff: the sprite offset
        '- TransCol: the transparent color of both the layer and the sprite (will not be checked for collision)

        DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	pF% = neoEMSpageFrame
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
        END IF

        FOR readX = 0 TO Wid% - 1
        	FOR readY = 0 TO Hei% - 1
                        realX = readX + X1
                        realY = readY + Y1

                        IF realX >= 0 AND realX <= 319 AND realY >= 0 AND realY <= 199 THEN
                        	DEF SEG = BufferSeg
                        	value% = PEEK(BufferOff + 4 + Wid% * readY + readX)

                        	DEF SEG = pF%
                        	valuethere% = PEEK(320& * realY + realX)

                        	IF value% <> TransCol AND valuethere% <> TransCol THEN neoSpriteCollideOnLayer = valuethere%: EXIT FUNCTION
                        END IF
        	NEXT readY
        NEXT readX

        neoSpriteCollideOnLayer = TransCol
END FUNCTION

'/////////////////////////////////////////////////////////////////////////
' SUBS
'/////////////////////////////////////////////////////////////////////////
SUB neoSpriteInitLayers
	'initializes the layers for sprite drawing

	SpriteHandle = neoLayerGetHandle

	sClipBox(0) = 0
	sClipBox(1) = 319
	sClipBox(2) = 0
	sClipBox(3) = 199
END SUB

SUB neoSpriteSetClipBox (X1 AS INTEGER, Y1 AS INTEGER, X2 AS INTEGER, Y2 AS INTEGER)
	'sets the clipbox for sprites
	'- X1: upperleft x-coordinate of the box
	'- Y1: upperleft y-coordinate of the box
	'- X2: lowerright x-coordinate of the box
	'- Y2: lowerright y-coordinate of the box

	sClipBox(0) = X1
	sClipBox(1) = X2
	sClipBox(2) = Y1
	sClipBox(3) = Y2
END SUB

SUB neoSpriteGet (Layer AS INTEGER, StartX AS INTEGER, StartY AS INTEGER, EndX AS INTEGER, EndY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER)
	'gets a sprite from a specified layer
	'- Layer: the layer to get the sprite from
	'- StartX: the upperleft x-coordinate of the sprite
	'- StartY: the upperleft y-coordinate of the sprite
	'- EndX: the lowerright x-coordinate of the sprite
	'- EndY: the lowerright y-coordinate of the sprite
	'- BufferSeg: the segment of the buffer to store the sprite in
	'- BufferOff: the offset of the buffer to store the sprite in

	Wid% = EndX - StartX + 1
	Hei% = EndY - StartY + 1
	NowOff% = BufferOff + 4
	DEF SEG = BufferSeg
	POKE BufferOff + 3, 0
        POKE BufferOff + 2, Hei%
        POKE BufferOff + 1, Wid% \ 32
        POKE BufferOff, (Wid% MOD 32) * 8

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
        	pF% = neoEMSpageFrame
        END IF

        FOR readY = StartY TO EndY
        	FOR readX = StartX TO EndX
                        DEF SEG = pF%
                        value% = PEEK(readX + readY * 320&)

                        DEF SEG = BufferSeg
                        POKE NowOff%, value%
                        NowOff% = NowOff% + 1
        	NEXT readX
        NEXT readY
END SUB

SUB neoSpritePut (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER)
	'puts a sprite to a specified layer on the specified coordinates
	'- Layer: the layer to put the sprite on
	'- PutX: the x-coordinate to put the sprite on (upperleft)
	'- PutY: the y-coordinate to put the sprite on (upperleft)
	'- BufferSeg: the buffer segment that contains the sprite data
	'- BufferOff: the buffer offset that contains the sprite data

        DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NowOff% = BufferOff + 4

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
                pF% = neoEMSpageFrame
        END IF

	FOR writeY = PutY TO PutY + Hei% - 1
		FOR writeX = PutX TO PutX + Wid% - 1
                        DEF SEG = BufferSeg
                        value% = PEEK(NowOff%)
                        NowOff% = NowOff% + 1

                        IF writeX >= sClipBox(0) AND writeX <= sClipBox(1) AND writeY >= sClipBox(2) AND writeY <= sClipBox(3) THEN
                        	DEF SEG = pF%
                        	POKE 320& * writeY + writeX, value%
                        END IF
		NEXT writeX
	NEXT writeY

        IF Layer <> VIDEO THEN
        	FOR I = 0 TO 3
                        neoEMSmove 0, pF%, &H4000& * I, SpriteHandle, (Layer - 1) * 4 + I, 0, 16384
        	NEXT I
        END IF
END SUB

SUB neoSpritePutTrans (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER)
	'puts a sprite to a specified layer on the specified coordinates, with all TransCol pixels skipped
	'- Layer: the layer to put the sprite on
	'- PutX: the x-coordinate to put the sprite on (upperleft)
	'- PutY: the y-coordinate to put the sprite on (upperleft)
	'- BufferSeg: the buffer segment that contains the sprite data
	'- BufferOff: the buffer offset that contains the sprite data
	'- TransCol: the transparent color, this will be skipped

	DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NowOff% = BufferOff + 4

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
                pF% = neoEMSpageFrame
        END IF

	FOR writeY = PutY TO PutY + Hei% - 1
		FOR writeX = PutX TO PutX + Wid% - 1
                        DEF SEG = BufferSeg
                        value% = PEEK(NowOff%)
                        NowOff% = NowOff% + 1

                        IF writeX >= sClipBox(0) AND writeX <= sClipBox(1) AND writeY >= sClipBox(2) AND writeY <= sClipBox(3) AND value% <> TransCol THEN
                        	DEF SEG = pF%
                        	POKE 320& * writeY + writeX, value%
                        END IF
		NEXT writeX
	NEXT writeY

        IF Layer <> VIDEO THEN
        	FOR I = 0 TO 3
                        neoEMSmove 0, pF%, &H4000& * I, SpriteHandle, (Layer - 1) * 4 + I, 0, 16384
        	NEXT I
        END IF
END SUB

SUB neoSpriteMirror (BufferSeg AS INTEGER, BufferOff AS INTEGER, HorVerFlags AS INTEGER)
	'mirrors a sprite in specified directions
        '- BufferSeg: the segment of the sprite to mirror
        '- BufferOff: the offset of the sprite to mirror
        '- HorVerFlags: use HORIZONTAL or VERTICAL here, or HORIZONTAL + VERTICAL for both mirroring sides

        DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)

        IF HorVerFlags AND HORIZONTAL THEN
                FOR sprY = 0 TO Hei% - 1
                	yaddr& = Wid% * sprY + BufferOff + 4
                	FOR sprX = 0 TO (Wid% - 1) \ 2
                                pixel1% = PEEK(yaddr& + sprX)
                                pixel2% = PEEK(yaddr& + (Wid% - 1 - sprX))

                                POKE yaddr& + sprX, pixel2%
                                POKE yaddr& + (Wid% - 1 - sprX), pixel1%
                	NEXT sprX
                NEXT sprY
        END IF

        IF HorVerFlags AND VERTICAL THEN
                FOR sprX = 0 TO Wid% - 1
                	xaddr% = BufferOff + 4 + sprX
                	FOR sprY = 0 TO (Hei% - 1) \ 2
                                pixel1% = PEEK(Wid% * sprY + xaddr%)
                                pixel2% = PEEK(Wid% * (Hei% - 1 - sprY) + xaddr%)

                                POKE Wid% * sprY + xaddr%, pixel2%
                                POKE Wid% * (Hei% - 1 - sprY) + xaddr%, pixel1%
                	NEXT sprY
                NEXT sprX
        END IF
END SUB

SUB neoSpritePutColor (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, PutColor AS INTEGER)
	'puts a sprite to a specified layer on the specified coordinates, with all TransCol pixels skipped, and all not Transcol pixels turned to PutColor
	'- Layer: the layer to put the sprite on
	'- PutX: the x-coordinate to put the sprite on (upperleft)
	'- PutY: the y-coordinate to put the sprite on (upperleft)
	'- BufferSeg: the buffer segment that contains the sprite data
	'- BufferOff: the buffer offset that contains the sprite data
	'- TransCol: the transparent color, this will be skipped
	'- PutColor: the color to draw the sprite in

	DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NowOff% = BufferOff + 4

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
                pF% = neoEMSpageFrame
        END IF

	FOR writeY = PutY TO PutY + Hei% - 1
		FOR writeX = PutX TO PutX + Wid% - 1
                        DEF SEG = BufferSeg
                        value% = PEEK(NowOff%)
                        NowOff% = NowOff% + 1

                        IF writeX >= sClipBox(0) AND writeX <= sClipBox(1) AND writeY >= sClipBox(2) AND writeY <= sClipBox(3) AND value% <> TransCol THEN
                        	DEF SEG = pF%
                        	POKE 320& * writeY + writeX, PutColor
                        END IF
		NEXT writeX
	NEXT writeY

        IF Layer <> VIDEO THEN
        	FOR I = 0 TO 3
                        neoEMSmove 0, pF%, &H4000& * I, SpriteHandle, (Layer - 1) * 4 + I, 0, 16384
        	NEXT I
        END IF
END SUB

SUB neoSpritePutRotate (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, Angle AS INTEGER)
	'draws a rotated sprite on the specified layer at the specified angle
	'- Layer: the layer to draw the sprite on
	'- PutX: the x-coordinate to put the sprite on
	'- PutY: the y-coordinate to put the sprite on
	'- BufferSeg: the segment of the sprite
	'- BufferOff: the offset of the sprite
	'- TransCol: the color which to skip when putting
	'- Angle: the angle in degrees to put the image on (0 - 360)

	PI# = 3.141592654#
	PIAngle# = CDBL(Angle) / 180# * PI#

	DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NowOff% = BufferOff + 4
        WidMiddle% = (Wid% - 1) \ 2
        HeiMiddle% = (Hei% - 1) \ 2
        Radius# = SQR(CLNG(WidMiddle%) ^ 2 + CLNG(HeiMiddle%) ^ 2)

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
                pF% = neoEMSpageFrame
        END IF

	FOR readY = 0 TO Hei% - 1
		FOR readX = 0 TO Wid% - 1
			xDif% = readX - WidMiddle%
			yDif% = readY - HeiMiddle%
			'use the matrix rotation formula to calculate the Rise (delta-y) and Run (delta-x)
			TheRise# = CDBL(yDif%) * COS(PIAngle#) + CDBL(xDif%) * SIN(PIAngle#)
			TheRun# = CDBL(xDif%) * COS(PIAngle#) - CDBL(yDif%) * SIN(PIAngle#)
			xNew% = WidMiddle% + CINT(TheRun#) + PutX
                        yNew% = HeiMiddle% + CINT(TheRise#) + PutY

                        DEF SEG = BufferSeg
                        value% = PEEK(BufferOff + 4 + readY * Wid% + readX)

                        IF xNew% >= sClipBox(0) AND xNew% <= sClipBox(1) AND yNew% >= sClipBox(2) AND yNew% <= sClipBox(3) AND value% <> TransCol THEN
                        	DEF SEG = pF%
                        	POKE 320& * yNew% + xNew%, value%
                        END IF
		NEXT readX
	NEXT readY

        IF Layer <> VIDEO THEN
        	FOR I = 0 TO 3
                        neoEMSmove 0, pF%, &H4000& * I, SpriteHandle, (Layer - 1) * 4 + I, 0, 16384
        	NEXT I
        END IF
END SUB

SUB neoSpriteScale (SpriteArray() AS INTEGER, ZoomFactor AS SINGLE)
	'scales a sprite to the specified ZoomFactor
	'- SpriteArray(): the array containing the sprite
	'- ZoomFactor: the zoomfactor (0 < Z < 1: for outzoom, Z = 1: for same, Z > 1: for inzoom)

	BufferSeg = VARSEG(SpriteArray(0))
	BufferOff = VARPTR(SpriteArray(0))
        DEF SEG = BufferSeg
	Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NewWid% = CSNG(Wid%) * ZoomFactor
        NewHei% = CSNG(Hei%) * ZoomFactor

        'temporary storage
        DIM MyBuffer(neoSpriteSize(0,0,Wid%-1,Hei%-1)\2) AS INTEGER
        FOR I = 0 TO UBOUND(MyBuffer)
        	MyBuffer(I) = SpriteArray(I)
        NEXT I
        pF% = VARSEG(MyBuffer(0))
        pFo% = VARPTR(MyBuffer(0))

        'resize the array
        REDIM SpriteArray(neoSpriteSize(0,0,NewWid%-1,NewHei%-1)\2) AS INTEGER
        BufferSeg = VARSEG(SpriteArray(0))
        BufferOff = VARPTR(SpriteArray(0))
        DEF SEG = BufferSeg
        POKE BufferOff + 3, 0
        POKE BufferOff + 2, NewHei%
        POKE BufferOff + 1, NewWid% \ 32
        POKE BufferOff, (NewWid% MOD 32) * 8

        'resize the sprite
        FOR writeX = 0 TO NewWid% - 1
        	FOR writeY = 0 TO NewHei% - 1
                        readX = INT(CSNG(writeX) / ZoomFactor)
                        readY = INT(CSNG(writeY) / ZoomFactor)

                        DEF SEG = pF%
                        value% = PEEK(readY * Wid% + readX + pFo% + 4)

                        DEF SEG = BufferSeg
                        POKE BufferOff + 4 + writeY * NewWid% + writeX, value%
        	NEXT writeY
        NEXT writeX

        ERASE MyBuffer
END SUB

SUB neoSpriteLighting (BufferSeg AS INTEGER, BufferOff AS INTEGER, LightFactor AS INTEGER, StdPal AS STRING)
        'increases all pixels in the sprite by LightFactor
        '- BufferSeg: the segment of the sprite
        '- BufferOff: the offset of the sprite
        '- LightFactor: the light coefficient, use < 0 for darkening, > 0 for enlightening (min -63, max 63)
        '- StdPal: the palette to select best lights from

	DEF SEG = BufferSeg
	Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)

        FOR readX = 0 TO Wid% - 1
        	FOR readY = 0 TO Hei% - 1
                        value% = PEEK(BufferOff + 4 + readY * Wid% + readX)

                        aR = ASC(MID$(StdPal, value% * 3 + 1, 1))
                        aG = ASC(MID$(StdPal, value% * 3 + 2, 1))
                        aB = ASC(MID$(StdPal, value% * 3 + 3, 1))

                        NewR = aR + LightFactor
                        NewG = aG + LightFactor
                        NewB = aB + LightFactor

			minimumcolor% = 0
			mindif% = 32000
                        FOR checkC = 0 TO 255
                       		aR = ASC(MID$(StdPal, checkC * 3 + 1, 1))
                        	aG = ASC(MID$(StdPal, checkC * 3 + 2, 1))
                        	aB = ASC(MID$(StdPal, checkC * 3 + 3, 1))
                        	dif% = ABS(NewR - aR) + ABS(NewG - aG) + ABS(NewB - aB)
                        	IF dif% < mindif% THEN mindif% = dif%: minimumcolor% = checkC
                        NEXT checkC

                        POKE BufferOff + 4 + readY * Wid% + readX, minimumcolor%
        	NEXT readY
        NEXT readX
END SUB

SUB neoSpritePutBlend (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, StdPal AS STRING, Neg AS INTEGER)
	'puts a sprite to a specified layer on the specified coordinates, with all TransCol pixels skipped and all non-TransCol pixels blended with the background
	'- Layer: the layer to put the sprite on
	'- PutX: the x-coordinate to put the sprite on (upperleft)
	'- PutY: the y-coordinate to put the sprite on (upperleft)
	'- BufferSeg: the buffer segment that contains the sprite data
	'- BufferOff: the buffer offset that contains the sprite data
	'- TransCol: the transparent color, this will be skipped
	'- StdPal: the current palette in a 768-byte string
	'- Neg: FALSE for positive blending, TRUE for negative blending

	DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NowOff% = BufferOff + 4

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
                pF% = neoEMSpageFrame
        END IF

	FOR writeY = PutY TO PutY + Hei% - 1
		FOR writeX = PutX TO PutX + Wid% - 1
                        DEF SEG = BufferSeg
                        value% = PEEK(NowOff%)
                        NowOff% = NowOff% + 1

                        IF writeX >= sClipBox(0) AND writeX <= sClipBox(1) AND writeY >= sClipBox(2) AND writeY <= sClipBox(3) AND value% <> TransCol THEN
                        	DEF SEG = pF%
                        	valuethere% = PEEK(320& * writeY + writeX)

                                aR = ASC(MID$(StdPal, value% * 3 + 1, 1))
                                aG = ASC(MID$(StdPal, value% * 3 + 2, 1))
                                aB = ASC(MID$(StdPal, value% * 3 + 3, 1))

                                dR = ASC(MID$(StdPal, valuethere% * 3 + 1, 1))
                                dG = ASC(MID$(StdPal, valuethere% * 3 + 2, 1))
                                dB = ASC(MID$(StdPal, valuethere% * 3 + 3, 1))

                                IF NOT Neg THEN
                                	mR = aR + dR
                                	mG = aG + dG
                                	mB = aB + dB
                                	IF mR > 63 THEN mR = 63
                                	IF mG > 63 THEN mG = 63
                                	IF mB > 63 THEN mB = 63
                                ELSE
                                	mR = ABS(aR - dR)
                                	mG = ABS(aG - dG)
                                	mB = ABS(aB - dB)
                                END IF

				mindif% = 32000
				minimum% = 0
                                FOR checkC = 0 TO 255
					aR = ASC(MID$(StdPal, checkC * 3 + 1, 1))
                                	aG = ASC(MID$(StdPal, checkC * 3 + 2, 1))
                                	aB = ASC(MID$(StdPal, checkC * 3 + 3, 1))
                                	dif% = ABS(mR - aR) + ABS(mG - aG) + ABS(mB - aB)
                                	IF dif% < mindif% THEN mindif% = dif%: minimum% = checkC
                                NEXT checkC

                                POKE 320& * writeY + writeX, minimum%
                        END IF
		NEXT writeX
	NEXT writeY

        IF Layer <> VIDEO THEN
        	FOR I = 0 TO 3
                        neoEMSmove 0, pF%, &H4000& * I, SpriteHandle, (Layer - 1) * 4 + I, 0, 16384
        	NEXT I
        END IF
END SUB

SUB neoSpritePutForward (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, CheckCol AS INTEGER)
	'puts a sprite to a specified layer on the specified coordinates, with all TransCol pixels skipped, but all destination pixels not equal to CheckCol are not drawn
	'- Layer: the layer to put the sprite on
	'- PutX: the x-coordinate to put the sprite on (upperleft)
	'- PutY: the y-coordinate to put the sprite on (upperleft)
	'- BufferSeg: the buffer segment that contains the sprite data
	'- BufferOff: the buffer offset that contains the sprite data
	'- TransCol: the transparent color, this will be skipped
	'- CheckCol: the color which the destination buffer contains, should it be overdrawn

	DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NowOff% = BufferOff + 4

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
                pF% = neoEMSpageFrame
        END IF

	FOR writeY = PutY TO PutY + Hei% - 1
		FOR writeX = PutX TO PutX + Wid% - 1
                        DEF SEG = BufferSeg
                        value% = PEEK(NowOff%)
                        NowOff% = NowOff% + 1

                        IF writeX >= sClipBox(0) AND writeX <= sClipBox(1) AND writeY >= sClipBox(2) AND writeY <= sClipBox(3) AND value% <> TransCol THEN
                        	DEF SEG = pF%
                        	valuethere% = PEEK(320& * writeY + writeX)

                        	IF valuethere% = CheckCol THEN POKE 320& * writeY + writeX, value%
                        END IF
		NEXT writeX
	NEXT writeY

        IF Layer <> VIDEO THEN
        	FOR I = 0 TO 3
                        neoEMSmove 0, pF%, &H4000& * I, SpriteHandle, (Layer - 1) * 4 + I, 0, 16384
        	NEXT I
        END IF
END SUB

SUB neoSpritePutBit (Layer AS INTEGER, PutX AS INTEGER, PutY AS INTEGER, BufferSeg AS INTEGER, BufferOff AS INTEGER, TransCol AS INTEGER, BitMode AS INTEGER)
	'puts a sprite to a specified layer on the specified coordinates, with all TransCol pixels skipped and all bits performed the requested operation on
	'- Layer: the layer to put the sprite on
	'- PutX: the x-coordinate to put the sprite on (upperleft)
	'- PutY: the y-coordinate to put the sprite on (upperleft)
	'- BufferSeg: the buffer segment that contains the sprite data
	'- BufferOff: the buffer offset that contains the sprite data
	'- TransCol: the transparent color, this will be skipped
	'- BitMode: the bit operations to perform, use any of the constant XORBITS, ORBITS or ANDBITS here.

	DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)
        NowOff% = BufferOff + 4

        IF Layer = VIDEO THEN
        	pF% = &HA000
        ELSE
        	neoEMSmapX (Layer - 1) * 4, 0, 4, SpriteHandle
                pF% = neoEMSpageFrame
        END IF

	FOR writeY = PutY TO PutY + Hei% - 1
		FOR writeX = PutX TO PutX + Wid% - 1
                        DEF SEG = BufferSeg
                        value% = PEEK(NowOff%)
                        NowOff% = NowOff% + 1

                        IF writeX >= sClipBox(0) AND writeX <= sClipBox(1) AND writeY >= sClipBox(2) AND writeY <= sClipBox(3) AND value% <> TransCol THEN
                        	DEF SEG = pF%
                        	valuethere% = PEEK(320& * writeY + writeX)

				bitwise% = 0
                        	IF BitMode AND XORBITS THEN bitwise% = (value% XOR valuethere%) XOR bitwise%
                        	IF BitMode AND ORBITS THEN bitwise% = (value% OR valuethere%) XOR bitwise%
                        	IF BitMode AND ANDBITS THEN bitwise% = (value% AND valuethere%) XOR bitwise%

                        	POKE 320& * writeY + writeX, bitwise%
                        END IF
		NEXT writeX
	NEXT writeY

        IF Layer <> VIDEO THEN
        	FOR I = 0 TO 3
                        neoEMSmove 0, pF%, &H4000& * I, SpriteHandle, (Layer - 1) * 4 + I, 0, 16384
        	NEXT I
        END IF
END SUB

SUB neoSpriteConvert (BufferSeg AS INTEGER, BufferOff AS INTEGER, DoNotConvert AS INTEGER, ConvertTo AS INTEGER)
	'generates a mask out of a sprite
	'- BufferSeg: segment of sprite
	'- BufferOff: offset of sprite
	'- DoNotConvert: which color is the transparent one (won't be included in the mask)
	'- ConvertTo: the colour the mask will have

	DEF SEG = BufferSeg
        Wid% = PEEK(BufferOff) \ 8 + PEEK(BufferOff + 1) * 32
        Hei% = PEEK(BufferOff + 2)

        FOR sprY = 0 TO Hei% - 1
                yaddr& = Wid% * sprY + BufferOff + 4
                FOR sprX = 0 TO Wid% - 1
                        pixel1% = PEEK(yaddr& + sprX)
                        IF pixel1% <> DoNotConvert THEN POKE yaddr& + (Wid% - 1 - sprX), ConvertTo
                NEXT sprX
        NEXT sprY
END SUB
