Browse Source

Usar prettier

prod
Nulo 5 months ago
parent
commit
080513daf6
  1. 6
      .prettierignore
  2. 1
      .prettierrc.json
  3. 4
      package.json
  4. 8
      pnpm-lock.yaml
  5. 62
      src/assets.ts
  6. 410
      src/jugando.ts
  7. 24
      src/lose.ts
  8. 280
      src/main.ts
  9. 123
      src/sprite.ts
  10. 3
      src/style.css
  11. 67
      src/utils.ts
  12. 45
      src/welcome.ts
  13. 20
      src/win.ts
  14. 26
      tsconfig.json

6
.prettierignore

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

1
.prettierrc.json

@ -0,0 +1 @@
{}

4
package.json

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

8
pnpm-lock.yaml

@ -1,10 +1,12 @@
lockfileVersion: 5.3
specifiers:
prettier: 2.3.2
typescript: ^4.3.4
vite: ^2.3.8
devDependencies:
prettier: 2.3.2
typescript: 4.3.4
vite: 2.3.8
@ -63,6 +65,12 @@ packages:
source-map-js: 0.6.2
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:
resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==}
dependencies:

62
src/assets.ts

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

410
src/jugando.ts

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

24
src/lose.ts

@ -1,18 +1,20 @@
import { Juego } from './main'
import { drawText } from './utils'
import { Juego } from "./main";
import { drawText } from "./utils";
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) {
drawText(juego, '¡Te convirtieron en baldosa!',
{ x: juego.canvas.width / 2, y: 100 },
{ bold: true, size: 3, align: 'center' })
drawText(
juego,
"¡Te convirtieron en baldosa!",
{ x: juego.canvas.width / 2, y: 100 },
{ bold: true, size: 3, align: "center" }
);
const width = juego.sprites.baldosa.getWidth(juego)
juego.sprites.baldosa.draw(juego, juego.canvas.width / 2 - width / 2, 100)
const width = juego.sprites.baldosa.getWidth(juego);
juego.sprites.baldosa.draw(juego, juego.canvas.width / 2 - width / 2, 100);
}

280
src/main.ts

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

123
src/sprite.ts

@ -1,65 +1,80 @@
import { Juego } from "./main"
import { Juego } from "./main";
export type Sprite = {
getWidth: (juego: Juego<any>) => number;
getHeight: (juego: Juego<any>) => number;
draw: (juego: Juego<any>, x: number, y: number, spriteIndex?: number, flipped?: boolean) => void;
width: number;
height: number;
}
getWidth: (juego: Juego<any>) => number;
getHeight: (juego: Juego<any>) => number;
draw: (
juego: Juego<any>,
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
export function loadSprite(
img: HTMLImageElement,
// The size of each tile
width: number, height: number,
// Calculates the size of the sprite when rendering
getHeight: (juego: Juego<any>) => number,
img: HTMLImageElement,
// The size of each tile
width: number,
height: number,
// Calculates the size of the sprite when rendering
getHeight: (juego: Juego<any>) => number
): Sprite {
const aspect = width / height
const rowSize = Math.floor(img.width / width)
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?`)
}
const aspect = width / height;
const rowSize = Math.floor(img.width / width);
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?`
);
}
function getWidth(juego: Juego<any>) {
return getHeight(juego) * aspect
}
function getWidth(juego: Juego<any>) {
return getHeight(juego) * aspect;
}
function draw(juego: Juego<any>, x: number, y: number, spriteIndex = 0, flipped = false) {
const drawHeight = getHeight(juego)
const drawWidth = getWidth(juego)
const tileX = spriteIndex % rowSize
const tileY = Math.floor(spriteIndex / rowSize)
if (flipped) {
juego.ctx.save()
juego.ctx.scale(-1, 1)
juego.ctx.drawImage(
img,
tileX * width,
tileY * height,
width,
height,
-x - drawWidth,
y,
drawWidth,
drawHeight,
)
juego.ctx.restore()
} else {
juego.ctx.drawImage(
img,
tileX * width,
tileY * height,
width,
height,
x,
y,
drawWidth,
drawHeight,
)
}
function draw(
juego: Juego<any>,
x: number,
y: number,
spriteIndex = 0,
flipped = false
) {
const drawHeight = getHeight(juego);
const drawWidth = getWidth(juego);
const tileX = spriteIndex % rowSize;
const tileY = Math.floor(spriteIndex / rowSize);
if (flipped) {
juego.ctx.save();
juego.ctx.scale(-1, 1);
juego.ctx.drawImage(
img,
tileX * width,
tileY * height,
width,
height,
-x - drawWidth,
y,
drawWidth,
drawHeight
);
juego.ctx.restore();
} else {
juego.ctx.drawImage(
img,
tileX * width,
tileY * height,
width,
height,
x,
y,
drawWidth,
drawHeight
);
}
}
return { getWidth, getHeight, draw, width, height }
return { getWidth, getHeight, draw, width, height };
}

3
src/style.css

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

67
src/utils.ts

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

45
src/welcome.ts

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

20
src/win.ts

@ -1,15 +1,17 @@
import { Juego } from './main'
import { drawText } from './utils'
import { Juego } from "./main";
import { drawText } from "./utils";
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) {
drawText(juego, '¡Salvaste la costanera!',
{ x: juego.canvas.width / 2, y: 100 },
{ bold: true, size: 3, align: 'center' })
drawText(
juego,
"¡Salvaste la costanera!",
{ x: juego.canvas.width / 2, y: 100 },
{ bold: true, size: 3, align: "center" }
);
}

26
tsconfig.json

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

Loading…
Cancel
Save