365 lines
8 KiB
Text
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()));
|
|
}
|
|
|
|
}
|