diff --git a/js-test-app/Dockerfile b/js-test-app/Dockerfile new file mode 100644 index 0000000..a77b238 --- /dev/null +++ b/js-test-app/Dockerfile @@ -0,0 +1,15 @@ +# Development environment +# ----------------------- +FROM node:latest +WORKDIR /webpack + +COPY package*.json ./ + +RUN npm install + +ENV NODE_ENV=development +ARG SPONSOR + +COPY docker ./ + +CMD node index.js \ No newline at end of file diff --git a/js-test-app/Procfile b/js-test-app/Procfile new file mode 100644 index 0000000..5ec9cc2 --- /dev/null +++ b/js-test-app/Procfile @@ -0,0 +1 @@ +web: node index.js \ No newline at end of file diff --git a/js-test-app/buildpack.yaml b/js-test-app/buildpack.yaml new file mode 100644 index 0000000..e0e8987 --- /dev/null +++ b/js-test-app/buildpack.yaml @@ -0,0 +1,15 @@ +version: v2 +name: js-test-app-buildpack +services: +- name: web + run: node index.js + type: web + port: 3000 +build: + context: ./ + method: pack + builder: heroku/buildpacks:20 + buildpacks: + - heroku/nodejs +predeploy: + run: ls diff --git a/js-test-app/dockerfile.yaml b/js-test-app/dockerfile.yaml new file mode 100644 index 0000000..6b3272f --- /dev/null +++ b/js-test-app/dockerfile.yaml @@ -0,0 +1,13 @@ +version: v2 +name: js-test-app-dockerfile +services: +- name: web + run: node index.js + type: web + port: 3000 +build: + context: ./ + method: docker + dockerfile: ./docker/Dockerfile +predeploy: + run: ls diff --git a/js-test-app/index.js b/js-test-app/index.js new file mode 100644 index 0000000..8b95d4b --- /dev/null +++ b/js-test-app/index.js @@ -0,0 +1,11 @@ +const express = require('express'); +const app = express(); + +app.use(express.static('public')); + +app.get('/', (req, res) => { + res.send('Tetris time.'); +}); + +var sponsor = process.env.SPONSOR || "unsponsored"; +app.listen(process.env.PORT || 3000, () => console.log('Tetris app listening on port 3000! Brought to you all by', sponsor + ", of course.")); diff --git a/js-test-app/package-lock.json b/js-test-app/package-lock.json new file mode 100644 index 0000000..13ed41f --- /dev/null +++ b/js-test-app/package-lock.json @@ -0,0 +1,374 @@ +{ + "name": "tetris", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/js-test-app/package.json b/js-test-app/package.json new file mode 100644 index 0000000..680bcd2 --- /dev/null +++ b/js-test-app/package.json @@ -0,0 +1,22 @@ +{ + "name": "tetris", + "version": "1.0.0", + "description": "[Have a try](http://farter.cn/tetr.js) in your browser.", + "main": "tetris.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/jusrhee/pure-tetr.js.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/jusrhee/pure-tetr.js/issues" + }, + "homepage": "https://github.com/jusrhee/pure-tetr.js#readme", + "dependencies": { + "express": "^4.17.1" + } +} diff --git a/js-test-app/public/.gitignore b/js-test-app/public/.gitignore new file mode 100755 index 0000000..64afd9e --- /dev/null +++ b/js-test-app/public/.gitignore @@ -0,0 +1,74 @@ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +### OSX template +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Created by .ignore support plugin (hsz.mobi) diff --git a/js-test-app/public/LICENSE b/js-test-app/public/LICENSE new file mode 100755 index 0000000..af64cd8 --- /dev/null +++ b/js-test-app/public/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012 Simon M. Laroche + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/js-test-app/public/MaterialIcons-Regular.woff b/js-test-app/public/MaterialIcons-Regular.woff new file mode 100755 index 0000000..627a518 Binary files /dev/null and b/js-test-app/public/MaterialIcons-Regular.woff differ diff --git a/js-test-app/public/bg.jpg b/js-test-app/public/bg.jpg new file mode 100755 index 0000000..411324a Binary files /dev/null and b/js-test-app/public/bg.jpg differ diff --git a/js-test-app/public/bg.js b/js-test-app/public/bg.js new file mode 100755 index 0000000..2f36c69 --- /dev/null +++ b/js-test-app/public/bg.js @@ -0,0 +1,48 @@ +//var bgCanvas = document.getElementById('bg'); +//var bgCtx = bgCanvas.getContext('2d'); +//var img = new Image(); +//img.src = 'bg.jpg'; +// +//function bgResize() { +// var ar = window.innerWidth / window.innerHeight; +// +// bgCanvas.width = 570 * ar; +// bgCanvas.height = 570; +// +// if (ar > 1) { +// bgCtx.drawImage(img, 0, -285 * ar + 285, bgCanvas.width, 570 * ar); +// } else { +// bgCtx.drawImage(img, -285 + bgCanvas.width / 2, 0, 570, 570); +// } +//} +//addEventListener('resize', bgResize, false); +//img.onload = function () { +// bgCanvas.style.opacity = 1; +// bgResize(); +//} +/* +var bgCanvas = document.getElementById('bg'); +var vidAr = bgCanvas.offsetWidth / bgCanvas.offsetHeight; +function bgResize() { + var ar = window.innerWidth / window.innerHeight; + if (ar > vidAr) { + bgCanvas.style.height = 'auto'; + bgCanvas.style.width = window.innerWidth + 'px'; + var height = bgCanvas.offsetHeight; + var shift = (height - window.innerHeight) / 2; + if (shift < 0) shift = 0; + bgCanvas.style.top = -shift + 'px'; + bgCanvas.style.left = 0; + } else { + bgCanvas.style.width = 'auto'; + bgCanvas.style.height = window.innerHeight + 'px'; + var width = bgCanvas.offsetWidth; + var shift = (width - window.innerWidth) / 2; + if (shift < 0) shift = 0; + bgCanvas.style.left = -shift + 'px'; + bgCanvas.style.top = 0; + } +} +addEventListener('resize', bgResize, false); +bgResize(); +*/ \ No newline at end of file diff --git a/js-test-app/public/bigmin.js b/js-test-app/public/bigmin.js new file mode 100755 index 0000000..88383b9 --- /dev/null +++ b/js-test-app/public/bigmin.js @@ -0,0 +1,15 @@ +var bigInt=function(n){function k(a,c){this.value=a;this.sign=c;this.isSmall=!1}function h(a){this.value=a;this.sign=0>a;this.isSmall=!0}function u(a){return-9007199254740992a}function x(a){return 1E7>a?[a]:1E14>a?[a%1E7,Math.floor(a/1E7)]:[a%1E7,Math.floor(a/1E7)%1E7,Math.floor(a/1E14)]}function y(a){w(a);var c=a.length;if(4>c&&0>z(a,L))switch(c){case 0:return 0;case 1:return a[0];case 2:return a[0]+1E7*a[1];default:return a[0]+1E7*(a[1]+1E7*a[2])}return a}function w(a){for(var c= +a.length;0===a[--c];);a.length=c+1}function B(a){for(var c=Array(a),b=-1;++bl?(l+=1E7,d=1):d=0,e[g]=l;for(g=f;gl)l+=1E7;else{e[g++]=l;break}e[g]=l}for(;gg?g%1E7+1E7:g;e=y(e);return"number"===typeof e?(b&&(e=-e),new h(e)):new k(e,b)}function I(a,c){var b=a.length,f=c.length,e=B(b+f),d,g,l;for(g=0;gd)return b=K(e,d),d=y(b[0]),b=b[1],a.sign&&(b=-b),"number"===typeof d?(a.sign!==f.sign&&(d=-d),[new h(d),new h(b)]):[new k(d,a.sign!==f.sign),new h(b)];d=x(d)}b=z(e,d);if(-1===b)return[m[0], +a];if(0===b)return[m[a.sign===f.sign?1:-1],m[0]];var g=d,l=e.length,d=g.length;b=B(g.length);var n=g[d-1],u=Math.ceil(1E7/(2*n)),e=C(e,u),g=C(g,u),t,p,v,q,w,D;e.length<=l&&e.push(0);g.push(0);n=g[d-1];for(t=l-d;0<=t;t--){l=Math.floor((1E7*e[t+d]+e[t+d-1])/n);v=p=0;w=g.length;for(q=0;qv?(e[t+q]=v+1E7,v=-1):(e[t+q]=v,v=0);for(;0!==v;){--l;for(q=p=0;qp?(e[t+q]=p+1E7,p=0):(e[t+q]=p,p=1);v+=p}b[t]=l}e=K(e,u)[0]; +b=[y(b),y(e)];d=b[0];f=a.sign!==f.sign;b=b[1];n=a.sign;"number"===typeof d?(f&&(d=-d),d=new h(d)):d=new k(d,f);"number"===typeof b?(n&&(b=-b),b=new h(b)):b=new k(b,n);return[d,b]}function z(a,c){if(a.length!==c.length)return a.length>c.length?1:-1;for(var b=a.length-1;0<=b;b--)if(a[b]!==c[b])return a[b]>c[b]?1:-1;return 0}function r(a){if(a instanceof k||a instanceof h)return a;if("number"===typeof a){if(u(a))return new h(a);a=String(a)}if("string"===typeof a){if(u(+a)){var c=+a;if(c===A(c))return new h(c); +throw Error("Bad int");}(c="-"===a[0])&&(a=a.slice(1));var b=a.split(/e/i);if(2a)throw Error("No e-");a=b+=Array(a+1).join("0")}if(!/^([0-9][0-9]*)$/.test(a))throw Error("Bad int");for(var b=[],f=a.length,e=f-7;0e&&(e=0),f-=7;w(b);return new k(b,c)}}"https://github.com/peterolson/BigInteger.js"; +var L=x(9007199254740992);k.prototype.add=function(a){a=r(a);if(this.sign!==a.sign)return this.sub(a.neg());var c=this.value,b=a.value;if(a.isSmall)return new k(F(c,Math.abs(b)),this.sign);a=c.length>=b.length?E(c,b):E(b,c);return new k(a,this.sign)};h.prototype.add=function(a){a=r(a);var c=this.value;if(0>c!==a.sign)return this.sub(a.neg());var b=a.value;if(a.isSmall){if(u(c+b))return new h(c+b);b=x(Math.abs(b))}return new k(F(b,Math.abs(c)),0>c)};k.prototype.sub=function(a){var c=r(a);if(this.sign!== +c.sign)return this.add(c.neg());a=this.value;var b=c.value;if(c.isSmall)return H(a,Math.abs(b),this.sign);c=this.sign;0<=z(a,b)?a=G(a,b):(a=G(b,a),c=!c);a=y(a);"number"===typeof a?(c&&(a=-a),a=new h(a)):a=new k(a,c);return a};h.prototype.sub=function(a){a=r(a);var c=this.value;if(0>c!==a.sign)return this.add(a.neg());var b=a.value;return a.isSmall?new h(c-b):H(b,Math.abs(c),0<=c)};k.prototype.neg=function(){return new k(this.value,!this.sign)};h.prototype.neg=function(){var a=this.sign,c=new h(-this.value); +c.sign=!a;return c};k.prototype.abs=function(){return new k(this.value,!1)};h.prototype.abs=function(){return new h(Math.abs(this.value))};k.prototype.mul=function(a){var c=r(a);a=this.value;var b=c.value,f=this.sign!==c.sign;if(c.isSmall){if(0===b)return m[0];if(1===b)return this;if(-1===b)return this.neg();c=Math.abs(b);if(1E7>c)return new k(C(a,c),f);b=x(c)}return new k(I(a,b),f)};h.prototype.mul=function(a){a=r(a);var c=this.value,b=a.value;if(0===c)return m[0];if(1===c)return a;if(-1===c)return a.neg(); +if(a.isSmall){if(u(c*b))return new h(c*b);b=x(Math.abs(b))}c=Math.abs(c);return 1E7>c?new k(C(b,c),this.sign!==a.sign):new k(I(b,x(c)),this.sign!==a.sign)};k.prototype.sqr=function(){return new k(J(this.value),!1)};h.prototype.sqr=function(){var a=this.value*this.value;return u(a)?new h(a):new k(J(x(Math.abs(this.value))),!1)};k.prototype.divmod=function(a){a=M(this,a);return{quo:a[0],rem:a[1]}};h.prototype.divmod=k.prototype.divmod;k.prototype.pow=function(a){var c=r(a),b=this.value;a=c.value;var f; +if(0===a)return m[1];if(0===b)return m[0];if(1===b)return m[1];if(c.sign)return m[0];if(!c.isSmall)throw Error("Exp too large");if(this.isSmall&&u(f=Math.pow(b,a)))return new h(A(f));f=this;for(c=m[1];;){a&1&&(c=c.mul(f),--a);if(0===a)break;a/=2;f=f.sqr()}return c};h.prototype.pow=k.prototype.pow;k.prototype.cmpAbs=function(a){a=r(a);return a.isSmall?1:z(this.value,a.value)};h.prototype.cmpAbs=function(a){a=r(a);var c=Math.abs(this.value),b=a.value;return a.isSmall?(b=Math.abs(b),c===b?0:c>b?1:-1): +-1};k.prototype.cmp=function(a){a=r(a);return this.sign!==a.sign?a.sign?1:-1:a.isSmall?this.sign?-1:1:z(this.value,a.value)*(this.sign?-1:1)};h.prototype.cmp=function(a){a=r(a);var c=this.value,b=a.value;return a.isSmall?c==b?0:c>b?1:-1:0>c!==a.sign?0>c?-1:1:0>c?1:-1};k.prototype.toString=function(){for(var a=this.value,c=a.length,b=String(a[--c]),f;0<=--c;)f=String(a[c]),b+="0000000".slice(f.length)+f;return(this.sign?"-":"")+b};h.prototype.toString=function(){return String(this.value)};var m=function(a){return"undefined"=== +typeof a?m[0]:r(a)};for(n=0;10>n;n++)m[n]=new h(n),0>= 3; + if(num !== 0) + halfByte |= 8; + arr.push(halfByte); + } while (num !== 0) +} + +function scanVL4(arr, ptr, refNum) { + var halfByte; + var len = 0; + var num = 0; + do { + halfByte = arr[ptr]; + if(halfByte === void 0) + return null; // error + //throw 4; + num |= (halfByte & 7) << (len * 3); + if((halfByte & 8) === 8) + len++; + ptr++; + } while ((halfByte & 8) === 8) + if(len > 0 && num < 8) + return -1; + else { + refNum[0] = num; + return ptr; + } +} + +var base67 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+=@"; // 67*67 < 16*16*16 + 16*16 + 16 +var base67rev = (function() { + var rev = {}; + for(var i=0; i= 3) { + sum = (arrHB[ptr] + arrHB[ptr+1]*16 + arrHB[ptr+2]*16*16); + } else if(nHB - ptr == 2) { + sum = (arrHB[ptr] + arrHB[ptr+1]*16 + 16*16*16); + } else if(nHB - ptr == 1) { + sum = (arrHB[ptr] + 16*16 + 16*16*16); + } + //console.log(sum); + arrBase67.push(base67[sum%67] + base67[~~(sum/67)]); + } + + return arrBase67.join(""); +} + +function keysDecode(str) { + var lastFrame = 0; + var lastKeys = 0; + var keys = {}; + var arrHB = []; + var arrBase67 = []; + var objNum = [0]; // pass by reference + + if(str.length%2 !== 0) + return null; + + for(var ptr=0; ptr>= 4; + arrHB.push(data & 15); data >>= 4; + arrHB.push(data & 15); + } else if(data < 16*16*16 + 16*16) { + data -= 16*16*16; + arrHB.push(data & 15); data >>= 4; + arrHB.push(data & 15); + } else if(data < 16*16*16 + 16*16 + 16) { + data -= 16*16*16 + 16*16; + arrHB.push(data & 15); + } else { + //return null; + throw 2; + } + } + + //console.log(arrHB.length, arrHB.toString()); + + for(var i=0; i + + + + + Tetr.js + + + + + + + + + + + + + + + + + +
+ + +
+

Hold

+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + +
0Score
0
Line
0Piece
+
+ +
+ + You need an up-to-date web browser to play this game. + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ +
+

Next

