import { PixelRendr , SpriteMultiple , SpriteSingle } from "pixelrendr" ; import { DrawingContexts } from "." ; import { BoundingBox , PixelDrawrSettings , Actor } from "./types" ; param actor returns const getTop = ( actor ) => ( actor . top + ( actor . offsetY || 0 ) ) | 0 ; param actor returns const getRight = ( actor ) => ( actor . right + ( actor . offsetX || 0 ) ) | 0 ; param actor returns const getBottom = ( actor ) => ( actor . bottom + ( actor . offsetY || 0 ) ) | 0 ; param actor returns const getLeft = ( actor ) => ( actor . left + ( actor . offsetX || 0 ) ) | 0 ; param actor returns const getMidX = ( actor ) => ( getLeft ( actor ) + actor . width / 2 ) | 0 ; param actor returns const getMidY = ( actor ) => ( getTop ( actor ) + actor . height / 2 ) | 0 ; /**
 * Real-time scene drawer for PixelRendr sprites.
 */ export class PixelDrawr { /**
     * Arrays of Actor[]s that are to be drawn in each refill.
     */ private readonly actorArrays : Actor [ ] [ ] ; /**
     * The bounds of the screen for bounds checking (often a MapScreenr).
     */ private readonly boundingBox : BoundingBox ; /**
     * Background and foreground contexts and sizing to draw on them within.
     */ private readonly contexts : DrawingContexts ; /**
     * An arbitrarily small minimum for opacity to be completely transparent.
     */ private readonly epsilon : number ; /**
     * Utility Function to generate a class key for an actor.
     */ private readonly generateObjectKey : ( actor : Actor ) => string ; /**
     * A PixelRendr used to obtain raw sprite data and canvases.
     */ private readonly pixelRender : PixelRendr ; /**
     * How often the screen redraws (1 for always, 2 for every other call, etc).
     */ private framerateSkip : number ; /**
     * How many frames have been drawn so far.
     */ private framesDrawn : number ; param settings public constructor ( settings ) { this . boundingBox = settings . boundingBox ; this . contexts = settings . contexts ; this . generateObjectKey = settings . generateObjectKey ; this . pixelRender = settings . pixelRender ; this . framerateSkip = settings . framerateSkip || 1 ; this . framesDrawn = 0 ; this . epsilon = settings . epsilon || 0.007 ; this . actorArrays = settings . actorArrays || [ ] ; if ( settings . background ) { this . setBackground ( settings . background ) ; } } param framerateSkip public setFramerateSkip ( framerateSkip ) { this . framerateSkip = framerateSkip ; } param fillStyle public setBackground ( fillStyle ) { this . contexts . background . fillStyle = fillStyle ; this . contexts . background . fillRect ( 0 , 0 , this . boundingBox . width , this . boundingBox . height ) ; } /**
     * Called every upkeep to refill the entire main canvas. All Actor arrays
     * are made to call this.refillActorArray in order.
     */ public refillGlobalCanvas ( ) { this . framesDrawn += 1 ; if ( this . framesDrawn % this . framerateSkip !== 0 ) { return ; } this . contexts . foreground . clearRect ( 0 , 0 , this . boundingBox . width , this . boundingBox . height ) ; for ( const array of this . actorArrays ) { this . refillActorArray ( array ) ; } } param array private refillActorArray ( array ) { for ( const member of array ) { this . drawActorOnContext ( this . contexts . foreground , member ) ; } } param context param Actor private drawActorOnContext ( context , actor ) { let left = getLeft ( actor ) ; let top = getTop ( actor ) ; if ( actor . hidden || actor . opacity < this . epsilon || actor . height < 1 || actor . width < 1 || top > this . boundingBox . height || getRight ( actor ) < 0 || getBottom ( actor ) < 0 || left > this . boundingBox . width ) { return ; } if ( actor . rotation !== undefined && actor . rotation !== 0 ) { context . save ( ) ; context . translate ( getMidX ( actor ) , getMidY ( actor ) ) ; context . rotate ( actor . rotation ) ; left = - actor . width / 2 ; top = - actor . height / 2 ; } const sprite = this . pixelRender . decode ( this . generateObjectKey ( actor ) , actor ) ; if ( sprite instanceof SpriteSingle ) { this . drawActorOnContextSingle ( context , actor , sprite , left , top ) ; } else { this . drawActorOnContextMultiple ( context , actor , sprite , left , top ) ; } if ( actor . rotation !== undefined && actor . rotation !== 0 ) { context . restore ( ) ; } } param context param Actor param sprite private drawActorOnContextSingle ( context , actor , sprite , left , top ) { const scale = actor . scale || 1 ; if ( actor . repeat ) { this . drawPatternOnContext ( context , sprite . getPattern ( context , actor . spritewidth , actor . spriteheight ) , left , top , actor . width , actor . height , actor . opacity || 1 ) ; return ; } const canvas = sprite . getCanvas ( actor . spritewidth , actor . spriteheight ) ; if ( actor . opacity !== 1 ) { context . globalAlpha = actor . opacity ; context . drawImage ( canvas , left , top , canvas . width * scale , canvas . height * scale ) ; context . globalAlpha = 1 ; } else { context . drawImage ( canvas , left , top , canvas . width * scale , canvas . height * scale ) ; } } param context param Actor param sprite private drawActorOnContextMultiple ( context , actor , sprite , left , top ) { const spriteWidth = actor . spritewidth ; const spriteHeight = actor . spriteheight ; const opacity = actor . opacity ; const widthDrawn = Math . min ( actor . width , spriteWidth ) ; const heightDrawn = Math . min ( actor . height , spriteHeight ) ; const patterns = sprite . getPatterns ( context , spriteWidth , spriteHeight ) ; let topReal = top ; let leftReal = left ; let rightReal = left + actor . width ; let bottomReal = top + actor . height ; let widthReal = actor . width ; let heightReal = actor . height ; let diffhoriz ; let diffvert ; switch ( patterns . direction ) { case "vertical" : if ( patterns . bottom ) { diffvert = sprite . bottomheight ? sprite . bottomheight : spriteHeight ; this . drawPatternOnContext ( context , patterns . bottom , leftReal , bottomReal - diffvert , widthReal , heightDrawn , opacity ) ; bottomReal -= diffvert ; heightReal -= diffvert ; } if ( patterns . top ) { diffvert = sprite . topheight ? sprite . topheight : spriteHeight ; this . drawPatternOnContext ( context , patterns . top , leftReal , topReal , widthReal , heightDrawn , opacity ) ; topReal += diffvert ; heightReal -= diffvert ; } break ; case "horizontal" : if ( patterns . left ) { diffhoriz = sprite . leftwidth ? sprite . leftwidth : spriteWidth ; this . drawPatternOnContext ( context , patterns . left , leftReal , topReal , widthDrawn , heightReal , opacity ) ; leftReal += diffhoriz ; widthReal -= diffhoriz ; } if ( patterns . right ) { diffhoriz = sprite . rightwidth ? sprite . rightwidth : spriteWidth ; this . drawPatternOnContext ( context , patterns . right , rightReal - diffhoriz , topReal , widthDrawn , heightReal , opacity ) ; rightReal -= diffhoriz ; widthReal -= diffhoriz ; } break ; case "corners" : diffvert = sprite . topheight ? sprite . topheight : spriteHeight ; diffhoriz = sprite . leftwidth ? sprite . leftwidth : spriteWidth ; this . drawPatternOnContext ( context , patterns . topLeft ! , leftReal , topReal , widthDrawn , heightDrawn , opacity ) ; this . drawPatternOnContext ( context , patterns . left ! , leftReal , topReal + diffvert , widthDrawn , heightReal - diffvert * 2 , opacity ) ; this . drawPatternOnContext ( context , patterns . bottomLeft ! , leftReal , bottomReal - diffvert , widthDrawn , heightDrawn , opacity ) ; leftReal += diffhoriz ; widthReal -= diffhoriz ; diffhoriz = sprite . rightwidth ? sprite . rightwidth : spriteWidth ; this . drawPatternOnContext ( context , patterns . top ! , leftReal , topReal , widthReal - diffhoriz , heightDrawn , opacity ) ; this . drawPatternOnContext ( context , patterns . topRight ! , rightReal - diffhoriz , topReal , widthDrawn , heightDrawn , opacity ) ; topReal += diffvert ; heightReal -= diffvert ; diffvert = sprite . bottomheight ? sprite . bottomheight : spriteHeight ; this . drawPatternOnContext ( context , patterns . right ! , rightReal - diffhoriz , topReal , widthDrawn , heightReal - diffvert , opacity ) ; this . drawPatternOnContext ( context , patterns . bottomRight ! , rightReal - diffhoriz , bottomReal - diffvert , widthDrawn , heightDrawn , opacity ) ; this . drawPatternOnContext ( context , patterns . bottom ! , leftReal , bottomReal - diffvert , widthReal - diffhoriz , heightDrawn , opacity ) ; rightReal -= diffhoriz ; widthReal -= diffhoriz ; bottomReal -= diffvert ; heightReal -= diffvert ; break ; } if ( patterns . middle && topReal < bottomReal && leftReal < rightReal ) { this . drawPatternOnContext ( context , patterns . middle , leftReal , topReal , widthReal , heightReal , opacity ) ; } } param context param pattern param left param top param width param left param opacity private drawPatternOnContext ( context , pattern , left , top , width , height , opacity ) { if ( opacity !== 1 ) { context . globalAlpha = opacity ; } context . translate ( left , top ) ; context . fillStyle = pattern ; context . fillRect ( 0 , 0 , width , height ) ; context . translate ( - left , - top ) ; if ( opacity !== 1 ) { context . globalAlpha = 1 ; } } }