Riichi/4
2025-03-23 20:05:42 +01:00

365 lines
8 KiB
Text

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<Hand>;
private discards: Array<Array<Tile>>;
private lastDiscard: number|undefined;
private groups: Array<Array<Group>>;
// 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<Hand> {
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<number>);
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<Tile>;
this.lastDiscard = undefined;
let t2 = this.hands[0].find(t.getFamily(), t.getValue()) as NonNullable<Tile>;
let t3 = this.hands[0].find(t.getFamily(), t.getValue()) as NonNullable<Tile>;
[t, t2, t3].forEach(t => t.setTilt());
this.groups[0].push(new Group([t, t2, t3], p));
}
private canDoAChii(): Array<number> {
let chii = [] as Array<number>;
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<Tile>
);
}
// 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<void> {
await this.deck.preload();
await Promise.all(this.hands.map(h => h.preload()));
}
}