added visual effects and refactored the code

This commit is contained in:
Didictateur 2025-03-23 20:05:42 +01:00
parent 86596a14e7
commit e74eaf0f80
18 changed files with 1663 additions and 170 deletions

365
4 Normal file
View file

@ -0,0 +1,365 @@
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()));
}
}

View file

@ -2,7 +2,7 @@ DISPLAY = $(wildcard src/display/*.ts)
OUTFILES = $(patsubst src/display/%.ts,build/%.js,$(DISPLAY)) OUTFILES = $(patsubst src/display/%.ts,build/%.js,$(DISPLAY))
all: all:
npx webpack --mode production npx webpack --mode development
clean: clean:
rm -rf build/* rm -rf build/*

8
Makefile.save Normal file
View file

@ -0,0 +1,8 @@
DISPLAY = $(wildcard src/display/*.ts)
OUTFILES = $(patsubst src/display/%.ts,build/%.js,$(DISPLAY))
all:
npx webpack --mode production
clean:
rm -rf build/*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

48
img/Regular/Gray.svg Normal file
View file

@ -0,0 +1,48 @@
<?xml version="1.0"?>
<svg width="300" height="400" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<defs id="defs4">
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath4243">
<circle cx="-264.66" cy="-198.21" fill="#000000" fill-opacity="0.3" fill-rule="nonzero" id="circle4245" r="293.95" stroke="#000000" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="19.13"/>
</clipPath>
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath7847">
<ellipse cx="394" cy="552.36" fill="#822600" fill-rule="nonzero" id="ellipse7849" rx="349.5" ry="216" stroke-dashoffset="0" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="12"/>
</clipPath>
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath4243-1">
<circle cx="-264.66" cy="-198.21" fill="#000000" fill-opacity="0.3" fill-rule="nonzero" id="circle4245-4" r="293.95" stroke="#000000" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="19.13"/>
</clipPath>
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath7876">
<circle cx="-264.66" cy="-198.21" fill="#000000" fill-opacity="0.3" fill-rule="nonzero" id="circle7878" r="293.95" stroke="#000000" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="19.13"/>
</clipPath>
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath14693">
<rect fill="#a53c3c" fill-rule="nonzero" height="168.82" id="rect14695" rx="1.26" ry="3.75" stroke-dashoffset="0" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="8" transform="matrix(0.99939083,-0.03489951,0.03489951,0.99939083,0,0)" width="131.78" x="-332.6" y="383.5"/>
</clipPath>
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath14952">
<ellipse cx="-271.34" cy="647.26" fill="#a53c3c" fill-rule="nonzero" id="ellipse14954" rx="69.06" ry="116.91" stroke-dashoffset="0" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="7" transform="matrix(0.99939083,-0.03489951,0.03489951,0.99939083,0,0)"/>
</clipPath>
<mask id="mask4222" maskUnits="userSpaceOnUse">
<rect fill="#ff3737" fill-rule="nonzero" height="400.78" id="rect4224" ry="40" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="10" width="300.06" x="0" y="652.28"/>
</mask>
<mask id="mask4216" maskUnits="userSpaceOnUse">
<rect fill="#ff3737" fill-rule="nonzero" height="400.78" id="rect4218" ry="40" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="10" transform="scale(-1,-1)" width="300.06" x="-451.94" y="-1325.6"/>
</mask>
<mask id="mask4222-0" maskUnits="userSpaceOnUse">
<rect fill="#ff3737" fill-rule="nonzero" height="400.78" id="rect4224-8" ry="40" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="10" width="300.06" x="0" y="652.28"/>
</mask>
<filter color-interpolation-filters="sRGB" id="filter4198-8">
<feGaussianBlur id="feGaussianBlur4200-3" result="blur" stdDeviation="2.51 2.51"/>
</filter>
<mask id="mask4216-0" maskUnits="userSpaceOnUse">
<rect fill="#ff3737" fill-rule="nonzero" height="400.78" id="rect4218-5" ry="40" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="10" transform="scale(-1,-1)" width="300.06" x="-451.94" y="-1325.6"/>
</mask>
<pattern height="6" id="EMFhbasepattern" patternUnits="userSpaceOnUse" width="6" x="0" y="0"/>
</defs>
<metadata id="metadata7">image/svg+xml</metadata>
<g class="layer">
<title>Layer 1</title>
<g id="layer1" opacity="0.35">
<rect fill="#000000" fill-rule="nonzero" height="400.78" id="rect4164" ry="40" stroke-dashoffset="0" stroke-linejoin="round" stroke-miterlimit="4" stroke-width="10" width="300.06" y="-0.08"/>
<path d="m-4.77,122.71c-4.88,-33.08 -12.08,-100.83 3.49,-123.01c20.28,-26.12 234.78,-20.43 264.59,1.85c12.9,8.74 -192.96,9.21 -216.21,37.83c-22.71,27.37 -46.45,126.45 -51.87,83.33z" fill="#000000" fill-rule="evenodd" filter="url(#filter4198-8)" id="path4166" mask="url(#mask4222-0)" stroke-width="1px" transform="translate(-1.34328e-07, -1.33688e-06)"/>
<path d="m151.74,1677.38c-3.33,-9.32 -10.25,-68.46 5.31,-90.63c20.28,-26.12 219.44,-16.46 231.56,-9.93c11.07,5.32 -178.6,0.06 -204.85,34.87c-21.6,30 -26.5,82.17 -32.02,65.69z" fill="#000000" fill-opacity="0.2" fill-rule="evenodd" filter="url(#filter4198-8)" id="path4221" mask="url(#mask4216-0)" stroke-width="1px" transform="matrix(-1, 0, 0, -1, 451.938, 1977.89)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -62,7 +62,7 @@
<body> <body>
<nav class="menu"> <nav class="menu">
<ul class="menu-item"> <ul class="menu-item">
<a href="#" onclick="loadScript('accueil.js')">Accueil</a> <a href="#" onclick="loadScript('')">Introduction</a>
</ul> </ul>
<ul class="menu-item"> <ul class="menu-item">
<a href="#">Riichi Mahjong</a> <a href="#">Riichi Mahjong</a>
@ -82,6 +82,14 @@
<a href="#" onclick="loadScript('dp13.js')">Chap 13: Partie avec les scores</a> <a href="#" onclick="loadScript('dp13.js')">Chap 13: Partie avec les scores</a>
</div> </div>
</ul> </ul>
<ul class="menu-item">
<a href="#">Partie réelle</a>
<div class="dropdown">
<a href="#" onclick="loadScript('')">Chap 1: Placement des joueurs</a>
<a href="#" onclick="loadScript('')">Chap 2: Construction des murs</a>
<a href="#" onclick="loadScript('')">Chap 3: Désigner la pioche</a>
<a href="#" onclick="loadScript('')">Chap 4: Le mur mort</a>
</ul>
<ul class="menu-item"> <ul class="menu-item">
<a href="#" onclick="loadScript('')">Riichi à trois</a> <a href="#" onclick="loadScript('')">Riichi à trois</a>
</ul> </ul>
@ -123,7 +131,7 @@
} }
//script initial //script initial
loadScript('dp1.js'); loadScript('dp4.js');
</script> </script>
</body> </body>
</html> </html>

177
src/button.ts Normal file
View file

@ -0,0 +1,177 @@
type button_t = (
arg0: CanvasRenderingContext2D,
arg1: number,
arg2: number
) => void;
export function clickAction(
x: number,
y: number,
chii: boolean,
pon: boolean,
kan: boolean,
ron: boolean,
tsumo: boolean
): number {
let buttons = [
chii,
pon,
kan,
ron,
tsumo
]
if (buttons.some(c => c)) {
buttons.unshift(true);
} else {
return -1;
}
let dx = 0;
let size = 0.6;
let xmin = 1025 - buttons.filter(c => c).length * 3 * size;
console.log(x > xmin, "\n");
return -1;
}
export function drawButtons(
ctx: CanvasRenderingContext2D,
chii: boolean,
pon: boolean,
kan: boolean,
ron: boolean,
tsumo: boolean
): void {
let buttons = [
[chii, buttonChii],
[pon, buttonPon],
[kan, buttonKan],
[ron, buttonRon],
[tsumo, buttonTsumo]
]
if (buttons.some(c => c[0])) {
buttons.unshift([true, buttonPass]);
}
let dx = 0;
for (let i = 0; i < buttons.length; i++) {
if (buttons[i][0]) {
(buttons[i][1] as button_t)(
ctx,
850 - dx * 120,
835
);
dx++;
}
}
}
function button(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
r: number,
w: number,
h: number,
color: string
): void {
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.fill();
ctx.fillStyle = "#606060";
ctx.stroke();
}
function buttonPass(
ctx: CanvasRenderingContext2D,
x: number,
y: number
): void {
const r = 8;
const w = 110;
const h = 50;
// button(ctx, x, y, r, w, h, "#FFAC4D");
button(ctx, x, y, r, w, h, "#FF9030");
ctx.fillStyle = "black";
ctx.font = "30px garamond";
ctx.fillText("Ignorer",x + w * 0.1, y + h/2 * 1.3);
}
function buttonPon(
ctx: CanvasRenderingContext2D,
x: number,
y: number
): void {
const r = 8;
const w = 110;
const h = 50;
button(ctx, x, y, r, w, h, "#FFCC33");
ctx.fillStyle = "black";
ctx.font = "30px garamond";
ctx.fillText("Pon",x + w * 0.28, y + h/2 * 1.3);
}
function buttonChii(
ctx: CanvasRenderingContext2D,
x: number,
y: number
): void {
const r = 8;
const w = 110;
const h = 50;
button(ctx, x, y, r, w, h, "#FFCC33");
ctx.fillStyle = "black";
ctx.font = "30px garamond";
ctx.fillText("Chii",x + w * 0.25, y + h/2 * 1.3);
}
function buttonKan(
ctx: CanvasRenderingContext2D,
x: number,
y: number
): void {
const r = 8;
const w = 110;
const h = 50;
button(ctx, x, y, r, w, h, "#FFCC33");
ctx.fillStyle = "black";
ctx.font = "30px garamond";
ctx.fillText("Kan",x + w * 0.28, y + h/2 * 1.3);
}
function buttonRon(
ctx: CanvasRenderingContext2D,
x: number,
y: number
): void {
const r = 8;
const w = 110;
const h = 50;
button(ctx, x, y, r, w, h, "#FF3060");
ctx.fillStyle = "black";
ctx.font = "30px garamond";
ctx.fillText("Ron",x + w * 0.28, y + h/2 * 1.3);
}
function buttonTsumo(
ctx: CanvasRenderingContext2D,
x: number,
y: number
): void {
const r = 8;
const w = 110;
const h = 50;
button(ctx, x, y, r, w, h, "#FF3060");
ctx.fillStyle = "black";
ctx.font = "30px garamond";
ctx.fillText("Tsumo",x + w * 0.13, y + h/2 * 1.3);
}

View file

@ -23,7 +23,7 @@ export class Deck {
if (i < 4) { // famille if (i < 4) { // famille
for (let j = 1; j < 10; j++) { for (let j = 1; j < 10; j++) {
const tile = this.find(i, j) as NonNullable<Tile>; const tile = this.find(i, j) as NonNullable<Tile>;
tile.drawTile(ctx, posX, posY, size); tile.drawTile(ctx, posX, posY, size, false, 0, false);
posX += (75 + xOffset) * size; posX += (75 + xOffset) * size;
} }
posX = x; posX = x;

View file

@ -74,6 +74,7 @@ async function display () {
} }
// dynamic hand // dynamic hand
ehand.isolate = true;
ehand.drawHand(offScreenCtx, x, 800, 5, size, selectedTile); ehand.drawHand(offScreenCtx, x, 800, 5, size, selectedTile);
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
@ -113,8 +114,8 @@ async function display () {
if (selectedTile !== undefined) { if (selectedTile !== undefined) {
edeck.push(ehand.eject(selectedTile)); edeck.push(ehand.eject(selectedTile));
edeck.shuffle(); edeck.shuffle();
ehand.push(edeck.pop());
ehand.sort(); ehand.sort();
ehand.push(edeck.pop());
} }
} }
); );

View file

@ -69,6 +69,7 @@ function drawFrame() {
HANDS[6].drawHand(staticCtx, x + (75+xos)*size, y + 2*(100+yos)*size, 5, 0.75); HANDS[6].drawHand(staticCtx, x + (75+xos)*size, y + 2*(100+yos)*size, 5, 0.75);
HANDS[7].drawHand(staticCtx, x + 2*(75+xos)*size, y + 2*(100+yos)*size, 5, 0.75); HANDS[7].drawHand(staticCtx, x + 2*(75+xos)*size, y + 2*(100+yos)*size, 5, 0.75);
HANDS[8].isolate = true;
HANDS[8].drawHand(staticCtx, 100, 800, 5, size, selectedTile); HANDS[8].drawHand(staticCtx, 100, 800, 5, size, selectedTile);
let groups = HANDS[8].toGroup(); let groups = HANDS[8].toGroup();
@ -117,8 +118,8 @@ function initEventListeners() {
if (selectedTile !== undefined) { if (selectedTile !== undefined) {
DECKS[0].push(HANDS[8].eject(selectedTile)); DECKS[0].push(HANDS[8].eject(selectedTile));
DECKS[0].shuffle(); DECKS[0].shuffle();
HANDS[8].push(DECKS[0].pop());
HANDS[8].sort(); HANDS[8].sort();
HANDS[8].push(DECKS[0].pop());
} }
} }
}; };

View file

@ -1,12 +1,11 @@
import { Deck } from "../deck"; import { Deck } from "../deck";
import { Hand } from "../hand"; import { Hand } from "../hand";
import { Group } from "../group";
import { Game } from "../game" import { Game } from "../game"
// Configuration globale // Configuration globale
const CANVAS_ID = "myCanvas"; const CANVAS_ID = "myCanvas";
const BG_COLOR = "#007730"; const BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };
const BG_RECT = { x: 50, y: 50, w: 1000, h: 1000 }; var MOUSE = { x: 0, y: 0 };
const FPS = 30; const FPS = 30;
const FRAME_INTERVAL = 1000 / FPS; const FRAME_INTERVAL = 1000 / FPS;
@ -15,13 +14,6 @@ const DECKS: Array<Deck> = [];
const HANDS: Array<Hand> = []; const HANDS: Array<Hand> = [];
var GAME: Game|undefined; var GAME: Game|undefined;
// variables for the game
var selectedTile: number|undefined;
var turn: number = 0;
var hasPlayed: boolean = false;
var pCurrentTime = 0;
var pDeltaTime = 500;
// Optimisation des références // Optimisation des références
let animationFrameId: number; let animationFrameId: number;
let lastFrameTime = 0; let lastFrameTime = 0;
@ -30,6 +22,8 @@ const callbacks: Array<() => void> = [];
// Pré-calcul des dimensions // Pré-calcul des dimensions
const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement; const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement;
const ctx = canvas.getContext("2d") as NonNullable<CanvasRenderingContext2D>; const ctx = canvas.getContext("2d") as NonNullable<CanvasRenderingContext2D>;
canvas.width = BG_RECT.w;
canvas.height = BG_RECT.h;
// Cache statique // Cache statique
const staticCanvas = document.createElement('canvas') as HTMLCanvasElement; const staticCanvas = document.createElement('canvas') as HTMLCanvasElement;
@ -37,51 +31,14 @@ const staticCtx = staticCanvas.getContext("2d") as NonNullable<CanvasRenderingCo
staticCanvas.width = canvas.width; staticCanvas.width = canvas.width;
staticCanvas.height = canvas.height; staticCanvas.height = canvas.height;
// Pré-rendu du fond
function prerenderBackground() {
staticCtx.clearRect(0, 0, canvas.width, canvas.height);
staticCtx.fillStyle = BG_COLOR;
staticCtx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);
};
function drawFrame() { function drawFrame() {
if (!ctx) return; if (!ctx) return;
GAME?.draw(MOUSE);
let size = 0.65;
let os = 5;
let w = size * (450 + 5 * os);
let center = 525;
// Effacement intelligent (uniquement la zone nécessaire)
prerenderBackground();
// Ici viendrait le dessin des éléments dynamiques
// Par exemple:
// drawDeck();
// drawHands();
// staticCtx.fillStyle = "#005530";
// staticCtx.fillRect(center - w/2, center - w/2, w, w);
GAME?.drawGame(staticCtx, size, 0.6, selectedTile);
// Dessin du cache statique
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(staticCanvas, 0, 0);
} }
function animationLoop(currentTime: number) { function animationLoop(currentTime: number) {
animationFrameId = requestAnimationFrame(animationLoop); animationFrameId = requestAnimationFrame(animationLoop);
// bot playing
if (turn !== 0 && (currentTime - pCurrentTime) > pDeltaTime && (GAME as NonNullable<Game>).getDeck().length() > 0) {
pCurrentTime = currentTime;
let n = Math.floor(Math.random() * (GAME as NonNullable<Game>).getHands()[turn].length());
GAME?.discard(turn, n);
turn = (turn + 1) % 4;
GAME?.pick(turn);
GAME?.getHands()[0].sort();
}
const deltaTime = currentTime - lastFrameTime; const deltaTime = currentTime - lastFrameTime;
if (deltaTime < FRAME_INTERVAL) return; if (deltaTime < FRAME_INTERVAL) return;
@ -91,37 +48,13 @@ function animationLoop(currentTime: number) {
function initEventListeners() { function initEventListeners() {
const handlers = { const handlers = {
mousemove: (e: MouseEvent) => { mousedown: (e: MouseEvent) => {
// Logique de gestion du mouvement de la souris GAME?.click(e);
let size = 0.75; },
let x = 2.5 * 75 * 0.75; mousemove: (e: MouseEvent) => {
let y = 1000 - 150 * 0.6; MOUSE.x = e.x;
const rect = canvas.getBoundingClientRect(); MOUSE.y = e.y;
const mouseX = e.clientX - rect.left - x; }
const mouseY = e.clientY - rect.top;
let q = Math.floor(mouseX / (80 * size));
let r = mouseX - q * 80 * size;
if (
r <= 75 &&
q >= 0 &&
q < (GAME as NonNullable<Game>).getHands()[0].length() &&
mouseY >= y &&
mouseY <= y + 100 * size
) {
selectedTile = q;
} else {
selectedTile = undefined;
}
},
mousedown: (e: MouseEvent) => {
// Logique de gestion du clic de souris
if (turn === 0 && selectedTile !== undefined && (GAME as NonNullable<Game>).getDeck().length() > 0) {
GAME?.discard(0, selectedTile);
turn++;
GAME?.pick(turn);
}
}
}; };
callbacks.push(() => { callbacks.push(() => {
@ -151,6 +84,7 @@ export async function initDisplay() {
return; return;
} }
console.log("Load begining\n");
// Préchargement des ressources si nécessaire // Préchargement des ressources si nécessaire
// const deck = new Deck(); // const deck = new Deck();
// await preloadDeck(deck); // await preloadDeck(deck);
@ -158,14 +92,17 @@ export async function initDisplay() {
); );
HANDS.push( HANDS.push(
); );
GAME = new Game(); GAME = new Game(
ctx,
canvas,
staticCtx,
staticCanvas
);
await Promise.all(DECKS.map(d => preloadDeck(d))); await Promise.all(DECKS.map(d => preloadDeck(d)));
await Promise.all(HANDS.map(h => preloadHand(h))); await Promise.all(HANDS.map(h => preloadHand(h)));
await GAME?.preload(); await GAME?.preload();
GAME.pick(0); console.log("Loaded completed\n");
GAME.getHands()[0].sort();
initEventListeners(); initEventListeners();
requestAnimationFrame(animationLoop); requestAnimationFrame(animationLoop);
window.cleanup = cleanup; window.cleanup = cleanup;
@ -180,6 +117,6 @@ declare global {
// Initialisation automatique si le script est chargé directement // Initialisation automatique si le script est chargé directement
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
initDisplay().catch(console.error); initDisplay().catch(console.error);
} }

View file

@ -1,13 +1,49 @@
import { Deck } from "./deck"; import { Deck } from "./deck";
import { Hand } from "./hand"; import { Hand } from "./hand";
import { Tile } from "./tile"; import { Tile } from "./tile";
import { Group } from "./group";
import { drawButtons, clickAction } from "./button";
export type mousePos = { x: number, y: number};
export class Game { export class Game {
private deck: Deck; private deck: Deck;
private hands: Array<Hand>; private hands: Array<Hand>;
private discards: Array<Array<Tile>>; private discards: Array<Array<Tile>>;
private lastDiscard: number|undefined;
private groups: Array<Array<Group>>;
public constructor(red: boolean = false) { // game values
private turn = 0;
private selectedTile: number|undefined = undefined;
private canCall: boolean = false;
private hasPicked: boolean = false;
private hasPlayed: boolean = false;
private lastPlayed: number = Date.now();
// 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.deck = new Deck(red);
this.hands = [ this.hands = [
this.deck.getRandomHand(), this.deck.getRandomHand(),
@ -16,6 +52,29 @@ export class Game {
this.deck.getRandomHand() this.deck.getRandomHand()
]; ];
this.discards = [[], [], [], []]; this.discards = [[], [], [], []];
this.lastDiscard = undefined;
this.groups = [[], [], [], []];
this.hands[0].sort();
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 { public getDeck(): Deck {
@ -26,105 +85,326 @@ export class Game {
return this.hands; return this.hands;
} }
public drawGame( public click(
ctx: CanvasRenderingContext2D, mp: mousePos,
discardSize: number,
handSize: number,
selectedTile: number|undefined = undefined
): void { ): void {
let action = clickAction(
mp.x,
mp.y,
this.canDoAChii().length > 0,
this.canDoAPon(),
false,
false,
false
);
if (action === -1 && this.canCall) {
if (this.turn === 3) {
this.canCall = false;
this.turn = 0;
this.pick(0);
} else {
this.turn++;
}
this.hasPicked = false;
this.hasPlayed = false;
} else {
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
) { // bot playing
if (!this.hasPicked) { // begin of his turn
this.pick(this.turn);
this.hasPicked = true;
} else if (!this.hasPlayed) { // middle of his turn
if (Date.now() - this.lastPlayed > 700) {
this.lastPlayed = Date.now();
let n = Math.floor(this.hands[this.turn].length() * Math.random());
this.discard(this.turn, n);
this.hasPlayed = true;
this.canCall = this.canDoAPon();
}
} else if (!this.canCall) { // end of his turn
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);
this.hands[player].sort();
tile.setTilt();
this.discards[player].push(tile);
this.hands[player].isolate = false;
this.hands[player].sort();
this.lastDiscard = player;
this.lastPlayed = Date.now();
}
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("Pon !\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 {
console.log("Chii !\n");
}
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; const pi = 3.141592;
this.hands[0].drawHand( this.hands[0].drawHand(
ctx, this.staticCtx,
2.5 * 75 * 0.75, 2.5 * 75 * 0.75,
1000 - 150 * handSize, 1000 - 150 * this.sizeHand,
5 * handSize, 5 * this.sizeHand,
0.75, 0.75,
selectedTile, this.selectedTile,
false, false,
0 0
); );
this.hands[1].drawHand( this.hands[1].drawHand(
ctx, this.staticCtx,
1000 - 150 * handSize, 1000 - 150 * this.sizeHiddenHand,
1000 - 75 * 5 * handSize, 1000 - 75 * 5 * this.sizeHiddenHand,
5 * handSize, 5 * this.sizeHiddenHand,
handSize, this.sizeHiddenHand,
undefined, undefined,
true, true,
- pi / 2 - pi / 2
); );
this.hands[2].drawHand( this.hands[2].drawHand(
ctx, this.staticCtx,
1000 - 75 * 5 * handSize, 1000 - 75 * 5 * this.sizeHiddenHand,
150 * handSize, 150 * this.sizeHiddenHand,
5 * handSize, 5 * this.sizeHiddenHand,
handSize, this.sizeHiddenHand,
undefined, undefined,
true, true,
- pi - pi
); );
this.hands[3].drawHand( this.hands[3].drawHand(
ctx, this.staticCtx,
150 * handSize, 150 * this.sizeHiddenHand,
75 * 5 * handSize, 75 * 5 * this.sizeHiddenHand,
5 * handSize, 5 * this.sizeHiddenHand,
handSize, this.sizeHiddenHand,
undefined, undefined,
true, true,
pi / 2 pi / 2
); );
for (let i = 0; i < 4; i++) { }
this.drawDiscrad(ctx, i, discardSize);
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
);
}
}
} }
} }
public pick(player: number): void { private drawDiscard(
this.hands[player].push(this.deck.pop()); p: number,
} highlitedTile: Tile|undefined
): void {
const pi = 3.141592;
public discard(player: number, n: number): void { this.staticCtx.save();
let tile = this.hands[player].eject(n); this.staticCtx.translate(525, 525);
tile.setTilt(); this.staticCtx.rotate([0, -pi/2, -pi, pi/2][p])
this.discards[player].push(tile);
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())
);
}
}
if (this.lastDiscard === p) {
const j = this.discards[p].length - 1;
let tx =
j < 12 ?
x + (j % 6) * 80 * this.sizeDiscard :
x + (j - 12) * 80 * this.sizeDiscard;
let ty =
j < 12 ?
y + Math.floor(j / 6) * 105 * this.sizeDiscard :
y + 2 * 105 * this.sizeDiscard;
tx += 75 / 2 * this.sizeDiscard;
ty += 115 * this.sizeDiscard;
const ts = 10;
this.staticCtx.fillStyle = "#ff0000";
this.staticCtx.beginPath();
this.staticCtx.moveTo(tx, ty);
this.staticCtx.lineTo(tx + ts/2, ty + 0.866 * ts);
this.staticCtx.lineTo(tx - ts/2, ty + 0.866 * ts);
this.staticCtx.lineTo(tx, ty);
this.staticCtx.fill();
this.staticCtx.stroke();
}
this.staticCtx.restore();
} }
public async preload(): Promise<void> { public async preload(): Promise<void> {
await this.deck.preload(); await this.deck.preload();
await Promise.all(this.hands.map(h => h.preload())); await Promise.all(this.hands.map(h => h.preload()));
} }
private drawDiscrad(
ctx: CanvasRenderingContext2D,
p: number,
discardSize: number
): void {
const pi = 3.141592;
ctx.save();
ctx.translate(525, 525);
ctx.rotate([0, -pi/2, -pi, pi/2][p])
let x = - discardSize * 475 / 2;
let y = discardSize * (475 / 2 + 5);
for (let i = 0; i < this.discards[p].length; i++) {
if (i < 12) {
this.discards[p][i].drawTile(
ctx,
x + (i % 6) * 80 * discardSize,
y + Math.floor(i / 6) * 105 * discardSize,
discardSize
);
} else {
this.discards[p][i].drawTile(
ctx,
x + (i - 12) * 80 * discardSize,
y + 2 * 105 * discardSize,
discardSize
);
}
}
ctx.restore();
}
} }

View file

@ -2,9 +2,11 @@ import { Tile } from "./tile"
export class Group { export class Group {
private tiles: Array<Tile>; private tiles: Array<Tile>;
private stolenFrom: number|undefined;
public constructor(tiles: Array<Tile> = []) { public constructor(tiles: Array<Tile> = [], stolenFrom: number|undefined = undefined) {
this.tiles = tiles; this.tiles = tiles;
this.stolenFrom = stolenFrom;
} }
public push(tile: Tile): void { public push(tile: Tile): void {
@ -18,4 +20,115 @@ export class Group {
public getTiles(): Array<Tile> { public getTiles(): Array<Tile> {
return this.tiles; return this.tiles;
} }
public drawGroup(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
os: number,
size: number,
rotation: number,
): void {
let v = 75 * size;
let w = 90 * size;
let vx = Math.cos(rotation);
let vy = Math.sin(rotation);
let osx = Math.sin(rotation) * 25 * size / 2;
let osy = Math.cos(rotation) * 25 * size / 2;
if (!this.stolenFrom) {
//TODO error
}
let p = 3 - (this.stolenFrom as NonNullable<number>);
if (p === 0) {
this.tiles[0].drawTile(
ctx,
x + osx,
y + osy,
size,
false,
3.141592 / 2,
false
);
this.tiles[1].drawTile(
ctx,
x + vx * w,
y + vy * w,
size,
false,
0,
false
);
this.tiles[2].drawTile(
ctx,
x + vx * (w + v + os * size),
y + vy * (w + v + os * size),
size,
false,
0,
false
);
} else if (p === 1) {
this.tiles[0].drawTile(
ctx,
x,
y,
size,
false,
0,
false
);
this.tiles[1].drawTile(
ctx,
x + vx * w + osx,
y + vy * w + osy,
size,
false,
0 - 3.141592 / 2,
false
);
this.tiles[2].drawTile(
ctx,
x + vx * (w + v + 3 *os * size),
y + vy * (w + v + 3 *os * size),
size,
false,
0,
false
);
} else if (p === 2) {
this.tiles[0].drawTile(
ctx,
x,
y,
size,
false,
0,
false
);
this.tiles[1].drawTile(
ctx,
x + vx * (v + os * size),
y + vy * (v + os * size),
size,
false,
0,
false
);
this.tiles[2].drawTile(
ctx,
x + vx * (w + v + os * size) + osx,
y + vy * (w + v + os * size) + osy,
size,
false,
0 - 3.141592 / 2,
false
);
} else {
//TODO error
}
}
} }

View file

@ -3,6 +3,7 @@ import { Group } from "./group"
export class Hand { export class Hand {
private tiles: Array<Tile>; private tiles: Array<Tile>;
public isolate: boolean = false;
public constructor(stiles: string = "") { public constructor(stiles: string = "") {
this.tiles = []; this.tiles = [];
@ -26,7 +27,7 @@ export class Hand {
return this.tiles.length; return this.tiles.length;
} }
public push(tile: Tile): undefined { public push(tile: Tile): void {
this.tiles.push(tile); this.tiles.push(tile);
} }
@ -59,6 +60,14 @@ export class Hand {
return tile as NonNullable<Tile>; return tile as NonNullable<Tile>;
} }
public get(idTile: number|undefined): Tile|undefined {
if (idTile !== undefined) {
return this.tiles[idTile];
} else {
return undefined;
}
}
public sort(): undefined { public sort(): undefined {
this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1); this.tiles.sort((a, b) => a.isLessThan(b) ? -1 : 1);
} }
@ -147,11 +156,18 @@ export class Hand {
let vx = Math.cos(rotation) * v; let vx = Math.cos(rotation) * v;
let vy = Math.sin(rotation) * v; let vy = Math.sin(rotation) * v;
for (let i = 0; i < this.tiles.length; i++) { for (let i = 0; i < this.tiles.length; i++) {
let e = (i === this.tiles.length - 1 && this.isolate) ? 10 : 0;
if (i === focusedTiled) { if (i === focusedTiled) {
this.tiles[i].drawTile( this.tiles[i].drawTile(
ctx, ctx,
x + i * vx + 25 * size * Math.sin(rotation), x +
y + i * vy - 25 * size * Math.cos(rotation), i * vx +
25 * size * Math.sin(rotation) +
e * size * Math.cos(rotation),
y +
i * vy -
25 * size * Math.cos(rotation) +
e * size * Math.sin(rotation),
size, size,
hidden, hidden,
rotation rotation
@ -159,8 +175,8 @@ export class Hand {
} else { } else {
this.tiles[i].drawTile( this.tiles[i].drawTile(
ctx, ctx,
x + i * vx, x + i * vx + e * size * Math.cos(rotation),
y + i * vy, y + i * vy + e * size * Math.sin(rotation),
size, size,
hidden, hidden,
rotation rotation

View file

@ -5,6 +5,7 @@ export class Tile {
private imgSrc: string; private imgSrc: string;
private imgFront: HTMLImageElement; private imgFront: HTMLImageElement;
private imgBack: HTMLImageElement; private imgBack: HTMLImageElement;
private imgGray: HTMLImageElement;
private img: HTMLImageElement; private img: HTMLImageElement;
private tilt: number; private tilt: number;
@ -15,6 +16,7 @@ export class Tile {
this.imgSrc = ""; this.imgSrc = "";
this.imgFront = new Image(); this.imgFront = new Image();
this.imgBack = new Image(); this.imgBack = new Image();
this.imgGray = new Image();
this.img = new Image(); this.img = new Image();
this.tilt = 0; this.tilt = 0;
this.setImgSrc(); this.setImgSrc();
@ -28,12 +30,16 @@ export class Tile {
return this.value; return this.value;
} }
public isEqual(family: number, value: number): boolean {
return this.family === family && this.value === value;
}
public isRed(): boolean { public isRed(): boolean {
return this.red; return this.red;
} }
public setTilt(): void { public setTilt(): void {
this.tilt = (1 - 2 * Math.random()) * 0.05; this.tilt = (1 - 2 * Math.random()) * 0.04;
} }
public drawTile( public drawTile(
@ -42,7 +48,8 @@ export class Tile {
y: number, y: number,
size: number, size: number,
hidden: boolean = false, hidden: boolean = false,
rotation: number = 0 rotation: number = 0,
gray: boolean = false
): void { ): void {
ctx.save(); ctx.save();
ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2); ctx.translate(x + (75 * size) / 2, y + (100 * size) / 2);
@ -69,6 +76,15 @@ export class Tile {
75 * size, 75 * size,
100 * size 100 * size
); );
if (gray) {
ctx.drawImage(
this.imgGray,
-(75 * size) / 2,
-(100 * size) / 2,
75 * size,
100 * size
);
}
} }
ctx.restore(); ctx.restore();
@ -89,6 +105,8 @@ export class Tile {
this.imgFront.onerror = null; this.imgFront.onerror = null;
this.imgBack.onload = null; this.imgBack.onload = null;
this.imgBack.onerror = null; this.imgBack.onerror = null;
this.imgGray.onload = null;
this.imgGray.onerror = null;
this.img.onload = null; this.img.onload = null;
this.img.onerror = null; this.img.onerror = null;
} }
@ -113,6 +131,7 @@ export class Tile {
await Promise.all([ await Promise.all([
this.loadImg(this.imgFront, "/img/Regular/Front.svg"), this.loadImg(this.imgFront, "/img/Regular/Front.svg"),
this.loadImg(this.imgBack, "/img/Regular/Back.svg"), this.loadImg(this.imgBack, "/img/Regular/Back.svg"),
this.loadImg(this.imgGray, "/img/Regular/Gray.svg"),
this.loadImg(this.img, this.imgSrc) this.loadImg(this.img, this.imgSrc)
]); ]);
} }