Usar prettier

This commit is contained in:
Nulo 2021-06-30 23:16:45 +00:00
parent b9e404bf29
commit 080513daf6
14 changed files with 594 additions and 445 deletions

6
.prettierignore Normal file
View File

@ -0,0 +1,6 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
pnpm-lock.yaml

1
.prettierrc.json Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -4,9 +4,11 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build --base=./", "build": "vite build --base=./",
"serve": "vite preview" "serve": "vite preview",
"format": "prettier --write ."
}, },
"devDependencies": { "devDependencies": {
"prettier": "2.3.2",
"typescript": "^4.3.4", "typescript": "^4.3.4",
"vite": "^2.3.8" "vite": "^2.3.8"
} }

View File

@ -1,10 +1,12 @@
lockfileVersion: 5.3 lockfileVersion: 5.3
specifiers: specifiers:
prettier: 2.3.2
typescript: ^4.3.4 typescript: ^4.3.4
vite: ^2.3.8 vite: ^2.3.8
devDependencies: devDependencies:
prettier: 2.3.2
typescript: 4.3.4 typescript: 4.3.4
vite: 2.3.8 vite: 2.3.8
@ -63,6 +65,12 @@ packages:
source-map-js: 0.6.2 source-map-js: 0.6.2
dev: true dev: true
/prettier/2.3.2:
resolution: {integrity: sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
/resolve/1.20.0: /resolve/1.20.0:
resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==}
dependencies: dependencies:

View File

@ -1,38 +1,42 @@
import botonJugarImg from "./assets/boton_jugar.png" import botonJugarImg from "./assets/boton_jugar.png";
import larretaImg from "./assets/Millonario Malo.png" import larretaImg from "./assets/Millonario Malo.png";
import fondoImg from "./assets/CieloRioCalle.png" import fondoImg from "./assets/CieloRioCalle.png";
import edificiosImg from "./assets/Edificios.png" import edificiosImg from "./assets/Edificios.png";
import jugadorxImg from "./assets/ProtagonistaCorriendo_1.png" import jugadorxImg from "./assets/ProtagonistaCorriendo_1.png";
import baldosaImg from "./assets/Baldosa.png" import baldosaImg from "./assets/Baldosa.png";
import semillaImg from "./assets/Semilla.png" import semillaImg from "./assets/Semilla.png";
import arbol1Img from "./assets/Árbol 1.png" import arbol1Img from "./assets/Árbol 1.png";
import arbol2Img from "./assets/Árbol 2.png" import arbol2Img from "./assets/Árbol 2.png";
function loadImage(url: string): Promise<HTMLImageElement> { function loadImage(url: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let img = new Image() let img = new Image();
img.onload = () => resolve(img) img.onload = () => resolve(img);
img.onerror = e => reject(e) img.onerror = (e) => reject(e);
img.src = url img.src = url;
}) });
} }
export const assetUrls = { export const assetUrls = {
botonJugar: botonJugarImg, botonJugar: botonJugarImg,
larreta: larretaImg, larreta: larretaImg,
fondo: fondoImg, fondo: fondoImg,
edificios: edificiosImg, edificios: edificiosImg,
jugadorx: jugadorxImg, jugadorx: jugadorxImg,
baldosa: baldosaImg, baldosa: baldosaImg,
semilla: semillaImg, semilla: semillaImg,
arbol1: arbol1Img, arbol1: arbol1Img,
arbol2: arbol2Img, arbol2: arbol2Img,
} };
export type Assets = { [key in keyof typeof assetUrls]: HTMLImageElement } export type Assets = { [key in keyof typeof assetUrls]: HTMLImageElement };
const assets = Object.fromEntries(Object.entries(assetUrls).map(([name, url]) => [name, loadImage(url)])) const assets = Object.fromEntries(
Object.entries(assetUrls).map(([name, url]) => [name, loadImage(url)])
);
export async function loadAssets() { export async function loadAssets() {
const imgs = await Promise.all(Object.values(assets)) const imgs = await Promise.all(Object.values(assets));
return Object.fromEntries(imgs.map((img, i) => [Object.keys(assetUrls)[i], img])) as Assets return Object.fromEntries(
imgs.map((img, i) => [Object.keys(assetUrls)[i], img])
) as Assets;
} }

View File