+ +
+ +
+ + + + + + + + + + + + + + + + + diff --git a/js-test-app/public/menu.js b/js-test-app/public/menu.js new file mode 100755 index 0000000..75f7e82 --- /dev/null +++ b/js-test-app/public/menu.js @@ -0,0 +1,312 @@ +var version = '0.1.8'; +var setLoop; +var arrowReleased = true; +var arrowDelay = 0; + +var key = { + 8: 'Backspace', + 9: 'Tab', + 13: 'Enter', + 16: 'Shift', + 17: 'Ctrl', + 18: 'Alt', + 19: 'Pause', + 20: 'Caps Lock', + 27: 'Esc', + 32: 'Space', + 33: 'PgUp', + 34: 'PgDn', + 35: 'End', + 36: 'Home', + 37: '←', + 38: '↑', + 39: '→', + 40: '↓', + 45: 'Insert', + 46: 'Delete', + 48: '0', + 49: '1', + 50: '2', + 51: '3', + 52: '4', + 53: '5', + 54: '6', + 55: '7', + 56: '8', + 57: '9', + 59: ';', + 61: '=', + 65: 'A', + 66: 'B', + 67: 'C', + 68: 'D', + 69: 'E', + 70: 'F', + 71: 'G', + 72: 'H', + 73: 'I', + 74: 'J', + 75: 'K', + 76: 'L', + 77: 'M', + 78: 'N', + 79: 'O', + 80: 'P', + 81: 'Q', + 82: 'R', + 83: 'S', + 84: 'T', + 85: 'U', + 86: 'V', + 87: 'W', + 88: 'X', + 89: 'Y', + 90: 'Z', + 96: '0kpad', + 97: '1kpad', + 98: '2kpad', + 99: '3kpad', + 100: '4kpad', + 101: '5kpad', + 102: '6kpad', + 103: '7kpad', + 104: '8kpad', + 105: '9kpad', + 106: '*', + 107: '+', + 109: '-', + 110: '.', + 111: '/', + 112: 'F1', + 113: 'F2', + 114: 'F3', + 115: 'F4', + 116: 'F5', + 117: 'F6', + 118: 'F7', + 119: 'F8', + 120: 'F9', + 121: 'F10', + 122: 'F11', + 123: 'F12', + 173: '-', + 186: ';', + 187: '=', + 188: ',', + 189: '-', + 190: '.', + 191: '/', + 192: '`', + 219: '[', + 220: '\\', + 221: ']', + 222: "'", + undefined: "---", + 0: "---" +}; + +/** + * Show and hide menus. + */ +var menus = document.getElementsByClassName('menu'); +var menuStack = []; +function menu(menuIndex, stackOper) { + var current = void 0; + for (var i = 0, len = menus.length; i < len; i++) { + if (menus[i].classList.contains('on')) { + current = i; + } + menus[i].classList.remove('on'); + } + + if (menuIndex !== void 0) { + menus[menuIndex].classList.add('on'); + } + + if (stackOper === 1) { + if (current !== void 0) { + menuStack.push(current); + } + } else if (stackOper === -1) { + current = menuStack.pop(); + if ((current !== void 0) && (menuIndex === void 0)) { + menus[current].classList.add('on'); + } + } else if (stackOper !== 0) { + menuStack = []; + } +} + +/** + * Controls Menu + */ +var newKey, currCell, tempKey, controls = document.getElementById('controls'), controlCells = controls.getElementsByTagName('td'); +// Give controls an event listener. +for (var i = 0, len = controlCells.length; i < len; i++) { + controlCells[i].onclick = function () { + // First check if we're already waiting for an input. + if (currCell) { + // TODO DRY + // Make this into a function and call it when we press Esc. + binds[currCell.id] = tempKey; + $setText(currCell, key[tempKey] || tempKey); + } + tempKey = binds[this.id]; + $setText(this, 'Press key'); + currCell = this; + } +} +// Listen for key input if a control has been clicked on. +addEventListener('keyup', function (e) { + // if click outside of cell or press esc clear currCell + // reset binds button. + if (currCell) { + var newKey=e.keyCode; + if(newKey===8){ + newKey=void 0; + } + // Checks if key already in use, and unbinds it. + if(newKey){ + for (var i in binds) { + if (newKey === binds[i]) { + binds[i] = void 0; + $setText($$(i), key[void 0]); + } + } + } + // Binds the key and saves the data. + binds[currCell.id] = newKey; + $setText(currCell, key[newKey] || newKey); + localStorage.setItem('binds', JSON.stringify(binds)); + currCell = 0; + } +}, false); + +/** + * Settings Menu + */ +function settingsLoop() { + if (arrowReleased || arrowDelay >= 6) { + if (settingsArrow) { + mySettings[s] = (mySettings[s] === 0) ? setting[s].length - 1 : mySettings[s] - 1; + } else { + mySettings[s] = (mySettings[s] === setting[s].length - 1) ? 0 : mySettings[s] + 1; + } + saveSetting(s); + arrowReleased = false; + } else { + arrowDelay++; + } + setLoop = setTimeout(settingsLoop, 50); +} +var s; +var settingsArrow; +// TODO DRY this. +function arrowRelease(e) { + //resize(); + arrowReleased = true; + arrowDelay = 0; + clearTimeout(setLoop); + this.onmouseup = void 0; + this.onmouseout = void 0; + this.ontouchend = void 0; + this.ontouchcancel = void 0; + if (e && e.preventDefault) { + e.preventDefault(); + } //avoid selection by touch +} +function left(e) { + settingsArrow = 1; + s = this.parentNode.id; + this.onmouseup = arrowRelease; + this.onmouseout = arrowRelease; + this.ontouchend = arrowRelease; + this.ontouchcancel = arrowRelease; + if (e && e.preventDefault) { + e.preventDefault(); + } //avoid selection by touch + settingsLoop(); +} +function right(e) { + settingsArrow = 0; + s = this.parentNode.id; + this.onmouseup = arrowRelease; + this.onmouseout = arrowRelease; + this.ontouchend = arrowRelease; + this.ontouchcancel = arrowRelease; + if (e && e.preventDefault) { + e.preventDefault(); + } //avoid selection by touch + settingsLoop(); +} + +/** + * LocalStorage functions + */ +function saveSetting(s) { + if (localStorage === void 0) { + localStorage = {}; + } + localStorage['version'] = version; + + $setText($$(s).getElementsByTagName('span')[0], setting[s][mySettings[s]]); + + localStorage['settings'] = JSON.stringify(mySettings); +} +function loadLocalData() { + if (localStorage === void 0) { + localStorage = {}; + } + if (localStorage['binds']) { + binds = JSON.parse(localStorage.getItem('binds')); + for (var i = 0, len = controlCells.length; i < len; i++) { + var keycode=binds[controlCells[i].id]; + $setText(controlCells[i], key[keycode] || keycode); + } + }else{ + $$("btnbinds").classList.add("highlight"); + } + // TODO When new version just update with new stuff, rest stays unchanged. + if (localStorage['version'] !== version) { + localStorage.removeItem('settings'); + localStorage.removeItem('binds'); + } + if (localStorage['settings']) { + var storedSettings = JSON.parse(localStorage.getItem('settings')); + for (var i in mySettings) { + if (i in storedSettings) { + mySettings[i] = storedSettings[i]; + } + } + } + +} + +loadLocalData(); +for (var s in mySettings) { + if (mySettings.hasOwnProperty(s)) { + var div = document.createElement('div'); + var sname = document.createElement('b'); + var iLeft = document.createElement('i'); + var span = document.createElement('span'); + var iRight = document.createElement('i'); + + div.id = s; + $setText(sname, settingName[s]); + $setText(span, setting[s][mySettings[s]]); + iLeft.className = 'material-icons left'; + iRight.className = 'material-icons right'; + $setText(iLeft, "\uE314"); + $setText(iRight, "\uE315"); + iLeft.onmousedown = left; + iLeft.ontouchstart = left; + iRight.onmousedown = right; + iRight.ontouchstart = right; + + set.appendChild(div); + div.appendChild(sname); + div.appendChild(iLeft); + div.appendChild(span); + div.appendChild(iRight); + } +} \ No newline at end of file diff --git a/js-test-app/public/piece.js b/js-test-app/public/piece.js new file mode 100755 index 0000000..343e9ae --- /dev/null +++ b/js-test-app/public/piece.js @@ -0,0 +1,475 @@ +function Piece() { + this.x; + this.y; + this.pos = 0; + this.tetro; + this.index; + this.gravity = gravityUnit; + this.lockDelay = 0; + this.lockDelayLimit = 30; + this.are = 0; + this.areLimit = 0; + this.irsDir = 0; + this.ihs = false; + this.shiftDelay = 0; + this.shiftDir = 0; + this.shiftReleased = false; + this.arrDelay = 0; + this.held = false; + this.finesse = 0; + this.dirty = false; + this.dead = true; +} +/** + * Removes last active piece, and gets the next active piece from the grab bag. + */ +Piece.prototype.new = function(index) { + // TODO if no arguments, get next grabbag piece + //console.log("new irs"+this.irsDir+", ihs"+this.ihs); + + this.pos = RotSys[settings.RotSys].initinfo[index][2]; + this.x = ~~((stack.width - 4) / 2) + RotSys[settings.RotSys].initinfo[index][0]; + this.y = stack.hiddenHeight - 2 + RotSys[settings.RotSys].initinfo[index][1]; + this.index = index; + this.tetro = []; + this.held = false; + this.ihs = false; + this.finesse = 0; + this.dirty = true; + this.dead = false; + + // TODO Do this better. Make clone object func maybe. + //for property in pieces, this.prop = piece.prop + if (this.irsDir !== 0) { + var curPos = this.pos; + var newPos = (this.pos+this.irsDir).mod(4); + var offsetX = + RotSys[settings.RotSys].offset[this.index][newPos][0] - + RotSys[settings.RotSys].offset[this.index][curPos][0]; + var offsetY = + RotSys[settings.RotSys].offset[this.index][newPos][1] - + RotSys[settings.RotSys].offset[this.index][curPos][1]; + this.tetro = pieces[index].tetro[newPos]; + if (!this.moveValid(offsetX, offsetY, this.tetro)) { + this.tetro = pieces[index].tetro[curPos]; + } else { + this.x += offsetX; + this.y += offsetY; + this.pos = newPos; + } + this.irsDir = 0; + } else { + this.tetro = pieces[index].tetro[this.pos]; + } + + this.lockDelayLimit = setting['Lock Delay'][settings['Lock Delay']]; + if (gametype === 6) { //Death + this.gravity = 20; + if (level < 20) { + this.lockDelayLimit = [ + 30, 25, 22, 20, 20, 18, 17, 17, 15, 15, + 13, 13, 13, 13, 13, 12, 12, 12, 11, 11 + ][level]; + } else { + this.lockDelayLimit = 11; + } + } else if (settings.Gravity !== 0) { + this.gravity = gravityArr[settings.Gravity - 1]; + } else if (gametype === 1) { //Marathon + if (gameparams.marathonType === 1) { + this.gravity = (level * 2 + 10) / 60; + this.lockDelayLimit = 8; + } else { + if (level < 20) { + this.gravity = [ + 1/60, 1/30, 1/25, 1/20, 1/15, 1/12, 1/10, 1/8, 1/6, 1/6, + 1/4, 1/4, 1/3, 1/3, 1/3, 1/2, 1, 1, 2, 3 + ] + [level]; + } else { + this.gravity = 20; + this.lockDelayLimit = ~~(30 * Math.pow(0.93, (Math.pow(level-20, 0.8)))); // magic! + } + } + } else { + this.gravity = gravityUnit; + } + if (gametype === 0){ + if(this.lockDelayLimit < 8) { + this.lockDelayLimit = 8; + } + } + + // Check for blockout. + if (!this.moveValid(0, 0, this.tetro)) { + //this.dead = true; //show it? + gameState = 9; + $setText(msg,'BLOCK OUT!'); + menu(3); + sound.playse("gameover"); + return; + } + + //real 20G + if(this.gravity >= 20) { + this.checkFall(); + } + if (flags.moveDown & keysDown) { + var grav = gravityArr[settings['Soft Drop'] + 1]; + if (grav >= 20) // 20G softdrop = 20G gravity + this.y += this.getDrop(grav); + //piece.finesse++; + } + landed = !this.moveValid(0, 1, this.tetro); + + // die-in-one-frame! + if(landed && (this.lockDelay >= this.lockDelayLimit)) { + this.checkLock(); + } +} +Piece.prototype.tryKickList = function(kickList, rotated, newPos, offsetX, offsetY) { + for (var k = 0, len = kickList.length; k < len; k++) { + if (this.moveValid( + offsetX + kickList[k][0], + offsetY + kickList[k][1], + rotated + )) { + this.x += offsetX + kickList[k][0]; + this.y += offsetY + kickList[k][1]; + this.tetro = rotated; + this.pos = newPos; + this.finesse++; + break; + } + } +} +Piece.prototype.rotate = function(direction) { + + // Goes thorugh kick data until it finds a valid move. + var curPos = this.pos.mod(4); + var newPos = (this.pos + direction).mod(4); + // Rotates tetromino. + var rotated = pieces[this.index].tetro[newPos]; + var offsetX = + RotSys[settings.RotSys].offset[this.index][newPos][0] - + RotSys[settings.RotSys].offset[this.index][curPos][0]; + var offsetY = + RotSys[settings.RotSys].offset[this.index][newPos][1] - + RotSys[settings.RotSys].offset[this.index][curPos][1]; + if (settings.RotSys === 2 || settings.RotSys === 14) { //ARS, Plus + var kickList = []; + if (this.index === PieceI.index) { + if(curPos === 1 || curPos === 3) + kickList = [[ 0, 0],[+1, 0],[-1, 0],[+2, 0]]; + else + kickList = [[ 0, 0],[ 0,-1],[ 0,-2]]; + } else { + if (newPos === 0 || + ((this.index === PieceS.index || this.index === PieceZ.index) && newPos === 2) + ) + kickList = [[ 0, 0],[+1, 0],[-1, 0],[ 0,-1]]; + else + kickList = [[ 0, 0],[+1, 0],[-1, 0]]; + } + this.tryKickList(kickList, rotated, newPos, offsetX, offsetY); + } else { + var kickIndex = [ 1, -1 ,2].indexOf(direction); // kickDataDirectionIndex + var kickList; + if(settings.RotSys === 0) + kickList = WKTableSRS[this.index][kickIndex][curPos]; + else if (settings.RotSys === 1) + kickList = WKTableCultris; + else if (settings.RotSys === 15) + kickList = WKTableDX[kickIndex][curPos] + else + kickList = WKTableDTET[kickIndex]; + this.tryKickList(kickList, rotated, newPos, offsetX, offsetY); + } +} + +Piece.prototype.checkShift = function() { + // Shift key pressed event. + if (keysDown & flags.moveLeft && !(lastKeys & flags.moveLeft)) { + this.shiftDelay = 0; + this.arrDelay = 0; + this.shiftReleased = true; + this.shiftDir = -1; + this.finesse++; + } else if (keysDown & flags.moveRight && !(lastKeys & flags.moveRight)) { + this.shiftDelay = 0; + this.arrDelay = 0; + this.shiftReleased = true; + this.shiftDir = 1; + this.finesse++; + } + // Shift key released event. + if (this.shiftDir === 1 && + !(keysDown & flags.moveRight) && lastKeys & flags.moveRight && keysDown & flags.moveLeft) { + this.shiftDelay = 0; + this.arrDelay = 0; + this.shiftReleased = true; + this.shiftDir = -1; + } else if (this.shiftDir === -1 && + !(keysDown & flags.moveLeft) && lastKeys & flags.moveLeft && keysDown & flags.moveRight) { + this.shiftDelay = 0; + this.arrDelay = 0; + this.shiftReleased = true; + this.shiftDir = 1; + } else if ( + !(keysDown & flags.moveRight) && lastKeys & flags.moveRight && keysDown & flags.moveLeft) { + this.shiftDir = -1; + } else if ( + !(keysDown & flags.moveLeft) && lastKeys & flags.moveLeft && keysDown & flags.moveRight) { + this.shiftDir = 1; + } else if ((!(keysDown & flags.moveLeft) && lastKeys & flags.moveLeft) || + (!(keysDown & flags.moveRight) && lastKeys & flags.moveRight)) { + this.shiftDelay = 0; + this.arrDelay = 0; + this.shiftReleased = true; + this.shiftDir = 0; + } + // Handle events + /* farter */ + // here problem causes it taking 2 frames to move 1 grid even ARR=1 + var dascut = [false,true][(settings.DASCut || 0)] + //if (dascut) { + // this.ShiftDir = 0; + // console.log("interrupt") + //} + if (this.shiftDir) { + // 1. When key pressed instantly move over once. + if (this.shiftReleased && settings.DAS !== 0) { + this.shift(this.shiftDir); + this.shiftDelay++; + this.shiftReleased = false; + // 2. Apply DAS delay + } else if (this.shiftDelay < settings.DAS) { + this.shiftDelay++; + // 3. Once the delay is complete, move over once. + // Increment delay so this doesn't run again. + // if arr=0, repeat here, not entering 4 + // but if dascut, let shiftdelay == das + 1 and arrdelay = 0 which is not < arr + } else if (this.shiftDelay === settings.DAS) { + this.shift(this.shiftDir); + if (settings.ARR !== 0 || dascut) this.shiftDelay++; + // 4. Apply ARR delay + } else if (this.arrDelay < settings.ARR) { + this.arrDelay++; + // 5. If ARR Delay is full, move piece, and reset delay and repeat. + /* + } else if (this.arrDelay === settings.ARR && settings.ARR !== 0) { + */ + if (this.arrDelay === settings.ARR && settings.ARR !== 0) { + this.shift(this.shiftDir); + } + } + } + if (flags.moveLeft3 & keysDown && !(lastKeys & flags.moveLeft3)) { + this.multiShift(-1, 3); + this.finesse++; + } else if (flags.moveRight3 & keysDown && !(lastKeys & flags.moveRight3)) { + this.multiShift(1, 3); + this.finesse++; + } +} +Piece.prototype.shift = function(direction) { + this.arrDelay = 0; + if (settings.ARR === 0 && this.shiftDelay === settings.DAS) { + while (true) { + if (this.moveValid(direction, 0, this.tetro)) { + this.x += direction; + /* farter */ //instant das under 20G + if(this.gravity >= 20) { + this.checkFall(); + } + if (flags.moveDown & keysDown) { + var grav = gravityArr[settings['Soft Drop'] + 1]; + if (grav >= 20) // 20G softdrop vs. 20G das + this.y += this.getDrop(grav); + //piece.finesse++; + } + } else { + break; + } + } + } else if (this.moveValid(direction, 0, this.tetro)) { + this.x += direction; + } +} +Piece.prototype.multiShift = function(direction, count) { + for (var i = 0; i < count && this.moveValid(direction, 0, this.tetro); ++i) { + this.x += direction; + if(this.gravity >= 20) { + this.checkFall(); + } + if (flags.moveDown & keysDown) { + var grav = gravityArr[settings['Soft Drop'] + 1]; + if (grav >= 20) // 20G softdrop vs. 20G das + this.y += this.getDrop(grav); + //piece.finesse++; + } + } +} +Piece.prototype.shiftDown = function() { + if (this.moveValid(0, 1, this.tetro)) { + var grav = gravityArr[settings['Soft Drop'] + 1]; + if (grav > 1) + this.y += this.getDrop(grav); + else + this.y += grav; + } +} +Piece.prototype.hardDrop = function() { + var distance = this.getDrop(2147483647); + this.y += distance; + score = score.add(bigInt(distance + this.lockDelayLimit - this.lockDelay)); + //statisticsStack(); + this.lockDelay = this.lockDelayLimit; +} +Piece.prototype.getDrop = function(distance) { + if (!this.moveValid(0, 0, this.tetro)) + return 0; + for (var i = 1; i <= distance; i++) { + if (!this.moveValid(0, i, this.tetro)) + return i - 1; + } + return i - 1; +} +Piece.prototype.hold = function() { + if (gametype === 1 && gameparams.marathonType === 1){ + return + } + var temp = hold.piece; + if (!this.held) { + if (hold.piece !== void 0) { + hold.piece = this.index; + this.new(temp); + } else { + hold.piece = this.index; + this.new(preview.next()); + } + this.held = true; + hold.draw(); + } +} +/** + * Checks if position and orientation passed is valid. + * We call it for every action instead of only once a frame in case one + * of the actions is still valid, we don't want to block it. + */ +Piece.prototype.moveValid = function(cx, cy, tetro) { + cx = cx + this.x; + cy = Math.floor(cy + this.y); + + for (var x = 0; x < tetro.length; x++) { + for (var y = 0; y < tetro[x].length; y++) { + if (tetro[x][y] && ( + (cx + x < 0 || cx + x >= stack.width || cy + y >= stack.height) || + (cy + y >=0 && stack.grid[cx + x][cy + y]) + )) { + return false; + } + } + } + this.lockDelay = 0; + return true; +} + +Piece.prototype.checkFall = function() { + var grav = this.gravity; + if (grav > 1) + this.y += this.getDrop(grav); + else { + this.y += grav; + } + /* farter */ // rounding problem + if (Math.abs(this.y - Math.round(this.y))<0.000001) + this.y = Math.round(this.y); +} + +Piece.prototype.checkLock = function() { + if (landed) { + this.y = Math.floor(this.y); //@sega + if (this.lockDelay >= this.lockDelayLimit) { + this.dead = true; + stack.addPiece(this.tetro); + sound.playse("lock"); + this.dirty = true; + if(gameState === 9){ // lockout! don't spawn next piece + return; + }else{ + this.held = false; + /* farter */ + // Win? + checkWin(); + if (gameState === 0 && piece.dead) { // still playing, then spawn the next piece + // determine next ARE limit + if (gametype === 6) { //Death + if (level < 20) { + this.areLimit = [ + 18, 18, 18, 15, 15, 12, 12, 12, 12, 12, + 12, 12, 10, 10, 10, 8, 8, 8, 8, 8 + ][level]; + } else { + this.lockDelayLimit = 11; + this.areLimit = 6; + } + } else if (gametype === 1 && gameparams.marathonType === 1) { + this.areLimit = 11; + } else { + this.areLimit = 0; + } + if (this.areLimit === 0) { // IRS IHS not possible + this.new(preview.next()); // may die-in-one-frame + } else { + gameState = 4; + this.are = 0; + } + } + } + /* farter */ + } + } +} + +Piece.prototype.update = function() { + if (this.moveValid(0, 1, this.tetro)) { + this.checkFall(); + } + landed = !this.moveValid(0, 1, this.tetro); + if (landed) { + this.lockDelay++; + } + this.checkLock(); +} + +Piece.prototype.draw = function() { + clear(activeCtx); + if (!this.dead) { + this.drawGhost(); + if (settings.Ghost !== 3) { + var a = void 0; + if (landed) { + a = this.lockDelay / this.lockDelayLimit; + if (this.lockDelayLimit === 0) + a = 0; + a = Math.pow(a,2)*0.5; + } + draw(this.tetro, this.x, Math.floor(this.y) - stack.hiddenHeight, activeCtx, RotSys[settings.RotSys].color[this.index], a); + } + } +} + +Piece.prototype.drawGhost = function() { + activeCtx.globalAlpha = 1.0; + if (settings.Ghost === 0 && !landed) { + drawGhost(this.tetro, this.x, + Math.floor(this.y + this.getDrop(2147483647)) - stack.hiddenHeight, activeCtx, 0); + } else if (settings.Ghost === 1 && !landed) { + draw(this.tetro, this.x, + Math.floor(this.y + this.getDrop(2147483647)) - stack.hiddenHeight, activeCtx, RotSys[settings.RotSys].color[this.index]); + } + activeCtx.globalAlpha = 1; +} + +var piece = new Piece(); diff --git a/js-test-app/public/preview.js b/js-test-app/public/preview.js new file mode 100755 index 0000000..f30d9be --- /dev/null +++ b/js-test-app/public/preview.js @@ -0,0 +1,76 @@ +function Preview() { + grabBag = this.gen(); +} +Preview.prototype.init = function() { + //XXX fix ugly code lolwut /* farter */ + while (1) { + this.grabBag = this.gen(); + break; + //if ([3,4,6].indexOf(this.grabBag[0]) === -1) break; + } + if (this.grabBag.length <= 7) { + this.grabBag.push.apply(this.grabBag, this.gen()); + } + this.dirty = true; + this.draw(); +} +Preview.prototype.next = function() { + var next; + next = this.grabBag.shift(); + if (this.grabBag.length <= 7) { + this.grabBag.push.apply(this.grabBag, this.gen()); + } + this.dirty = true; + return next; + //TODO Maybe return the next piece? +} +/** + * Creates a "grab bag" of the 7 tetrominos. + */ +Preview.prototype.gen = function() { + var pieceList = void 0; + if(gameparams && gameparams.pieceSet){ + switch(gameparams.pieceSet){ + case 1: pieceList=[1,2,3,4,5,6];break; + case 2: pieceList=[0,0,0,0,0,0,0];break; + } + }else{ + pieceList= [0, 1, 2, 3, 4, 5, 6]; + } + //return pieceList.sort(function() {return 0.5 - rng.next()}); + /* farter */ // proven random shuffle algorithm + for (var i=0;i9?"":"0")+hs); + else if(m<10) + return m+":"+((s>9?"":"0")+s)+"."+((hs>9?"":"0")+hs); + else + return m+":"+((s>9?"":"0")+s)+"."+(~~(hs/10)); // + } + var getlinetxt = function(l) + { + if(l<1000) + return l+"L"; + else + return l+""; + } + var getscoretext = function(s) + { + var arr=s.split(""); + for(var i=arr.length-1-3;i>=0;i-=3){ + arr[i]+=" "; + } + return arr.join(""); + } + for (var i=0;i= 0) { + this.grid[x + piece.x][y + piece.y] = RotSys[settings.RotSys].color[piece.index]; + // Get column for finesse + if (!once || x + piece.x < column) { + column = x + piece.x; + once = true; + } + // Check which lines get modified + if (range.indexOf(y + piece.y) === -1) { + range.push(y + piece.y); + // This checks if any cell is in the play field. If there + // isn't any this is called a lock out and the game ends. + if (y + piece.y >= this.hiddenHeight) valid = true; + } + } + } + } + + // Lock out + if (!valid) { + gameState = 9; + $setText(msg,'LOCK OUT!'); + menu(3); + sound.playse("gameover"); + return; + } + + // Check modified lines for full lines. + range = range.sort(function(a,b){return a-b}); + for (var row = range[0], len = row + range.length; row < len; row++) { + var count = 0; + for (var x = 0; x < this.width; x++) { + if (this.grid[x][row]) count++; + } + // Clear the line. This basically just moves down the stack. + // TODO Ponder during the day and see if there is a more elegant solution. + if (count === this.width) { + lineClear++; // NOTE stats + var rowInDig = digLines.indexOf(row); + if (rowInDig !== -1) { + for (var y = 0; y < rowInDig; y++) { + digLines[y]++; + } + digLines.splice(rowInDig, 1); + } + for (var y = row; y >= 1; y--) { + for (var x = 0; x < this.width; x++) { + this.grid[x][y] = this.grid[x][y - 1]; + } + } + for (var x = 0; x < this.width; x++) { + this.grid[x][0] = void 0; + } + } + } + + var scoreAdd = bigInt(level + 1); + var garbage = 0; + if (lineClear !== 0) { + //console.log("C"+combo+" B"+b2b) + if (isSpin) { + scoreAdd = scoreAdd.mul( + bigInt([800,1200,1600,2000][lineClear - 1]) + .mul(bigInt(2).pow(b2b + combo)) + ); + garbage = [[2,4,6,8],[3,6,9,12]][b2b != 0 ? 1 : 0][lineClear - 1]; + b2b += 1; + sound.playse("tspin",lineClear); + } else if (lineClear === 4) { + scoreAdd = scoreAdd.mul( + bigInt(800) + .mul(bigInt(2).pow(b2b + combo)) + ); + garbage = [4,5][b2b != 0 ? 1 : 0]; + b2b += 1; + sound.playse("erase",lineClear); + } else { + scoreAdd = scoreAdd.mul( + bigInt([100,300,500,800][lineClear - 1]) + .mul(bigInt(2).pow(combo)) + ); + b2b = 0; + garbage = [0,1,2,4][lineClear - 1]; + sound.playse("erase",lineClear); + } + garbage += ~~(combo / 2); //[0,0,1,1,2,2,3,3,4,4,5,5,6,6,...] + combo += 1; + } else { + if (isSpin) { + scoreAdd = scoreAdd.mul( + bigInt(2).pow(bigInt(b2b)) + .mul(bigInt(400)) + ); + sound.playse("tspin",lineClear); + } else { + scoreAdd = bigInt(0); + } + combo = 0; + } + lines += lineClear; + if (gametype === 1 || gametype === 6) { + level = ~~(lines / 10); + } else if (gametype === 7) { + level = ~~(lines / 30); + } + score = score.add(scoreAdd.mul(bigInt(16).pow(allclear))); + + var pc = true; + for (var x = 0; x < this.width; x++) + for (var y = 0; y < this.height; y++) + if (this.grid[x][y]) + pc = false; + if (pc) { + score = score.add(bigInt(1000000).mul(bigInt(16).pow(allclear))); + allclear ++; + sound.playse("bravo"); + garbage += 10; + } + + if(gameparams.backFire){ + if(gameparams.backFire === 1){ + garbage = [0, 0,1,2,4][lineClear]; + }else if(gameparams.backFire === 3){ + garbage *= ~~(lines/2); + } + if(garbage !== 0) { + if(gameparams.backFire === 1){ + for(var y = 0; y < garbage; y++){ + this.rowRise(bottomRow, piece); + } + }else if(gameparams.backFire === 2 || gameparams.backFire === 3){ + var hole = ~~(rng.next() * 10); + var arrRow = [8,8,8,8,8,8,8,8,8,8]; + arrRow[hole] = 0; + for(var y = 0; y < garbage; y++){ + this.rowRise(arrRow, piece); + } + } + } + } + + //if (scoreAdd.cmp(0) > 0) + //console.log(scoreAdd.toString()); + + statsFinesse += piece.finesse - finesse[piece.index][piece.pos][column]; + piecesSet++; // NOTE Stats + // TODO Might not need this (same for in init) + column = 0; + + this.dirty = true; +} +/** + * Raise a garbage line. farter + */ +Stack.prototype.rowRise = function(arrRow, objPiece) { + var isEmpty = true; + for(var x = 0; x < this.width; x++) { + for(var y = 0; y < this.height - 1; y++) { + this.grid[x][y] = this.grid[x][y+1]; + } + if(arrRow[x]) + isEmpty = false; + this.grid[x][this.height-1]=arrRow[x]; + } + var topout = false; + for(var y = 0; y < digLines.length; y++) { + digLines[y]--; + if(digLines[y] < 0) { // top out, but only detecting added lines + topout = true; + } + } + if(topout) { + gameState = 9; + $setText(msg,'TOP OUT!'); + menu(3); + sound.playse("gameover"); + } + if(!isEmpty) { + digLines.push(this.height - 1); + } + if (!piece.dead) { + if (!piece.moveValid(0, 0, piece.tetro)) { + piece.y-=1; + if (piece.y + pieces[piece.index].rect[3] <= this.hiddenHeight - 2) { // the bottom is >=2 cell away from visible part + gameState = 9; + $setText(msg,'OOPS!'); + menu(3); + sound.playse("gameover"); + } + } + piece.dirty = true; + } + this.dirty = true; +} +/** + * Draws the stack. + */ +Stack.prototype.draw = function() { + + clear(stackCtx); + if(settings.Outline === 0 || settings.Outline === 1 || + (settings.Outline === 2 && (gameState === 9 || gameState === 1)) + ) { + draw(this.grid, 0, -this.hiddenHeight, stackCtx, void 0, 0.0); + } + + // Darken Stack + // TODO wrap this with an option. + // no fullscreen flush, see above + //stackCtx.globalCompositeOperation = 'source-atop'; + //stackCtx.fillStyle = 'rgba(0,0,0,0.3)'; + //stackCtx.fillRect(0, 0, stackCanvas.width, stackCanvas.height); + //stackCtx.globalCompositeOperation = 'source-over'; + + if (settings.Outline === 1 || settings.Outline === 3) { + var b = ~~(cellSize / 8); + var c = cellSize; + var hhc = this.hiddenHeight * c; + var pi = Math.PI; + var lineCanvas = document.createElement('canvas'); + lineCanvas.width = stackCanvas.width; + lineCanvas.height = stackCanvas.height; + + var lineCtx = lineCanvas.getContext('2d'); + lineCtx.shadowBlur = 10; + lineCtx.shadowColor = '#ffffff'; + lineCtx.fillStyle = 'rgba(255,255,255,0.5)'; + lineCtx.beginPath(); + for (var x = 0, len = this.width; x < len; x++) { + for (var y = 0, wid = this.height; y < wid; y++) { + if (this.grid[x][y]) { + if (x < this.width - 1 && !this.grid[x + 1][y]) { + lineCtx.fillRect(x * c + c - b, y * c - hhc, b, c); + } + if (x > 0 && !this.grid[x - 1][y]) { + lineCtx.fillRect(x * c, y * c - hhc, b, c); + } + if (y < this.height - 1 && !this.grid[x][y + 1]) { + lineCtx.fillRect(x * c, y * c - hhc + c - b, c, b); + } + if (!this.grid[x][y - 1]) { + lineCtx.fillRect(x * c, y * c - hhc, c, b); + } + // Diags + if (x < this.width - 1 && y < this.height - 1) { + if (!this.grid[x + 1][y] && !this.grid[x][y + 1]) { + lineCtx.clearRect(x * c + c - b, y * c - hhc + c - b, b, b); + lineCtx.fillRect(x * c + c - b, y * c - hhc + c - b, b, b); + } else if (!this.grid[x + 1][y + 1] && this.grid[x + 1][y] && this.grid[x][y + 1]) { + lineCtx.moveTo(x * c + c, y * c - hhc + c - b); + lineCtx.lineTo(x * c + c, y * c - hhc + c); + lineCtx.lineTo(x * c + c - b, y * c - hhc + c); + lineCtx.arc(x * c + c, y * c - hhc + c, b, 3 * pi / 2, pi, true); + } + } + if (x < this.width - 1 && y > -this.hiddenHeight) { + if (!this.grid[x + 1][y] && !this.grid[x][y - 1]) { + lineCtx.clearRect(x * c + c - b, y * c - hhc, b, b); + lineCtx.fillRect(x * c + c - b, y * c - hhc, b, b); + } else if (!this.grid[x + 1][y - 1] && this.grid[x + 1][y] && this.grid[x][y - 1]) { + lineCtx.moveTo(x * c + c - b, y * c - hhc); + lineCtx.lineTo(x * c + c, y * c - hhc); + lineCtx.lineTo(x * c + c, y * c - hhc + b); + lineCtx.arc(x * c + c, y * c - hhc, b, pi / 2, pi, false); + } + } + if (x > 0 && y < this.height - 1) { + if (!this.grid[x - 1][y] && !this.grid[x][y + 1]) { + lineCtx.clearRect(x * c, y * c - hhc + c - b, b, b); + lineCtx.fillRect(x * c, y * c - hhc + c - b, b, b); + } else if (!this.grid[x - 1][y + 1] && this.grid[x - 1][y] && this.grid[x][y + 1]) { + lineCtx.moveTo(x * c, y * c - hhc + c - b); + lineCtx.lineTo(x * c, y * c - hhc + c); + lineCtx.lineTo(x * c + b, y * c - hhc + c); + lineCtx.arc(x * c, y * c - hhc + c, b, pi * 2, 3 * pi / 2, true); + } + } + if (x > 0 && y > -this.hiddenHeight) { + if (!this.grid[x - 1][y] && !this.grid[x][y - 1]) { + lineCtx.clearRect(x * c, y * c - hhc, b, b); + lineCtx.fillRect(x * c, y * c - hhc, b, b); + } else if (!this.grid[x - 1][y - 1] && this.grid[x - 1][y] && this.grid[x][y - 1]) { + lineCtx.moveTo(x * c + b, y * c - hhc); + lineCtx.lineTo(x * c, y * c - hhc); + lineCtx.lineTo(x * c, y * c - hhc + b); + lineCtx.arc(x * c, y * c - hhc, b, pi / 2, pi * 2, true); + } + } + } + } + } + lineCtx.fill(); + stackCtx.globalCompositeOperation = 'source-over'; + stackCtx.drawImage(lineCanvas, 0, 0); + } + + statisticsStack(); + + this.dirty = false; +} +var stack = new Stack(); diff --git a/js-test-app/public/style.css b/js-test-app/public/style.css new file mode 100755 index 0000000..21f3371 --- /dev/null +++ b/js-test-app/public/style.css @@ -0,0 +1,518 @@ +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: local('Material Icons'), local('MaterialIcons-Regular'), url(MaterialIcons-Regular.woff) format('woff'); +} + +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + src: local('Roboto'), local('Roboto-Regular'), url(roboto-regular.woff2) format('woff2'); +} +/* latin */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + src: local('Roboto Bold'), local('Roboto-Bold'), url(roboto-bold.woff2) format('woff2'); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + -webkit-font-feature-settings: 'liga'; + -webkit-font-smoothing: antialiased; +} +i.material-icons { + font-family: 'Material Icons'; + font-size: 1em; + direction: ltr; + display: inline-block; + padding-top: .25rem; + vertical-align: top; + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + /* Support for IE. */ + font-feature-settings: 'liga'; + pointer-events: none; +} +::-webkit-scrollbar { + width: .6rem; + height: 10px; +} +::-webkit-scrollbar-button { + width: 0px; + height: 0px; +} +::-webkit-scrollbar-thumb { + background: #e1e1e1; + border: 2px solid #000; + border-radius: 50px; +} +::-webkit-scrollbar-thumb:hover { + background: #ffffff; +} +::-webkit-scrollbar-thumb:active { + background: #bababa; +} +::-webkit-scrollbar-track { + background: transparent; + border: 0px none #ffffff; + border-radius: 50px; +} +::-webkit-scrollbar-track:hover { + background: transparent; +} +::-webkit-scrollbar-track:active { + background: transparent; +} +::-webkit-scrollbar-corner { + background: transparent; +} + +/* General */ +@-ms-viewport { + user-zoom: fixed; +} +html { + font-size: 1em; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +body { + font-size: 1em; + margin: 0; + background: #000; + background-position: center center; + background-repeat: no-repeat; + background-size: cover; + /*background-image: url('bg.jpg');*/ + background-attachment: fixed; + -webkit-font-smoothing: antialiased; + color: #FFF; + -webkit-touch-callout: none; +} +body, span, a, p, div, h2, h3, h4, h5, h6 { + font-family: 'Roboto', 'Trebuchet MS', 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif; +} + +span.rank-score { + color: #ffabab; +} + +div.has-replay:hover span { + text-decoration: underline; + cursor: pointer; +} + +a.btn i { + float: left; +} + +a.btn.btn-inline i { + float: none; +} + +#stats tbody tr:not(:last-of-type) { + margin: 1rem 0; +} +#stats tbody tr th:not(:first-child) { + font-size: 1.3rem; +} + +a.link { + text-decoration: underline; +} +a.btn { + display: block; + padding: 0.22rem 0.6rem 0.4rem; + margin: .7rem auto; + width: 75%; + height: 2rem; + box-shadow: 0 0 0 .1rem #999, 0 0 .3rem .2rem rgba(0, 0, 0, .5); + border-radius: 1px; + font-size: 1rem; + font-weight: bold; + line-height: 1.6rem; + color: #FFF; + background-color: #000; + cursor: pointer; + transition: background-color .5s ease; + box-sizing: border-box; +} +a.btn:hover { + background-color: #222; + box-shadow: 0 0 0 .1rem #fff; + transition: background-color .1s ease; +} +a.btn.highlight { + // box-shadow: 0 0 1rem #fff; + // background: #444; + box-shadow: 0 0 0 0.2rem #3fc5ff, 0 0 1rem #21a3d8; +} +a.btn.btn-inline { + display: inline-block; + margin: 0; +} +a.btn.btn-inline:not(:first-child) { + margin-left: .7rem; +} +div.btn-line { + margin: .7rem auto; +} +div.btn-container { + margin: 1rem auto; +} +div.btn-container.btn-container-bottom { + position: absolute; + width: 100%; + bottom: 0; +} +#msg { + text-shadow: #000 0 0 10px; +} +textarea { + resize: none; +} +textarea:focus { + outline-color: #BFB; +} + +a { + color: #FFF; + text-decoration: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +th { + padding: 0; + font-weight: normal; +} +td { + padding: 0; +} +/* Header */ +h1 { + color: #FFF; + font-size: 2rem; + margin: 0.65em 0; +} +h1 { + color: #FFF; + font-size: 1.5rem; + margin: 0.65em 0; +} +h3 { + margin: 0; + text-transform: uppercase; + font-weight: normal; + text-align: center; +} +nav > ul { + margin: 0; + padding: 0; +} +.button, +nav li { + display: block; +} +/* Content */ +#content { + display: table; + margin: auto; + margin-top: 80px; + background: rgba(0, 0, 0, 0.7); +} +/* Canvases */ +#sprite { + display: none; +} +/*#bg { + position: fixed; + top:0; + left:0; + height:100%; + width:100%; + z-index: -1; + opacity: 0; + transition: opacity .5s ease-in; +}*/ +#bg { + position: fixed; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: -1; +} +#a { + position: relative; + overflow: hidden; + float: left; +} +#b { + box-shadow: 0 0 0 1px #fff, 0 0 20px rgba(255, 255, 255, 0.5); + position: relative; + overflow: hidden; + float: left; + z-index: 3; +} +#c { + padding: 0 0.5em; + position: relative; + overflow: hidden; + float: left; +} +#bgStack { + background: #000; + position: absolute; + z-index: 2; +} +#hold, +#preview, +#stack { + position: absolute; + z-index: 2; +} +#active { + position: absolute; + z-index: 3; +} +#msgdiv { + position: absolute; + text-align: center; + width: 100%; + height: 100%; + line-height: 642px; + z-index: 4; +} +#msg { + font-weight: bold; + font-size: 4rem; + color: #FFF; + margin: 0; + z-index: 4; + display: inline-block; + vertical-align: middle; +} +#d { + float: left; +} +/* Stats */ +#stats { + display: block; + position: absolute; + padding: 0; +} +#stats tbody { + display: block; +} +#stats tr { + display: block; + width: 100%; +} +#stats th { + text-transform: uppercase; + display: block; + width: 100%; + vertical-align: top; +} +#stats td { + font-size: 220%; + font-weight: 900; + text-align: center; + line-height: 1; + display: block; + width: 100%; +} +#level { + font-size: 50%; +} +#time { + text-align: center; + display: block; +} +#time > canvas { + width: 100%; + height: 1.125em; +} +#score { + text-align: center; + font-weight: 900; + font-size: 0.9em; + display: block; +} +/* Menus */ +.menu { + text-align: center; + width: 100%; + height: 100%; + overflow-y: auto; + overflow-x: hidden; + // margin-left: -100%; + position: absolute; + background: rgba(0, 0, 0, 0); + top: 0; + z-index: 50; + opacity: 0; + /*-webkit-transition: opacity .4s ease-in-out; + -moz-transition: opacity .4s ease-in-out; + -o-transition: opacity .4s ease-in-out;*/ + transition: opacity .5s ease, + background .2s ease, + transform .3s ease, + margin .2s ease; + -webkit-transition: opacity .5s ease, + background .2s ease, + transform .3s ease, + margin .2s ease; + transform: translateX(-100%); + -webkit-transform: translateX(-100%); + -webkit-overflow-scrolling: touch; + pointer-events: none; +} +.on { + background: rgba(0, 0, 0, 0.8); + opacity: 1; + transform: translateX(0); + -webkit-transform: translateX(0); + pointer-events: auto; + // margin-left: 0; +} +/* Controls */ +#controls { + margin: 1em auto; +} +#controls th { + font-weight: bold; + text-align: left; + line-height: 1.6; +} +#controls td { + font-weight: bold; + cursor: pointer; + display: block; + margin: 0.2em 0 0.2em 1.5em; + text-align: center; + width: 6em; + line-height: 1.6; + background: #111; + border-radius: 5px; +} +#controls td:hover { + background: #191919; +} +#controls td:active { + background: #1A1A1A; +} +/* Settings */ +.left, +.right { + color: #aaa; + font-weight: normal; + font-style: normal; + cursor: pointer; + -webkit-transition: all .1s ease-out; + transition: all .1s ease-out; +} +.left:hover, +.right:hover { + color: #fff; +} +i.left.material-icons, i.right.material-icons { + pointer-events: all; +} +#settings { + margin: 0 0 1em; +} +#settings > div { + margin: 0.3em 0; +} +#settings b { + display: inline-block; + width: 8.5rem; + font-size: 0.9em; + text-align: left; + font-weight: normal; + vertical-align: middle; +} +#settings span { + text-align: center; + font-size: .9em; + font-weight: bold; + display: inline-block; + width: 4rem; +} +#leader div { + margin: 1em 0; +} +#leader span { + text-align: right; + font-weight: bold; + display: inline-block; + overflow: hidden; +} +#replay #replaydata { + width: 85%; + height: 50%; + font-family: 'Lucida Console', monospace; + font-size: 0.5em; + word-wrap: break-word; + word-break: break-all; + background-color: #000; + color: #BFB; + margin: 1rem auto; + display: block; +} +/* Special Menus */ +#go { + background: none; + // background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 45%, rgba(0, 0, 0, .6) 90%); + transition: opacity .8s ease-out .2s; +} +#go ul { + width: 100%; + position: absolute; + bottom: 1em; +} +#pause { + background: none; + transition: opacity .8s ease-out; +} +#pause ul { + width: 100%; + position: absolute; + bottom: 2em; +} +/* Touch Buttons */ +.touchbutton { + position: absolute; + font-family: Arial, sans-serif; + text-align: center; + color: #FFF; + border-radius: 5px; + opacity: 0; + /* padding: 0.3rem 0.6rem 0.4rem; */ + background: #444; + z-index: 100; +} diff --git a/js-test-app/public/tetris.js b/js-test-app/public/tetris.js new file mode 100755 index 0000000..b33be0e --- /dev/null +++ b/js-test-app/public/tetris.js @@ -0,0 +1,2066 @@ +/* +Author: Simon Laroche +Site: http://simon.lc/ +Demo: http://simon.lc/tetr.js + +Note: Before looking at this code, it would be wise to do a bit of reading about +the game so you know why some things are done a certain way. +*/ +'use strict'; + +// boom. +function ObjectClone(obj) { + var copy = (obj instanceof Array) ? [] : {}; + for (var attr in obj) { + if (!obj.hasOwnProperty(attr)) continue; + copy[attr] = (typeof obj[attr] == "object")?ObjectClone(obj[attr]):obj[attr]; + } + return copy; +} +function $$(id){ + return document.getElementById(id); +} +function $setText(elm,s){ + if(typeof elm.innerText==="string"){ + elm.innerText=s; + }else{ + elm.textContent=s; + } +} + +/** + * Playfield. + */ +var cellSize; +var column; + +/** + * Get html elements. + */ +var msg = $$('msg'); +var stats = $$('stats'); +var statsTime = $$('time'); +var statsLines = $$('line'); +var statsPiece = $$('piece'); +var statsScore = $$('score'); +var statsLevel = $$('level'); + +var h3 = document.getElementsByTagName('h3'); +var set = $$('settings'); +var leaderboard = $$('leaderboard'); +var replaydata = $$('replaydata'); +var hidescroll = $$('hidescroll'); + +// Get canvases and contexts +var holdCanvas = $$('hold'); +var bgStackCanvas = $$('bgStack'); +var stackCanvas = $$('stack'); +var activeCanvas = $$('active'); +var previewCanvas = $$('preview'); +var spriteCanvas = $$('sprite'); + +var timeCanvas = $$('time').childNodes[0]; + +var holdCtx = holdCanvas.getContext('2d'); +var bgStackCtx = bgStackCanvas.getContext('2d'); +var stackCtx = stackCanvas.getContext('2d'); +var activeCtx = activeCanvas.getContext('2d'); +var previewCtx = previewCanvas.getContext('2d'); +var spriteCtx = spriteCanvas.getContext('2d'); + +var timeCtx = timeCanvas.getContext('2d'); + +var touchLeft = $$('touchLeft'); +var touchRight = $$('touchRight'); +var touchDown = $$('touchDown'); +var touchDrop = $$('touchDrop'); +var touchHold = $$('touchHold'); +var touchRotLeft = $$('touchRotLeft'); +var touchRotRight = $$('touchRotRight'); +var touchRot180 = $$('touchRot180'); + +var touchLayout = $$('touchLayout'); + +var touchButtons = [ + touchLeft, touchRight, touchDown, touchDrop, + touchHold, touchRotRight, touchRotLeft, touchRot180 +]; +touchLeft.bindsMemberName = "moveLeft"; +touchRight.bindsMemberName = "moveRight"; +touchDown.bindsMemberName = "moveDown"; +touchDrop.bindsMemberName = "hardDrop"; +touchHold.bindsMemberName = "holdPiece"; +touchRotRight.bindsMemberName = "rotRight"; +touchRotLeft.bindsMemberName = "rotLeft"; +touchRot180.bindsMemberName = "rot180"; + +var nLayouts = 7, currLayout = -2 /* none */; + +/** + * Piece data + */ + +// [r][x][y] +var TetroI = [ + [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], + [[0,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,0,0]], + [[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]], + [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]]]; +var TetroJ = [ + [[2,2,0,0],[0,2,0,0],[0,2,0,0],[0,0,0,0]], + [[0,0,0,0],[2,2,2,0],[2,0,0,0],[0,0,0,0]], + [[0,2,0,0],[0,2,0,0],[0,2,2,0],[0,0,0,0]], + [[0,0,2,0],[2,2,2,0],[0,0,0,0],[0,0,0,0]]]; +var TetroL = [ + [[0,3,0,0],[0,3,0,0],[3,3,0,0],[0,0,0,0]], + [[0,0,0,0],[3,3,3,0],[0,0,3,0],[0,0,0,0]], + [[0,3,3,0],[0,3,0,0],[0,3,0,0],[0,0,0,0]], + [[3,0,0,0],[3,3,3,0],[0,0,0,0],[0,0,0,0]]]; +var TetroO = [ + [[0,0,0,0],[4,4,0,0],[4,4,0,0],[0,0,0,0]], + [[0,0,0,0],[4,4,0,0],[4,4,0,0],[0,0,0,0]], + [[0,0,0,0],[4,4,0,0],[4,4,0,0],[0,0,0,0]], + [[0,0,0,0],[4,4,0,0],[4,4,0,0],[0,0,0,0]]]; +var TetroS = [ + [[0,5,0,0],[5,5,0,0],[5,0,0,0],[0,0,0,0]], + [[0,0,0,0],[5,5,0,0],[0,5,5,0],[0,0,0,0]], + [[0,0,5,0],[0,5,5,0],[0,5,0,0],[0,0,0,0]], + [[5,5,0,0],[0,5,5,0],[0,0,0,0],[0,0,0,0]]]; +var TetroT = [ + [[0,6,0,0],[6,6,0,0],[0,6,0,0],[0,0,0,0]], + [[0,0,0,0],[6,6,6,0],[0,6,0,0],[0,0,0,0]], + [[0,6,0,0],[0,6,6,0],[0,6,0,0],[0,0,0,0]], + [[0,6,0,0],[6,6,6,0],[0,0,0,0],[0,0,0,0]]]; +var TetroZ = [ + [[7,0,0,0],[7,7,0,0],[0,7,0,0],[0,0,0,0]], + [[0,0,0,0],[0,7,7,0],[7,7,0,0],[0,0,0,0]], + [[0,7,0,0],[0,7,7,0],[0,0,7,0],[0,0,0,0]], + [[0,7,7,0],[7,7,0,0],[0,0,0,0],[0,0,0,0]]]; +// [r][MINX MINY MAXX MAXY] +var RectI = [[0,1,4,2],[2,0,3,4],[0,2,4,3],[1,0,2,4]]; +var RectJ = [[0,0,3,2],[1,0,3,3],[0,1,3,3],[0,0,2,3]]; +var RectL = [[0,0,3,2],[1,0,3,3],[0,1,3,3],[0,0,2,3]]; +var RectO = [[1,0,3,2],[1,0,3,2],[1,0,3,2],[1,0,3,2]]; +var RectS = [[0,0,3,2],[1,0,3,3],[0,1,3,3],[0,0,2,3]]; +var RectT = [[0,0,3,2],[1,0,3,3],[0,1,3,3],[0,0,2,3]]; +var RectZ = [[0,0,3,2],[1,0,3,3],[0,1,3,3],[0,0,2,3]]; + +var WKTableSRSI_R = [ + [[ 0, 0],[-2, 0],[+1, 0],[-2,+1],[+1,-2]], + [[ 0, 0],[-1, 0],[+2, 0],[-1,-2],[+2,+1]], + [[ 0, 0],[+2, 0],[-1, 0],[+2,-1],[-1,+2]], + [[ 0, 0],[+1, 0],[-2, 0],[+1,+2],[-2,-1]]]; +var WKTableSRSI_L = [ + [[ 0, 0],[-1, 0],[+2, 0],[-1,-2],[+2,+1]], + [[ 0, 0],[+2, 0],[-1, 0],[+2,-1],[-1,+2]], + [[ 0, 0],[+1, 0],[-2, 0],[+1,+2],[-2,-1]], + [[ 0, 0],[-2, 0],[+1, 0],[-2,+1],[+1,-2]]]; +var WKTableSRSI_2 = [ + [[ 0, 0],[-1, 0],[-2, 0],[+1, 0],[+2, 0],[ 0,+1]], + [[ 0, 0],[ 0,+1],[ 0,+2],[ 0,-1],[ 0,-2],[-1, 0]], + [[ 0, 0],[+1, 0],[+2, 0],[-1, 0],[-2, 0],[ 0,-1]], + [[ 0, 0],[ 0,+1],[ 0,+2],[ 0,-1],[ 0,-2],[+1, 0]]]; +var WKTableSRSX_R = [ + [[ 0, 0],[-1, 0],[-1,-1],[ 0,+2],[-1,+2]], + [[ 0, 0],[+1, 0],[+1,+1],[ 0,-2],[+1,-2]], + [[ 0, 0],[+1, 0],[+1,-1],[ 0,+2],[+1,+2]], + [[ 0, 0],[-1, 0],[-1,+1],[ 0,-2],[-1,-2]]]; +var WKTableSRSX_L = [ + [[ 0, 0],[+1, 0],[+1,-1],[ 0,+2],[+1,+2]], + [[ 0, 0],[+1, 0],[+1,+1],[ 0,-2],[+1,-2]], + [[ 0, 0],[-1, 0],[-1,-1],[ 0,+2],[-1,+2]], + [[ 0, 0],[-1, 0],[-1,+1],[ 0,-2],[-1,-2]]]; +var WKTableSRSX_2 = [ + [[ 0, 0],[+1, 0],[+2, 0],[+1,+1],[+2,+1],[-1, 0],[-2, 0],[-1,+1],[-2,+1],[ 0,-1],[+3, 0],[-3, 0]], + [[ 0, 0],[ 0,+1],[ 0,+2],[-1,+1],[-1,+2],[ 0,-1],[ 0,-2],[-1,-1],[-1,-2],[+1, 0],[ 0,+3],[ 0,-3]], + [[ 0, 0],[-1, 0],[-2, 0],[-1,-1],[-2,-1],[+1, 0],[+2, 0],[+1,-1],[+2,-1],[ 0,+1],[-3, 0],[+3, 0]], + [[ 0, 0],[ 0,+1],[ 0,+2],[+1,+1],[+1,+2],[ 0,-1],[ 0,-2],[+1,-1],[+1,-2],[-1, 0],[ 0,+3],[ 0,-3]]]; +var WKTableSRSI = [WKTableSRSI_R,WKTableSRSI_L,WKTableSRSI_2]; +var WKTableSRSX = [WKTableSRSX_R,WKTableSRSX_L,WKTableSRSX_2]; +var WKTableSRS = [WKTableSRSI,WKTableSRSX,WKTableSRSX,WKTableSRSX,WKTableSRSX,WKTableSRSX,WKTableSRSX]; + +var WKTableCultris = [[ 0, 0],[-1, 0],[+1, 0],[ 0,+1],[-1,+1],[+1,+1],[-2, 0],[+2, 0],[ 0,-1]]; + +var WKTableDTET_R = [[ 0, 0],[+1, 0],[-1, 0],[ 0,+1],[+1,+1],[-1,+1],[ 0,-1]]; +var WKTableDTET_L = [[ 0, 0],[-1, 0],[+1, 0],[ 0,+1],[-1,+1],[+1,+1],[ 0,-1]]; +var WKTableDTET = [WKTableDTET_R,WKTableDTET_L,WKTableDTET_L]; + +var WKTableDX_R = [[[0, 0], [-1, -1]], [[0, 0], [+1, -1]], [[0, 0], [+1, +1]], [[0, 0], [-1, +1]]]; +var WKTableDX_L = [[[0, 0], [+1, -1]], [[0, 0], [+1, +1]], [[0, 0], [-1, +1]], [[0, 0], [-1, -1]]]; +var WKTableDX_2 = [[[0, 0], [ 0, -2]], [[0, 0], [-2, 0]], [[0, 0], [ 0, +2]], [[0, 0], [+2, 0]]]; +var WKTableDX = [WKTableDX_R,WKTableDX_L,WKTableDX_2]; + +var OffsetSRS = [ + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]]]; +var OffsetARS = [ + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]], + [[ 0,+1],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+1],[-1, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0, 0],[ 0, 0],[+1, 0]]]; +var OffsetDTET = [ + [[ 0,+1],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+2],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+2],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+2],[ 0,+2],[ 0,+2],[ 0,+2]], + [[ 0,+2],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+2],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+2],[ 0,+1],[ 0,+1],[ 0,+1]]]; +var OffsetQQ = [ + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]]]; +var OffsetAtari = [ + [[ 0,-1],[-1, 0],[ 0,-2],[ 0, 0]], + [[ 0, 0],[-1, 0],[ 0,-1],[ 0, 0]], + [[ 0, 0],[-1, 0],[ 0,-1],[ 0, 0]], + [[-1, 0],[-1, 0],[-1, 0],[-1, 0]], + [[ 0, 0],[-1, 0],[ 0,-1],[ 0, 0]], + [[ 0, 0],[-1, 0],[ 0,-1],[ 0, 0]], + [[ 0, 0],[-1, 0],[ 0,-1],[ 0, 0]]]; +var OffsetNBlox = [ + [[ 0, 0],[-1, 0],[ 0,-1],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+1],[ 0, 0],[ 0, 0],[+1, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0, 0],[ 0, 0],[+1, 0]]]; +var OffsetNintendo = [ + [[ 0,+1],[ 0, 0],[ 0, 0],[+1, 0]], + [[+1,+1],[+1,+1],[+1,+1],[+1,+1]], + [[+1,+1],[+1,+1],[+1,+1],[+1,+1]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[+1,+2],[+1,+1],[+1,+1],[+2,+1]], + [[+1,+1],[+1,+1],[+1,+1],[+1,+1]], + [[+1,+2],[+1,+1],[+1,+1],[+2,+1]]]; +var OffsetMS = [ + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[+1,+1],[ 0,+1],[+1, 0],[+1,+1]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[+1,+1],[ 0,+1],[+1, 0],[+1,+1]]]; +var OffsetE60 = [ + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[+1,+1],[+1, 0],[+1, 0],[+2, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[+1,+1],[+1, 0],[+1, 0],[+2, 0]]]; +var OffsetJJSRS = [ + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]], + [[+1, 0],[+1, 0],[+1, 0],[+1, 0]]]; +var Offset5000 = [ + [[ 0,+1],[-1, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]], + [[ 0,+1],[-1, 0],[ 0,-1],[+1, 0]], + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]]]; +var OffsetPlus = [ + [[ 0, 0],[ 0, 0],[ 0,-1],[+1, 0]], + [[+1,+1],[+1, 0],[+1, 0],[+1, 0]], + [[+1,+1],[+1, 0],[+1, 0],[+1, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[+1,+1],[ 0, 0],[+1, 0],[+1, 0]], + [[+1,+1],[+1, 0],[+1, 0],[+1, 0]], + [[+1,+1],[+1, 0],[+1, 0],[+2, 0]]]; +var OffsetDX = [ + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]]]; +var OffsetNintendoL = [ + [[ 0,+1],[-1, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+2],[-1,+1],[ 0,+1],[ 0,+1]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+2],[-1,+1],[ 0,+1],[ 0,+1]]]; +var OffsetQuadra = [ + [[ 0, 0],[-1, 0],[ 0,-1],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[ 0,+1],[ 0,+1],[ 0,+1]], + [[ 0,+1],[-1, 0],[ 0, 0],[ 0, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[ 0,+1],[-1, 0],[ 0, 0],[ 0, 0]]]; +var OffsetMybo = [ + [[ 0,+2],[-1, 0],[ 0,+1],[ 0, 0]], + [[+1,+2],[ 0,+1],[+1,+1],[+1,+1]], + [[+1,+2],[ 0,+1],[+1,+1],[+1,+1]], + [[ 0,+2],[ 0,+2],[ 0,+2],[ 0,+2]], + [[+1,+2],[+1,+1],[+1,+1],[+2,+1]], + [[+1,+2],[+1,+1],[+1,+1],[+1,+1]], + [[+1,+2],[+1,+1],[+1,+1],[+2,+1]]]; +var OffsetTNET = [ + [[ 0,-1],[ 0, 0],[ 0,-2],[+1, 0]], + [[+1, 0],[ 0, 0],[+1,-1],[+1, 0]], + [[+1, 0],[ 0, 0],[+1,-1],[+1, 0]], + [[ 0, 0],[ 0, 0],[ 0, 0],[ 0, 0]], + [[+1, 0],[ 0, 0],[+1,-1],[+1, 0]], + [[+1, 0],[ 0, 0],[+1,-1],[+1, 0]], + [[+1, 0],[ 0, 0],[+1,-1],[+1, 0]]]; + +//x, y, r +var InitInfoSRS = [[ 0, 0, 0],[ 0, 0, 0],[ 0, 0, 0],[ 0, 0, 0],[ 0, 0, 0],[ 0, 0, 0],[ 0, 0, 0]]; +var InitInfoARS = [[ 0, 0, 0],[ 0, 0, 2],[ 0, 0, 2],[ 0,+1, 0],[ 0,+1, 0],[ 0, 0, 2],[ 0,+1, 0]]; +var InitInfoDTET = [[ 0, 0, 0],[ 0, 0, 2],[ 0, 0, 2],[ 0,+1, 0],[ 0,+1, 0],[ 0, 0, 2],[ 0,+1, 0]]; +var InitInfoQQ = [[ 0, 0, 0],[ 0, 0, 1],[ 0, 0, 3],[ 0, 0, 0],[ 0, 0, 0],[ 0, 0, 2],[ 0, 0, 0]]; +var InitInfoAtari = [[+1, 0, 0],[+1, 0, 2],[+1, 0, 2],[ 0,+1, 0],[+1,+1, 0],[+1, 0, 2],[+1,+1, 0]]; +var InitInfoNBlox = [[ 0, 0, 0],[ 0, 0, 2],[ 0, 0, 2],[ 0,+1, 0],[ 0,+1, 0],[ 0, 0, 2],[ 0,+1, 0]]; +var InitInfoNintendo = [[ 0, 0, 0],[+1, 0, 2],[+1, 0, 2],[ 0,+1, 0],[+1,+1, 0],[+1, 0, 2],[+1,+1, 0]]; +var InitInfoMS = [[ 0, 0, 0],[+1, 0, 2],[+1, 0, 2],[ 0,+1, 0],[+1,+1, 0],[+1, 0, 2],[+1,+1, 0]]; +var InitInfoE60 = [[ 0, 0, 0],[+1, 0, 2],[+1, 0, 2],[ 0,+1, 0],[+1,+1, 0],[+1, 0, 2],[+1,+1, 0]]; +var InitInfoJJSRS = [[ 0, 0, 0],[+1, 0, 0],[+1, 0, 0],[ 0, 0, 0],[+1, 0, 0],[+1, 0, 0],[+1, 0, 0]]; +var InitInfo5000 = [[ 0, 0, 3],[ 0, 0, 1],[+1, 0, 3],[ 0, 0, 0],[ 0, 0, 0],[ 0, -1, 2],[ 0, 0, 0]]; +var InitInfoPlus = [[ 0, 0, 0],[+1, 0, 2],[+1, 0, 2],[ 0,+1, 0],[+1,+1, 0],[+1, 0, 2],[+1,+1, 0]]; +var InitInfoDX = [[ 0, 0, 0],[ 0, 0, 2],[ 0, 0, 2],[ 0,+1, 0],[ 0,+1, 0],[ 0, 0, 2],[ 0,+1, 0]]; +var InitInfoNintendoL = [[ 0, 0, 0],[ 0, 0, 2],[ 0, 0, 2],[ 0,+1, 0],[ 0,+1, 0],[ 0, 0, 2],[ 0,+1, 0]]; +var InitInfoQuadra = [[ 0, 0, 0],[ 0, 0, 2],[ 0, 0, 2],[ 0,+1, 0],[ 0,+1, 0],[ 0, 0, 2],[ 0,+1, 0]]; +var InitInfoMybo = [[ 0,+1, 0],[+1, 0, 2],[+1, 0, 2],[ 0,+1, 0],[+1,+1, 0],[+1, 0, 2],[+1,+1, 0]]; +var InitInfoTNET = [[ 0, 0, 0],[+1, 0, 2],[+1, 0, 2],[ 0,+1, 0],[+1,+1, 0],[+1, 0, 2],[+1,+1, 0]]; + +var ColorSRS = [1, 2, 3, 4, 5, 6, 7]; +var ColorSega = [7, 2, 3, 4, 6, 1, 5]; +var ColorQQ = [7, 1, 3, 4, 5, 6, 2]; +var ColorTengen = [7, 3, 6, 2, 5, 4, 1]; +var ColorAtari = [7, 4, 6, 2, 1, 5, 3]; +var ColorNBlox = [3, 6, 2, 7, 1, 4, 5]; +var ColorC2 = [5, 2, 6, 4, 1, 7, 9]; +var ColorNintendo = [9, 2, 7, 9, 2, 9, 7]; +var ColorMS = [7, 6, 4, 1, 2, 8, 5]; +var ColorE60 = [5, 5, 5, 5, 5, 5, 5]; +var ColorIBM = [7, 9, 6, 2, 5, 3, 1]; +var ColorJJSRS = [5, 1, 3, 4, 7, 6, 2]; +var Color5000 = [7, 6, 8, 4, 5, 1, 2]; +var ColorDX = [9, 7, 2, 4, 3, 5, 6]; +var ColorMybo = [5, 6, 7, 4, 3, 2, 1]; +var ColorQuadra = [5, 4, 6, 3, 1, 2, 7]; +var ColorGameBoy = [9, 2, 7, 8, 7, 9, 2]; +var ColorTNET = [5, 5, 6, 4, 2, 4, 7]; + +var RotSys = [ + { + initinfo: InitInfoSRS, + offset: OffsetSRS, + color: ColorSRS, + }, + { + initinfo: InitInfoSRS, + offset: OffsetSRS, + color: ColorC2, + }, + { + initinfo: InitInfoARS, + offset: OffsetARS, + color: ColorSega, + }, + { + initinfo: InitInfoDTET, + offset: OffsetDTET, + color: ColorSega, + }, + { + initinfo: InitInfoQQ, + offset: OffsetQQ, + color: ColorQQ, + }, + { + initinfo: InitInfoAtari, + offset: OffsetAtari, + color: ColorAtari, + }, + { + initinfo: InitInfoAtari, + offset: OffsetAtari, + color: ColorTengen, + }, + { + initinfo: InitInfoNBlox, + offset: OffsetNBlox, + color: ColorNBlox, + }, + { + initinfo: InitInfoNintendo, + offset: OffsetNintendo, + color: ColorNintendo, + }, + { + initinfo: InitInfoMS, + offset: OffsetMS, + color: ColorMS, + }, + { + initinfo: InitInfoE60, + offset: OffsetE60, + color: ColorE60, + }, + { + initinfo: InitInfoE60, + offset: OffsetE60, + color: ColorIBM, + }, + { + initinfo: InitInfoJJSRS, + offset: OffsetJJSRS, + color: ColorJJSRS, + }, + { + initinfo: InitInfo5000, + offset: Offset5000, + color: Color5000, + }, + { + initinfo: InitInfoPlus, + offset: OffsetPlus, + color: ColorSega, + }, + { + initinfo: InitInfoDX, + offset: OffsetDX, + color: ColorDX, + }, + { + initinfo: InitInfoNintendoL, + offset: OffsetNintendoL, + color: ColorGameBoy, + }, + { + initinfo: InitInfoQuadra, + offset: OffsetQuadra, + color: ColorQuadra, + }, + { + initinfo: InitInfoMybo, + offset: OffsetMybo, + color: ColorMybo, + }, + { + initinfo: InitInfoTNET, + offset: OffsetTNET, + color: ColorTNET, + }, +]; + +// Define shapes and spawns. +var PieceI = { + index: 0, + tetro: TetroI, + rect: RectI +}; +var PieceJ = { + index: 1, + tetro: TetroJ, + rect: RectJ +}; +var PieceL = { + index: 2, + tetro: TetroL, + rect: RectL +}; +var PieceO = { + index: 3, + tetro: TetroO, + rect: RectO +}; +var PieceS = { + index: 4, + tetro: TetroS, + rect: RectS +}; +var PieceT = { + index: 5, + tetro: TetroT, + rect: RectT +}; +var PieceZ = { + index: 6, + tetro: TetroZ, + rect: RectZ +}; + +var pieces = [PieceI, PieceJ, PieceL, PieceO, PieceS, PieceT, PieceZ]; + +// Finesse data +// index x orientatio x column = finesse +// finesse[0][0][4] = 1 +// TODO double check these. +var finesse = [ + [ + [1, 2, 1, 0, 1, 2, 1], + [2, 2, 2, 2, 1, 1, 2, 2, 2, 2], + [1, 2, 1, 0, 1, 2, 1], + [2, 2, 2, 2, 1, 1, 2, 2, 2, 2] + ], + [ + [1, 2, 1, 0, 1, 2, 2, 1], + [2, 2, 3, 2, 1, 2, 3, 3, 2], + [2, 3, 2, 1, 2, 3, 3, 2], + [2, 3, 2, 1, 2, 3, 3, 2, 2] + ], + [ + [1, 2, 1, 0, 1, 2, 2, 1], + [2, 2, 3, 2, 1, 2, 3, 3, 2], + [2, 3, 2, 1, 2, 3, 3, 2], + [2, 3, 2, 1, 2, 3, 3, 2, 2] + ], + [ + [1, 2, 2, 1, 0, 1, 2, 2, 1], + [1, 2, 2, 1, 0, 1, 2, 2, 1], + [1, 2, 2, 1, 0, 1, 2, 2, 1], + [1, 2, 2, 1, 0, 1, 2, 2, 1] + ], + [ + [1, 2, 1, 0, 1, 2, 2, 1], + [2, 2, 2, 1, 1, 2, 3, 2, 2], + [1, 2, 1, 0, 1, 2, 2, 1], + [2, 2, 2, 1, 1, 2, 3, 2, 2] + ], + [ + [1, 2, 1, 0, 1, 2, 2, 1], + [2, 2, 3, 2, 1, 2, 3, 3, 2], + [2, 3, 2, 1, 2, 3, 3, 2], + [2, 3, 2, 1, 2, 3, 3, 2, 2] + ], + [ + [1, 2, 1, 0, 1, 2, 2, 1], + [2, 2, 2, 1, 1, 2, 3, 2, 2], + [1, 2, 1, 0, 1, 2, 2, 1], + [2, 2, 2, 1, 1, 2, 3, 2, 2] + ] +]; + +/** + * Gameplay specific vars. + */ +var gravityUnit = 1.0/64; +var gravity; + +var gravityArr = (function() { + var array = []; + array.push(0); + for (var i = 1; i < 64; i++) array.push(i / 64); + for (var i = 1; i <= 20; i++) array.push(i); + return array; +})(); + +var lockDelayLimit = void 0; + +var mySettings = { + DAS: 9, + ARR: 1, + Gravity: 0, + 'Soft Drop': 6, + 'Lock Delay': 30, + RotSys: 0, + Next: 6, + Size: 2, + Sound: 0, + Volume: 50, + Block: 2, + Ghost: 0, + Grid: 0, + Outline: 1, + DASCut: 0, + NextSide: 0 +}; + +var settings = mySettings; // initialized by reference; replaced when game starts and replay + +var settingName = { + DAS: "DAS", + ARR: "ARR", + Gravity: "Gravity", + 'Soft Drop': "Soft Drop", + 'Lock Delay': "Lock Delay", + RotSys: "Rotation", + Next: "Next", + Size: "Size", + Sound: "On", + Volume: "Volume", + Block: "Block", + Ghost: "Ghost", + Grid: "Grid", + Outline: "Outline", + DASCut: "DAS Cut", + NextSide: "Next Side" +}; +var setting = { + DAS: range(0,31), + ARR: range(0,11), + Gravity: (function() { + var array = []; + array.push('Auto'); + array.push('0G'); + for (var i = 1; i < 64; i*=2) + array.push('1/'+(64/i)+'G'); + for (var i = 1; i <= 20; i+=19) + array.push(i + 'G'); + return array; + })(), + 'Soft Drop': (function() { + var array = []; + for (var i = 1; i < 64; i*=2) + array.push('1/'+(64/i)+'G'); + for (var i = 1; i <= 20; i+=19) + array.push(i + 'G'); + return array; + })(), + 'Lock Delay': range(0, 101), + RotSys: ['Super', 'C2', 'Arika*', 'DTET', 'QQ', 'Atari', 'Tengen', 'N-Blox', 'Nintendo', 'MS', 'E-60', 'IBM PC', 'JJ', '5000', 'Plus', 'DX', 'GameBoy', 'Quadra', 'Mybo', 'TNET'], + Next: ['-', '1', '2', '3', '4', '5', '6'], + Size: ['Auto', 'Small', 'Medium', 'Large', 'Larger'], + Sound: ['Off', 'On'], + Volume: range(0, 101), + Block: ['Shaded', 'Solid', 'Glossy', 'Arika', 'World'], + Ghost: ['Normal', 'Colored', 'Off', 'Hidden'], + Grid: ['Off', 'On'], + Outline: ['Off', 'On', 'Hidden', 'Only'], + DASCut: ['Off', 'On'], + NextSide: ['Right', 'Left'] +}; +var arrRowGen = { + 'simple': + function(arr,offset,range,width) { + var holex = ~~(rng.next()*range)+offset; + for(var x = 0; x < width; x++){ + arr[holex + x] = 0; + } + }, + 'simplemessy': + function(arr,ratio) { + var hashole = false; + for(var x = 0; x < stack.width; x++){ + if(rng.next()>=ratio) { + hashole=true; + arr[x] = 0; + } + } + if(hashole===false){ + arr[~~(rng.next()*10)] = 0; + } + }, +}; + +var arrStages = [ + {begin: 0, delay: 60*5, gen:function(arr){arrRowGen.simple(arr,0,7,4)}}, + {begin: 5, delay: 60*7, gen:function(arr){arrRowGen.simple(arr,0,7,4)}}, + {begin: 20, delay: 60*5, gen:function(arr){arrRowGen.simple(arr,0,7,4)}}, + {begin: 40, delay: 60*4, gen:function(arr){arrRowGen.simple(arr,2,3,4)}}, + {begin: 50, delay: 60*2, gen:function(arr){arrRowGen.simple(arr,4,1,2)}}, + {begin: 70, delay: 60*5, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + {begin: 80, delay: 60*4, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + {begin: 90, delay: 60*3, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + + {begin: 100, delay: 60*4, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin: 120, delay: 60*3.5, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin: 150, delay: 60*4, gen:function(arr){arrRowGen.simple(arr,0,7,4)}}, + {begin: 170, delay: 60*3.5, gen:function(arr){arrRowGen.simple(arr,0,7,4)}}, + + {begin: 200, delay: 60*3.5, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin: 220, delay: 60*3, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin: 250, delay: 60*2.5, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + + {begin: 300, delay: 60*3.5, gen:function(arr){arrRowGen.simplemessy(arr,0.9)}}, + {begin: 320, delay: 60*3, gen:function(arr){arrRowGen.simplemessy(arr,0.9)}}, + {begin: 350, delay: 60*3.5, gen:function(arr){arrRowGen.simplemessy(arr,0.8)}}, + {begin: 390, delay: 60*3, gen:function(arr){arrRowGen.simplemessy(arr,0.8)}}, + {begin: 400, delay: 60*4, gen:function(arr){arrRowGen.simplemessy(arr,0.6)}}, + {begin: 430, delay: 60*5, gen:function(arr){arrRowGen.simplemessy(arr,0.4)}}, + {begin: 450, delay: 60*7, gen:function(arr){arrRowGen.simplemessy(arr,0.1)}}, + + {begin: 470, delay: 60*7, gen:function(arr){arrRowGen.simplemessy(arr,0.4)}}, + {begin: 500, delay: 60*3, gen:function(arr){arrRowGen.simplemessy(arr,0.8)}}, + {begin: 550, delay: 60*2.5, gen:function(arr){arrRowGen.simplemessy(arr,0.8)}}, + {begin: 600, delay: 60*3, gen:function(arr){arrRowGen.simplemessy(arr,0.6)}}, + {begin: 650, delay: 60*2.5, gen:function(arr){arrRowGen.simplemessy(arr,0.6)}}, + {begin: 700, delay: 60*3.5, gen:function(arr){arrRowGen.simplemessy(arr,0.4)}}, + {begin: 750, delay: 60*3, gen:function(arr){arrRowGen.simplemessy(arr,0.4)}}, + {begin: 780, delay: 60*2.5, gen:function(arr){arrRowGen.simplemessy(arr,0.4)}}, + {begin: 800, delay: 60*2, gen:function(arr){arrRowGen.simplemessy(arr,0.9)}}, + {begin: 900, delay: 60*1.75, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin: 950, delay: 60*1.5, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + + {begin:1000, delay: 60*5, gen:function(arr){arrRowGen.simplemessy(arr,0.0)}}, + {begin:1020, delay: 60*4, gen:function(arr){arrRowGen.simplemessy(arr,0.0)}}, + {begin:1050, delay: 60*4, gen:function(arr){arrRowGen.simple(arr,1,1,8)}}, + {begin:1100, delay: 60*3, gen:function(arr){arrRowGen.simple(arr,2,1,6)}}, + {begin:1150, delay: 60*3, gen:function(arr){arrRowGen.simple(arr,3,1,4)}}, + {begin:1200, delay: 60*2, gen:function(arr){arrRowGen.simple(arr,4,1,2)}}, + {begin:1210, delay: 60*1.5, gen:function(arr){arrRowGen.simple(arr,4,1,2)}}, + {begin:1210, delay: 60*1, gen:function(arr){arrRowGen.simple(arr,4,1,2)}}, + {begin:1250, delay: 60*2, gen:function(arr){arrRowGen.simple(arr,9,1,1)}}, + {begin:1260, delay: 60*0.5, gen:function(arr){arrRowGen.simple(arr,9,1,1)}}, + {begin:1300, delay: 60*3, gen:function(arr){arrRowGen.simplemessy(arr,0.0)}}, + {begin:1350, delay: 60*3, gen:function(arr){arrRowGen.simplemessy(arr,0.1)}}, + {begin:1400, delay: 60*4, gen:function(arr){arrRowGen.simplemessy(arr,0.15)}}, + {begin:1450, delay: 60*4, gen:function(arr){arrRowGen.simplemessy(arr,0.2)}}, + {begin:1480, delay: 60*5, gen:function(arr){arrRowGen.simplemessy(arr,0.2)}}, + + {begin:1500, delay: 60*1.5, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + {begin:1550, delay: 60*1.4, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + {begin:1600, delay: 60*1.3, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + {begin:1650, delay: 60*1.2, gen:function(arr){arrRowGen.simple(arr,0,9,2)}}, + {begin:1700, delay: 60*1.3, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:1800, delay: 60*1.2, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:1850, delay: 60*1.15, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:1900, delay: 60*1.1, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:1950, delay: 60*1.05, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + + {begin:2000, delay: 60*1.0, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2050, delay: 60*0.95, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2100, delay: 60*0.9, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2150, delay: 60*0.85, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2180, delay: 60*0.8, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2190, delay: 60*1.0, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2200, delay: 60*0.8, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2300, delay: 60*0.75, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2400, delay: 60*0.7, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2450, delay: 60*0.6, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + {begin:2500, delay: 60*0.5, gen:function(arr){arrRowGen.simple(arr,0,10,1)}}, + +]; + +var sprintRanks= [ + {t:600, b:"Zen"}, + {t:540, b:"9 min...?"}, + {t:480, b:"8 min...?"}, + {t:420, b:"7 min...?"}, + {t:360, b:"6 min...?"}, + {t:300, b:"5 min...?"}, + {t:240, b:"Finally..."}, + {t:210, b:"Too slow."}, + {t:180, b:"Well..."}, + {t:160, b:"Gotta go fast."}, + {t:140, b:"Any more?"}, + {t:120, b:"Beat 2 min."}, + {t:110, b:"So easy."}, + {t:100, b:"New world."}, + {t: 90, b:"1 drop/sec!"}, + {t: 80, b:"Not bad."}, + {t: 73, b:"Going deeper."}, + {t: 69, b:"10 sec faster."}, + {t: 63, b:"Approaching."}, + {t: 60, b:"Almost there!"}, + {t: 56, b:"1-min Sprinter!"}, + {t: 53, b:"No longer rookie."}, + {t: 50, b:"Beat 50."}, + {t: 48, b:"2 drops/sec!"}, + {t: 45, b:"u can tetris."}, + {t: 42, b:"Interesting."}, + {t: 40, b:"So?"}, + {t: 38, b:"Good."}, + {t: 35, b:"Unstoppable."}, + {t: 33, b:"Octopus"}, + {t: 31, b:"3 drops/sec!"}, + {t: 30, b:"Noooo"}, + {t: 29, b:"You win."}, + {t: 27, b:"Magic."}, + {t: 25, b:"Lightning!"}, + {t: 24, b:"4 drops/sec!"}, + {t: 23, b:"Alien."}, + {t: 22, b:"Beats Alien."}, + {t: 21, b:"Save the world?"}, + {t: 20, b:"r u sure?"}, + {t: 19, b:"5pps"}, + {t: 18, b:"..."}, + {t: 16.66, b:"......"}, + {t: 14.28, b:"6pps"}, + {t: 12.50, b:"7pps"}, + {t: 11.11, b:"8pps"}, + {t: 10.00, b:"9pps"}, + {t: 9.00, b:"10pps"}, + {t: 0.00, b:"→_→"}, + {t: -1/0, b:"↓_↓"} +]; + +var frame; +var frameSkipped; + +/** +* for dig challenge mode +*/ + +var frameLastRise; +var frameLastHarddropDown; + +/** +* for dig zen mode +*/ + +var digZenBuffer; +var lastPiecesSet; + +/** +* Pausing variables +*/ + +var startPauseTime; +var pauseTime; + +/** + * 0 = Normal + * 1 = win + * 2 = countdown + * 3 = game not played + * 9 = loss + */ +var gameState = 3; + +var paused = false; +var lineLimit; + +var replay; +var watchingReplay = false; +var toGreyRow; +var gametype; +var gameparams; +//TODO Make dirty flags for each canvas, draw them all at once during frame call. +// var dirtyHold, dirtyActive, dirtyStack, dirtyPreview; +var lastX, lastY, lastPos, lastLockDelay, landed; + +// Scoreing related status +var b2b; +var combo; +var level; +var allclear; + +// Stats +var lines; +var score; +var statsFinesse; +var piecesSet; +var startTime; +var scoreTime; +var scoreStartTime; +var digLines = []; + +// Keys +var keysDown; +var lastKeys; +var released; + +var binds = { + pause: 27, + moveLeft: 37, + moveRight: 39, + moveLeft3: 0, + moveRight3: 0, + moveDown: 40, + hardDrop: 32, + holdPiece: 67, + rotRight: 88, + rotLeft: 90, + rot180: 16, + retry: 82 +}; +var flags = { + hardDrop: 1, + moveRight: 2, + moveLeft: 4, + moveDown: 8, + holdPiece: 16, + rotRight: 32, + rotLeft: 64, + rot180: 128, + moveRight3: 256, + moveLeft3: 512, +}; + +function resize() { + var a = $$('a'); + var b = $$('b'); + var c = $$('c'); + var d = $$('d'); + var content = $$('content'); + + if (settings.NextSide === 1) { + content.innerHTML = ""; + content.appendChild(c); + content.appendChild(b); + content.appendChild(d); + } else { + content.innerHTML = ""; + content.appendChild(d); + content.appendChild(b); + content.appendChild(c); + } + + // TODO Finalize this. + // Aspect ratio: 1.024 + var padH = 12; + var screenHeight = window.innerHeight - padH * 2; + var screenWidth = ~~(screenHeight * 1.0); + if (screenWidth > window.innerWidth) + screenHeight = ~~(window.innerWidth / 1.0); + + cellSize = Math.max(~~(screenHeight / 20), 10); + if (settings.Size === 1 && cellSize >= 16) cellSize = 16; + else if (settings.Size === 2 && cellSize >= 24) cellSize = 24; + else if (settings.Size === 3 && cellSize >= 32) cellSize = 32; + else if (settings.Size === 4 && cellSize >= 48) cellSize = 48; + + var pad = (window.innerHeight - (cellSize * 20 + 2)); + var padFinal = Math.min(pad/2, padH); + //console.log(pad); + content.style.padding = + //"0 0"; + //(pad / 2) + 'px' + ' 0'; + (padFinal) + 'px' + ' 0'; + + stats.style.bottom = + //(pad) + 'px'; + //(pad / 2) + 'px'; + (pad - padFinal) + 'px'; + //(pad - padH) + 'px'; + + // Size elements + a.style.padding = '0 0.5rem ' + ~~(cellSize / 2) + 'px'; + + stackCanvas.width = activeCanvas.width = bgStackCanvas.width = cellSize * 10; + stackCanvas.height = activeCanvas.height = bgStackCanvas.height = cellSize * 20; + b.style.width = stackCanvas.width + 'px'; + b.style.height = stackCanvas.height + 'px'; + + holdCanvas.width = cellSize * 4; + holdCanvas.height = cellSize * 3; + a.style.width = holdCanvas.width + 'px'; + a.style.height = holdCanvas.height + 'px'; + + previewCanvas.width = cellSize * 4; + previewCanvas.height = stackCanvas.height - cellSize * 2; + c.style.width = previewCanvas.width + 'px'; + c.style.height = b.style.height; + + // Scale the text so it fits in the thing. + // TODO get rid of extra font sizes here. + msgdiv.style.lineHeight = b.style.height; + msg.style.fontSize = ~~(stackCanvas.width / 6) + 'px'; + msg.style.lineHeight = msg.style.fontSize; + stats.style.fontSize = ~~(stackCanvas.width / 11) + 'px'; + document.documentElement.style.fontSize = ~~(stackCanvas.width / 16) + 'px'; + + for (var i = 0, len = h3.length; i < len; i++) { + h3[i].style.lineHeight = (cellSize * 2) + 'px'; + h3[i].style.fontSize = stats.style.fontSize; + } + stats.style.width = d.clientWidth + 'px'; + + timeCanvas.width = d.clientWidth; + timeCanvas.height = timeCanvas.clientHeight || timeCanvas.offsetHeight || timeCanvas.getBoundingClientRect().height; + timeCtx.fillStyle = "#fff"; + timeCtx.font = 'bold 1.125em Roboto, "Trebuchet MS"'; + timeCtx.textAlign = "center"; + timeCtx.textBaseline = "middle"; + + touchButtonsLayout(); + + // Redraw graphics + makeSprite(); + + if (settings.Grid === 1) + bg(bgStackCtx); + + //if (gameState === 0) { + try { + piece.draw(); + stack.draw(); + preview.draw(); + if (hold.piece !== void 0) { + hold.draw(); + } + statistics(); + statisticsStack(); + } catch(e) { + } + //} +} +addEventListener('resize', resize, false); +addEventListener('load', resize, false); + +/** + * ========================== Model =========================================== + */ + +/** + * Resets all the settings and starts the game. + */ +function init(gt, params) { + if (gt === 'replay') { + watchingReplay = true; + if(params !== void 0) { + try { + if(typeof params !== "string") + throw "wtf"; + if(params === "" || params.slice(0,1) !=="{") + throw "please paste replay data, correctly..." + replay = JSON.parse(params.replace(/\n/g,"")); + if(typeof replay !== "object") + throw "json parse fail"; + if((replay.gametype === void 0) + || (replay.keys === void 0) + || (replay.settings === void 0) + || (replay.seed === void 0) + ) { + throw "something's missing..."; + } + replay.keys = keysDecode(replay.keys); + if(replay.keys === null) + throw "keys decode fail" + } catch(e) { + alert("invalid replay data...\n" + e.toString()); + return; + } + } + gametype = replay.gametype; + gameparams = replay.gameparams || {}; + settings = replay.settings; // by reference + rng.seed = replay.seed; + } else { + watchingReplay = false; + settings = ObjectClone(mySettings); // by value: prevent from being modified when paused + gametype = gt; + gameparams = params || {}; + + var seed = ~~(Math.random() * 2147483645) + 1; + rng.seed = seed; + + replay = {}; + replay.keys = {}; + // TODO Make new seed and rng method. + replay.seed = seed; + replay.gametype = gametype; + replay.gameparams = gameparams; + replay.settings = settings; + } + + if(gametype === void 0) //sometimes happens..... + gametype = 0; + + if(gametype === 0) // sprint + lineLimit = gameparams.lineLimit || 40; + else if(gametype === 5) // score attack + lineLimit = 200; + else + lineLimit = 0; + + //html5 mobile device sound + if(settings.Sound === 1) + sound.init(); + + //Reset + column = 0; + keysDown = 0; + lastKeys = 0; + released = 255; + //TODO Check if needed. + piece = new Piece(); + + frame = 0; + frameSkipped = 0; + lastPos = 'reset'; + stack.new(10, 20, 4); + toGreyRow = stack.height - 1; + hold.piece = void 0; + if (settings.Gravity === 0) gravity = gravityUnit; + + preview.init() + //preview.draw(); + + b2b = 0; + combo = 0; + level = 0; + allclear = 0; + statsFinesse = 0; + lines = 0; + score = bigInt(0); + piecesSet = 0; + + clear(stackCtx); + clear(activeCtx); + clear(holdCtx); + + digLines = []; + if (gametype === 3) { + frameLastRise = 0; + frameLastHarddropDown = 0; + } + if (gametype === 4) { + // Dig Race + // make ten random numbers, make sure next isn't the same as last? t=rnd()*(size-1);t>=arr[i-1]?t++:; /* farter */ + //TODO make into function or own file. + if (gameparams.digraceType === void 0 || gameparams.digraceType === "checker") { + // harder digrace: checkerboard + digLines = range(stack.height - 10, stack.height); + $setText(statsLines,10); + for (var y = stack.height - 1; y > stack.height - 10 - 1; y--) { + for (var x = 0; x < stack.width; x++) { + if ((x+y)&1) + stack.grid[x][y] = 8; + } + } + } else if(gameparams.digraceType === "easy") { + var begin = ~~(rng.next()*stack.width); + var dire = (~~(rng.next()*2))*2-1; + digLines = range(stack.height - 10, stack.height); + $setText(statsLines,10); + for (var y = stack.height - 1; y > stack.height - 10 - 1; y--) { + for (var x = 0; x < stack.width; x++) { + if ((begin+dire*y+x+stack.width*2)%10 !== 0) + stack.grid[x][y] = 8; + } + } + } + //stack.draw(); //resize + } + if (gametype === 7){ + lastPiecesSet = 0; + digZenBuffer = 0; + } + if (gametype === 1 && gameparams.marathonType === 1){ + if (settings.ARR < 1){ + settings.ARR = 1; + } + if (settings["Soft Drop"] > 6){ + settings["Soft Drop"] = 6; + } + if (settings.Next > 1){ + settings.Next = 1; + } + } + if (gametype === 0){ + // don't care about digLines since it's not dig mode + if (gameparams.lineLimit === 1){ + for (var y = stack.height - 1; y > stack.height - 10 - 1; y--) { + stack.grid[~~(rng.next()*stack.width)][y] = 8; + } + } else if(gameparams.lineLimit === 25){ + for (var y = stack.height - 1; y > stack.height - 10 - 1; y--) { + var pattern = ~~(rng.next() * 1022) + 1; + for (var x = 0; x < stack.width; x++) { + if ((1<= 0; i -= 3) { + strsplit[i] += (spacetoggle === n-1 ?" ":"\xA0"); + spacetoggle = (spacetoggle + 1) % n; + } + return strsplit.join(""); +} + +function updateScoreTime(){ + scoreTime = Date.now() - scoreStartTime - pauseTime; +} + +/** + * Draws the stats next to the tetrion. + */ +function statistics() { + + var time = scoreTime || 0; + var seconds = ((time % 60000) / 1000).toFixed(2); + var minutes = ~~(time / 60000); + var displayTime = + (minutes < 10 ? '0' : '') + minutes + + (seconds < 10 ? ':0' : ':') + seconds; + var fsbl = 30; /* frameskip bar length */ + var pos = frameSkipped % (fsbl*2); + if (frameSkipped < 0 && pos !== 0) { + pos = 60 + pos; // euclid division modulus + } + var skipL = pos, skipR = pos; + skipL = (skipL-fsbl<0)?0:(skipL-fsbl); + skipR = (skipR>fsbl)?fsbl:skipR; + skipL = skipL/fsbl*timeCanvas.width; + skipR = skipR/fsbl*timeCanvas.width; + + timeCtx.clearRect(0, 0, timeCanvas.width, timeCanvas.height); + timeCtx.fillText(displayTime, timeCanvas.width/2, timeCanvas.height/2); + timeCtx.fillRect(skipL,timeCanvas.height-0.4,skipR,timeCanvas.height); +} + +/** + * Draws the stats about the stack next to the tetrion. + */ +// /* farter */ +function statisticsStack() { + $setText(statsPiece, piecesSet); + + if(gametype === 0 || gametype === 5) { + $setText(statsLines, lineLimit - lines); + $setText(statsLevel, ""); + }else if(gametype === 1 || gametype === 6 || gametype === 7){ + $setText(statsLines, lines); + $setText(statsLevel, "Lv. " + level); + }else if (gametype === 3){ + if (gameparams.digOffset || gameparams.digOffset !== 0){ + $setText(statsLevel, gameparams.digOffset + "+"); + }else{ + $setText(statsLevel, ""); + } + $setText(statsLines, lines); + }//else if (gametype === 4){ + // $setText(statsLines, digLines.length); + //} + else{ + $setText(statsLines, lines); + $setText(statsLevel, ""); + } + + var light=['#ffffff','#EFB08C','#EDDD82','#8489C7','#FFDB94','#EFAFC5','#98DF6E','#6FC5C5','#9A7FD1','#78D4A3']; + + statsScore.style.color=(b2b===0?'':light[b2b%10]); + statsScore.style.textShadow=(combo===0?'':('0 0 0.5em '+light[(combo-1)%10])); + $setText(statsScore,scorestring(score.toString(), 2)); +} +// ========================== View ============================================ + +/** + * Draws grid in background. + */ +function bg(ctx) { + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.fillStyle = '#1c1c1c'; + for (var x = -1; x < ctx.canvas.width + 1; x += cellSize) { + ctx.fillRect(x, 0, 2, ctx.canvas.height); + } + for (var y = -1; y < ctx.canvas.height + 1; y += cellSize) { + ctx.fillRect(0, y, ctx.canvas.width, 2); + } +} + +/** + * Draws a pre-rendered mino. + */ +function drawCell(x, y, color, ctx, darkness) { + x = Math.floor(x * cellSize); + y = Math.floor(y * cellSize); + ctx.drawImage(spriteCanvas, color * cellSize, 0, cellSize, cellSize, x, y, cellSize, cellSize); + if (darkness !== void 0) { + //ctx.globalCompositeOperation = 'source-atop'; + ctx.fillStyle = 'rgba(0,0,0,' + darkness + ')'; + ctx.fillRect(x, y, cellSize, cellSize); + //ctx.globalCompositeOperation = 'source-over'; + } +} + +/** + * Pre-renders all mino types in all colors. + */ +function makeSprite() { + var shaded = [ + // 0 +10 -10 -20 + ['#c1c1c1', '#dddddd', '#a6a6a6', '#8b8b8b'], + ['#25bb9b', '#4cd7b6', '#009f81', '#008568'], + ['#3397d9', '#57b1f6', '#007dbd', '#0064a2'], + ['#e67e23', '#ff993f', '#c86400', '#a94b00'], + ['#efc30f', '#ffdf3a', '#d1a800', '#b38e00'], + ['#9ccd38', '#b9e955', '#81b214', '#659700'], + ['#9c5ab8', '#b873d4', '#81409d', '#672782'], + ['#e64b3c', '#ff6853', '#c62c25', '#a70010'], + ['#898989', '#a3a3a3', '#6f6f6f', '#575757'], + ['#c1c1c1', '#dddddd', '#a6a6a6', '#8b8b8b'], + ]; + var glossy = [ + //25 37 52 -21 -45 + ['#ffffff', '#ffffff', '#ffffff', '#888888', '#4d4d4d'], + ['#7bffdf', '#9fffff', '#ccffff', '#008165', '#00442e'], + ['#6cdcff', '#93feff', '#c2ffff', '#00629f', '#002c60'], + ['#ffc166', '#ffe386', '#ffffb0', '#aa4800', '#650500'], + ['#ffff6a', '#ffff8c', '#ffffb8', '#b68a00', '#714f00'], + ['#efff81', '#ffffa2', '#ffffcd', '#6b9200', '#2c5600'], + ['#dc9dfe', '#ffbeff', '#ffe9ff', '#5d287e', '#210043'], + ['#ff9277', '#ffb497', '#ffe0bf', '#a7000a', '#600000'], + ['#cbcbcb', '#ededed', '#ffffff', '#545454', '#1f1f1f'], + ['#ffffff', '#ffffff', '#ffffff', '#888888', '#4d4d4d'], + ]; + var tgm = [ + ['#ababab', '#5a5a5a', '#9b9b9b', '#626262'], + ['#00e8f0', '#0070a0', '#00d0e0', '#0080a8'], + ['#00a8f8', '#0000b0', '#0090e8', '#0020c0'], + ['#f8a800', '#b84000', '#e89800', '#c85800'], + ['#e8e000', '#886800', '#d8c800', '#907800'], + ['#78f800', '#007800', '#58e000', '#008800'], + ['#f828f8', '#780078', '#e020e0', '#880088'], + ['#f08000', '#a00000', '#e86008', '#b00000'], + ['#7b7b7b', '#303030', '#6b6b6b', '#363636'], + ['#ababab', '#5a5a5a', '#9b9b9b', '#626262'], + ]; + + spriteCanvas.width = cellSize * 10; + spriteCanvas.height = cellSize; + for (var i = 0; i < 10; i++) { + var x = i * cellSize; + if (settings.Block === 0) { + // Shaded + spriteCtx.fillStyle = shaded[i][1]; + spriteCtx.fillRect(x, 0, cellSize, cellSize); + + spriteCtx.fillStyle = shaded[i][3]; + spriteCtx.fillRect(x, cellSize / 2, cellSize, cellSize / 2); + + spriteCtx.fillStyle = shaded[i][0]; + spriteCtx.beginPath(); + spriteCtx.moveTo(x, 0); + spriteCtx.lineTo(x + cellSize / 2, cellSize / 2); + spriteCtx.lineTo(x, cellSize); + spriteCtx.fill(); + + spriteCtx.fillStyle = shaded[i][2]; + spriteCtx.beginPath(); + spriteCtx.moveTo(x + cellSize, 0); + spriteCtx.lineTo(x + cellSize / 2, cellSize / 2); + spriteCtx.lineTo(x + cellSize, cellSize); + spriteCtx.fill(); + } else if (settings.Block === 1) { + // Flat + spriteCtx.fillStyle = shaded[i][0]; + spriteCtx.fillRect(x, 0, cellSize, cellSize); + } else if (settings.Block === 2) { + + // Custom [Pure] + var k = Math.max(~~(cellSize * 0.15), 1); + spriteCtx.shadowBlur = 10; + spriteCtx.shadowColor = '#ffffff'; + var grd = spriteCtx.createLinearGradient(0,0, 100,100); + grd.addColorStop(0, '#4da3ff'); + grd.addColorStop(1, '#db87ff'); + + // Fill with gradient + spriteCtx.fillStyle = grd; + + // Set the fill style and draw a rectangle + spriteCtx.fillRect(x + k, k, cellSize - k * 2, cellSize - k * 2); + spriteCtx.strokeStyle = '#ffffff'; + spriteCtx.lineWidth = 2; + spriteCtx.strokeRect(x + k - 1, k - 1, cellSize - k * 2 + 2, cellSize - k * 2 + 2); + + } else if (settings.Block === 3 || settings.Block === 4) { + var k = Math.max(~~(cellSize * 0.125), 1); + + spriteCtx.fillStyle = tgm[i][1]; + spriteCtx.fillRect(x, 0, cellSize, cellSize); + spriteCtx.fillStyle = tgm[i][0]; + spriteCtx.fillRect(x, 0, cellSize, ~~(cellSize / 2)); + + var grad = spriteCtx.createLinearGradient(x, k, x, cellSize - k); + grad.addColorStop(0, tgm[i][2]); + grad.addColorStop(1, tgm[i][3]); + spriteCtx.fillStyle = grad; + spriteCtx.fillRect(x + k, k, cellSize - k*2, cellSize - k*2); + + var grad = spriteCtx.createLinearGradient(x, k, x, cellSize); + grad.addColorStop(0, tgm[i][0]); + grad.addColorStop(1, tgm[i][3]); + spriteCtx.fillStyle = grad; + spriteCtx.fillRect(x, k, k, cellSize - k); + + var grad = spriteCtx.createLinearGradient(x, 0, x, cellSize - k); + grad.addColorStop(0, tgm[i][2]); + grad.addColorStop(1, tgm[i][1]); + spriteCtx.fillStyle = grad; + spriteCtx.fillRect(x + cellSize - k, 0, k, cellSize - k); + } + } +} + +/** + * Clear canvas. + */ +function clear(ctx) { + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); +} + +/** + * Draws a 2d array of minos. + */ +function draw(tetro, cx, cy, ctx, color, darkness) { + for (var x = 0, len = tetro.length; x < len; x++) { + for (var y = 0, wid = tetro[x].length; y < wid; y++) { + if (tetro[x][y]) { + drawCell(x + cx, y + cy, color !== void 0 ? color : tetro[x][y], ctx, darkness); + } + } + } +} + +// [Pure] Draw ghost +function drawGhost(tetro, cx, cy, ctx, color, darkness) { + ctx.strokeStyle = '#ffffff'; + for (var x = 0, len = tetro.length; x < len; x++) { + for (var y = 0, wid = tetro[x].length; y < wid; y++) { + var xGrid = Math.floor((x + cx) * cellSize); + var yGrid = Math.floor((y + cy) * cellSize); + if (tetro[x][y]) { + ctx.shadowBlur = 10; + ctx.shadowColor = '#ffffff'; + + // Draw top lines + if (y > 0) { + if (!tetro[x][y - 1]) { + ctx.strokeRect(xGrid, yGrid, cellSize, 1); + } + } else { + ctx.strokeRect(xGrid, yGrid, cellSize, 1); + } + + // Draw bottom lines + if (y < 3) { + if (!tetro[x][y + 1]) { + ctx.strokeRect(xGrid, yGrid + cellSize, cellSize, 1); + } + } else { + ctx.strokeRect(xGrid, yGrid + cellSize, cellSize, 1); + } + + // Draw left lines + if (x > 0) { + if (!tetro[x - 1][y]) { + ctx.strokeRect(xGrid, yGrid, 1, cellSize); + } + } else { + ctx.strokeRect(xGrid, yGrid, 1, cellSize); + } + + // Draw right lines + if (x < 3) { + if (!tetro[x + 1][y]) { + ctx.strokeRect(xGrid + cellSize, yGrid, 1, cellSize); + } + } else { + ctx.strokeRect(xGrid + cellSize, yGrid, 1, cellSize); + } + } + } + } + ctx.shadowColor = '#00000000'; +} + + +// ========================== Controller ====================================== + +function keyUpDown(e) { + // TODO send to menu or game depending on context. + if ([32,37,38,39,40].indexOf(e.keyCode) !== -1) + e.preventDefault(); + //TODO if active, prevent default for binded keys + //if (bindsArr.indexOf(e.keyCode) !== -1) + // e.preventDefault(); + if (e.type === "keydown" && e.keyCode === binds.pause) { + if (paused) { + unpause(); + } else { + pause(); + } + } + if (e.type === "keydown" && e.keyCode === binds.retry) { + init(gametype,gameparams); + } + if (!watchingReplay) { + if (e.type === "keydown") { + if (e.keyCode === binds.moveLeft) { + keysDown |= flags.moveLeft; + } else if (e.keyCode === binds.moveRight) { + keysDown |= flags.moveRight; + } else if (e.keyCode === binds.moveDown) { + keysDown |= flags.moveDown; + } else if (e.keyCode === binds.hardDrop) { + keysDown |= flags.hardDrop; + } else if (e.keyCode === binds.rotRight) { + keysDown |= flags.rotRight; + } else if (e.keyCode === binds.rotLeft) { + keysDown |= flags.rotLeft; + } else if (e.keyCode === binds.rot180) { + keysDown |= flags.rot180; + } else if (e.keyCode === binds.moveLeft3) { + keysDown |= flags.moveLeft3; + } else if (e.keyCode === binds.moveRight3) { + keysDown |= flags.moveRight3; + } else if (e.keyCode === binds.holdPiece) { + keysDown |= flags.holdPiece; + } + } + else if (e.type === "keyup") + { + if (e.keyCode === binds.moveLeft && keysDown & flags.moveLeft) { + keysDown ^= flags.moveLeft; + } else if (e.keyCode === binds.moveRight && keysDown & flags.moveRight) { + keysDown ^= flags.moveRight; + } else if (e.keyCode === binds.moveDown && keysDown & flags.moveDown) { + keysDown ^= flags.moveDown; + } else if (e.keyCode === binds.hardDrop && keysDown & flags.hardDrop) { + keysDown ^= flags.hardDrop; + } else if (e.keyCode === binds.rotRight && keysDown & flags.rotRight) { + keysDown ^= flags.rotRight; + } else if (e.keyCode === binds.rotLeft && keysDown & flags.rotLeft) { + keysDown ^= flags.rotLeft; + } else if (e.keyCode === binds.rot180 && keysDown & flags.rot180) { + keysDown ^= flags.rot180; + } else if (e.keyCode === binds.moveLeft3 && keysDown & flags.moveLeft3) { + keysDown ^= flags.moveLeft3; + } else if (e.keyCode === binds.moveRight3 && keysDown & flags.moveRight3) { + keysDown ^= flags.moveRight3; + } else if (e.keyCode === binds.holdPiece && keysDown & flags.holdPiece) { + keysDown ^= flags.holdPiece; + } + } + } +} +addEventListener('keydown', keyUpDown, false); +addEventListener('keyup', keyUpDown, false); + +// ========================== Loop ============================================ + +//TODO Cleanup gameloop and update. +/** + * Runs every frame. + */ +function update() { + //TODO Das preservation broken. + if (lastKeys !== keysDown && !watchingReplay) { + replay.keys[frame] = keysDown; + } else if (frame in replay.keys) { + keysDown = replay.keys[frame]; + } + + //if (piece.dead) { + // piece.new(preview.next()); + //} + + do { // for breaking + if (!(lastKeys & flags.holdPiece) && flags.holdPiece & keysDown) { + piece.hold(); // may cause death + } + if (gameState === 9) { + break; + } + + if (flags.rotLeft & keysDown && !(lastKeys & flags.rotLeft)) { + piece.rotate(-1); + piece.finesse++; + } else if (flags.rotRight & keysDown && !(lastKeys & flags.rotRight)) { + piece.rotate(1); + piece.finesse++; + } else if (flags.rot180 & keysDown && !(lastKeys & flags.rot180)) { + piece.rotate(2); + piece.finesse++; + } + + piece.checkShift(); + + if (flags.moveDown & keysDown) { + piece.shiftDown(); + //piece.finesse++; + } + if (!(lastKeys & flags.hardDrop) && flags.hardDrop & keysDown) { + frameLastHarddropDown = frame; + piece.hardDrop(); + } + + piece.update(); // may turn to locked, even lock out death. + if (gameState === 9) { + break; + } + + if(gametype === 3) { //Dig + var fromLastRise = frame-frameLastRise; + var fromLastHD = (flags.hardDrop & keysDown)?(frame-frameLastHarddropDown):0; + + var arrRow = [8,8,8,8,8,8,8,8,8,8]; + var curStage = 0, objCurStage; + + while(curStage= objCurStage.delay || (fromLastHD >= 20 && fromLastRise >= 15)) { + //IJLOSTZ + var arrRainbow=[ + 2,-1,1,5,4,3,7,6,-1,8, + 8,8,8,6,6,2,1,5,8,-1, + 7,7,-1,8,8]; + var idxRainbow,flagAll,colorUsed; + idxRainbow = ~~(objCurStage.begin/100); + flagAll = (~~(objCurStage.begin/50))%2; + if(idxRainbow >= arrRainbow.length) { + idxRainbow = arrRainbow.length - 1; + } + colorUsed = arrRainbow[idxRainbow]; + for(var x=0; x10?10:level]; + if(digZenBuffer-piecePerRise > -0.000000001){ + digZenBuffer-=piecePerRise; + if(Math.abs(digZenBuffer) < -0.000000001){ + digZenBuffer = 0; + } + var arrRow=[8,8,8,8,8,8,8,8,8,8]; + arrRow[~~(rng.next()*10)]=0; + + stack.rowRise(arrRow, piece); + sound.playse("garbage"); + } + } + } + } while(false) // break when game over + + updateScoreTime(); + + if (lastKeys !== keysDown) { + lastKeys = keysDown; + } +} + +var inloop = false; //debug +function gameLoop() { + + //if (frame % 60 == 0) console.log("running"); + var fps=60; + + if (!paused && gameState !== 3) { + requestAnimFrame(gameLoop); + + // anti turbulance + // requestanimationframe is not perfectly 60fps, also with turbulance + var repeat = (Date.now() - startTime - pauseTime)/1000*fps - frame; + if (repeat>=2) { + repeat = Math.floor(repeat); + frameSkipped += repeat-1; + } else if (repeat <= 0) { + repeat = Math.ceil(repeat); + frameSkipped += repeat-1; + } else { + repeat = 1; + } + + for (var repf=0;repf= fps*10/6) || + (gameState === 4 && piece.are >= piece.areLimit) + ) { + gameState = 0; + // console.time("123"); + if (piece.ihs) { + piece.index = preview.next(); + piece.hold(); + } else { + piece.new(preview.next()); + } + piece.draw(); + // console.timeEnd("123"); + // console.log(frame); + updateScoreTime(); + } + + } else if (gameState === 9 || gameState === 1) { + if (toGreyRow >= stack.hiddenHeight) { + /** + * Fade to grey animation played when player loses. + */ + if (frame % 2) { + for (var x = 0; x < stack.width; x++) { + /* farter */ //WTF gamestate-1 + if (stack.grid[x][toGreyRow]) + stack.grid[x][toGreyRow] = + (gameState === 9 ? 8 : 0); + } + stack.draw(); + toGreyRow--; + } + } else { + //clear(activeCtx); + //piece.dead = true; + trysubmitscore(); + gameState = 3; + } + } + frame++; + } + + statistics(); + + // TODO improve this with 'dirty' flags. + /* farter */ // as you draw for lock delay brightness gradient... give this up.. + + if (piece.x !== lastX || + Math.floor(piece.y) !== lastY || + piece.pos !== lastPos || + piece.lockDelay !== lastLockDelay || + piece.dirty) { + piece.draw(); + } + lastX = piece.x; + lastY = Math.floor(piece.y); + lastPos = piece.pos; + lastLockDelay = piece.lockDelay; + piece.dirty = false; + + if (stack.dirty) { + stack.draw(); + } + if (preview.dirty) { + preview.draw(); + } + + } else { + console.log("stop inloop",inloop) + inloop = false; + } +} + +// called after piece lock, may be called multple times when die-in-one-frame +function checkWin(){ + if (gametype === 0 && lineLimit >= 40) { // 40L + longer sprints + if (lines >= lineLimit) { + gameState = 1; + if (gameparams.backFire){ + msg.innerHTML = "GREAT!"; + } else { + var rank = null; + var time = (Date.now() - scoreStartTime - pauseTime) / 1000; + if (lineLimit !== 40) { + time = time * 40 / lineLimit; + } + for (var i in sprintRanks) { + if (time > sprintRanks[i].t) { + rank = sprintRanks[i]; + break; + } + } + msg.innerHTML = rank.b +""; + } + piece.dead = true; + menu(3); + sound.playse("endingstart"); + } + } else { + var isend=false; + if (gametype === 1) { // Marathon + if (settings.Gravity !== 0 && lines>=200) { // not Auto, limit to 200 Lines + isend=true; + } + } else if (gametype === 5) { // Score Attack + if (lines>=lineLimit) { // not Auto, limit to 200 Lines + isend=true; + } + } else if (gametype === 4) { // Dig race + if (digLines.length === 0) { + isend=true; + } + } else if (gametype === 6) { // 20G + if (lines>=300) { // 200 + 100 + isend=true; + } + } else if (gametype === 7) { // dig zen + if (lines>=400) { // 300 + 100 + isend=true; + } + } else if (gametype === 0) { // misc line limited modes + if (lines>=lineLimit) { + isend=true; + } + } + if(isend){ + gameState = 1; + $setText(msg,'GREAT!'); + piece.dead = true; + menu(3); + sound.playse("endingstart"); + } + } +} + +var playername=void 0; + +function requireplayername(){ + if(playername===void 0) + playername=prompt("Enter your name for leaderboard\n",""); + if(playername===null) + playername="anonymous"; + if(playername==="") + playername="unnamed"; +} + +function trysubmitscore() { + if(watchingReplay) + return; + var obj={req:"ranking"}; + var time = scoreTime; + + if(gametype===0) // 40L + obj.mode="sprint" + + (gameparams.lineLimit?""+gameparams.lineLimit:"") + + (gameparams.pieceSet?["","noi","alli"][gameparams.pieceSet]:"") + + (gameparams.backFire?["","bf1","bf2","bf3"][gameparams.backFire]:""); + else if(gametype===3) // dig + obj.mode="dig" + (gameparams.digOffset?gameparams.digOffset:""); + else if(gametype===4) // dig race + obj.mode="digrace" + (gameparams.digraceType?gameparams.digraceType:"checker"); + else if(gametype===1) // marathon + obj.mode="marathon" + (gameparams.marathonType?["","cls"][gameparams.marathonType]:""); + else if(gametype===5) // score attack + obj.mode="score"; + else if(gametype===6) // 20g + obj.mode="marathon20g"; + else if(gametype===7) // dig zen + obj.mode="digzen"; + else + return; + + if( + (gametype===0 && gameState===1)|| + (gametype===3 && gameState===9)|| + (gametype===4 && gameState===1)|| + (gametype===1 && settings.Gravity === 0)|| + (gametype===5)|| + (gametype===6)|| + (gametype===7)|| + false + ){ + requireplayername(); + obj.lines=lines; + obj.time=time; + obj.score=score.toString(); + obj.name=playername; + obj.replay=curreplaydata(); + + submitscore(obj); + } else{ + submitscore(obj); + } +} + +function tryreplaydata() { +/* + var strreplay = prompt("Paste replay data here: 在此贴入录像数据:"); + if (strreplay === null) + return; +*/ + var strreplay = replaydata.value; + init('replay',strreplay); +} + +function showreplaydata(strreplay) { + /* + var objblob = new Blob([strreplay],{type:"text/plain"}); + var url=URL.createObjectURL(objblob); + window.open(url); + */ + replaydata.value = strreplay; + replaydata.select(); + menu(6,1); +} + +function curreplaydata() { + //var strreplay = Compress(JSON.stringify(replay)); + var objKeys = replay.keys; + replay.keys = keysEncode(replay.keys); + var strreplay = JSON.stringify(replay); + replay.keys = objKeys; + //strreplay = strreplay + Compress(strreplay); + return strreplay; +} diff --git a/js-test-app/public/touch.js b/js-test-app/public/touch.js new file mode 100755 index 0000000..c8a76b2 --- /dev/null +++ b/js-test-app/public/touch.js @@ -0,0 +1,256 @@ +function FollowingButton(button) +{ + this.rectX0 = button.offsetLeft; + this.rectY0 = button.offsetTop; + this.rectX1 = button.offsetLeft + button.offsetWidth; + this.rectY1 = button.offsetTop + button.offsetHeight; + this.x = (this.rectX0 + this.rectX1) / 2; + this.y = (this.rectY0 + this.rectY1) / 2; + this.recentTouches = []; + this.enabled = true; +} + +function FollowingButtonSet(){ + +} + +FollowingButtonSet.RANGE = 96; + +FollowingButtonSet.prototype.touchStart = function(pos) { + +} + +FollowingButtonSet.prototype.posToBinds = function(pos) { + var mindist = Infinity; + var minbtnid = void 0; + for(var i = 0; i < touchButtons.length; i++){ + var btnflw = touchButtons[i].follow; + var dist = Math.hypot(pos.x - btnflw.x, pos.y - btnflw.y); + if(dist < mindist){ + mindist = dist; + minbtnid = i; + } + } + return minbtnid; +} + +for(var i = 0; i < touchButtons.length; i++){ + var btn = touchButtons[i]; + btn.follow = new FollowingButton(btn); +} + +function touchButtonsLayout() +{ + var dpiX = 96; + var dpiY = 96; + var winW = window.innerWidth / dpiX; + var winH = window.innerHeight / dpiY; + var buttonH = 0.7, buttonW = 1, fontSize=0.55, margin=0.1, unit="in"; + + var setPos = function(elem, posX, posY, sizeW, sizeH, + alignX, alignY, offsetX, offsetY, clientW, clientH) + { + elem.style.width = "" + sizeW + unit; + elem.style.height = "" + sizeH + unit; + // border ignored, for now + elem.style.left = "" + (offsetX + alignX * 0.5 * (clientW - sizeW) + posX * sizeW - ( (alignX-1) * margin/2 )) + unit; + elem.style.top = "" + (offsetY + alignY * 0.5 * (clientH - sizeH) + posY * sizeH - ( (alignY-1) * margin/2 )) + unit; + elem.style.display = "block"; + elem.style.fontSize = "" + fontSize + unit; + } + + var layouts = { //function array + "NONE": + function() { + for (var i = 0, len = touchButtons.length; i < len; i++) + touchButtons[i].style.display = "none"; + }, + "KBD_R": + function() { + setPos(touchRotLeft, 0, -1, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchRot180, 0.5, -2, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchRotRight, 1, -1, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchHold, 1.5, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchRight, 0, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchLeft, -2, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchDown, -1, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchDrop, -1, -1, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + }, + "KBD_L": + function() { + setPos(touchRotLeft, -1, -1, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchRot180, -0.4, -2, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchRotRight, 0, -1, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchHold, -1.5, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchRight, 2, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchLeft, 0, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchDown, 1, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchDrop, 1, -1, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + }, + "JOY": + function() { + var oy/*offset Y by block*/,ay/*align Y*/; + if (winH-winW>buttonH*1.5) { + oy=-1; ay=2; + } else { + oy=0; ay=1; + } + /* single finger */ + buttonW = 0.8; + if ((winW-0.1)/4buttonH*1.5) { + setPos(touchDown, -1, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchDrop, -1, -1, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + } else { + setPos(touchDown, 0, -1, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchDrop, -1, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + } + setPos(touchRotLeft, 0, -1.2, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + setPos(touchRotRight, 0, 0, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + setPos(touchHold, 0, 1.2, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + setPos(touchRot180, 0, -2.4, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + }, + "NARROW_L": + function() { + setPos(touchRotLeft, -2, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchRotRight, -1, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchDrop, 0, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + if (winH-winW>buttonH*1.5) { + setPos(touchRot180, -1, -1, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + } else { + setPos(touchRot180, 0, -1, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + } + setPos(touchLeft, 0, -1.2, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + setPos(touchDown, 0, 0, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + setPos(touchRight, 0, 1.2, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + setPos(touchHold, 0, -2.4, buttonW, buttonH, 0, 1, 0, 0, winW, winH); + }, + "NARROW_LM": + function() { + setPos(touchLeft, 0, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchRight, 2, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + if (winH-winW>buttonH*1.5) { + setPos(touchDown, 1, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchDrop, 1, -1, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + } + else { + setPos(touchDown, 0, -1, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + setPos(touchDrop, 1, 0, buttonW, buttonH, 0, 2, 0, 0, winW, winH); + } + setPos(touchRotLeft, 0, -1.2, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchRotRight, 0, -2.4, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchHold, 0, 0, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + setPos(touchRot180, 0, -3.6, buttonW, buttonH, 2, 2, 0, 0, winW, winH); + }, + + "DELUXE": + function() { + buttonW = 0.8; + if ((winW-0.1)/4buttonW*4.5) || + (winH-winW>4*buttonH && winW>buttonW*5.5)) { + layouts["KBD_R"](); + } + else if(winW-(winH*0.5)>buttonW*3) { + layouts["JOY"](); + } + else if(winH-winW>0) { + layouts["NARROW"](); + } + else if(winW>=buttonW*4) { + layouts["DELUXE"](); + } + else { + layouts["NONE"](); + } + } + else { + layouts[["KBD_R","KBD_L","JOY","NARROW","NARROW_L","NARROW_LM","DELUXE"][currLayout]](); + } +} + +function touch(e) +{ + var winH = window.innerHeight, winW = window.innerWidth; + //if (e.type==="touchmove") + //e.preventDefault(); + if ((e.type === "touchstart" || e.type === "click") && e.target === touchLayout) { + currLayout++; + if (currLayout === nLayouts) { + currLayout = -2; //none, auto, 0, 1, 2... + } + resize(); + } + if (e.type === "touchstart" || e.type === "touchmove" || e.type === "touchend") { + for (var i in binds) + keyUpDown({ + type: "keyup", + keyCode: binds[i], + preventDefault: function(){} + }); + for (var i = 0, l = e.touches.length; i < l; i++) { + var tX = e.touches[i].pageX, tY = e.touches[i].pageY; + for (var j = 0; j < touchButtons.length; j++) { + var oRef = touchButtons[j]; + if (tX>=oRef.offsetLeft && tX=oRef.offsetTop && tY a.btn:not(.btn-inline) { + margin: .7em auto; +} +#msg { + text-shadow: #000 0 0 10px; +} +textarea { + resize: none; +} +textarea:focus { + outline-color: #BFB; +} +#settings > div { + margin: 0.3em 0; +} \ No newline at end of file