| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- <script>
- const Fifo = require('fifo')
- const SCREEN_WIDTH = 256;
- const SCREEN_HEIGHT = 240;
- const BUFFER_FRAMES = 120;
- const SAMPLE_REDUCTION = 4;
- const MAX_BUFFER = 0; 1200;
- const WORKING_MEMORY = 0x800;
- const { cartesianToPolar:c2p, polarToCartesian:p2c, findMemory } = require('../common/util')
- window.findMemory = findMemory
- export default {
- data: () => ({
- gameState: {},
- gameStateString: '',
- memory: new Uint8Array(WORKING_MEMORY),
- watch: [
- 0x0086
- ]
- }),
- beforeDestroy() {
- console.log('beforeDestroy()')
- this.stop = true
- this.ws.close()
- },
- mounted() {
- console.log('mounted()')
- const buffer = new Fifo()
- const url = new URL('game', location.href)
- url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
- const connect = () => {
- this.ws = new WebSocket(url)
- this.ws.binaryType = 'arraybuffer'
- this.ws.addEventListener('message', msg => {
- if (typeof msg.data === 'string') {
- this.gameState = JSON.parse(msg.data)
- this.gameStateString = JSON.stringify(this.gameState.enemyVision, null, 2)
- .replace(/\{[^\{\}\[\]]*\}/g, x => x.replace(/\s+/g, ' '))
- .replace(/\[[^\{\}\[\]]*\]/g, x => x.replace(/\s+/g, ' '))
- } else {
- //buffer.push(msg.data)
- //if (buffer.length === MAX_BUFFER) buffer.shift()
- // if (!timer) {
- // timer = true
- this.renderFrame(msg.data)
- this.renderDebug(this.gameState)
- //}
- }
- })
- this.ws.addEventListener('close', () => {
- if (!this.stop) setTimeout(connect, 1000)
- })
- }
- connect()
- this.canvas = this.$refs.canvas
- this.context = this.canvas.getContext('2d')
- this.imageData = this.context.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
- this.memoryCanvas = this.$refs.memoryCanvas
- this.memoryContext = this.memoryCanvas.getContext('2d')
- this.memoryImageData = this.memoryContext.getImageData(0, 0, this.memoryCanvas.getAttribute('width'), this.memoryCanvas.getAttribute('height'))
- this.buf = new ArrayBuffer(this.imageData.data.length);
- // Get the canvas buffer in 8bit and 32bit
- this.buf8 = new Uint8ClampedArray(this.buf);
- this.buf32 = new Uint32Array(this.buf);
- this.timer = null
- this.keyMap = {
- a: /*BUTTON_B*/ 1,
- s: /*BUTTON_A*/ 0,
- '\\': /*BUTTON_SELECT*/ 2,
- Enter: /*BUTTON_START*/ 3,
- ArrowUp: /*BUTTON_UP*/ 4,
- ArrowDown: /*BUTTON_DOWN*/ 5,
- ArrowLeft: /*BUTTON_LEFT*/ 6,
- ArrowRight: /*BUTTON_RIGHT*/ 7
- }
- this.key = (ev, pressed) => {
- if (this.keyMap.hasOwnProperty(ev.key)) {
- const button = this.keyMap[ev.key]
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
- this.ws.send(JSON.stringify({
- type: 'button',
- button,
- pressed
- }))
- } else {
- console.warn('Not connected')
- }
- }
- }
- document.addEventListener('keydown', (ev) => this.key(ev, true))
- document.addEventListener('keyup', (ev) => this.key(ev, false))
- },
- methods: {
- renderDebug(data) {
- window.data = data
- this.context.save()
- // this.context.translate(8, 8)
- this.context.translate(data.x, data.y + 32)
- // this.context.beginPath()
- // this.context.fillStyle = '#080'
- // //this.context.fillRect(data.hitbox.x1, data.hitbox.y1, data.hitbox.x2 - data.hitbox.x1, data.hitbox.y2 - data.hitbox.y1)
- // this.context.arc(0, 0, 8, 0, 2 * Math.PI)
- // this.context.fill()
- // data.enemies.forEach(enemy => {
- // if (enemy && enemy.hitbox.y2 > enemy.hitbox.y1) {
- // this.context.beginPath()
- // this.context.fillStyle = '#800'
- // this.context.arc(enemy.x, enemy.y, 8, 0, 2 * Math.PI)
- // //this.context.fillRect(enemy.hitbox.x1, enemy.hitbox.y1, enemy.hitbox.x2 - enemy.hitbox.x1, enemy.hitbox.y2 - enemy.hitbox.y1)
- // this.context.fill()
- // }
- // })
- const renderVision = (collection, color) => {
- const arc = (Math.PI * 2 / data.slices)
- this.context.save()
- this.context.translate((data.hitbox.x2 - data.hitbox.x1) / 2, (data.hitbox.y2 - data.hitbox.y1) / -2)
- for (let i = 0; i < data.slices; i++) {
- const r = collection[i]
- const {x, y} = p2c({
- r: (1 - r) * 30,
- t: arc * i + arc / 2 - Math.PI
- })
- this.context.beginPath()
- this.context.fillStyle = color
- this.context.arc(x, y, 2, 0, 2 * Math.PI)
- //this.context.fillRect(enemy.hitbox.x1, enemy.hitbox.y1, enemy.hitbox.x2 - enemy.hitbox.x1, enemy.hitbox.y2 - enemy.hitbox.y1)
- this.context.fill()
- }
- this.context.restore()
- }
- renderVision(data.enemyVision, 'red')
- renderVision(data.solidVision, 'blue')
- renderVision(data.breakableVision, 'yellow')
- renderVision(data.magicVision, 'green')
- const renderHitboxes = (collection, color) => {
- this.context.strokeStyle = color
- this.context.beginPath()
- collection.forEach(entity => {
- if (entity) this.context.rect(entity.hitbox.x1, entity.hitbox.y1, entity.hitbox.x2 - entity.hitbox.x1, entity.hitbox.y2 - entity.hitbox.y1)
- })
- this.context.stroke()
- }
- renderHitboxes(this.gameState.blocks, 'blue')
- renderHitboxes(data.enemies, 'red')
- renderHitboxes([data], 'yellow')
- // this.gameState.blocks.forEach(({x, y, v}) => {
- // // v = v.toString(16).padStart(2)
- // this.context.rect(x, y, 16, 16)
- // for (let xo = -512; xo <= 512; xo += 512) {
- // const xo = 0
- // this.context.rect(x * 16 + 0.5 - this.gameState.levelX % 512 + xo, y * 16 + 0.5 - data.y, 16, 16)
- // this.context.fillStyle = 'black'
- // this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo + 1, y * 16 + 0.5 - data.y + 10)
- // this.context.fillStyle = 'black'
- // this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo - 1, y * 16 + 0.5 - data.y + 7)
- // this.context.fillStyle = 'black'
- // this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo + 1, y * 16 + 0.5 - data.y + 7)
- // this.context.fillStyle = 'black'
- // this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo - 1, y * 16 + 0.5 - data.y + 10)
- // this.context.fillStyle = '#aaaa00'
- // this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo, y * 16 + 0.5 - data.y + 9)
- // }
- // })
- // this.context.stroke();
- // this.context.strokeStyle = null
- this.context.restore()
- },
- renderFrame(data) {
- //const data = buffer.shift()
- if (data) {
- const palette = new Uint32Array(data, 0, 256 * 4)
- const pixels = new Uint8Array(data, 256 * 4, SCREEN_WIDTH * SCREEN_HEIGHT)
- const memory = new Uint8Array(data, 256 * 4 + SCREEN_WIDTH * SCREEN_HEIGHT, WORKING_MEMORY)
- Object.assign(this.memory, memory)
- window.memory = this.memory
- for (let i = 0; i < pixels.length; i++) {
- this.buf32[i] = 0xff000000 | palette[pixels[i]]
- }
- this.imageData.data.set(this.buf8)
- for (let i = 32 * 40; i < memory.length; i++) {
- const m = memory[i]
- for (let x = 0; x < 4; x++) {
- this.memoryImageData.data[i * 4 + x - 32 * 40] = m
- }
- }
- this.context.putImageData(this.imageData, 0, 0)
- this.memoryContext.putImageData(this.memoryImageData, 0, 0)
- }
- //const ms = Math.max(-0.8 * buffer.length + 160, 0)
- //timer = setTimeout(renderFrame, ms)
- //requestAnimationFrame(renderFrame)
- /* https://www.calculator.net/slope-calculator.html?type=1&x11=0&y11=500&x12=100&y12=16&x=12&y=7
- l = buffer.length
- l = 0, sleep 500
- l = 100, sleep 1000/60
- l = 200, sleep more
- */
- }
- },
- destroyed() {
- console.log('destroyed()')
- if (this.ws) this.ws.close()
- }
- }
- </script>
- <template>
- <v-app>
- <v-content>
- <v-toolbar color="primary">
- <v-toolbar-side-icon />
- <v-toolbar-title>Super Mario Bros</v-toolbar-title>
- </v-toolbar>
- <v-container>
- <canvas style="zoom: 2;" ref="canvas" width="256" height="240" />
- <canvas style="zoom: 4;" ref="memoryCanvas" width="16" height="128" />
- <pre>{{gameStateString}}</pre>
- <ul>
- <!-- "0x0069", "0x0075", "0x0093", "0x0109", "0x0113", "0x0114", "0x0115", "0x0118", "0x0119", "0x0130", "0x0163", "0x0164", "0x0168", "0x0184", "0x0185", "0x0186", "0x0187", "0x0190", "0x0191", "0x0202", "0x0215", "0x0216", "0x0239", "0x0246", "0x0269", "0x0281", "0x0282", "0x0283", "0x0289", "0x0637", "0x0641", "0x0642", "0x0645", "0x0646", "0x0791", "0x0987", "0x0988", "0x0989", "0x0996", "0x0997", "0x1002", "0x1003", "0x1008", "0x1050", "0x1052", "0x1084", "0x1178", "0x1180", "0x1181", "0x1182", "0x1183", "0x1212", "0x1213", "0x1214", "0x1215", "0x1220", "0x1221", "0x1222", "0x1223", "0x1286", "0x1302", "0x1318", "0x1334", "0x1350", "0x1366", "0x1382", "0x1398", "0x1563", "0x1564", "0x1565", "0x1578", "0x1579", "0x1580", "0x1581", "0x1593", "0x1594", "0x1595", "0x1596", "0x1597", "0x1611", "0x1612", "0x1613", "0x1623", "0x1624", "0x1627", "0x1628", "0x1629", "0x1636", "0x1638", "0x1639", "0x1640", "0x1643", "0x1653", "0x1654", "0x1655", "0x1656", "0x1659", "0x1675", "0x1676", "0x1677" -->
- <li> x, y: {{gameState.x}}, {{gameState.y}}</li>
- <li v-for="(key, index) in watch" :key="index">
- {{key.toString(16)}}: {{memory[key]}}
- </li>
- <li>0x006d (Level x): {{gameState.levelX}}</li>
- <li>0x0491 (Enemy collision): {{memory[0x0491]}}</li>
- <li>0x04AC: {{memory[0x04AC]}}</li>
- <li>0x04AC: {{memory[0x04AC]}}</li>
- <li>0x0750: {{memory[0x0750]}}</li>
- </ul>
- <!-- <div style="position: relative;">
- <div v-for="block in gameState.blocks"
- :key="gameState.blocks.indexOf(block)"
- :style="{
- display: 'block',
- width: '4px',
- height: '4px',
- left: (block.x * 4) + 'px',
- top: (block.y * 4) + 'px',
- position: 'absolute',
- backgroundColor: 'brown'
- }"
- >
- </div>
- </div> -->
- </v-container>
- </v-content>
- </v-app>
- </template>
- <style scoped>
- canvas {
- image-rendering: pixelated;
- }
- </style>
|