import { Deck } from "./deck"; import { Hand } from "./hand"; import { Tile } from "./tile"; import { Group } from "./group"; import { drawButtons, clickAction } from "./button"; export type mousePos = { x: number, y: number}; export class Game { private deck: Deck; private hands: Array; private discards: Array>; private lastDiscard: number|undefined; private groups: Array>; // game values private turn = 0; private selectedTile: number|undefined = undefined; private canCall: boolean = false; private hasPicked: boolean = false; private hasPlayed: boolean = false; // display parameter private BG_RECT = {color: "#007730", x: 0, y: 0, w: 1050, h: 1050}; private sizeHand = 0.7; private sizeHiddenHand = 0.6; private sizeDiscard = 0.6; // canvas private ctx: CanvasRenderingContext2D; private cv: HTMLCanvasElement; private staticCtx: CanvasRenderingContext2D; private staticCv: HTMLCanvasElement; public constructor( ctx: CanvasRenderingContext2D, cv: HTMLCanvasElement, staticCtx: CanvasRenderingContext2D, staticCv: HTMLCanvasElement, red: boolean = false ) { this.ctx = ctx; this.cv = cv; this.staticCtx = staticCtx; this.staticCv = staticCv; this.deck = new Deck(red); this.hands = [ this.deck.getRandomHand(), this.deck.getRandomHand(), this.deck.getRandomHand(), this.deck.getRandomHand() ]; this.discards = [[], [], [], []]; this.lastDiscard = undefined; this.groups = [[], [], [], []]; this.pick(0); } public draw(mp: mousePos) { // background this.staticCtx.clearRect(0, 0, this.cv.width, this.cv.height); this.staticCtx.fillStyle = this.BG_RECT.color; this.staticCtx.fillRect( this.BG_RECT.x, this.BG_RECT.y, this.BG_RECT.w, this.BG_RECT.h ); this.getSelected(mp); this.drawGame(); this.ctx.clearRect(0, 0, this.cv.width, this.cv.height); this.ctx.drawImage(this.staticCv, 0, 0); } public getDeck(): Deck { return this.deck; } public getHands(): Array { return this.hands; } public click( mp: mousePos, ): void { let action = clickAction( mp.x, mp.y, this.canDoAChii().length > 0, this.canDoAPon(), false, false, false ); this.getSelected(mp); if (this.turn === 0 && this.selectedTile !== undefined) { this.discard(0, this.selectedTile as NonNullable); this.turn++; } if (this.canDoAPon()) { this.pon(this.turn > 0 ? this.turn-1 : 3); } } private getSelected( mp: mousePos, ): void { const rect = this.cv.getBoundingClientRect(); let x = 2.5 * 75 * 0.75; let y = 1050 - 250 * 0.6; const mouseX = mp.x - rect.left - x; const mouseY = mp.y - rect.top; const s = 83.9; let q = Math.floor(mouseX / (s * this.sizeHand)); let r = mouseX - q * s * this.sizeHand; if ( r <= (s - 3) * this.sizeHand && q >= 0 && q < this.hands[0].length() && mouseY >= y && mouseY <= y + 100 * this.sizeHand ) { this.selectedTile = q; } else { this.selectedTile = undefined; } }; private play(): void { if ( this.turn !== 0 ) { if (!this.hasPicked) { this.pick(this.turn); this.hasPicked = true; } else if (!this.hasPlayed) { this.discard(this.turn, 0); this.hasPlayed = true; this.canCall = this.canDoAChii().length > 0 || this.canDoAPon(); } else if (!this.canCall) { if (this.turn === 3) { this.turn = 0; this.pick(0); } else { this.turn++; } this.hasPicked = false; this.hasPlayed = false; } } } private pick(player: number): void { this.hands[player].push(this.deck.pop()); this.hands[player].isolate = true; } private discard(player: number, n: number): void { let tile = this.hands[player].eject(n); tile.setTilt(); this.discards[player].push(tile); this.hands[player].isolate = false; this.hands[player].sort(); this.lastDiscard = player; } private canDoAPon(): boolean { if (this.lastDiscard !== undefined && this.lastDiscard !== 0) { let t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1]; return this.hands[0].count(t.getFamily(), t.getValue()) >= 2; } else { return false; } } private pon(p: number): void { console.log(p, "\n"); let t = this.discards[p].pop() as NonNullable; this.lastDiscard = undefined; let t2 = this.hands[0].find(t.getFamily(), t.getValue()) as NonNullable; let t3 = this.hands[0].find(t.getFamily(), t.getValue()) as NonNullable; [t, t2, t3].forEach(t => t.setTilt()); this.groups[0].push(new Group([t, t2, t3], p)); } private canDoAChii(): Array { let chii = [] as Array; if ( this.lastDiscard !== undefined && this.lastDiscard === 3 && this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1].getFamily() < 4 ) { let t = this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1]; let h = this.hands[0]; if ( h.count(t.getFamily(), t.getValue()-2) > 0 && h.count(t.getFamily(), t.getValue()-1) > 0 ) { chii.push(t.getValue()-2); } else if ( h.count(t.getFamily(), t.getValue()-1) > 0 && h.count(t.getFamily(), t.getValue()+1) > 0 ) { chii.push(t.getValue()-1); } else if ( h.count(t.getFamily(), t.getValue()+1) > 0 && h.count(t.getFamily(), t.getValue()+2) > 0 ) { chii.push(t.getValue()); } } return chii; } private chii(minValue: number): void { } private drawGame(): void { // update game this.play(); // hands this.drawHands(); // groups this.drawGroups(0.6); // discards for (let i = 0; i < 4; i++) { this.drawDiscard( i, this.hands[0].get(this.selectedTile) as NonNullable ); } // called drawButtons( this.staticCtx, this.canDoAChii().length > 0, this.canDoAPon(), false, false, false ); } private drawHands() { const pi = 3.141592; this.hands[0].drawHand( this.staticCtx, 2.5 * 75 * 0.75, 1000 - 150 * this.sizeHand, 5 * this.sizeHand, 0.75, this.selectedTile, false, 0 ); this.hands[1].drawHand( this.staticCtx, 1000 - 150 * this.sizeHiddenHand, 1000 - 75 * 5 * this.sizeHiddenHand, 5 * this.sizeHiddenHand, this.sizeHiddenHand, undefined, true, - pi / 2 ); this.hands[2].drawHand( this.staticCtx, 1000 - 75 * 5 * this.sizeHiddenHand, 150 * this.sizeHiddenHand, 5 * this.sizeHiddenHand, this.sizeHiddenHand, undefined, true, - pi ); this.hands[3].drawHand( this.staticCtx, 150 * this.sizeHiddenHand, 75 * 5 * this.sizeHiddenHand, 5 * this.sizeHiddenHand, this.sizeHiddenHand, undefined, true, pi / 2 ); } private drawGroups( size: number ): void { let os = 25; const pi = 3.141592; for ( let p = 0; p < 4; p++) { let rotation = [0, -pi / 2, -pi, pi / 2][p]; if (this.groups[p].length > 0) { for (let i = this.groups[p].length-1; i >= 0; i--) { this.groups[p][i].drawGroup( this.staticCtx, 1050 - 240 - (260 + os) * size * i, 1050 - 62, 5, 0.6, rotation ); } } } } private drawDiscard( p: number, highlitedTile: Tile|undefined ): void { const pi = 3.141592; this.staticCtx.save(); this.staticCtx.translate(525, 525); this.staticCtx.rotate([0, -pi/2, -pi, pi/2][p]) let x = - this.sizeDiscard * 475 / 2; let y = this.sizeDiscard * (475 / 2 + 5); for (let i = 0; i < this.discards[p].length; i++) { let tile = this.discards[p][i]; if (i < 12) { tile.drawTile( this.staticCtx, x + (i % 6) * 80 * this.sizeDiscard, y + Math.floor(i / 6) * 105 * this.sizeDiscard, this.sizeDiscard, false, 0, highlitedTile?.isEqual(tile.getFamily(), tile.getValue()) ); } else { tile.drawTile( this.staticCtx, x + (i - 12) * 80 * this.sizeDiscard, y + 2 * 105 * this.sizeDiscard, this.sizeDiscard, false, 0, highlitedTile?.isEqual(tile.getFamily(), tile.getValue()) ); } } this.staticCtx.restore(); } public async preload(): Promise { await this.deck.preload(); await Promise.all(this.hands.map(h => h.preload())); } }