@ -1,221 +1,255 @@
import { Juego } from './main' import { Juego } from "./main";
import { Sprite } from './sprite' import { Sprite } from "./sprite";
import { Box, boxCollision, drawText, randomFromArray } from './utils' import { Box, boxCollision, drawText, randomFromArray } from "./utils";
const ENEMIES_NUM = 12 const ENEMIES_NUM = 12;
const SEED_COOLDOWN = 300 const SEED_COOLDOWN = 300;
const MAP_MIN = 1000 const MAP_MIN = 1000;
const MAP_MAX = 5000 const MAP_MAX = 5000;
const MAP_SIZE = MAP_MAX - MAP_MIN const MAP_SIZE = MAP_MAX - MAP_MIN;
const TREES_TO_WIN = 30 const TREES_TO_WIN = 30;
const TIME = 2 * 60 * 1000 const TIME = 2 * 60 * 1000;
export type State = { export type State = {
current: "jugando" current: "jugando";
pos: { x: number } pos: { x: number };
view: { x: number } view: { x: number };
side: "left" | "right" side: "left" | "right";
enemies: { x: number }[] enemies: { x: number }[];
seeds: { x: number; velocity: { x: number } }[] seeds: { x: number; velocity: { x: number } }[];
trees: { x: number, sprite: Sprite }[] trees: { x: number; sprite: Sprite }[];
time: number, time: number;
seedCooldown: number, seedCooldown: number;
} };
export function createJugandoState(): State { export function createJugandoState(): State {
return { return {
current: "jugando", current: "jugando",
pos: { x: MAP_MIN + MAP_SIZE / 2 }, pos: { x: MAP_MIN + MAP_SIZE / 2 },
view: { x: 0 }, view: { x: 0 },
side: "right", side: "right",
enemies: [], enemies: [],
seeds: [], seeds: [],
trees: [], trees: [],
time: TIME, time: TIME,
seedCooldown: 0, seedCooldown: 0,
} };
} }
export function update(juego: Juego<State>, dt: number) { export function update(juego: Juego<State>, dt: number) {
juego.state.time -= dt juego.state.time -= dt;
if (juego.state.time < 0) { if (juego.state.time < 0) {
(juego as Juego<any>).state = { current: "lose" } (juego as Juego<any>).state = { current: "lose" };
return return;
} }
const playerSpeed = juego.canvas.width * 0.15 const playerSpeed = juego.canvas.width * 0.15;
const enemySpeed = juego.canvas.width * 0.05 const enemySpeed = juego.canvas.width * 0.05;
if (juego.keyboard.keys.d || juego.keyboard.keys.ArrowRight) { if (juego.keyboard.keys.d || juego.keyboard.keys.ArrowRight) {
juego.state.side = "right" juego.state.side = "right";
juego.state.pos.x += (dt / 1000) * playerSpeed juego.state.pos.x += (dt / 1000) * playerSpeed;
} }
if (juego.keyboard.keys.a || juego.keyboard.keys.ArrowLeft) { if (juego.keyboard.keys.a || juego.keyboard.keys.ArrowLeft) {
juego.state.side = "left" juego.state.side = "left";
juego.state.pos.x -= (dt / 1000) * playerSpeed juego.state.pos.x -= (dt / 1000) * playerSpeed;
} }
if (juego.state.pos.x < MAP_MIN) juego.state.pos.x = MAP_MIN if (juego.state.pos.x < MAP_MIN) juego.state.pos.x = MAP_MIN;
if (juego.state.pos.x > MAP_MAX) juego.state.pos.x = MAP_MAX if (juego.state.pos.x > MAP_MAX) juego.state.pos.x = MAP_MAX;
juego.state.seedCooldown -= dt
if (juego.keyboard.keys[' '] && juego.state.seedCooldown < 0) {
const seedSpeed = juego.canvas.width * 0.7
juego.state.seeds.push({
x: juego.state.pos.x,
velocity: { x: juego.state.side === "left" ? -seedSpeed : seedSpeed },
})
juego.state.seedCooldown = SEED_COOLDOWN
}
for (const seed of juego.state.seeds) {
seed.x += (dt / 1000) * seed.velocity.x
seed.velocity.x *= 0.97
for (const enemy of juego.state.enemies) {
if (boxCollision({
x: seed.x,
y: getFloorY(juego) - juego.sprites.semilla.getHeight(juego),
width: juego.sprites.semilla.getWidth(juego),
height: juego.sprites.semilla.getHeight(juego),
}, {
x: enemy.x,
y: getFloorY(juego) - juego.sprites.larreta.getHeight(juego),
width: juego.sprites.larreta.getWidth(juego),
height: juego.sprites.larreta.getHeight(juego),
})) {
juego.state.seeds = juego.state.seeds.filter(s => s.x !== seed.x)
juego.state.enemies = juego.state.enemies.filter(e => e.x !== enemy.x)
juego.state.trees.push({
x: enemy.x,
sprite: randomFromArray([juego.sprites.arbol1, juego.sprites.arbol2]),
})
}
}
if (Math.abs(seed.velocity.x) < 100)
juego.state.seeds = juego.state.seeds.filter(s => s.velocity.x !== seed.velocity.x)
}
if (juego.state.trees.length >= TREES_TO_WIN) {
(juego as Juego<any>).state = { current: "win" }
return
}
while (juego.state.enemies.length < ENEMIES_NUM) {
const x = Math.random() * MAP_SIZE + MAP_MIN
// Don't spawn enemies too close
if (Math.abs(juego.state.pos.x - x) < 300) continue
juego.state.enemies.push({ x })
}
juego.state.seedCooldown -= dt;
if (juego.keyboard.keys[" "] && juego.state.seedCooldown < 0) {
const seedSpeed = juego.canvas.width * 0.7;
juego.state.seeds.push({
x: juego.state.pos.x,
velocity: { x: juego.state.side === "left" ? -seedSpeed : seedSpeed },
});
juego.state.seedCooldown = SEED_COOLDOWN;
}
for (const seed of juego.state.seeds) {
seed.x += (dt / 1000) * seed.velocity.x;
seed.velocity.x *= 0.97;
for (const enemy of juego.state.enemies) { for (const enemy of juego.state.enemies) {
const distance = enemy.x - juego.state.pos.x if (
if (distance < 0) { boxCollision(
enemy.x += (dt / 1000) * enemySpeed {
} else { x: seed.x,
enemy.x -= (dt / 1000) * enemySpeed y: getFloorY(juego) - juego.sprites.semilla.getHeight(juego),
} width: juego.sprites.semilla.getWidth(juego),
height: juego.sprites.semilla.getHeight(juego),
},
{
x: enemy.x,
y: getFloorY(juego) - juego.sprites.larreta.getHeight(juego),
width: juego.sprites.larreta.getWidth(juego),
height: juego.sprites.larreta.getHeight(juego),
}
)
) {
juego.state.seeds = juego.state.seeds.filter((s) => s.x !== seed.x);
juego.state.enemies = juego.state.enemies.filter(
(e) => e.x !== enemy.x
);
juego.state.trees.push({
x: enemy.x,
sprite: randomFromArray([juego.sprites.arbol1, juego.sprites.arbol2]),
});
}
} }
if (Math.abs(seed.velocity.x) < 100)
juego.state.seeds = juego.state.seeds.filter(
(s) => s.velocity.x !== seed.velocity.x
);
}
juego.state.view.x = -juego.state.pos.x + juego.canvas.width / 2 - juego.sprites.jugadorx.getWidth(juego) / 2 if (juego.state.trees.length >= TREES_TO_WIN) {
(juego as Juego<any>).state = { current: "win" };
return;
}
while (juego.state.enemies.length < ENEMIES_NUM) {
const x = Math.random() * MAP_SIZE + MAP_MIN;
// Don't spawn enemies too close
if (Math.abs(juego.state.pos.x - x) < 300) continue;
juego.state.enemies.push({ x });
}
for (const enemy of juego.state.enemies) {
const distance = enemy.x - juego.state.pos.x;
if (distance < 0) {
enemy.x += (dt / 1000) * enemySpeed;
} else {
enemy.x -= (dt / 1000) * enemySpeed;
}
}
juego.state.view.x =
-juego.state.pos.x +
juego.canvas.width / 2 -
juego.sprites.jugadorx.getWidth(juego) / 2;
} }
function drawJugadorx(juego: Juego<State>) { function drawJugadorx(juego: Juego<State>) {
const floorY = getFloorY(juego) const floorY = getFloorY(juego);
juego.sprites.jugadorx.draw( juego.sprites.jugadorx.draw(
juego, juego,
juego.state.pos.x + juego.state.view.x, juego.state.pos.x + juego.state.view.x,
floorY - juego.sprites.jugadorx.getHeight(juego), floorY - juego.sprites.jugadorx.getHeight(juego),
0, 0,
juego.state.side === "left", juego.state.side === "left"
) );
} }
function drawTrees(juego: Juego<State>) { function drawTrees(juego: Juego<State>) {
const floorY = getFloorY(juego) const floorY = getFloorY(juego);
for (const tree of juego.state.trees) { for (const tree of juego.state.trees) {
const height = tree.sprite.getHeight(juego) const height = tree.sprite.getHeight(juego);
tree.sprite.draw( tree.sprite.draw(juego, tree.x + juego.state.view.x, floorY - height);
juego, }
tree.x + juego.state.view.x,
floorY - height,
)
}
} }
function drawEnemies(juego: Juego<State>) { function drawEnemies(juego: Juego<State>) {
const height = juego.sprites.larreta.getHeight(juego) const height = juego.sprites.larreta.getHeight(juego);
const floorY = getFloorY(juego) const floorY = getFloorY(juego);
for (const enemy of juego.state.enemies) { for (const enemy of juego.state.enemies) {
juego.sprites.larreta.draw( juego.sprites.larreta.draw(
juego, juego,
enemy.x + juego.state.view.x, enemy.x + juego.state.view.x,
floorY - height, floorY - height
) );
} }
} }
function drawSeeds(juego: Juego<State>) { function drawSeeds(juego: Juego<State>) {
const height = juego.sprites.semilla.getHeight(juego) const height = juego.sprites.semilla.getHeight(juego);
const floorY = getFloorY(juego) const floorY = getFloorY(juego);
for (const seed of juego.state.seeds) { for (const seed of juego.state.seeds) {
juego.sprites.semilla.draw( juego.sprites.semilla.draw(
juego, juego,
seed.x + juego.state.view.x, seed.x + juego.state.view.x,
floorY - height, floorY - height
) );
} }
} }
function getFloorY(juego: Juego<State>): number { function getFloorY(juego: Juego<State>): number {
return juego.canvas.height * 65 / 96 return (juego.canvas.height * 65) / 96;
} }
function drawBackground( function drawBackground(
juego: Juego<State>, juego: Juego<State>,
y: number, y: number,
height: number, height: number,
img: HTMLImageElement, img: HTMLImageElement,
sourceBox?: Box, sourceBox?: Box
) { ) {
const aspect = img.width / img.height const aspect = img.width / img.height;
const width = height * aspect const width = height * aspect;
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
if (sourceBox) { if (sourceBox) {
juego.ctx.drawImage( juego.ctx.drawImage(
img, img,
sourceBox.x, sourceBox.y, sourceBox.width, sourceBox.height, sourceBox.x,
i * (width - 1) + juego.state.view.x, y, width, height, sourceBox.y,
) sourceBox.width,
} else { sourceBox.height,
juego.ctx.drawImage(img, i * (width - 1) + juego.state.view.x, y, width, height) i * (width - 1) + juego.state.view.x,
} y,
width,
height
);
} else {
juego.ctx.drawImage(
img,
i * (width - 1) + juego.state.view.x,
y,
width,
height
);
} }
}
} }
export function draw(juego: Juego<State>, timestamp: number) { export function draw(juego: Juego<State>, timestamp: number) {
drawBackground(juego, 0, juego.canvas.height, juego.assets.fondo) drawBackground(juego, 0, juego.canvas.height, juego.assets.fondo);
drawBackground( drawBackground(juego, 0, getFloorY(juego), juego.assets.edificios, {
juego, 0, getFloorY(juego), juego.assets.edificios, x: 0,
{ x: 0, y: 0, width: 89, height: 48 }) y: 0,
width: 89,
height: 48,
});
drawTrees(juego) drawTrees(juego);
drawEnemies(juego) drawEnemies(juego);
drawJugadorx(juego) drawJugadorx(juego);
drawSeeds(juego) drawSeeds(juego);
juego.ctx.fillStyle = "white" juego.ctx.fillStyle = "white";
drawText(juego, 'Usá las flechitas para moverte, y espacio para "disparar" semillas.', { x: 0, y: 100 }, {}) drawText(
juego,
'Usá las flechitas para moverte, y espacio para "disparar" semillas.',
{ x: 0, y: 100 },
{}
);
const arbolesBox = drawText( const arbolesBox = drawText(
juego, `Arboles: ${juego.state.trees.length}/${TREES_TO_WIN}`, juego,
{ x: juego.canvas.width - 10, y: 10 }, `Arboles: ${juego.state.trees.length}/${TREES_TO_WIN}`,
{ bold: true, align: 'right' }) { x: juego.canvas.width - 10, y: 10 },
{ bold: true, align: "right" }
);
const tiempoBox = drawText( const tiempoBox = drawText(
juego, `Tiempo restante`, juego,
{ x: 10, y: 10 }, `Tiempo restante`,
{ bold: false, align: 'left' }) { x: 10, y: 10 },
{ bold: false, align: "left" }
);
juego.ctx.fillRect( juego.ctx.fillRect(
10, tiempoBox.y + tiempoBox.height, 10,
(juego.canvas.width - arbolesBox.width - 20) * (juego.state.time / TIME), tiempoBox.y + tiempoBox.height,
30) (juego.canvas.width - arbolesBox.width - 20) * (juego.state.time / TIME),
30
);
} }

