Skip to content
Snippets Groups Projects
Level.js 4.02 KiB
import React from 'react'
import LevelData, { Inputs } from '../game/Level'

function* __rangeInner(start, end, step) {
    let c = start
    while (c < end) {
        yield c
        c += step
    }
}

export function range(...args) {
    if (args.length === 1) {
        const [end] = args
        return __rangeInner(0, end, 1)
    }
    if (args.length === 2) {
        const [start, end] = args
        return __rangeInner(start, end, 1)
    }
    if (args.length === 3) {
        const [start, end, step] = args
        return __rangeInner(start, end, step)
    }
    throw new TypeError('Invalid number of arguments')
}

type Props = {
    scale?: number,
    size?: number,
    margin?: number,
    level: LevelData,
}

export default class Level extends React.Component<Props> {
    static defaultProps = {
        scale: 1,
        size: 32,
        margin: 0,
        active: false,
    }

    state = {
        nonce: Math.random(),
    }

    componentDidMount(): void {
        window.addEventListener('keyup', this.handleKeyPress)
    }

    handleKeyPress = async e => {
        e.preventDefault()
        if (this.state.isWin || this.state.active > 0) {
            return
        }
        const { key } = e
        let input = null
        switch (key.toLowerCase()) {
            case 'arrowleft':
            case 'a':
                input = Inputs.Controls.Left
                break
            case 'arrowright':
            case 'd':
                input = Inputs.Controls.Right
                break
            case 'arrowdown':
            case 's':
                input = Inputs.Controls.Down
                break
            case 'arrowup':
            case 'w':
                input = Inputs.Controls.Up
                break
            case ' ':
                input = Inputs.Actions.Wait
                break
        }

        if (input) {
            this.setState(s => ({ active: s.active + 1 }))
            const isWin = await this.props.level.processInput(input)
            if (isWin && !this.state.isWin) {
                this.props.onWin()
            }
            this.setState(s => ({ nonce: Math.random(), active: s.active - 1, isWin }))
        }
    }

    render() {
        const {scale, size, margin, level} = this.props
        const {width, height} = level

        return (
            <div className="level-container">
                {[...range(width)].map(x =>
                    [...range(height)].map(y => (
                        <img src="/background.png" className="level-tile" style={{
                            left: (margin * x) + (x * 32 * scale),
                            top: (margin * y) + (y * 32 * scale),
                            width: size * scale,
                            height: size * scale
                        }}/>
                    ))
                ).reduce((a, c) => (a || []).concat(c))}
                {level.entities.map(entity => {
                    const {x, y, image} = entity.renderProps
                    const left = (margin * x) + (x * 32 * scale)
                    const top = (margin * y) + (y * 32 * scale)

                    return (
                        <img className="level-entity" src={image} style={{
                            left,
                            top,
                            width: size * scale,
                            height: size * scale
                        }}/>
                    )
                })}
                {level.connectors.map(connector => {
                    const {x, y, type: cType} = connector
                    const left = (margin * x) + (x * 32 * scale)
                    const top = (margin * y) + (y * 32 * scale)

                    return (
                        <img className="level-entity" src={`/${ cType }-text.png`} style={{
                            left,
                            top,
                            width: size * scale,
                            height: size * scale
                        }}/>
                    )
                })}
            </div>
        )
    }

}