refactor animations to be promise based
This commit is contained in:
@ -2,30 +2,41 @@ import type { Container, Sprite, Ticker, TickerCallback } from "pixi.js";
|
||||
import { CardStatus, Hand } from "./Hand";
|
||||
import { getSpriteSheet } from "./spritesheet";
|
||||
import { CARD_HEIGHT, CARD_WIDTH } from "./constants";
|
||||
import { Card } from "./Card";
|
||||
|
||||
// I can't find an actual type for the promise resolution callback, but this is what
|
||||
// it should look like.
|
||||
type PromiseResolve = (value?: unknown) => void;
|
||||
|
||||
const spritesheet = await getSpriteSheet();
|
||||
|
||||
export abstract class Animation {
|
||||
protected ticker: Ticker;
|
||||
protected boundCallback: TickerCallback<any>;
|
||||
#resolve: PromiseResolve;
|
||||
|
||||
constructor(ticker: Ticker) {
|
||||
console.log("this got called", ticker);
|
||||
if (ticker === undefined) throw new Error("got nothing");
|
||||
this.ticker = ticker;
|
||||
this.boundCallback = () => {};
|
||||
constructor(hand: Hand) {
|
||||
if (hand.ticker === null)
|
||||
throw new Error("the hand does not have a ticker");
|
||||
|
||||
this.ticker = hand.ticker;
|
||||
this.#resolve = Promise.resolve;
|
||||
this.boundCallback = () => this.#resolve();
|
||||
}
|
||||
|
||||
abstract onTick(ticker: Ticker): void;
|
||||
|
||||
getCallback() {
|
||||
start() {
|
||||
return new Promise((resolve) => {
|
||||
this.#resolve = resolve;
|
||||
this.boundCallback = this.onTick.bind(this);
|
||||
return this.boundCallback;
|
||||
this.ticker.add(this.boundCallback);
|
||||
});
|
||||
}
|
||||
|
||||
done() {
|
||||
console.log("removing", this.ticker);
|
||||
this.ticker.remove(this.boundCallback);
|
||||
this.#resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,27 +45,28 @@ export class FlipCard extends Animation {
|
||||
cardWidth: number;
|
||||
status: CardStatus;
|
||||
flipped: boolean;
|
||||
to: Card;
|
||||
|
||||
constructor(ticker: Ticker, card: Sprite, status: CardStatus) {
|
||||
super(ticker);
|
||||
constructor(hand: Hand, index: number) {
|
||||
super(hand);
|
||||
|
||||
this.boundCallback = () => {};
|
||||
this.card = card;
|
||||
const { sprite, ...status } = hand.getCard(index);
|
||||
this.card = sprite;
|
||||
this.status = status;
|
||||
this.flipped = false;
|
||||
this.cardWidth = card.width;
|
||||
this.cardWidth = sprite.width;
|
||||
this.to = this.status.isFaceDown ? this.status.face : "unknown";
|
||||
this.card.pivot.set(this.cardWidth / 2, 0);
|
||||
this.card.x += this.cardWidth / 2; // moving the pivot will shift the card
|
||||
}
|
||||
|
||||
onTick(ticker: Ticker) {
|
||||
const to = this.status.isFaceDown ? this.status.face : "unknown";
|
||||
this.status.isFaceDown = !this.status.isFaceDown;
|
||||
|
||||
this.card.pivot.set(this.cardWidth / 2, 0);
|
||||
|
||||
// the card has just fipped
|
||||
if (!this.flipped && this.card.width <= 10) {
|
||||
this.flipped = true;
|
||||
this.card.texture = spritesheet.textures[to];
|
||||
this.card.texture = spritesheet.textures[this.to];
|
||||
}
|
||||
|
||||
// the other side of the card is how showing
|
||||
@ -88,8 +100,8 @@ export class ToHorizontal extends Animation {
|
||||
#containerSizeDelta: [number, number]; // change in size of container
|
||||
#containerFinalPosition: [number, number]; // final x and y of container
|
||||
|
||||
constructor(ticker: Ticker, hand: Hand) {
|
||||
super(ticker);
|
||||
constructor(hand: Hand) {
|
||||
super(hand);
|
||||
|
||||
if (hand.size() < 2) throw new Error("cannot spread fewer than two cards");
|
||||
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
import {
|
||||
Assets,
|
||||
Container,
|
||||
Sprite,
|
||||
Spritesheet,
|
||||
Ticker,
|
||||
TickerCallback,
|
||||
} from "pixi.js";
|
||||
import { Assets, Container, Sprite, Spritesheet, Ticker } from "pixi.js";
|
||||
import { Card } from "./Card";
|
||||
import { CARD_HOVER_DIST, CARD_WIDTH } from "./constants";
|
||||
import { Animation, FlipCard, ToHorizontal } from "./Animation";
|
||||
import { FlipCard, ToHorizontal } from "./Animation";
|
||||
|
||||
type Pivot = "left" | "right" | "center";
|
||||
type Layout = "horizontal" | "ascending" | "stacked";
|
||||
@ -16,12 +9,10 @@ type Layout = "horizontal" | "ascending" | "stacked";
|
||||
const spritesheet = await Assets.load<Spritesheet>("/public/assets/cards.json");
|
||||
export type CardStatus = {
|
||||
face: Card;
|
||||
isAnimating: boolean;
|
||||
isFaceDown: boolean;
|
||||
};
|
||||
|
||||
export class Hand {
|
||||
#animations: Animation[];
|
||||
#cardSprites: Sprite[];
|
||||
#statuses: CardStatus[];
|
||||
#container: Container;
|
||||
@ -29,7 +20,7 @@ export class Hand {
|
||||
#layout: Layout;
|
||||
#maxWidth: number;
|
||||
#pivot: Pivot;
|
||||
#ticker: Ticker | null;
|
||||
ticker: Ticker | null;
|
||||
#gap: number;
|
||||
|
||||
constructor(maxWidth: number, cards: Card[] = []) {
|
||||
@ -37,7 +28,6 @@ export class Hand {
|
||||
throw new Error("hand cannot be narrower than a single card");
|
||||
}
|
||||
|
||||
this.#animations = [];
|
||||
this.#container = new Container();
|
||||
this.#hover = false;
|
||||
this.#maxWidth = maxWidth;
|
||||
@ -49,7 +39,7 @@ export class Hand {
|
||||
isAnimating: false,
|
||||
isFaceDown: false,
|
||||
}));
|
||||
this.#ticker = null;
|
||||
this.ticker = null;
|
||||
|
||||
if (cards.length > 0) {
|
||||
const sprites: Sprite[] = [];
|
||||
@ -66,12 +56,12 @@ export class Hand {
|
||||
}
|
||||
|
||||
add(card: Card, faceDown = false) {
|
||||
const sprite = new Sprite(spritesheet.textures[card]);
|
||||
const spriteName = faceDown ? "unknown" : card;
|
||||
const sprite = new Sprite(spritesheet.textures[spriteName]);
|
||||
|
||||
this.#cardSprites.push(sprite);
|
||||
this.#statuses.push({
|
||||
face: card,
|
||||
isAnimating: false,
|
||||
isFaceDown: faceDown,
|
||||
});
|
||||
this.#container.addChild(sprite);
|
||||
@ -81,42 +71,34 @@ export class Hand {
|
||||
}
|
||||
|
||||
animationTicker(ticker: Ticker) {
|
||||
this.#ticker = ticker;
|
||||
|
||||
ticker.add((ticker) => {
|
||||
if (this.#animations.length) {
|
||||
const animation = this.#animations.shift()!;
|
||||
ticker.add(animation.getCallback());
|
||||
}
|
||||
});
|
||||
this.ticker = ticker;
|
||||
}
|
||||
|
||||
setGap(gap: number) {
|
||||
this.#gap = gap;
|
||||
}
|
||||
|
||||
flip(cardIdx: number) {
|
||||
async flip(cardIdx: number) {
|
||||
if (cardIdx >= this.#cardSprites.length)
|
||||
throw new Error(`card index out of range: ${cardIdx}`);
|
||||
|
||||
if (!this.#ticker)
|
||||
if (!this.ticker)
|
||||
throw new Error("can't animate hand before passing it a ticker");
|
||||
|
||||
const card = this.#cardSprites[cardIdx];
|
||||
const status = this.#statuses[cardIdx];
|
||||
const ticker = this.#ticker;
|
||||
this.#animations.push(new FlipCard(ticker, card, status));
|
||||
const animation = new FlipCard(this, cardIdx);
|
||||
await animation.start();
|
||||
}
|
||||
|
||||
size(): number {
|
||||
return this.#cardSprites.length;
|
||||
}
|
||||
|
||||
spread() {
|
||||
if (!this.#ticker)
|
||||
async spread() {
|
||||
if (!this.ticker)
|
||||
throw new Error("can't animate hand before passing it a ticker");
|
||||
|
||||
this.#animations.push(new ToHorizontal(this.#ticker, this));
|
||||
const animation = new ToHorizontal(this);
|
||||
await animation.start();
|
||||
}
|
||||
|
||||
#setCardHover(sprite: Sprite) {
|
||||
@ -141,6 +123,13 @@ export class Hand {
|
||||
return this.#cardSprites;
|
||||
}
|
||||
|
||||
getCard(index: number) {
|
||||
if (this.#cardSprites.length <= index)
|
||||
throw new Error(`card index ${index} out of bounds`);
|
||||
|
||||
return { sprite: this.#cardSprites[index], ...this.#statuses[index] };
|
||||
}
|
||||
|
||||
getLayout() {
|
||||
return this.#layout;
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ const playerCards: Card[] = [
|
||||
"nineOfDiamonds",
|
||||
];
|
||||
|
||||
const dealerCards: Card[] = ["unknown", "aceOfHearts"];
|
||||
const dealerCards: Card[] = ["aceOfClubs", "aceOfHearts"];
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
@ -113,11 +113,12 @@ const dealerCards: Card[] = ["unknown", "aceOfHearts"];
|
||||
|
||||
dealerHand.setGap(CARD_WIDTH * 0.1);
|
||||
dealerHand.animationTicker(app.ticker);
|
||||
dealerHand.spread();
|
||||
|
||||
playerHand.setGap(CARD_WIDTH * 0.1);
|
||||
playerHand.animationTicker(app.ticker);
|
||||
playerHand.spread();
|
||||
|
||||
await dealerHand.spread();
|
||||
await dealerHand.flip(0);
|
||||
|
||||
// let ms = 0;
|
||||
// let elapsed = 0;
|
||||
@ -141,7 +142,10 @@ const dealerCards: Card[] = ["unknown", "aceOfHearts"];
|
||||
|
||||
function positionPlayer(app: Application, h: Hand) {
|
||||
h.setPivot("center");
|
||||
h.setPosition(app.screen.width / 2, CARD_HEIGHT * 3);
|
||||
h.setPosition(
|
||||
app.screen.width / 2,
|
||||
CARD_HEIGHT * 2.15 + 4 + CARD_HEIGHT / 2.5 + CARD_WIDTH,
|
||||
);
|
||||
h.setCardLayout("ascending");
|
||||
}
|
||||
|
||||
@ -180,6 +184,23 @@ function markTable(app: Application, g: Graphics) {
|
||||
app.screen.width - CARD_WIDTH,
|
||||
CARD_HEIGHT / 2.5,
|
||||
);
|
||||
|
||||
g.stroke({
|
||||
color: "#ffffff",
|
||||
width: 8,
|
||||
});
|
||||
|
||||
g.circle(
|
||||
app.screen.width / 2,
|
||||
CARD_HEIGHT * 2.15 +
|
||||
4 +
|
||||
CARD_HEIGHT / 2.5 +
|
||||
CARD_WIDTH +
|
||||
CARD_HEIGHT +
|
||||
CARD_WIDTH,
|
||||
CARD_HEIGHT / 2,
|
||||
);
|
||||
|
||||
g.stroke({
|
||||
color: "#ffffff",
|
||||
width: 8,
|
||||
|
||||
Reference in New Issue
Block a user