View File

@ -1,18 +1,20 @@
import { Juego } from './main' import { Juego } from "./main";
import { drawText } from './utils' import { drawText } from "./utils";
export type State = { export type State = {
current: "lose" current: "lose";
} };
export function update(juego: Juego<State>, dt: number) { export function update(juego: Juego<State>, dt: number) {}
}
export function draw(juego: Juego<State>, timestamp: number) { export function draw(juego: Juego<State>, timestamp: number) {
drawText(juego, '¡Te convirtieron en baldosa!', drawText(
{ x: juego.canvas.width / 2, y: 100 }, juego,
{ bold: true, size: 3, align: 'center' }) "¡Te convirtieron en baldosa!",
{ x: juego.canvas.width / 2, y: 100 },
{ bold: true, size: 3, align: "center" }
);
const width = juego.sprites.baldosa.getWidth(juego) const width = juego.sprites.baldosa.getWidth(juego);
juego.sprites.baldosa.draw(juego, juego.canvas.width / 2 - width / 2, 100) juego.sprites.baldosa.draw(juego, juego.canvas.width / 2 - width / 2, 100);
} }

View File

@ -1,131 +1,181 @@
import './style.css' import "./style.css";
import * as welcome from "./welcome" import * as welcome from "./welcome";
import * as jugando from "./jugando" import * as jugando from "./jugando";
import * as win from "./win" import * as win from "./win";
import * as lose from "./lose" import * as lose from "./lose";
import { Assets, loadAssets } from './assets' import { Assets, loadAssets } from "./assets";
import { loadSprite, Sprite } from './sprite' import { loadSprite, Sprite } from "./sprite";
export type State = welcome.State | jugando.State | win.State | lose.State export type State = welcome.State | jugando.State | win.State | lose.State;
export type Juego<T extends State> = { export type Juego<T extends State> = {
canvas: HTMLCanvasElement canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D ctx: CanvasRenderingContext2D;
assets: Assets assets: Assets;
sprites: { [key in "jugadorx" | "baldosa" | "larreta" | "semilla" | "arbol1" | "arbol2"]: Sprite } sprites: {
mouse: { x: number; y: number, down: boolean } [key in
keyboard: { keys: { [key: string]: boolean } } | "jugadorx"
state: T | "baldosa"
} | "larreta"
| "semilla"
| "arbol1"
| "arbol2"]: Sprite;
};
mouse: { x: number; y: number; down: boolean };
keyboard: { keys: { [key: string]: boolean } };
state: T;
};
function update(juego: Juego<any>, dt: number) { function update(juego: Juego<any>, dt: number) {
switch (juego.state.current) { switch (juego.state.current) {
case "welcome": case "welcome":
welcome.update(juego, dt) welcome.update(juego, dt);
break break;
case "jugando": case "jugando":
jugando.update(juego, dt) jugando.update(juego, dt);
break break;
case "win": case "win":
win.update(juego, dt) win.update(juego, dt);
break break;
case "lose": case "lose":
lose.update(juego, dt) lose.update(juego, dt);
break break;
} }
} }
function draw(juego: Juego<any>, timestamp: number) { function draw(juego: Juego<any>, timestamp: number) {
const { width, height } = juego.canvas const { width, height } = juego.canvas;
juego.ctx.fillStyle = "white" juego.ctx.fillStyle = "white";
juego.ctx.fillRect(0, 0, width, height) juego.ctx.fillRect(0, 0, width, height);
juego.ctx.fillStyle = "black" juego.ctx.fillStyle = "black";
switch (juego.state.current) { switch (juego.state.current) {
case "welcome": case "welcome":
welcome.draw(juego, timestamp) welcome.draw(juego, timestamp);
break break;
case "jugando": case "jugando":
jugando.draw(juego, timestamp) jugando.draw(juego, timestamp);
break break;
case "win": case "win":
win.draw(juego, timestamp) win.draw(juego, timestamp);
break break;
case "lose": case "lose":
lose.draw(juego, timestamp) lose.draw(juego, timestamp);
break break;
} }
} }
function resizeCanvas(canvas: HTMLCanvasElement) { function resizeCanvas(canvas: HTMLCanvasElement) {
canvas.width = canvas.clientWidth canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight canvas.height = canvas.clientHeight;
} }
async function initJuego() { async function initJuego() {
const canvas = document.querySelector<HTMLCanvasElement>("#juego")! const canvas = document.querySelector<HTMLCanvasElement>("#juego")!;
const ctx = canvas.getContext("2d", { const ctx = canvas.getContext("2d", {
alpha: false, alpha: false,
desynchronized: true, desynchronized: true,
})! })!;
window.addEventListener("resize", () => resizeCanvas(canvas), false) window.addEventListener("resize", () => resizeCanvas(canvas), false);
resizeCanvas(canvas) resizeCanvas(canvas);
const assets = await loadAssets() const assets = await loadAssets();
const sprites = { const sprites = {
jugadorx: loadSprite(assets.jugadorx, 65, 94, juego => juego.canvas.height / 4), jugadorx: loadSprite(
baldosa: loadSprite(assets.baldosa, 72, 72, juego => juego.canvas.height * 0.8), assets.jugadorx,
larreta: loadSprite(assets.larreta, 50, 71, juego => juego.canvas.height / 4), 65,
semilla: loadSprite(assets.semilla, 72, 73, juego => juego.canvas.height / 8), 94,
arbol1: loadSprite(assets.arbol1, 72, 68, juego => juego.canvas.height / 3), (juego) => juego.canvas.height / 4
arbol2: loadSprite(assets.arbol2, 72, 56, juego => juego.canvas.height / 4), ),
} baldosa: loadSprite(
assets.baldosa,
72,
72,
(juego) => juego.canvas.height * 0.8
),
larreta: loadSprite(
assets.larreta,
50,
71,
(juego) => juego.canvas.height / 4
),
semilla: loadSprite(
assets.semilla,
72,
73,
(juego) => juego.canvas.height / 8
),
arbol1: loadSprite(
assets.arbol1,
72,
68,
(juego) => juego.canvas.height / 3
),
arbol2: loadSprite(
assets.arbol2,
72,
56,
(juego) => juego.canvas.height / 4
),
};
let juego: Juego<welcome.State> = { let juego: Juego<welcome.State> = {
canvas, canvas,
ctx, ctx,
assets, assets,
sprites, sprites,
mouse: { x: 0, y: 0, down: false }, mouse: { x: 0, y: 0, down: false },
keyboard: { keys: {} }, keyboard: { keys: {} },
state: { state: {
current: "welcome", current: "welcome",
}, },
}; };
(window as any).juego = juego (window as any).juego = juego;
juego.ctx.imageSmoothingEnabled = false juego.ctx.imageSmoothingEnabled = false;
canvas.addEventListener("mousemove", e => { canvas.addEventListener(
juego.mouse.x = e.clientX "mousemove",
juego.mouse.y = e.clientY (e) => {
}, false) juego.mouse.x = e.clientX;
canvas.addEventListener("mousedown", () => { juego.mouse.y = e.clientY;
juego.mouse.down = true },
}, false) false
canvas.addEventListener("mouseup", () => { );
juego.mouse.down = false canvas.addEventListener(
}, false) "mousedown",
window.addEventListener("keydown", e => { () => {
juego.keyboard.keys[e.key] = true juego.mouse.down = true;
}) },
window.addEventListener("keyup", e => { false
juego.keyboard.keys[e.key] = false );
}) canvas.addEventListener(
"mouseup",
() => {
juego.mouse.down = false;
},
false
);
window.addEventListener("keydown", (e) => {
juego.keyboard.keys[e.key] = true;
});
window.addEventListener("keyup", (e) => {
juego.keyboard.keys[e.key] = false;
});
let lastRender = 0 let lastRender = 0;
const loop: FrameRequestCallback = timestamp => { const loop: FrameRequestCallback = (timestamp) => {
const progress = timestamp - lastRender const progress = timestamp - lastRender;
update(juego, progress) update(juego, progress);
draw(juego, timestamp) draw(juego, timestamp);
lastRender = timestamp lastRender = timestamp;
window.requestAnimationFrame(loop) window.requestAnimationFrame(loop);
} };
window.requestAnimationFrame(loop) window.requestAnimationFrame(loop);
} }
initJuego() initJuego();

