general cleanup
This commit is contained in:
parent
12638b4131
commit
21ee80b08a
|
@ -96,7 +96,7 @@
|
||||||
font-size: medium;
|
font-size: medium;
|
||||||
}
|
}
|
||||||
#infobox ul {
|
#infobox ul {
|
||||||
margin: 0 0.5vw;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
@ -105,17 +105,20 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
#infobox h2 {
|
||||||
|
border-bottom: 1px solid gray;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
kbd {
|
kbd {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
height: 1.8vw;
|
padding: 4px 6px;
|
||||||
width: 1.8vw;
|
margin: 1px 2px;
|
||||||
font-size: 1.5vw;
|
|
||||||
margin-block: 2px;
|
|
||||||
color: black;
|
color: black;
|
||||||
border: 1px solid #b4b4b4;
|
border: 1px solid #b4b4b4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.with-border {
|
.with-border {
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
border: 4px solid #525252;
|
border: 4px solid #525252;
|
||||||
|
@ -131,6 +134,12 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
#quitButton a {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin-block: 0.5vw;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="module" src="main.js"></script>
|
<script type="module" src="main.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -147,10 +156,11 @@
|
||||||
<div id="infobox" class="with-border">
|
<div id="infobox" class="with-border">
|
||||||
<h2>Controls</h2>
|
<h2>Controls</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><kbd>↑</kbd> Rotate</li>
|
<li><kbd>↑</kbd> <span>Rotate</span></li>
|
||||||
<li><kbd>↓</kbd> Drop</li>
|
<li><kbd>↓</kbd> <span>Drop</span></li>
|
||||||
<li><kbd>←</kbd> Left</li>
|
<li><kbd>←</kbd> <span>Left</span></li>
|
||||||
<li><kbd>→</kbd> Right</li>
|
<li><kbd>→</kbd> <span>Right</span></li>
|
||||||
|
<!-- <li><kbd>CTRL</kbd> + <kbd>↓</kbd> Hard Drop</li> -->
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -160,6 +170,7 @@
|
||||||
<h1>You’ve been froobed!</h1>
|
<h1>You’ve been froobed!</h1>
|
||||||
<div id="scoreCounter">With a score of</div>
|
<div id="scoreCounter">With a score of</div>
|
||||||
<h2 id="tryAgain">try again?</h2>
|
<h2 id="tryAgain">try again?</h2>
|
||||||
|
<h2 id="quitButton"><a href="../">quit</a></h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
135
src/gameState.ts
135
src/gameState.ts
|
@ -1,5 +1,4 @@
|
||||||
import { RotationStyle, TileColor } from "./tetrisPieces";
|
import { RotationStyle, TileColor } from "./tetrisPieces";
|
||||||
import { Option, ifSome, isNone, isSome } from "./lib/option";
|
|
||||||
import { Point, addPoint } from "./lib/util";
|
import { Point, addPoint } from "./lib/util";
|
||||||
import { TextureStore } from "./textureStore";
|
import { TextureStore } from "./textureStore";
|
||||||
import { Sidebar } from "./sidebar";
|
import { Sidebar } from "./sidebar";
|
||||||
|
@ -33,10 +32,10 @@ function posToGridIndex(x: number, y: number): number {
|
||||||
export class GameState {
|
export class GameState {
|
||||||
// Whenever an operation is performed which requires the board to be redrawn, reDraw is set to true
|
// Whenever an operation is performed which requires the board to be redrawn, reDraw is set to true
|
||||||
reDraw: boolean;
|
reDraw: boolean;
|
||||||
heldPiece: Option<HeldPiece>;
|
heldPiece: null | HeldPiece;
|
||||||
fallTimer: number;
|
fallTimer: number;
|
||||||
movementTimer: number;
|
movementTimer: number;
|
||||||
grid: Array<Option<TileColor>>;
|
grid: Array<TileColor | null>;
|
||||||
sidebarRef: Sidebar;
|
sidebarRef: Sidebar;
|
||||||
|
|
||||||
constructor(sidebarRef: Sidebar) {
|
constructor(sidebarRef: Sidebar) {
|
||||||
|
@ -57,24 +56,22 @@ export class GameState {
|
||||||
PLAYFIELD_HEIGHT
|
PLAYFIELD_HEIGHT
|
||||||
);
|
);
|
||||||
for (const [i, tile] of this.grid.entries()) {
|
for (const [i, tile] of this.grid.entries()) {
|
||||||
if (isNone(tile)) {
|
if (tile === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ifSome(tile, (tile) => {
|
GameState.drawTile(...gridIndexToPos(i), tile, ctx, textures);
|
||||||
GameState.drawTile(...gridIndexToPos(i), tile, ctx, textures);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
ifSome(this.heldPiece, (held) => {
|
if (this.heldPiece !== null ) {
|
||||||
for (const tileOffset of held.tiles) {
|
for (const tileOffset of this.heldPiece.tiles) {
|
||||||
const pos = addPoint(held.pos, tileOffset);
|
const pos = addPoint(this.heldPiece.pos, tileOffset);
|
||||||
GameState.drawTile(...pos, held.schema.color, ctx, textures);
|
GameState.drawTile(...pos, this.heldPiece.schema.color, ctx, textures);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
this.reDraw = false;
|
this.reDraw = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fallTick(): void | boolean {
|
fallTick(): void | boolean {
|
||||||
if (isNone(this.heldPiece)) {
|
if (this.heldPiece === null) {
|
||||||
this.heldPiece = this.sidebarRef.getNextPiece();
|
this.heldPiece = this.sidebarRef.getNextPiece();
|
||||||
if (this.allPointsInEmptyGridSpace(this.heldPieceTilesAdjusted())) {
|
if (this.allPointsInEmptyGridSpace(this.heldPieceTilesAdjusted())) {
|
||||||
this.reDraw = true;
|
this.reDraw = true;
|
||||||
|
@ -86,12 +83,14 @@ export class GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.moveHeld(0, 1)) {
|
if (!this.moveHeld(0, 1)) {
|
||||||
|
console.log("move failed");
|
||||||
this.flashHeldToGrid();
|
this.flashHeldToGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputTick(pressed: Array<KeyEvent>): void {
|
inputTick(pressed: Array<KeyEvent>): void {
|
||||||
if (isNone(this.heldPiece)) return;
|
|
||||||
|
if (this.heldPiece === null) return;
|
||||||
for (const key of pressed) {
|
for (const key of pressed) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case KeyEvent.Up:
|
case KeyEvent.Up:
|
||||||
|
@ -115,54 +114,57 @@ export class GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
rotate(): void {
|
rotate(): void {
|
||||||
ifSome(this.heldPiece, (held) => {
|
if (this.heldPiece === null) return;
|
||||||
const style = held.schema.rotation;
|
const held = this.heldPiece;
|
||||||
if (style === RotationStyle.None) {
|
const style = held.schema.rotation;
|
||||||
return;
|
if (style === RotationStyle.None) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let rotationFunc: (p: Point) => Point;
|
let rotationFunc: (p: Point) => Point;
|
||||||
if (style === RotationStyle.Center) {
|
if (style === RotationStyle.Center) {
|
||||||
// For 3x2
|
// For 3x2
|
||||||
rotationFunc = (p): Point => [-p[1], p[0]];
|
rotationFunc = (p): Point => [-p[1], p[0]];
|
||||||
} else {
|
} else {
|
||||||
// For 1x4
|
// For 1x4
|
||||||
rotationFunc = (p): Point => [1 - p[1], p[0]];
|
rotationFunc = (p): Point => [1 - p[1], p[0]];
|
||||||
}
|
}
|
||||||
const rotated = held.tiles.map(rotationFunc);
|
const rotated = held.tiles.map(rotationFunc);
|
||||||
const rotatedAdjusted = rotated.map((p) => addPoint(p, held.pos));
|
|
||||||
if (this.allPointsInEmptyGridSpace(rotatedAdjusted)) {
|
const rotatedAdjusted = rotated.map((p) => addPoint(p, held.pos));
|
||||||
held.tiles = rotated;
|
if (this.allPointsInEmptyGridSpace(rotatedAdjusted)) {
|
||||||
this.reDraw = true;
|
this.heldPiece.tiles = rotated;
|
||||||
}
|
this.reDraw = true;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveHeld(dx: number, dy: number): boolean {
|
private moveHeld(dx: number, dy: number): boolean {
|
||||||
let success = false;
|
|
||||||
ifSome(this.heldPiece, (held) => {
|
if (this.heldPiece === null) return false;
|
||||||
const newPos: Point = addPoint(held.pos, [dx, dy]);
|
const held = this.heldPiece;
|
||||||
if (
|
const newPos: Point = addPoint(held.pos, [dx, dy]);
|
||||||
this.allPointsInEmptyGridSpace(this.heldPieceTilesAdjusted([dx, dy]))
|
if (
|
||||||
) {
|
this.allPointsInEmptyGridSpace(this.heldPieceTilesAdjusted([dx, dy]))
|
||||||
held.pos = newPos;
|
) {
|
||||||
this.reDraw = true;
|
held.pos = newPos;
|
||||||
success = true;
|
this.reDraw = true;
|
||||||
}
|
return true;
|
||||||
});
|
}
|
||||||
return success;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private allPointsInEmptyGridSpace(points: Array<Point>): boolean {
|
private allPointsInEmptyGridSpace(points: Array<Point>): boolean {
|
||||||
return (
|
for (const p of points ) {
|
||||||
points
|
if (this.isGridSquareFilled(...p)) {
|
||||||
.map((p): boolean => this.isGridSquareFilled(...p))
|
return false;
|
||||||
.find((v) => v) === undefined
|
}
|
||||||
);
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should only be called when held piece is non none; uses unsafe type assertion.
|
// Should only be called when held piece is non none; uses unsafe type assertion.
|
||||||
private heldPieceTilesAdjusted(offset: Point = [0, 0]): Array<Point> {
|
private heldPieceTilesAdjusted(offset: Point = [0, 0]): Array<Point> {
|
||||||
|
console.assert(this.heldPiece !== null);
|
||||||
const held = this.heldPiece as HeldPiece;
|
const held = this.heldPiece as HeldPiece;
|
||||||
|
|
||||||
const adjusted: Array<Point> = held.tiles.map((t): Point => {
|
const adjusted: Array<Point> = held.tiles.map((t): Point => {
|
||||||
|
@ -172,23 +174,25 @@ export class GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
private flashHeldToGrid(): void {
|
private flashHeldToGrid(): void {
|
||||||
ifSome(this.heldPiece, (held) => {
|
if (this.heldPiece === null) {
|
||||||
this.heldPieceTilesAdjusted().forEach((pos) => {
|
return;
|
||||||
const index = posToGridIndex(...pos);
|
}
|
||||||
this.grid[index] = held.schema.color;
|
const held = this.heldPiece;
|
||||||
});
|
this.heldPieceTilesAdjusted().forEach((pos) => {
|
||||||
|
const index = posToGridIndex(...pos);
|
||||||
this.heldPiece = null;
|
this.grid[index] = held.schema.color;
|
||||||
this.reDraw = true;
|
|
||||||
this.checkForTetris();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.heldPiece = null;
|
||||||
|
this.reDraw = true;
|
||||||
|
this.checkForTetris();
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkForTetris(): void {
|
private checkForTetris(): void {
|
||||||
// Iterate through rows, starting at bottom and going to top
|
// Iterate through rows, starting at bottom and going to top
|
||||||
for (let i = 0; i < this.grid.length - GRID_WIDTH; i += GRID_WIDTH) {
|
for (let i = 0; i < this.grid.length - GRID_WIDTH; i += GRID_WIDTH) {
|
||||||
// While the current row is completely filled, thus tetris
|
// While the current row is completely filled, thus tetris
|
||||||
while (this.grid.slice(i, i + GRID_WIDTH).every((t) => isSome(t))) {
|
while (this.grid.slice(i, i + GRID_WIDTH).every((t) => t !== null)) {
|
||||||
// Remove all of the tiles from the current row.
|
// Remove all of the tiles from the current row.
|
||||||
this.grid.splice(i, GRID_WIDTH);
|
this.grid.splice(i, GRID_WIDTH);
|
||||||
// Then fill the empty space with new tiles
|
// Then fill the empty space with new tiles
|
||||||
|
@ -204,10 +208,13 @@ export class GameState {
|
||||||
if (x >= GRID_WIDTH || x < 0 || y >= GRID_HEIGHT) return true;
|
if (x >= GRID_WIDTH || x < 0 || y >= GRID_HEIGHT) return true;
|
||||||
|
|
||||||
const index = posToGridIndex(x, y);
|
const index = posToGridIndex(x, y);
|
||||||
if (isSome(this.grid[index])) {
|
|
||||||
return true;
|
// There's a bug here where sometimes a tile can be undefined. It's supposed to only ever be a valid tile or null.
|
||||||
|
// Couldn't find it so just check for undefined too.
|
||||||
|
if (this.grid[index] === null || this.grid[index] === undefined) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static drawTile(
|
private static drawTile(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { ifSome } from "./option";
|
|
||||||
|
|
||||||
export const enum KeyEventType {
|
export const enum KeyEventType {
|
||||||
IfCaught,
|
IfCaught,
|
||||||
|
@ -57,21 +56,24 @@ export class Keyboard<T> {
|
||||||
}
|
}
|
||||||
pollSpecificKey(key: T, callback: () => void): void {
|
pollSpecificKey(key: T, callback: () => void): void {
|
||||||
const v = this.keyQueue.get(key);
|
const v = this.keyQueue.get(key);
|
||||||
ifSome(v, (vv) => {
|
if (v === null || v === undefined) {
|
||||||
if (!this.keysPressed.has(vv.outputValue)) {
|
return;
|
||||||
if (vv.eventType === KeyEventType.IfCaught || vv.polled) {
|
}
|
||||||
vv.polled = false;
|
if (!this.keysPressed.has(v.outputValue)) {
|
||||||
this.keyQueue.delete(vv.outputValue);
|
if (v.eventType === KeyEventType.IfCaught || v.polled) {
|
||||||
return;
|
v.polled = false;
|
||||||
}
|
this.keyQueue.delete(v.outputValue);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!vv.polled && vv.eventType === KeyEventType.OnlyOnce) {
|
}
|
||||||
callback();
|
if (!v.polled && v.eventType === KeyEventType.OnlyOnce) {
|
||||||
} else if (vv.eventType !== KeyEventType.OnlyOnce) {
|
callback();
|
||||||
callback();
|
} else if (v.eventType !== KeyEventType.OnlyOnce) {
|
||||||
}
|
callback();
|
||||||
vv.polled = true;
|
}
|
||||||
});
|
v.polled = true;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
poll(): Array<T> {
|
poll(): Array<T> {
|
||||||
|
@ -100,16 +102,19 @@ export class Keyboard<T> {
|
||||||
|
|
||||||
private keyDown(key: string): void {
|
private keyDown(key: string): void {
|
||||||
const mapped = this.keyMap.get(key);
|
const mapped = this.keyMap.get(key);
|
||||||
ifSome(mapped, (m) => {
|
if (mapped === null || mapped === undefined) {
|
||||||
this.keysPressed.add(m.outputValue);
|
return;
|
||||||
if (!this.keyQueue.has(m.outputValue))
|
}
|
||||||
this.keyQueue.set(m.outputValue, m);
|
this.keysPressed.add(mapped.outputValue);
|
||||||
});
|
if (!this.keyQueue.has(mapped.outputValue)) {
|
||||||
|
this.keyQueue.set(mapped.outputValue, mapped);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private keyUp(key: string): void {
|
private keyUp(key: string): void {
|
||||||
const mapped = this.keyMap.get(key);
|
const mapped = this.keyMap.get(key);
|
||||||
ifSome(mapped, (m) => {
|
if (mapped === null || mapped === undefined) {
|
||||||
this.keysPressed.delete(m.outputValue);
|
return;
|
||||||
});
|
}
|
||||||
|
this.keysPressed.delete(mapped.outputValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +1,12 @@
|
||||||
//
|
type Option<T> = null | undefined | T;
|
||||||
// Options (inspired by rust)
|
|
||||||
//
|
|
||||||
|
|
||||||
// Most methods like `document.getElementById("something")` will naturally return something of the type `Option<T>`
|
|
||||||
// This type has been chosen to remain compatible with most vanilla JS methods.
|
|
||||||
// A more capable option type could have been made by creating an option object, but would lose compatibility with JS.
|
|
||||||
export type Option<T> = null | undefined | T;
|
|
||||||
|
|
||||||
export function isNone<T>(input: Option<T>): boolean {
|
|
||||||
if (input === null || input === undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSome<T>(input: Option<T>): boolean {
|
|
||||||
if (input === null || input === undefined) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
type SingleArgCallback<T> = (v: T) => void;
|
|
||||||
type EmptyCallback = () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the input `Option<T>` is some, then run the `doAfter` callback.
|
|
||||||
* Intended to mimic rust's conditional enum matching pattern: `if let Some(v) = opt {<do things with value v>}`
|
|
||||||
*/
|
|
||||||
export function ifSome<T>(
|
|
||||||
input: Option<T>,
|
|
||||||
doAfter: SingleArgCallback<T>
|
|
||||||
): void {
|
|
||||||
if (isSome(input)) {
|
|
||||||
doAfter(input as T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ifNone<T>(input: Option<T>, doAfter: EmptyCallback): void {
|
|
||||||
if (isNone(input)) {
|
|
||||||
doAfter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not sure how ergonomic this is in actual use. It may get axed.
|
|
||||||
export function ifEither<T>(
|
|
||||||
input: Option<T>,
|
|
||||||
doIfSome: SingleArgCallback<T>,
|
|
||||||
doIfNone: EmptyCallback
|
|
||||||
): void {
|
|
||||||
if (isSome(input)) {
|
|
||||||
doIfSome(input as T);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
doIfNone();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Unwrap option of `null|undefined|T` to `T` throw error if value is not `T`.
|
Unwrap option of `null|undefined|T` to `T` throw error if value is not `T`.
|
||||||
`expect()` is preferred to this function as it gives better error messages
|
`expect()` is preferred to this function as it gives better error messages
|
||||||
*/
|
*/
|
||||||
export function unwrap<T>(input: Option<T>): T {
|
export function unwrap<T>(input: Option<T>): T {
|
||||||
if (isNone(input)) {
|
if (input === null || input === undefined) {
|
||||||
throw new TypeError("Unwrap called on null/undefined value");
|
throw new TypeError("Unwrap called on null/undefined value");
|
||||||
}
|
}
|
||||||
return input as T;
|
return input as T;
|
||||||
|
@ -71,7 +16,7 @@ export function unwrap<T>(input: Option<T>): T {
|
||||||
Unwrap option of `null|undefined|T` to `T` throw error with `exceptionMessage` if value is not `T`
|
Unwrap option of `null|undefined|T` to `T` throw error with `exceptionMessage` if value is not `T`
|
||||||
*/
|
*/
|
||||||
export function expect<T>(input: Option<T>, exceptionMessage: string): T {
|
export function expect<T>(input: Option<T>, exceptionMessage: string): T {
|
||||||
if (isNone(input)) {
|
if (input === null || input === undefined) {
|
||||||
throw new TypeError(exceptionMessage);
|
throw new TypeError(exceptionMessage);
|
||||||
}
|
}
|
||||||
return input as T;
|
return input as T;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Option, ifSome } from "./option";
|
|
||||||
|
|
||||||
export type Point = [number, number];
|
export type Point = [number, number];
|
||||||
export function addPoint(p1: Point, p2: Point): Point {
|
export function addPoint(p1: Point, p2: Point): Point {
|
||||||
|
@ -10,24 +9,28 @@ export function addPoint(p1: Point, p2: Point): Point {
|
||||||
//
|
//
|
||||||
|
|
||||||
// Who needs Jquery?
|
// Who needs Jquery?
|
||||||
export function $(elementId: string): Option<HTMLElement> {
|
export function $(elementId: string): null | HTMLElement {
|
||||||
return document.getElementById(elementId);
|
return document.getElementById(elementId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCanvas(
|
export function createCanvas(
|
||||||
width: number,
|
width: number,
|
||||||
height: number
|
height: number
|
||||||
): Option<{ canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D }> {
|
): null | { canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D } {
|
||||||
let ret = null;
|
const canvas = document.createElement("canvas");
|
||||||
ifSome(document.createElement("canvas"), (canvas) => {
|
if (canvas === null) {
|
||||||
canvas.width = width;
|
return null;
|
||||||
canvas.height = height;
|
}
|
||||||
ifSome(canvas.getContext("2d"), (ctx) => {
|
canvas.width = width;
|
||||||
ctx.imageSmoothingEnabled = false;
|
canvas.height = height;
|
||||||
ret = { canvas, ctx };
|
|
||||||
});
|
const ctx = canvas.getContext("2d");
|
||||||
});
|
if (ctx === null) {
|
||||||
return ret;
|
return null;
|
||||||
|
}
|
||||||
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
return { canvas, ctx };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearBody(): void {
|
export function clearBody(): void {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { SIDEBAR_HEIGHT, SIDEBAR_WIDTH, TILE_SIZE } from "./constants";
|
import { SIDEBAR_HEIGHT, SIDEBAR_WIDTH, TILE_SIZE } from "./constants";
|
||||||
import { HeldPiece } from "./heldPiece";
|
import { HeldPiece } from "./heldPiece";
|
||||||
import { addPoint, minMax, pickRandom } from "./lib/util";
|
import { minMax, pickRandom } from "./lib/util";
|
||||||
import { TETRIS_PIECE, TETRIS_PIECE_LIST } from "./tetrisPieces";
|
import { TETRIS_PIECE, TETRIS_PIECE_LIST } from "./tetrisPieces";
|
||||||
import { TextureStore } from "./textureStore";
|
import { TextureStore } from "./textureStore";
|
||||||
|
|
||||||
|
|
Binary file not shown.
Loading…
Reference in a new issue