puzzlescript
Version:
Play PuzzleScript games in your terminal!
155 lines (143 loc) • 5.77 kB
text/typescript
import { getLetterSprites } from '../letters'
import { Level, SoundItem } from '../parser/astTypes'
import { Optional } from '../util'
import { IGameCode } from './BaseForLines'
import { CollisionLayer } from './collisionLayer'
import { GameMetadata } from './metadata'
import { SimpleRuleGroup } from './rule'
import { GameSprite, IGameTile } from './tile'
import { WinConditionSimple } from './winCondition'
export interface IGameNode {
__source: {code: string, sourceOffset: number}
__coverageCount: Optional<number>
__getSourceLineAndColumn(): { lineNum: number, colNum: number }
__getLineAndColumnRange(): { start: { line: number, col: number }, end: { line: number, col: number } }
toString(): string
toSourceString(): string
}
export class GameData {
public readonly title: string
public readonly metadata: GameMetadata
public readonly objects: GameSprite[]
public readonly legends: IGameTile[]
public readonly sounds: Array<SoundItem<IGameTile>>
public readonly collisionLayers: CollisionLayer[]
public readonly rules: SimpleRuleGroup[]
public readonly winConditions: WinConditionSimple[]
public readonly levels: Array<Level<IGameTile>>
private readonly cacheSpriteSize: {spriteHeight: number, spriteWidth: number}
private cachedBackgroundSprite: Optional<GameSprite>
private readonly letterSprites: Map<string, GameSprite>
constructor(
source: IGameCode,
title: string,
metadata: GameMetadata,
objects: GameSprite[],
legends: IGameTile[],
sounds: Array<SoundItem<IGameTile>>,
collisionLayers: CollisionLayer[],
rules: SimpleRuleGroup[],
winConditions: WinConditionSimple[],
levels: Array<Level<IGameTile>>
) {
this.title = title
this.metadata = metadata
this.objects = objects
this.legends = legends
this.sounds = sounds
this.collisionLayers = collisionLayers
this.winConditions = winConditions
this.levels = levels
this.rules = rules
this.cachedBackgroundSprite = null
const firstSpriteWithPixels = this.objects.filter((sprite) => sprite.hasPixels())[0]
if (firstSpriteWithPixels) {
const firstSpritePixels = firstSpriteWithPixels.getPixels(1, 1) // We don't care about these args
this.cacheSpriteSize = {
spriteHeight: firstSpritePixels.length,
spriteWidth: firstSpritePixels[0].length
}
} else {
// All the sprites are just a single color, so set the size to be 5x5
this.cacheSpriteSize = {
spriteHeight: 1,
spriteWidth: 1
}
}
// Create a collisionlayer for the letter sprites
let spriteIndexCounter = this.objects.length // 1 more than all the game sprites
this.letterSprites = getLetterSprites(source)
for (const letterSprite of this.letterSprites.values()) {
letterSprite.allSpritesBitSetIndex = spriteIndexCounter++
}
const letterCollisionLayer = new CollisionLayer(source, [...this.letterSprites.values()])
this.collisionLayers.push(letterCollisionLayer)
}
public _getSpriteByName(name: string) {
return this.objects.find((sprite) => sprite.getName().toLowerCase() === name.toLowerCase()) || null
}
public _getTileByName(name: string) {
return this.legends.find((tile) => tile.getName().toLowerCase() === name.toLowerCase())
}
public getSpriteByName(name: string) {
const sprite = this._getSpriteByName(name)
if (!sprite) {
throw new Error(`BUG: Could not find sprite "${name}" but expected one to exist.`)
}
return sprite
}
public getTileByName(name: string) {
const tile = this._getTileByName(name)
if (!tile) {
throw new Error(`BUG: Could not find tile "${name}" but expected one to exist.`)
}
return tile
}
public getMagicBackgroundSprite() {
if (this.cachedBackgroundSprite) {
return this.cachedBackgroundSprite
} else {
const background: Optional<GameSprite> = this._getSpriteByName('background')
if (!background) {
const legendBackground = this.legends.find((tile) => tile.getName().toLowerCase() === 'background')
if (legendBackground) {
if (legendBackground.isOr()) {
return null
} else {
return legendBackground.getSprites()[0]
}
}
}
if (!background) {
throw new Error(`ERROR: Game does not have a Background Sprite or Tile`)
}
this.cachedBackgroundSprite = background
return background
}
}
public getPlayer(): IGameTile {
const player = this._getSpriteByName('player') || this.legends.find((tile) => tile.getName().toLowerCase() === 'player')
if (!player) {
throw new Error(`BUG: Could not find the Player sprite or tile in the game`)
}
return player
}
public clearCaches() {
for (const rule of this.rules) {
rule.clearCaches()
}
for (const sprite of this.objects) {
sprite.clearCaches()
}
}
public getSpriteSize() {
return this.cacheSpriteSize
}
public getLetterSprite(char: string) {
const sprite = this.letterSprites.get(char)
if (!sprite) {
throw new Error(`BUG: Cannot find sprite for letter "${char}"`)
}
return sprite
}
}