View File

@ -1,65 +1,80 @@
import { Juego } from "./main" import { Juego } from "./main";
export type Sprite = { export type Sprite = {
getWidth: (juego: Juego<any>) => number; getWidth: (juego: Juego<any>) => number;
getHeight: (juego: Juego<any>) => number; getHeight: (juego: Juego<any>) => number;
draw: (juego: Juego<any>, x: number, y: number, spriteIndex?: number, flipped?: boolean) => void; draw: (
width: number; juego: Juego<any>,
height: number; x: number,
} y: number,
spriteIndex?: number,
flipped?: boolean
) => void;
width: number;
height: number;
};
// Sprites are image assets that have a grid of tiles to animate // Sprites are image assets that have a grid of tiles to animate
export function loadSprite( export function loadSprite(
img: HTMLImageElement, img: HTMLImageElement,
// The size of each tile // The size of each tile
width: number, height: number, width: number,
// Calculates the size of the sprite when rendering height: number,
getHeight: (juego: Juego<any>) => number, // Calculates the size of the sprite when rendering
getHeight: (juego: Juego<any>) => number
): Sprite { ): Sprite {
const aspect = width / height const aspect = width / height;
const rowSize = Math.floor(img.width / width) const rowSize = Math.floor(img.width / width);
if (img.width / width !== rowSize) { if (img.width / width !== rowSize) {
console.warn(`The sprite grid for ${img.src} has extra space after rows, are you sure the width/height of each tile is right?`) console.warn(
} `The sprite grid for ${img.src} has extra space after rows, are you sure the width/height of each tile is right?`
);
}
function getWidth(juego: Juego<any>) { function getWidth(juego: Juego<any>) {
return getHeight(juego) * aspect return getHeight(juego) * aspect;
} }
function draw(juego: Juego<any>, x: number, y: number, spriteIndex = 0, flipped = false) { function draw(
const drawHeight = getHeight(juego) juego: Juego<any>,
const drawWidth = getWidth(juego) x: number,
const tileX = spriteIndex % rowSize y: number,
const tileY = Math.floor(spriteIndex / rowSize) spriteIndex = 0,
if (flipped) { flipped = false
juego.ctx.save() ) {
juego.ctx.scale(-1, 1) const drawHeight = getHeight(juego);
juego.ctx.drawImage( const drawWidth = getWidth(juego);
img, const tileX = spriteIndex % rowSize;
tileX * width, const tileY = Math.floor(spriteIndex / rowSize);
tileY * height, if (flipped) {
width, juego.ctx.save();
height, juego.ctx.scale(-1, 1);
-x - drawWidth, juego.ctx.drawImage(
y, img,
drawWidth, tileX * width,
drawHeight, tileY * height,
) width,
juego.ctx.restore() height,
} else { -x - drawWidth,
juego.ctx.drawImage( y,
img, drawWidth,
tileX * width, drawHeight
tileY * height, );
width, juego.ctx.restore();
height, } else {
x, juego.ctx.drawImage(
y, img,
drawWidth, tileX * width,
drawHeight, tileY * height,
) width,
} height,
x,
y,
drawWidth,
drawHeight
);
} }
}
return { getWidth, getHeight, draw, width, height } return { getWidth, getHeight, draw, width, height };
} }

