Compare commits
	
		
			5 Commits
		
	
	
		
			f9c95993c4
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c344c60db0 | |||
| 0ce2e6b328 | |||
| cf4f3832ff | |||
| ec46c5c6da | |||
| a5e3e04790 | 
@ -935,13 +935,31 @@
 | 
				
			|||||||
				"x": 0,
 | 
									"x": 0,
 | 
				
			||||||
				"y": 0
 | 
									"y": 0
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"unknown": {
 | 
				
			||||||
 | 
								"frame": {
 | 
				
			||||||
 | 
									"w": 144,
 | 
				
			||||||
 | 
									"h": 224,
 | 
				
			||||||
 | 
									"x": 1872,
 | 
				
			||||||
 | 
									"y": 0
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"sourceSize": {
 | 
				
			||||||
 | 
									"w": 144,
 | 
				
			||||||
 | 
									"h": 224
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"spriteSourceSize": {
 | 
				
			||||||
 | 
									"w": 144,
 | 
				
			||||||
 | 
									"h": 224,
 | 
				
			||||||
 | 
									"x": 0,
 | 
				
			||||||
 | 
									"y": 0
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"meta": {
 | 
						"meta": {
 | 
				
			||||||
		"image": "cards.png",
 | 
							"image": "cards.png",
 | 
				
			||||||
		"format": "RGBA8888",
 | 
							"format": "RGBA8888",
 | 
				
			||||||
		"size": {
 | 
							"size": {
 | 
				
			||||||
			"w": 1872,
 | 
								"w": 2016,
 | 
				
			||||||
			"h": 896
 | 
								"h": 896
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"scale": 1
 | 
							"scale": 1
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 229 KiB  | 
							
								
								
									
										88
									
								
								client/src/Animation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								client/src/Animation.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					import type { Sprite, Ticker, TickerCallback } from "pixi.js";
 | 
				
			||||||
 | 
					import { CardStatus } from "./Hand";
 | 
				
			||||||
 | 
					import { getSpriteSheet } from "./spritesheet";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const spritesheet = await getSpriteSheet();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export abstract class Animation {
 | 
				
			||||||
 | 
						protected ticker: Ticker;
 | 
				
			||||||
 | 
						protected cb: TickerCallback<any>;
 | 
				
			||||||
 | 
						#next: Animation | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(ticker: Ticker) {
 | 
				
			||||||
 | 
							console.log("this got called", ticker);
 | 
				
			||||||
 | 
							if (ticker === undefined) throw new Error("got nothing");
 | 
				
			||||||
 | 
							this.ticker = ticker;
 | 
				
			||||||
 | 
							this.cb = () => {};
 | 
				
			||||||
 | 
							this.#next = null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						abstract getCallback(): TickerCallback<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						next(animation: Animation) {
 | 
				
			||||||
 | 
							this.#next = animation;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						done() {
 | 
				
			||||||
 | 
							console.log("removing", this.ticker);
 | 
				
			||||||
 | 
							this.ticker.remove(this.cb);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class FlipCard extends Animation {
 | 
				
			||||||
 | 
						card: Sprite;
 | 
				
			||||||
 | 
						cardWidth: number;
 | 
				
			||||||
 | 
						status: CardStatus;
 | 
				
			||||||
 | 
						flipped: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(ticker: Ticker, card: Sprite, status: CardStatus) {
 | 
				
			||||||
 | 
							super(ticker);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.cb = () => {};
 | 
				
			||||||
 | 
							this.card = card;
 | 
				
			||||||
 | 
							this.status = status;
 | 
				
			||||||
 | 
							this.flipped = false;
 | 
				
			||||||
 | 
							this.cardWidth = card.width;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getCallback() {
 | 
				
			||||||
 | 
							const callback = (ticker: Ticker) => {
 | 
				
			||||||
 | 
								const to = this.status.isFaceDown ? this.status.face : "unknown";
 | 
				
			||||||
 | 
								this.status.isFaceDown = !this.status.isFaceDown;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								console.log(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								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];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// the other side of the card is how showing
 | 
				
			||||||
 | 
								if (this.flipped) {
 | 
				
			||||||
 | 
									const change = 20 * ticker.deltaTime;
 | 
				
			||||||
 | 
									if (this.card.width + change >= this.cardWidth)
 | 
				
			||||||
 | 
										this.card.width = this.cardWidth;
 | 
				
			||||||
 | 
									else this.card.width += change;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// the original side is still showing
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									const change = 20 * ticker.deltaTime;
 | 
				
			||||||
 | 
									if (this.card.width <= change) this.card.width = 0;
 | 
				
			||||||
 | 
									else this.card.width -= change;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (this.flipped && this.card.width === this.cardWidth) {
 | 
				
			||||||
 | 
									console.log("remove", this.flipped, this.card.width, this.cardWidth);
 | 
				
			||||||
 | 
									super.done();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.cb = callback.bind(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return this.cb;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -50,4 +50,5 @@ export type Card =
 | 
				
			|||||||
	| "jackOfDiamonds"
 | 
						| "jackOfDiamonds"
 | 
				
			||||||
	| "queenOfDiamonds"
 | 
						| "queenOfDiamonds"
 | 
				
			||||||
	| "kingOfDiamonds"
 | 
						| "kingOfDiamonds"
 | 
				
			||||||
	| "aceOfDiamonds";
 | 
						| "aceOfDiamonds"
 | 
				
			||||||
 | 
						| "unknown";
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +1,53 @@
 | 
				
			|||||||
import { Assets, Container, Size, PointData, Sprite } from "pixi.js";
 | 
					import {
 | 
				
			||||||
 | 
						Assets,
 | 
				
			||||||
 | 
						Container,
 | 
				
			||||||
 | 
						Sprite,
 | 
				
			||||||
 | 
						Spritesheet,
 | 
				
			||||||
 | 
						Ticker,
 | 
				
			||||||
 | 
						type TickerCallback,
 | 
				
			||||||
 | 
					} from "pixi.js";
 | 
				
			||||||
import { Card } from "./Card";
 | 
					import { Card } from "./Card";
 | 
				
			||||||
import { CARD_HEIGHT, CARD_WIDTH } from "./constants";
 | 
					import { CARD_HOVER_DIST, CARD_WIDTH } from "./constants";
 | 
				
			||||||
 | 
					import { Animation, FlipCard } from "./Animation";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const spritesheet = await Assets.load("/public/assets/cards.json");
 | 
					type Pivot = "left" | "right" | "center";
 | 
				
			||||||
 | 
					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 {
 | 
					export class Hand {
 | 
				
			||||||
	#cards: Sprite[];
 | 
						#animations: Animation[];
 | 
				
			||||||
 | 
						#cardSprites: Sprite[];
 | 
				
			||||||
 | 
						#statuses: CardStatus[];
 | 
				
			||||||
	#container: Container;
 | 
						#container: Container;
 | 
				
			||||||
 | 
						#hover: boolean;
 | 
				
			||||||
 | 
						#layout: Layout;
 | 
				
			||||||
	#maxWidth: number;
 | 
						#maxWidth: number;
 | 
				
			||||||
	#parent: Size & PointData;
 | 
						#pivot: Pivot;
 | 
				
			||||||
 | 
						#ticker: Ticker | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(maxWidth: number, cards: Card[] = []) {
 | 
						constructor(maxWidth: number, cards: Card[] = []) {
 | 
				
			||||||
		if (maxWidth < CARD_WIDTH) {
 | 
							if (maxWidth < CARD_WIDTH) {
 | 
				
			||||||
			throw new Error("hand cannot be narrower than a single card");
 | 
								throw new Error("hand cannot be narrower than a single card");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.#maxWidth = maxWidth;
 | 
							this.#animations = [];
 | 
				
			||||||
		this.#container = new Container();
 | 
							this.#container = new Container();
 | 
				
			||||||
		this.#parent = { x: 0, y: 0, width: 0, height: 0 };
 | 
							this.#hover = false;
 | 
				
			||||||
 | 
							this.#maxWidth = maxWidth;
 | 
				
			||||||
 | 
							this.#pivot = "left";
 | 
				
			||||||
 | 
							this.#layout = "horizontal";
 | 
				
			||||||
 | 
							this.#statuses = cards.map((card) => ({
 | 
				
			||||||
 | 
								face: card,
 | 
				
			||||||
 | 
								isAnimating: false,
 | 
				
			||||||
 | 
								isFaceDown: false,
 | 
				
			||||||
 | 
							}));
 | 
				
			||||||
 | 
							this.#ticker = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (cards.length > 0) {
 | 
							if (cards.length > 0) {
 | 
				
			||||||
			const sprites: Sprite[] = [];
 | 
								const sprites: Sprite[] = [];
 | 
				
			||||||
@ -25,47 +55,133 @@ export class Hand {
 | 
				
			|||||||
				this.add(card);
 | 
									this.add(card);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			this.#cards = sprites;
 | 
								this.#cardSprites = sprites;
 | 
				
			||||||
			this.#container.addChild(...sprites);
 | 
								this.#container.addChild(...sprites);
 | 
				
			||||||
			this.fanCards();
 | 
								this.fanCards();
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this.#cards = [];
 | 
								this.#cardSprites = [];
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	add(card: Card) {
 | 
						add(card: Card, faceDown = false) {
 | 
				
			||||||
		const sprite = new Sprite(spritesheet.textures[card]);
 | 
							const sprite = new Sprite(spritesheet.textures[card]);
 | 
				
			||||||
		sprite.eventMode = "dynamic";
 | 
					
 | 
				
			||||||
		sprite.onmouseenter = () => {
 | 
							this.#cardSprites.push(sprite);
 | 
				
			||||||
			sprite.y -= 50;
 | 
							this.#statuses.push({
 | 
				
			||||||
		};
 | 
								face: card,
 | 
				
			||||||
		sprite.onmouseleave = () => {
 | 
								isAnimating: false,
 | 
				
			||||||
			sprite.y += 50;
 | 
								isFaceDown: faceDown,
 | 
				
			||||||
		};
 | 
							});
 | 
				
			||||||
		this.#cards.push(sprite);
 | 
					 | 
				
			||||||
		this.#container.addChild(sprite);
 | 
							this.#container.addChild(sprite);
 | 
				
			||||||
 | 
							this.#setCardHover(sprite);
 | 
				
			||||||
		this.fanCards();
 | 
							this.fanCards();
 | 
				
			||||||
		this.repositionContainer();
 | 
							this.setPivot(this.#pivot);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	parentArea(x: number, y: number, width: number, height: number) {
 | 
						animationTicker(ticker: Ticker) {
 | 
				
			||||||
		this.#parent = { x, y, width, height };
 | 
							this.#ticker = ticker;
 | 
				
			||||||
		this.repositionContainer();
 | 
					
 | 
				
			||||||
 | 
							ticker.add((ticker) => {
 | 
				
			||||||
 | 
								if (this.#animations.length) {
 | 
				
			||||||
 | 
									const animation = this.#animations.shift()!;
 | 
				
			||||||
 | 
									ticker.add(animation.getCallback());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repositionContainer() {
 | 
						flip(cardIdx: number) {
 | 
				
			||||||
		this.#container.pivot.set(this.#container.width / 2, CARD_HEIGHT);
 | 
							if (cardIdx >= this.#cardSprites.length)
 | 
				
			||||||
		this.#container.position.set(this.#parent.width / 2, this.#parent.height);
 | 
								throw new Error(`card index out of range: ${cardIdx}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#setCardHover(sprite: Sprite) {
 | 
				
			||||||
 | 
							if (this.#hover) {
 | 
				
			||||||
 | 
								const { y } = sprite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sprite.eventMode = "dynamic";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sprite.onmouseenter = () => {
 | 
				
			||||||
 | 
									sprite.y -= CARD_HOVER_DIST;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sprite.onmouseleave = () => {
 | 
				
			||||||
 | 
									sprite.y = y;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								sprite.eventMode = "static";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hover(hasHover: boolean) {
 | 
				
			||||||
 | 
							if (this.#hover === hasHover) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.#hover = hasHover;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (const sprite of this.#cardSprites) {
 | 
				
			||||||
 | 
								this.#setCardHover(sprite);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setPosition(x: number, y: number) {
 | 
				
			||||||
 | 
							this.#container.position.set(x, y);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setCardLayout(layout: Layout) {
 | 
				
			||||||
 | 
							this.#layout = layout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setPivot(pivot: "left" | "right" | "center") {
 | 
				
			||||||
 | 
							switch (pivot) {
 | 
				
			||||||
 | 
								case "left":
 | 
				
			||||||
 | 
									this.#container.pivot.set(0, 0);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case "right":
 | 
				
			||||||
 | 
									this.#container.pivot.set(this.#container.width, 0);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case "center":
 | 
				
			||||||
 | 
									this.#container.pivot.set(this.#container.width / 2, 0);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									throw new Error(`unknown pivot position: ${pivot}`);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.#pivot = pivot;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fanCards() {
 | 
						fanCards() {
 | 
				
			||||||
		const count = this.#cards.length;
 | 
							const count = this.#cardSprites.length;
 | 
				
			||||||
		const max = this.#maxWidth;
 | 
							const max = this.#maxWidth;
 | 
				
			||||||
		const offset = count * CARD_WIDTH > max ? max / count : CARD_WIDTH;
 | 
							let offset = CARD_WIDTH / 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this.#layout === "horizontal") {
 | 
				
			||||||
 | 
								offset = count * CARD_WIDTH > max ? max / count : CARD_WIDTH;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		let x = 0;
 | 
							let x = 0;
 | 
				
			||||||
		for (const card of this.#cards) {
 | 
							let y = 0;
 | 
				
			||||||
 | 
							for (const card of this.#cardSprites) {
 | 
				
			||||||
			card.x = x;
 | 
								card.x = x;
 | 
				
			||||||
			x += offset;
 | 
								card.y = y;
 | 
				
			||||||
 | 
								switch (this.#layout) {
 | 
				
			||||||
 | 
									case "ascending":
 | 
				
			||||||
 | 
										x += offset;
 | 
				
			||||||
 | 
										y -= offset;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case "horizontal":
 | 
				
			||||||
 | 
										x += offset;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case "stacked":
 | 
				
			||||||
 | 
										x += CARD_WIDTH / 20;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,2 +1,3 @@
 | 
				
			|||||||
export const CARD_WIDTH = 144;
 | 
					export const CARD_WIDTH = 144;
 | 
				
			||||||
export const CARD_HEIGHT = 224;
 | 
					export const CARD_HEIGHT = 224;
 | 
				
			||||||
 | 
					export const CARD_HOVER_DIST = 50;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,19 @@
 | 
				
			|||||||
import { Application } from "pixi.js";
 | 
					import {
 | 
				
			||||||
 | 
						Application,
 | 
				
			||||||
 | 
						Bounds,
 | 
				
			||||||
 | 
						FillGradient,
 | 
				
			||||||
 | 
						Graphics,
 | 
				
			||||||
 | 
						Text,
 | 
				
			||||||
 | 
						TextStyle,
 | 
				
			||||||
 | 
						TextStyleOptions,
 | 
				
			||||||
 | 
					} from "pixi.js";
 | 
				
			||||||
import { Hand } from "./Hand";
 | 
					import { Hand } from "./Hand";
 | 
				
			||||||
import { Card } from "./Card";
 | 
					import { Card } from "./Card";
 | 
				
			||||||
 | 
					import { CARD_HEIGHT, CARD_HOVER_DIST, CARD_WIDTH } from "./constants";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cards: Card[] = [
 | 
					const playerCards: Card[] = ["nineOfSpades", "sixOfClubs"];
 | 
				
			||||||
	"threeOfClubs",
 | 
					
 | 
				
			||||||
	"twoOfDiamonds",
 | 
					const dealerCards: Card[] = ["unknown", "aceOfHearts"];
 | 
				
			||||||
	"aceOfClubs",
 | 
					 | 
				
			||||||
	"eightOfDiamonds",
 | 
					 | 
				
			||||||
	"nineOfSpades",
 | 
					 | 
				
			||||||
	"sixOfClubs",
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
(async () => {
 | 
					(async () => {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
@ -24,11 +28,110 @@ const cards: Card[] = [
 | 
				
			|||||||
		// Append the application canvas to the document body
 | 
							// Append the application canvas to the document body
 | 
				
			||||||
		document.getElementById("pixi-container")!.appendChild(app.canvas);
 | 
							document.getElementById("pixi-container")!.appendChild(app.canvas);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const hand = new Hand(350, []);
 | 
							const payoutStyle: TextStyleOptions = {
 | 
				
			||||||
 | 
								fontFamily: "Fira Sans",
 | 
				
			||||||
 | 
								fontWeight: "bold",
 | 
				
			||||||
 | 
								fontSize: CARD_HEIGHT / 5,
 | 
				
			||||||
 | 
								fill: "#ff9900",
 | 
				
			||||||
 | 
								padding: 10,
 | 
				
			||||||
 | 
								letterSpacing: 10,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		hand.parentArea(0, 0, app.screen.width, app.screen.height);
 | 
							const blackjackPayout = new Text({
 | 
				
			||||||
 | 
								text: "BLACKJACK PAYS 3 TO 2",
 | 
				
			||||||
 | 
								style: payoutStyle,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		app.stage.addChild(hand.getContainer());
 | 
							const tableMarkings = new Graphics();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const markTable = () => {
 | 
				
			||||||
 | 
								tableMarkings.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// dark green dealer area
 | 
				
			||||||
 | 
								tableMarkings.rect(0, 0, app.screen.width, CARD_HEIGHT * 1.8);
 | 
				
			||||||
 | 
								tableMarkings.fill("#055010");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// white insurance info box
 | 
				
			||||||
 | 
								tableMarkings.rect(
 | 
				
			||||||
 | 
									CARD_WIDTH / 2,
 | 
				
			||||||
 | 
									CARD_HEIGHT * 2.15,
 | 
				
			||||||
 | 
									app.screen.width - CARD_WIDTH,
 | 
				
			||||||
 | 
									CARD_HEIGHT / 2.5,
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								tableMarkings.stroke({
 | 
				
			||||||
 | 
									color: "#ffffff",
 | 
				
			||||||
 | 
									width: 8,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const positionPayoutInfo = () => {
 | 
				
			||||||
 | 
								blackjackPayout.pivot.set(blackjackPayout.width / 2, 0);
 | 
				
			||||||
 | 
								blackjackPayout.position.set(app.screen.width / 2, CARD_HEIGHT * 1.5);
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const dealerRulesStyle: TextStyleOptions = {
 | 
				
			||||||
 | 
								fontFamily: "Fira Sans",
 | 
				
			||||||
 | 
								fontStyle: "italic",
 | 
				
			||||||
 | 
								fontSize: CARD_HEIGHT / 7,
 | 
				
			||||||
 | 
								fill: "#ffffff",
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const dealerRules = new Text({
 | 
				
			||||||
 | 
								text: "Dealer must stand on 17 and must draw to 16",
 | 
				
			||||||
 | 
								style: dealerRulesStyle,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const positionDealerInfo = () => {
 | 
				
			||||||
 | 
								dealerRules.pivot.set(dealerRules.width / 2, 0);
 | 
				
			||||||
 | 
								dealerRules.position.set(app.screen.width / 2, CARD_HEIGHT * 1.85);
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const insuranceStyle: TextStyleOptions = {
 | 
				
			||||||
 | 
								fontFamily: "Fira Sans",
 | 
				
			||||||
 | 
								fontWeight: "bold",
 | 
				
			||||||
 | 
								fontSize: CARD_HEIGHT / 5,
 | 
				
			||||||
 | 
								fill: "#ffffff",
 | 
				
			||||||
 | 
								letterSpacing: 10,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const insurancePayout = new Text({
 | 
				
			||||||
 | 
								text: "INSURANCE PAYS 2 TO 1",
 | 
				
			||||||
 | 
								style: insuranceStyle,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const positionInsurancePayout = () => {
 | 
				
			||||||
 | 
								insurancePayout.pivot.set(insurancePayout.width / 2, 0);
 | 
				
			||||||
 | 
								insurancePayout.position.set(app.screen.width / 2, CARD_HEIGHT * 2.2);
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const playerHand = new Hand(350, []);
 | 
				
			||||||
 | 
							const dealerHand = new Hand(350, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const positionPlayer = () => {
 | 
				
			||||||
 | 
								playerHand.setPivot("center");
 | 
				
			||||||
 | 
								playerHand.setPosition(app.screen.width / 2, CARD_HEIGHT * 3);
 | 
				
			||||||
 | 
								playerHand.setCardLayout("ascending");
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const positionDealer = () => {
 | 
				
			||||||
 | 
								dealerHand.setPivot("center");
 | 
				
			||||||
 | 
								dealerHand.setPosition(app.screen.width / 2, 0 + CARD_HOVER_DIST);
 | 
				
			||||||
 | 
								dealerHand.setCardLayout("stacked");
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							markTable();
 | 
				
			||||||
 | 
							positionPayoutInfo();
 | 
				
			||||||
 | 
							positionDealerInfo();
 | 
				
			||||||
 | 
							positionInsurancePayout();
 | 
				
			||||||
 | 
							positionPlayer();
 | 
				
			||||||
 | 
							positionDealer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							app.stage.addChild(tableMarkings);
 | 
				
			||||||
 | 
							app.stage.addChild(blackjackPayout);
 | 
				
			||||||
 | 
							app.stage.addChild(dealerRules);
 | 
				
			||||||
 | 
							app.stage.addChild(insurancePayout);
 | 
				
			||||||
 | 
							app.stage.addChild(playerHand.getContainer());
 | 
				
			||||||
 | 
							app.stage.addChild(dealerHand.getContainer());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// When the user resizes the window, resize the app. There is a plugin
 | 
							// When the user resizes the window, resize the app. There is a plugin
 | 
				
			||||||
		// for doing this automatically, but it doesn't reposition stage's
 | 
							// for doing this automatically, but it doesn't reposition stage's
 | 
				
			||||||
@ -41,16 +144,27 @@ const cards: Card[] = [
 | 
				
			|||||||
		// https://pixijs.com/8.x/guides/concepts/scene-graph#local-vs-global-coordinates
 | 
							// https://pixijs.com/8.x/guides/concepts/scene-graph#local-vs-global-coordinates
 | 
				
			||||||
		window.addEventListener?.("resize", () => {
 | 
							window.addEventListener?.("resize", () => {
 | 
				
			||||||
			app.renderer.resize(window.innerWidth, window.innerHeight);
 | 
								app.renderer.resize(window.innerWidth, window.innerHeight);
 | 
				
			||||||
			hand.parentArea(0, 0, app.screen.width, app.screen.height);
 | 
								markTable();
 | 
				
			||||||
 | 
								positionPayoutInfo();
 | 
				
			||||||
 | 
								positionDealerInfo();
 | 
				
			||||||
 | 
								positionInsurancePayout();
 | 
				
			||||||
 | 
								positionPlayer();
 | 
				
			||||||
 | 
								positionDealer();
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let ms = 0;
 | 
							let ms = 0;
 | 
				
			||||||
		let elapsed = 0;
 | 
							let elapsed = 0;
 | 
				
			||||||
		app.ticker.add((time) => {
 | 
							app.ticker.add((time) => {
 | 
				
			||||||
			elapsed += time.elapsedMS;
 | 
								elapsed += time.elapsedMS;
 | 
				
			||||||
			if (cards.length && elapsed >= ms) {
 | 
								if (elapsed >= ms) {
 | 
				
			||||||
				ms += 300;
 | 
									ms += 300;
 | 
				
			||||||
				hand.add(cards.pop()!);
 | 
									if (playerCards.length) {
 | 
				
			||||||
 | 
										playerHand.add(playerCards.shift()!);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (dealerCards.length) {
 | 
				
			||||||
 | 
										dealerHand.add(dealerCards.shift()!);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	} catch (err) {
 | 
						} catch (err) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								client/src/spritesheet.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								client/src/spritesheet.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					import { Assets, Spritesheet } from "pixi.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let spriteSheet: Spritesheet | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getSpriteSheet(): Promise<Spritesheet> {
 | 
				
			||||||
 | 
						if (spriteSheet === null) {
 | 
				
			||||||
 | 
							spriteSheet = await Assets.load<Spritesheet>("public/assets/cards.json");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return spriteSheet;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -26,7 +26,7 @@ function getCardSpritesheet() {
 | 
				
			|||||||
		meta: {
 | 
							meta: {
 | 
				
			||||||
			image: "/public/assets/cards.png",
 | 
								image: "/public/assets/cards.png",
 | 
				
			||||||
			format: "RGBA8888",
 | 
								format: "RGBA8888",
 | 
				
			||||||
			size: { w: 1872, h: 896 },
 | 
								size: { w: 2016, h: 896 },
 | 
				
			||||||
			scale: 1,
 | 
								scale: 1,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
@ -48,10 +48,10 @@ function getCardSpritesheet() {
 | 
				
			|||||||
		"ace",
 | 
							"ace",
 | 
				
			||||||
	];
 | 
						];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const size = { w: 144, h: 224 };
 | 
				
			||||||
	for (let i = 0; i < 4; i++) {
 | 
						for (let i = 0; i < 4; i++) {
 | 
				
			||||||
		for (let j = 0; j < 13; j++) {
 | 
							for (let j = 0; j < 13; j++) {
 | 
				
			||||||
			const cardName = `${values[j]}Of${suits[i]}`;
 | 
								const cardName = `${values[j]}Of${suits[i]}`;
 | 
				
			||||||
			const size = { w: 144, h: 224 };
 | 
					 | 
				
			||||||
			const position = { x: j * size.w, y: i * size.h };
 | 
								const position = { x: j * size.w, y: i * size.h };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			spriteSheet.frames[cardName] = {
 | 
								spriteSheet.frames[cardName] = {
 | 
				
			||||||
@ -62,5 +62,11 @@ function getCardSpritesheet() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spriteSheet.frames["unknown"] = {
 | 
				
			||||||
 | 
							frame: { ...size, x: size.w * 13, y: 0 },
 | 
				
			||||||
 | 
							sourceSize: size,
 | 
				
			||||||
 | 
							spriteSourceSize: { ...size, x: 0, y: 0 },
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return spriteSheet;
 | 
						return spriteSheet;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user