anticipate next chapter and few rectification
This commit is contained in:
parent
b1ddcc6c84
commit
8678f46816
7 changed files with 183 additions and 17 deletions
22
index.html
22
index.html
|
|
@ -75,15 +75,17 @@
|
||||||
<a href="#" onclick="loadScript('dp2.js')">Chap 2: La main</a>
|
<a href="#" onclick="loadScript('dp2.js')">Chap 2: La main</a>
|
||||||
<a href="#" onclick="loadScript('dp3.js')">Chap 3: Les groupes</a>
|
<a href="#" onclick="loadScript('dp3.js')">Chap 3: Les groupes</a>
|
||||||
<a href="#" onclick="loadScript('dp4.js')">Chap 4: Avoir une main valide en jeu</a>
|
<a href="#" onclick="loadScript('dp4.js')">Chap 4: Avoir une main valide en jeu</a>
|
||||||
<a href="#" onclick="loadScript('dp5.js')">Chap 5: Le vent du joueur</a>
|
<a href="#" onclick="loadScript('dp5.js')">Chap 5: Annoncer la victoire</a>
|
||||||
<a href="#" onclick="loadScript('dp6.js')">Chap 6: Les yakus</a>
|
<a href="#" onclick="loadScript('dp6.js')">Chap 6: Le furiten</a>
|
||||||
<a href="#" onclick="loadScript('dp7.js')">Chap 7: Le riichi</a>
|
<a href="#" onclick="loadScript('dp7.js')">Chap 7: Le vent du joueur</a>
|
||||||
<a href="#" onclick="loadScript('dp8.js')">Chap 8: Première partie complète</a>
|
<a href="#" onclick="loadScript('dp8.js')">Chap 8: Les yakus</a>
|
||||||
<a href="#" onclick="loadScript('dp9.js')">Chap 9: Score: les Hans</a>
|
<a href="#" onclick="loadScript('dp9.js')">Chap 9: Le riichi</a>
|
||||||
<a href="#" onclick="loadScript('dp10.js')">Chap 10: Score: les doras</a>
|
<a href="#" onclick="loadScript('dp10.js')">Chap 10: Première partie complète</a>
|
||||||
<a href="#" onclick="loadScript('dp11.js')">Chap 11: Score: les Fus</a>
|
<a href="#" onclick="loadScript('dp11.js')">Chap 11: Score: les Hans</a>
|
||||||
<a href="#" onclick="loadScript('dp12.js')">Chap 12: Score: le calcul des points</a>
|
<a href="#" onclick="loadScript('dp12.js')">Chap 12: Score: les doras</a>
|
||||||
<a href="#" onclick="loadScript('dp13.js')">Chap 13: Partie avec les scores</a>
|
<a href="#" onclick="loadScript('dp13.js')">Chap 13: Score: les Fus</a>
|
||||||
|
<a href="#" onclick="loadScript('dp14.js')">Chap 14: Score: le calcul des points</a>
|
||||||
|
<a href="#" onclick="loadScript('dp15.js')">Chap 15: Partie avec les scores</a>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="menu-item">
|
<ul class="menu-item">
|
||||||
|
|
@ -158,7 +160,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
//script initial
|
//script initial
|
||||||
loadScript('dp4.js');
|
loadScript('dp5.js');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
102
src/display/dp5.ts
Normal file
102
src/display/dp5.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { Deck } from "../deck";
|
||||||
|
import { Hand } from "../hand";
|
||||||
|
import { Game } from "../game"
|
||||||
|
|
||||||
|
// Configuration globale
|
||||||
|
const CANVAS_ID = "myCanvas";
|
||||||
|
const BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 };
|
||||||
|
var MOUSE = { x: 0, y: 0 };
|
||||||
|
const FPS = 30;
|
||||||
|
const FRAME_INTERVAL = 1000 / FPS;
|
||||||
|
|
||||||
|
// variables globales
|
||||||
|
const DECKS: Array<Deck> = [];
|
||||||
|
const HANDS: Array<Hand> = [];
|
||||||
|
var GAME: Game|undefined;
|
||||||
|
|
||||||
|
// Optimisation des références
|
||||||
|
let animationFrameId: number;
|
||||||
|
let lastFrameTime = 0;
|
||||||
|
const callbacks: Array<() => void> = [];
|
||||||
|
|
||||||
|
// Pré-calcul des dimensions
|
||||||
|
const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement;
|
||||||
|
const ctx = canvas.getContext("2d") as NonNullable<CanvasRenderingContext2D>;
|
||||||
|
canvas.width = BG_RECT.w;
|
||||||
|
canvas.height = BG_RECT.h;
|
||||||
|
|
||||||
|
// Cache statique
|
||||||
|
const staticCanvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||||
|
const staticCtx = staticCanvas.getContext("2d") as NonNullable<CanvasRenderingContext2D>;
|
||||||
|
staticCanvas.width = canvas.width;
|
||||||
|
staticCanvas.height = canvas.height;
|
||||||
|
|
||||||
|
function drawFrame() {
|
||||||
|
if (!ctx) return;
|
||||||
|
GAME?.draw(MOUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animationLoop(currentTime: number) {
|
||||||
|
animationFrameId = requestAnimationFrame(animationLoop);
|
||||||
|
|
||||||
|
const deltaTime = currentTime - lastFrameTime;
|
||||||
|
if (deltaTime < FRAME_INTERVAL) return;
|
||||||
|
|
||||||
|
lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL);
|
||||||
|
drawFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEventListeners() {
|
||||||
|
const handlers = {
|
||||||
|
mousedown: (e: MouseEvent) => {
|
||||||
|
GAME?.click(e);
|
||||||
|
},
|
||||||
|
mousemove: (e: MouseEvent) => {
|
||||||
|
MOUSE.x = e.x;
|
||||||
|
MOUSE.y = e.y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
callbacks.push(() => {
|
||||||
|
canvas.removeEventListener('mousemove', handlers.mousemove);
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.addEventListener('mousedown', handlers.mousedown);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function preloadDeck(deck: Deck) {
|
||||||
|
await deck.preload();
|
||||||
|
}
|
||||||
|
async function preloadHand(hand: Hand) {
|
||||||
|
await hand.preload();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanup() {
|
||||||
|
cancelAnimationFrame(animationFrameId);
|
||||||
|
callbacks.forEach(fn => fn());
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initDisplay() {
|
||||||
|
if (!ctx) {
|
||||||
|
console.error("Context canvas indisponible");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Load begining\n");
|
||||||
|
// Préchargement des ressources si nécessaire
|
||||||
|
// const deck = new Deck();
|
||||||
|
// await preloadDeck(deck);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Déclaration globale pour TypeScript
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
cleanup: () => void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation automatique si le script est chargé directement
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
initDisplay().catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
21
src/game.ts
21
src/game.ts
|
|
@ -18,6 +18,9 @@ export class Game {
|
||||||
// game values
|
// game values
|
||||||
private level: number;
|
private level: number;
|
||||||
private turn = 0;
|
private turn = 0;
|
||||||
|
private minWaitingTime = 500;
|
||||||
|
private maxWaitingTime = 2000;
|
||||||
|
private waitingTime = 700;
|
||||||
private selectedTile: number|undefined = undefined;
|
private selectedTile: number|undefined = undefined;
|
||||||
private canCall: boolean = false;
|
private canCall: boolean = false;
|
||||||
private hasPicked: boolean = false;
|
private hasPicked: boolean = false;
|
||||||
|
|
@ -139,6 +142,7 @@ export class Game {
|
||||||
} else {
|
} else {
|
||||||
this.turn++;
|
this.turn++;
|
||||||
}
|
}
|
||||||
|
this.updateWaitingTime();
|
||||||
this.hasPicked = false;
|
this.hasPicked = false;
|
||||||
this.hasPlayed = false;
|
this.hasPlayed = false;
|
||||||
} else if (action === 1) { // chii
|
} else if (action === 1) { // chii
|
||||||
|
|
@ -162,6 +166,7 @@ export class Game {
|
||||||
let i = Math.floor(Math.random() * chiis.length);
|
let i = Math.floor(Math.random() * chiis.length);
|
||||||
this.chii(chiis[i], 1);
|
this.chii(chiis[i], 1);
|
||||||
} else {
|
} else {
|
||||||
|
this.updateWaitingTime();
|
||||||
this.turn = (this.turn + 1) % 4;
|
this.turn = (this.turn + 1) % 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -196,6 +201,15 @@ export class Game {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private updateWaitingTime(): void {
|
||||||
|
this.waitingTime =
|
||||||
|
Math.floor(
|
||||||
|
Math.random() *
|
||||||
|
(this.maxWaitingTime - this.minWaitingTime) +
|
||||||
|
this.minWaitingTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private play(): void {
|
private play(): void {
|
||||||
if (
|
if (
|
||||||
this.turn !== 0 && !this.end
|
this.turn !== 0 && !this.end
|
||||||
|
|
@ -208,7 +222,7 @@ export class Game {
|
||||||
if (this.hasWin(this.turn)) {
|
if (this.hasWin(this.turn)) {
|
||||||
this.end = true;
|
this.end = true;
|
||||||
this.result = 2;
|
this.result = 2;
|
||||||
} else if (Date.now() - this.lastPlayed > 700) {
|
} else if (Date.now() - this.lastPlayed > this.waitingTime) {
|
||||||
this.lastPlayed = Date.now();
|
this.lastPlayed = Date.now();
|
||||||
let n = Math.floor(this.hands[this.turn].length() * Math.random());
|
let n = Math.floor(this.hands[this.turn].length() * Math.random());
|
||||||
this.discard(this.turn, n);
|
this.discard(this.turn, n);
|
||||||
|
|
@ -238,6 +252,7 @@ export class Game {
|
||||||
} else {
|
} else {
|
||||||
this.turn++;
|
this.turn++;
|
||||||
}
|
}
|
||||||
|
this.updateWaitingTime();
|
||||||
this.hasPicked = false;
|
this.hasPicked = false;
|
||||||
this.hasPlayed = false;
|
this.hasPlayed = false;
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +329,7 @@ export class Game {
|
||||||
this.end = true;
|
this.end = true;
|
||||||
this.result = p === 0 ? 1 : 2;
|
this.result = p === 0 ? 1 : 2;
|
||||||
}
|
}
|
||||||
|
this.updateWaitingTime();
|
||||||
this.turn = p;
|
this.turn = p;
|
||||||
this.hasPicked = true;
|
this.hasPicked = true;
|
||||||
this.hasPlayed = false;
|
this.hasPlayed = false;
|
||||||
|
|
@ -381,7 +396,7 @@ export class Game {
|
||||||
this.end = true;
|
this.end = true;
|
||||||
this.result = thief === 0 ? 1 : 2;
|
this.result = thief === 0 ? 1 : 2;
|
||||||
}
|
}
|
||||||
|
this.updateWaitingTime();
|
||||||
this.turn = thief;
|
this.turn = thief;
|
||||||
this.hasPicked = true;
|
this.hasPicked = true;
|
||||||
this.hasPlayed = false;
|
this.hasPlayed = false;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,11 @@ Bienvenu sur ce site dédié au *Mahjong* !
|
||||||
Ce site est principalement destiné aux personnes découvrant ce
|
Ce site est principalement destiné aux personnes découvrant ce
|
||||||
formidable jeu.
|
formidable jeu.
|
||||||
|
|
||||||
De nombreux services sont proposés:
|
Le nombre de chapitre de chaque section peut faire peur aux
|
||||||
|
premiers abords, mais chacun deux n'est pas plus long que ce
|
||||||
|
même texte
|
||||||
|
|
||||||
|
Cependant, de nombreux services y sont proposés:
|
||||||
|
|
||||||
- *Introduction riichi majhong* dans l'onglet #ff00ff{Riichi Mahjong}
|
- *Introduction riichi majhong* dans l'onglet #ff00ff{Riichi Mahjong}
|
||||||
|
|
||||||
|
|
@ -19,8 +23,6 @@ De nombreux services sont proposés:
|
||||||
#ff00ff{MCR}
|
#ff00ff{MCR}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Evidemment, ce site n'est pas encore fini, et possède encore
|
Evidemment, ce site n'est pas encore fini, et possède encore
|
||||||
beaucoup de problème. Pour le moindre retour, ne pas hésiter
|
beaucoup de problème. Pour le moindre retour, ne pas hésiter
|
||||||
à le faire savoir directement sur le Github pour pour aider
|
à le faire savoir directement sur le Github pour pour aider
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,4 @@ veut faire un *Pon*, alors le *Pon* a *toujours* la priorité
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
~Il est maintenant tant de choisir une tuile à défausser !~
|
~Il est maintenant temps de choisir une tuile à défausser !~
|
||||||
|
|
|
||||||
18
src/text/txt5.ts
Normal file
18
src/text/txt5.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { drawText } from "./parse"
|
||||||
|
|
||||||
|
const CANVAS_ID = "myTextCanvas";
|
||||||
|
const BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: "#007733" };
|
||||||
|
|
||||||
|
const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement;
|
||||||
|
const ctx = canvas.getContext("2d") as NonNullable<CanvasRenderingContext2D>;
|
||||||
|
canvas.width = BG_RECT.w;
|
||||||
|
canvas.height = BG_RECT.h;
|
||||||
|
|
||||||
|
const path = "../src/text/";
|
||||||
|
|
||||||
|
ctx.fillStyle = BG_RECT.color;
|
||||||
|
ctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);
|
||||||
|
|
||||||
|
drawText(path + "txt5.txt", ctx).catch(error => console.error(error));
|
||||||
|
|
||||||
|
export {};
|
||||||
27
src/text/txt5.txt
Normal file
27
src/text/txt5.txt
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
Nous avons appris jusque là que pour annoncer un *Chii*, il
|
||||||
|
fallait nécessairement que cela se fasse via le joueur précédent.
|
||||||
|
|
||||||
|
Il existe cependant une exception: *s'il ne nous manque plus
|
||||||
|
qu'une seule tuile*. Dans ce cas précis, nous disons que le
|
||||||
|
joueur est en #ff00ff{Tenpai} (il n'est pas obligé de retenir ce terme pour
|
||||||
|
jouer). À ce moment là, #ff0000{qui que ce soit} qui défausse une tuile,
|
||||||
|
un joueur en *Tenpai* peut la récupérer directement si elle lui
|
||||||
|
donne la victoire
|
||||||
|
|
||||||
|
Il y a alors deux situations possibles:
|
||||||
|
- Le joueur en *Tenpai* pioche une tuile qui le fait gagner. Il
|
||||||
|
l'annonce alors avec #ff00ff{Tsumo !}
|
||||||
|
- Le joueur en *Tenpai* gagne sur la défausse d'un autre joueur.
|
||||||
|
Il l'annonce alors avec #ff00ff{Ron !}
|
||||||
|
|
||||||
|
Par la suite, un nouveau bouton apparaitra pour permettre au
|
||||||
|
joueur d'annoncer sa victoire. Le plus important à retenir,
|
||||||
|
c'est que *Pon* et *Tsumo* signifie la victoire
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
~Dans l'exemple ci-contre, le joueur peut annoncer sa victoire
|
||||||
|
avec *Ron* sur la défausse du joueur d'en face en complétant
|
||||||
|
un *Chii*~
|
||||||
Loading…
Reference in a new issue