View File

@ -1,4 +1,5 @@
html, body { html,
body {
font-family: Avenir, Helvetica, Arial, sans-serif; font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;

View File

@ -1,47 +1,62 @@
import { Juego } from "./main"; import { Juego } from "./main";
export type Pos = { export type Pos = {
x: number, y: number, x: number;
y: number;
}; };
export type Box = Pos & { export type Box = Pos & {
width: number, height: number, width: number;
} height: number;
};
export function posInBox(box: Box, pos: Pos) { export function posInBox(box: Box, pos: Pos) {
return pos.x > box.x && pos.x < box.x + box.width return (
&& pos.y > box.y && pos.y < box.y + box.height pos.x > box.x &&
pos.x < box.x + box.width &&
pos.y > box.y &&
pos.y < box.y + box.height
);
} }
export function boxCollision(box1: Box, box2: Box) { export function boxCollision(box1: Box, box2: Box) {
// http://stackoverflow.com/questions/2440377/ddg#7301852 // http://stackoverflow.com/questions/2440377/ddg#7301852
return !( return !(
((box1.y + box1.height) < (box2.y)) || box1.y + box1.height < box2.y ||
(box1.y > (box2.y + box2.height)) || box1.y > box2.y + box2.height ||
((box1.x + box1.width) < box2.x) || box1.x + box1.width < box2.x ||
(box1.x > (box2.x + box2.width)) box1.x > box2.x + box2.width
) );
} }
export function drawText(juego: Juego<any>, text: string, pos: Pos, { export function drawText(
juego: Juego<any>,
text: string,
pos: Pos,
{
bold = false, bold = false,
size = 2, size = 2,
align = "left", align = "left",
baseline = "top", baseline = "top",
}: { }: {
align?: CanvasTextAlign, align?: CanvasTextAlign;
baseline?: CanvasTextBaseline, baseline?: CanvasTextBaseline;
bold?: boolean, bold?: boolean;
// in rem // in rem
size?: number, size?: number;
}): Box { }
juego.ctx.font = `${bold ? 'bold ' : ''}${size}rem sans-serif` ): Box {
juego.ctx.textAlign = align juego.ctx.font = `${bold ? "bold " : ""}${size}rem sans-serif`;
juego.ctx.textBaseline = baseline juego.ctx.textAlign = align;
juego.ctx.fillText(text, pos.x, pos.y) juego.ctx.textBaseline = baseline;
juego.ctx.fillText(text, pos.x, pos.y);
const measure = juego.ctx.measureText(text) const measure = juego.ctx.measureText(text);
return { ...pos, width: measure.width, height: measure.actualBoundingBoxDescent } return {
...pos,
width: measure.width,
height: measure.actualBoundingBoxDescent,
};
} }
export function randomFromArray<T>(array: T[]): T { export function randomFromArray<T>(array: T[]): T {
return array[Math.floor(Math.random() * array.length)] return array[Math.floor(Math.random() * array.length)];
} }

View File

@ -1,29 +1,38 @@
import { posInBox } from './utils' import { posInBox } from "./utils";
import { createJugandoState } from './jugando' import { createJugandoState } from "./jugando";
import { Juego } from './main' import { Juego } from "./main";
export type State = { export type State = {
current: "welcome" current: "welcome";
} };
function startButton(juego: Juego<State>) { function startButton(juego: Juego<State>) {
const [width, height] = [juego.assets.botonJugar.width / 4, juego.assets.botonJugar.height / 4] const [width, height] = [
return { juego.assets.botonJugar.width / 4,
x: juego.canvas.width - width - 30, juego.assets.botonJugar.height / 4,
y: juego.canvas.height - height - 30, ];
width, return {
height, x: juego.canvas.width - width - 30,
} y: juego.canvas.height - height - 30,
width,
height,
};
} }
export function update(juego: Juego<State>, dt: number) { export function update(juego: Juego<State>, dt: number) {
const btn = startButton(juego) const btn = startButton(juego);
if (juego.mouse.down && posInBox(btn, juego.mouse)) { if (juego.mouse.down && posInBox(btn, juego.mouse)) {
(juego as Juego<any>).state = createJugandoState() (juego as Juego<any>).state = createJugandoState();
} }
} }
export function draw(juego: Juego<State>, timestamp: number) { export function draw(juego: Juego<State>, timestamp: number) {
const btn = startButton(juego) const btn = startButton(juego);
juego.ctx.drawImage(juego.assets.botonJugar, btn.x, btn.y, btn.width, btn.height) juego.ctx.drawImage(
juego.assets.botonJugar,
btn.x,
btn.y,
btn.width,
btn.height
);
} }

View File

@ -1,15 +1,17 @@
import { Juego } from './main' import { Juego } from "./main";
import { drawText } from './utils' import { drawText } from "./utils";
export type State = { export type State = {
current: "win" current: "win";
} };
export function update(juego: Juego<State>, dt: number) { export function update(juego: Juego<State>, dt: number) {}
}
export function draw(juego: Juego<State>, timestamp: number) { export function draw(juego: Juego<State>, timestamp: number) {
drawText(juego, '¡Salvaste la costanera!', drawText(
{ x: juego.canvas.width / 2, y: 100 }, juego,
{ bold: true, size: 3, align: 'center' }) "¡Salvaste la costanera!",
{ x: juego.canvas.width / 2, y: 100 },
{ bold: true, size: 3, align: "center" }
);
} }

View File

@ -1,15 +1,15 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"lib": ["ESNext", "DOM"], "lib": ["ESNext", "DOM"],
"moduleResolution": "Node", "moduleResolution": "Node",
"strict": true, "strict": true,
"sourceMap": true, "sourceMap": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"noEmit": true, "noEmit": true,
"noImplicitReturns": true "noImplicitReturns": true
}, },
"include": ["./src"] "include": ["./src"]
} }