From 9e288a26f78cf5afb4fd525298296881a0fb292d Mon Sep 17 00:00:00 2001 From: Didictateur Date: Tue, 15 Apr 2025 16:59:37 +0200 Subject: [PATCH] added chapter 6 --- build/dp4.js | 2 +- build/dp6.js | 1 + build/txt6.js | 1 + index.html | 8 +- src/display/dp6.ts | 256 +++++++++++++++++++++++++++++++++++++++++++++ src/game.ts | 8 +- src/text/txt6.ts | 18 ++++ src/text/txt6.txt | 28 +++++ 8 files changed, 315 insertions(+), 7 deletions(-) create mode 100644 build/dp6.js create mode 100644 build/txt6.js create mode 100644 src/display/dp6.ts create mode 100644 src/text/txt6.ts create mode 100644 src/text/txt6.txt diff --git a/build/dp4.js b/build/dp4.js index 385ef4b..7156e25 100644 --- a/build/dp4.js +++ b/build/dp4.js @@ -1 +1 @@ -(()=>{"use strict";class t{constructor(t,i,s){this.family=t,this.value=i,this.red=s,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,e=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}loadImg(t,i){return new Promise(((s,e)=>{t.onload=()=>s(),t.onerror=()=>e(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,s){this.tiles=t,this.stolenFrom=i,this.belongsTo=s}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,s,e,h,a,n){t.save(),t.translate(525,525),t.rotate(a),t.translate(-525,-525);const l=75*h,r=90*h,o=25*h/2,c=(this.belongsTo-this.stolenFrom-1+4)%4,d=void 0===n?-1:n.getFamily(),u=void 0===n?0:n.getValue(),g=(i,s,e,a)=>{i.drawTile(t,s,e,h,!1,a,i.isEqual(d,u))},f=Math.PI/2;switch(c){case 0:g(this.tiles[0],i,s+o,f),g(this.tiles[1],i+r,s,0),g(this.tiles[2],i+r+l+e*h,s,0);break;case 1:g(this.tiles[0],i,s,0),g(this.tiles[1],i+r,s+o,-f),g(this.tiles[2],i+r+l+3*e*h,s,0);break;case 2:g(this.tiles[0],i,s,0),g(this.tiles[1],i+l+e*h,s,0),g(this.tiles[2],i+r+l+e*h,s+o,-f);break;default:console.error(`Position non prise en charge: ${c}`)}t.restore()}}const s="m",e=1,h="p",a=2,n="s",l=3,r="w",o=4,c="d",d=5;class u{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;is.getFamily()===t&&s.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((s=>s.getFamily()===t&&s.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),s=i.getFamily(),e=i.getValue();if(this.count(s,e)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(s,e)>=2){const s=this.tryFormTriplet(i,t);if(s)return s}const h=this.count(s,e-1)>0,a=this.count(s,e-2)>0;if(h&&a){const s=this.tryFormSequence(i,t);if(s)return s}this.tiles.push(i),this.sort()}tryFormPair(t){const s=this.find(t.getFamily(),t.getValue()),e=this.toGroup(!0);if(this.tiles.push(s),this.sort(),void 0!==e)return this.tiles.push(t),this.sort(),e.push(new i([t,s],0,0)),e}tryFormTriplet(t,s){const e=this.find(t.getFamily(),t.getValue()),h=this.find(t.getFamily(),t.getValue()),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([t,e,h],0,0)),this.tiles.push(t),this.sort(),a}tryFormSequence(t,s){const e=this.find(t.getFamily(),t.getValue()-1),h=this.find(t.getFamily(),t.getValue()-2),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([h,e,t],0,0)),this.tiles.push(t),this.sort(),a}drawHand(t,i,s,e,h,a=void 0,n=!1,l=0){const r=(75+e)*h,o=Math.cos(l)*r,c=Math.sin(l)*r;for(let e=0;et.preloadImg())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class g{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,s,e,h=0,a=0){let n=i,l=s;const r=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const s of r){for(let i=s.start;i<=s.end;i++){const a=this.find(s.family,i);a&&(a.drawTile(t,n,l,e,!1,0,!1),n+=(75+h)*e)}n=i,l+=(100+a)*e}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(i);return s&&s.length>0&&(s.pop(),0===s.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,s)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const s=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(s)||[];e.push(i),this.tileIndexMap.set(s,e)}find(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);if(!e||0===e.length)return;const h=e[0];if(h>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[h],this.tiles[0]]=[this.tiles[0],this.tiles[h]],this.updateIndicesAfterSwap(0,h);const a=this.tiles.shift();return this.decrementIndicesAfterShift(),a}count(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);return e?e.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new u;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,e=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const s=this.tiles[t],e=this.tiles[i],h=this.getTileKey(s.getFamily(),s.getValue()),a=this.getTileKey(e.getFamily(),e.getValue()),n=this.tileIndexMap.get(h)||[],l=this.tileIndexMap.get(a)||[],r=n.indexOf(i),o=l.indexOf(t);-1!==r&&(n[r]=t),-1!==o&&(l[o]=i),this.tileIndexMap.set(h,n),this.tileIndexMap.set(a,l)}decrementIndicesAfterShift(){var t;for(const[i,s]of this.tileIndexMap.entries())this.tileIndexMap.set(i,s.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;tt)).map((([,t])=>t));return a.length>0&&a.push(m.action),a}(s,e,h,a,n);if(0===l.length)return-1;const r=960-l.length*f;if(!(838=0&&o10?l[o]:-1}(t.x-i.x,t.y-i.y,this.canDoAChii().length>0,this.canDoAPon(),!1,!1,!1);this.canCall&&-1!==s?this.handleCallAction(s):0===this.turn&&void 0!==this.selectedTile&&this.handlePlayerDiscard()}handleChiiSelection(t,i){const s=this.getChii(0),e=function(t,i,s){if(0===s.length)return-1;const e=960-(s.length+1)*f;if(!(838=0&&h10?h===s.length?0:s[h][0].getValue():-1}(t.x-i.x,t.y-i.y,s);0===e?this.chooseChii=!1:(this.chooseChii=!1,this.chii(e,0))}handleCallAction(t){if(0===t)this.canCall=!1,this.advanceTurn();else if(1===t){const t=this.canDoAChii();1===t.length?this.chii(t[0],0):(this.chooseChii=!0,this.drawGame())}else 2===t&&this.pon(this.turn)}handlePlayerDiscard(){if(this.discard(0,this.selectedTile),!this.checkPon()){const t=this.canDoAChii(1);if(t.length>0){const i=Math.floor(Math.random()*t.length);this.chii(t[i],1)}else this.advanceTurn()}}advanceTurn(){this.updateWaitingTime(),this.turn=(this.turn+1)%4,this.pick(this.turn),this.hasPicked=!0,this.hasPlayed=!1}getSelected(t){this.cv.getBoundingClientRect();const i=E.HAND_SIZE,s=t.x-140.625,e=t.y,h=Math.floor(s/(83.9*i));s-83.9*h*i<=80.9*i&&h>=0&&h=900&&e<=900+100*i?this.selectedTile=h:this.selectedTile=void 0}updateWaitingTime(){this.waitingTime=Math.floor(Math.random()*(S-P)+P)}play(){0===this.turn||this.end||(this.hasPicked?this.hasPlayed?this.canCall||this.advanceBotTurn():this.handleBotTurn():(this.lastPlayed=Date.now(),this.pick(this.turn),this.hasPicked=!0))}handleBotTurn(){if(this.hasWin(this.turn))return this.end=!0,void(this.result=2);if(Date.now()-this.lastPlayed>this.waitingTime){this.lastPlayed=Date.now();const t=Math.floor(this.hands[this.turn].length()*Math.random());if(this.discard(this.turn,t),this.hasPlayed=!0,this.deck.length()<=0)return this.result=0,void(this.end=!0);this.end||(this.checkPon(),this.checkForChii(),this.canCall=this.canDoAChii().length>0||this.canDoAPon())}}checkForChii(){if(3===this.turn)return;const t=this.turn+1,i=this.canDoAChii(t);if(i.length>0){const s=Math.floor(Math.random()*i.length);this.chii(i[s],t)}}advanceBotTurn(){3===this.turn?(this.turn=0,this.pick(0),this.hasWin(0)&&(this.end=!0,this.result=1)):this.turn++,this.updateWaitingTime(),this.hasPicked=!1,this.hasPlayed=!1}pick(t){this.hands[t].push(this.deck.pop()),this.hands[t].isolate=!0}discard(t,i){const s=this.hands[t].eject(i);this.hands[t].sort(),s.setTilt(),this.discards[t].push(s),this.hands[t].isolate=!1,this.hands[t].sort(),this.lastDiscard=t,this.lastPlayed=Date.now()}canDoAChii(t=0){const i=[];if(void 0===this.lastDiscard||(this.lastDiscard+1)%4!==t||(this.turn+1)%4!==t||this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1].getFamily()>=4)return i;const s=this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1],e=this.hands[t],h=s.getFamily(),a=s.getValue();return e.count(h,a-2)>0&&e.count(h,a-1)>0&&i.push(a-2),e.count(h,a-1)>0&&e.count(h,a+1)>0&&i.push(a-1),e.count(h,a+1)>0&&e.count(h,a+2)>0&&i.push(a),i}chii(t,s){const e=0===s?3:s-1,h=this.discards[e].pop();this.lastDiscard=void 0;const a=[];let n=0,l=t;for(;n<2;)l===h.getValue()||(a[n]=this.hands[s].find(h.getFamily(),l),n++),l++;[h,a[0],a[1]].forEach((t=>t.setTilt())),this.groups[s].push(new i([h,a[0],a[1]],e,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}getChii(t){const i=[],s=this.canDoAChii(),e=0===t?3:t-1,h=this.discards[e],a=h[h.length-1];for(let e=0;e=2}pon(t,s=0){const e=this.discards[t].pop();this.lastDiscard=void 0;const h=this.hands[s].find(e.getFamily(),e.getValue()),a=this.hands[s].find(e.getFamily(),e.getValue());[e,h,a].forEach((t=>t.setTilt())),this.groups[s].push(new i([e,h,a],t,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}hasWin(t){return void 0!==this.hands[t].toGroup()}drawGame(){this.play(),function(t,i=0,s=0){let e=525,h=Math.PI,a=630;t.save(),t.translate(525,525),t.rotate([0,-h/2,h,h/2][i]),t.translate(-525,-525),t.fillStyle="#ffcc33",t.beginPath(),t.moveTo(e,a),t.lineTo(452,a),t.quadraticCurveTo(450,a,450,632),t.lineTo(450,632),t.quadraticCurveTo(450,634,452,634),t.lineTo(598,634),t.quadraticCurveTo(600,634,600,632),t.lineTo(600,632),t.quadraticCurveTo(600,a,598,a),t.lineTo(e,a),t.fill(),t.stroke(),t.restore(),t.save(),t.translate(525,525),t.rotate(s),t.translate(-525,-525),t.fillStyle="#e0e0f0",t.beginPath(),t.moveTo(425,e),t.lineTo(425,595),t.quadraticCurveTo(425,625,455,625),t.lineTo(595,625),t.quadraticCurveTo(625,625,625,595),t.lineTo(625,455),t.quadraticCurveTo(625,425,595,425),t.lineTo(455,425),t.quadraticCurveTo(425,425,425,455),t.lineTo(425,e),t.fill(),t.stroke(),t.fillStyle="#000000",t.font="40px garamond",t.fillText("Est",505,615),t.save(),t.translate(525,525),t.rotate(-1.570796),t.translate(-525,-525),t.fillText("Sud",500,615),t.restore(),t.save(),t.translate(525,525),t.rotate(3.141592),t.translate(-525,-525),t.fillText("Ouest",480,615),t.restore(),t.save(),t.translate(525,525),t.rotate(1.570796),t.translate(-525,-525),t.fillText("Nord",485,615),t.restore(),t.restore()}(this.staticCtx,this.turn),this.drawDiscardSize(),this.drawResult(),this.drawHands(),this.drawGroups(E.GROUP_SIZE);for(let t=0;t<4;t++)this.drawDiscard(t,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0);this.chooseChii?function(t,i){const s=[...i].reverse();I(t,850,835,T);let e=1;for(const i of s)w(t,850-e*f,835,i),e++}(this.staticCtx,this.getChii(0)):function(t,i,s){const e=[[i,(t,i,s)=>I(t,i,s,p)],[s,(t,i,s)=>I(t,i,s,v)],[!1,(t,i,s)=>I(t,i,s,y)],[!1,(t,i,s)=>I(t,i,s,C)],[!1,(t,i,s)=>I(t,i,s,x)]];if(!e.some((([t])=>t)))return;e.unshift([!0,(t,i,s)=>I(t,i,s,m)]);let h=0;for(const[i,s]of e)i&&(s(t,850-h*f,835),h++)}(this.staticCtx,this.canDoAChii().length>0,this.canDoAPon())}drawHands(){const{HAND_SIZE:t,HIDDEN_HAND_SIZE:i}=E;this.hands[0].drawHand(this.staticCtx,140.625,1e3-150*t,5*t,.75,this.selectedTile,!1,0),this.hands[1].drawHand(this.staticCtx,1e3-150*i,1e3-375*i,5*i,i,void 0,!0,-M/2),this.hands[2].drawHand(this.staticCtx,1e3-375*i,150*i,5*i,i,void 0,!0,-M),this.hands[3].drawHand(this.staticCtx,150*i,375*i,5*i,i,void 0,!0,M/2)}drawGroups(t){for(let i=0;i<4;i++){const s=this.rotations[i],e=this.groups[i];if(e.length>0)for(let i=e.length-1;i>=0;i--)e[i].drawGroup(this.staticCtx,810-285*t*i,988,5,.6,s,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0)}}drawDiscard(t,i){const s=E.DISCARD_SIZE;this.staticCtx.save(),this.staticCtx.translate(525,525),this.staticCtx.rotate(this.rotations[t]);const e=475*-s/2,h=242.5*s;for(let a=0;at.preload())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}}var R=function(t,i,s,e){return new(s||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}))};class V{constructor(){this.CANVAS_ID="myCanvas",this.BG_RECT={x:0,y:0,w:1050,h:1050},this.FPS=60,this.FRAME_INTERVAL=1e3/this.FPS,this.mouse={x:0,y:0},this.game=null,this.decks=[],this.hands=[],this.animationFrameId=null,this.lastFrameTime=0,this.isInitialized=!1,this.cleanupCallbacks=[];const t=document.getElementById(this.CANVAS_ID);if(!t)throw new Error(`Canvas avec ID '${this.CANVAS_ID}' introuvable`);this.canvas=t,this.canvas.width=this.BG_RECT.w,this.canvas.height=this.BG_RECT.h;const i=t.getContext("2d",{alpha:!1});if(!i)throw new Error("Impossible d'obtenir le contexte 2D du canvas");this.ctx=i,this.staticCanvas=document.createElement("canvas"),this.staticCanvas.width=t.width,this.staticCanvas.height=t.height;const s=this.staticCanvas.getContext("2d",{alpha:!1});if(!s)throw new Error("Impossible d'obtenir le contexte du canvas statique");this.staticCtx=s}static getInstance(){return V.instance||(V.instance=new V),V.instance}drawFrame(){this.game&&this.game.draw(this.mouse)}startAnimationLoop(){const t=i=>{this.animationFrameId=requestAnimationFrame(t);const s=i-this.lastFrameTime;s{const i=this.canvas.getBoundingClientRect();this.mouse.x=t.clientX-i.left,this.mouse.y=t.clientY-i.top},i=t=>{this.game&&this.game.click(t)};this.canvas.addEventListener("mousemove",t),this.canvas.addEventListener("mousedown",i),this.cleanupCallbacks.push((()=>{this.canvas.removeEventListener("mousemove",t),this.canvas.removeEventListener("mousedown",i)}))}preloadDeck(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}preloadHand(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}initialize(){return R(this,void 0,void 0,(function*(){if(!this.isInitialized)try{console.log("Chargement en cours..."),this.game=new A(this.ctx,this.canvas,this.staticCtx,this.staticCanvas),yield Promise.all([...this.decks.map((t=>this.preloadDeck(t))),...this.hands.map((t=>this.preloadHand(t))),this.game.preload()]),console.log("Chargement terminé"),this.initEventListeners(),this.startAnimationLoop(),this.isInitialized=!0,window.cleanup=this.cleanup.bind(this)}catch(t){console.error("Erreur lors de l'initialisation:",t)}}))}cleanup(){var t,i;null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.cleanupCallbacks.forEach((t=>t())),this.cleanupCallbacks=[],this.game&&(null===(i=(t=this.game).cleanup)||void 0===i||i.call(t),this.game=null),this.decks.forEach((t=>t.cleanup())),this.hands.forEach((t=>t.cleanup())),this.decks=[],this.hands=[],this.isInitialized=!1,this.lastFrameTime=0,this.mouse={x:0,y:0},this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.staticCtx.clearRect(0,0,this.staticCanvas.width,this.staticCanvas.height)}}"undefined"!=typeof window&&function(){return R(this,void 0,void 0,(function*(){yield V.getInstance().initialize()}))}().catch(console.error)})(); \ No newline at end of file +(()=>{"use strict";class t{constructor(t,i,s){this.family=t,this.value=i,this.red=s,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,e=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}loadImg(t,i){return new Promise(((s,e)=>{t.onload=()=>s(),t.onerror=()=>e(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,s){this.tiles=t,this.stolenFrom=i,this.belongsTo=s}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,s,e,h,a,n){t.save(),t.translate(525,525),t.rotate(a),t.translate(-525,-525);const l=75*h,r=90*h,o=25*h/2,c=(this.belongsTo-this.stolenFrom-1+4)%4,d=void 0===n?-1:n.getFamily(),u=void 0===n?0:n.getValue(),g=(i,s,e,a)=>{i.drawTile(t,s,e,h,!1,a,i.isEqual(d,u))},f=Math.PI/2;switch(c){case 0:g(this.tiles[0],i,s+o,f),g(this.tiles[1],i+r,s,0),g(this.tiles[2],i+r+l+e*h,s,0);break;case 1:g(this.tiles[0],i,s,0),g(this.tiles[1],i+r,s+o,-f),g(this.tiles[2],i+r+l+3*e*h,s,0);break;case 2:g(this.tiles[0],i,s,0),g(this.tiles[1],i+l+e*h,s,0),g(this.tiles[2],i+r+l+e*h,s+o,-f);break;default:console.error(`Position non prise en charge: ${c}`)}t.restore()}}const s="m",e=1,h="p",a=2,n="s",l=3,r="w",o=4,c="d",d=5;class u{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;is.getFamily()===t&&s.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((s=>s.getFamily()===t&&s.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),s=i.getFamily(),e=i.getValue();if(this.count(s,e)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(s,e)>=2){const s=this.tryFormTriplet(i,t);if(s)return s}const h=this.count(s,e-1)>0,a=this.count(s,e-2)>0;if(h&&a){const s=this.tryFormSequence(i,t);if(s)return s}this.tiles.push(i),this.sort()}tryFormPair(t){const s=this.find(t.getFamily(),t.getValue()),e=this.toGroup(!0);if(this.tiles.push(s),this.sort(),void 0!==e)return this.tiles.push(t),this.sort(),e.push(new i([t,s],0,0)),e}tryFormTriplet(t,s){const e=this.find(t.getFamily(),t.getValue()),h=this.find(t.getFamily(),t.getValue()),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([t,e,h],0,0)),this.tiles.push(t),this.sort(),a}tryFormSequence(t,s){const e=this.find(t.getFamily(),t.getValue()-1),h=this.find(t.getFamily(),t.getValue()-2),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([h,e,t],0,0)),this.tiles.push(t),this.sort(),a}drawHand(t,i,s,e,h,a=void 0,n=!1,l=0){const r=(75+e)*h,o=Math.cos(l)*r,c=Math.sin(l)*r;for(let e=0;et.preloadImg())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class g{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,s,e,h=0,a=0){let n=i,l=s;const r=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const s of r){for(let i=s.start;i<=s.end;i++){const a=this.find(s.family,i);a&&(a.drawTile(t,n,l,e,!1,0,!1),n+=(75+h)*e)}n=i,l+=(100+a)*e}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(i);return s&&s.length>0&&(s.pop(),0===s.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,s)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const s=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(s)||[];e.push(i),this.tileIndexMap.set(s,e)}find(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);if(!e||0===e.length)return;const h=e[0];if(h>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[h],this.tiles[0]]=[this.tiles[0],this.tiles[h]],this.updateIndicesAfterSwap(0,h);const a=this.tiles.shift();return this.decrementIndicesAfterShift(),a}count(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);return e?e.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new u;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,e=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const s=this.tiles[t],e=this.tiles[i],h=this.getTileKey(s.getFamily(),s.getValue()),a=this.getTileKey(e.getFamily(),e.getValue()),n=this.tileIndexMap.get(h)||[],l=this.tileIndexMap.get(a)||[],r=n.indexOf(i),o=l.indexOf(t);-1!==r&&(n[r]=t),-1!==o&&(l[o]=i),this.tileIndexMap.set(h,n),this.tileIndexMap.set(a,l)}decrementIndicesAfterShift(){var t;for(const[i,s]of this.tileIndexMap.entries())this.tileIndexMap.set(i,s.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;tt)).map((([,t])=>t));return a.length>0&&a.push(m.action),a}(s,e,h,a,n);if(0===l.length)return-1;const r=960-l.length*f;if(!(838=0&&o10?l[o]:-1}(t.x-i.x,t.y-i.y,this.canDoAChii().length>0,this.canDoAPon(),!1,!1,!1);this.canCall&&-1!==s?this.handleCallAction(s):0===this.turn&&void 0!==this.selectedTile&&this.handlePlayerDiscard()}handleChiiSelection(t,i){const s=this.getChii(0),e=function(t,i,s){if(0===s.length)return-1;const e=960-(s.length+1)*f;if(!(838=0&&h10?h===s.length?0:s[h][0].getValue():-1}(t.x-i.x,t.y-i.y,s);0===e?this.chooseChii=!1:(this.chooseChii=!1,this.chii(e,0))}handleCallAction(t){if(0===t)this.canCall=!1,this.advanceTurn();else if(1===t){const t=this.canDoAChii();1===t.length?this.chii(t[0],0):(this.chooseChii=!0,this.drawGame())}else 2===t&&this.pon(this.turn)}handlePlayerDiscard(){if(this.discard(0,this.selectedTile),!this.checkPon()){const t=this.canDoAChii(1);if(t.length>0){const i=Math.floor(Math.random()*t.length);this.chii(t[i],1)}else this.advanceTurn()}}advanceTurn(){this.updateWaitingTime(),this.turn=(this.turn+1)%4,this.pick(this.turn),this.hasPicked=!0,this.hasPlayed=!1}getSelected(t){this.cv.getBoundingClientRect();const i=E.HAND_SIZE,s=t.x-140.625,e=t.y,h=Math.floor(s/(83.9*i));s-83.9*h*i<=80.9*i&&h>=0&&h=900&&e<=900+100*i?this.selectedTile=h:this.selectedTile=void 0}updateWaitingTime(){this.waitingTime=Math.floor(Math.random()*(S-P)+P)}play(){0===this.turn||this.end||(this.hasPicked?this.hasPlayed?this.canCall||this.advanceBotTurn():this.handleBotTurn():(this.lastPlayed=Date.now(),this.pick(this.turn),this.hasPicked=!0))}handleBotTurn(){if(this.hasWin(this.turn))return this.end=!0,void(this.result=2);if(Date.now()-this.lastPlayed>this.waitingTime){this.lastPlayed=Date.now();const t=Math.floor(this.hands[this.turn].length()*Math.random());if(this.discard(this.turn,t),this.hasPlayed=!0,this.deck.length()<=0)return this.result=0,void(this.end=!0);this.end||(this.checkPon(),this.checkForChii(),this.canCall=this.canDoAChii().length>0||this.canDoAPon())}}checkForChii(){if(3===this.turn)return;const t=this.turn+1,i=this.canDoAChii(t);if(i.length>0){const s=Math.floor(Math.random()*i.length);this.chii(i[s],t)}}advanceBotTurn(){3===this.turn?(this.turn=0,this.pick(0),this.hasWin(0)&&(this.end=!0,this.result=1)):this.turn++,this.updateWaitingTime(),this.hasPicked=!1,this.hasPlayed=!1}pick(t){this.hands[t].push(this.deck.pop()),this.hands[t].isolate=!0}discard(t,i){const s=this.hands[t].eject(i);this.hands[t].sort(),s.setTilt(),this.discards[t].push(s),this.hands[t].isolate=!1,this.hands[t].sort(),this.lastDiscard=t,this.lastPlayed=Date.now()}canDoAChii(t=0){const i=[];if(void 0===this.lastDiscard||(this.lastDiscard+1)%4!==t||(this.turn+1)%4!==t||this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1].getFamily()>=4)return i;const s=this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1],e=this.hands[t],h=s.getFamily(),a=s.getValue();return e.count(h,a-2)>0&&e.count(h,a-1)>0&&i.push(a-2),e.count(h,a-1)>0&&e.count(h,a+1)>0&&i.push(a-1),e.count(h,a+1)>0&&e.count(h,a+2)>0&&i.push(a),i}chii(t,s){const e=0===s?3:s-1,h=this.discards[e].pop();this.lastDiscard=void 0;const a=[];let n=0,l=t;for(;n<2;)l===h.getValue()||(a[n]=this.hands[s].find(h.getFamily(),l),n++),l++;[h,a[0],a[1]].forEach((t=>t.setTilt())),this.groups[s].push(new i([h,a[0],a[1]],e,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}getChii(t){const i=[],s=this.canDoAChii(),e=0===t?3:t-1,h=this.discards[e],a=h[h.length-1];for(let e=0;e=2}pon(t,s=0){const e=this.discards[t].pop();this.lastDiscard=void 0;const h=this.hands[s].find(e.getFamily(),e.getValue()),a=this.hands[s].find(e.getFamily(),e.getValue());[e,h,a].forEach((t=>t.setTilt())),this.groups[s].push(new i([e,h,a],t,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}hasWin(t){return void 0!==this.hands[t].toGroup()}drawGame(){this.play(),function(t,i=0,s=0){let e=525,h=Math.PI,a=630;t.save(),t.translate(525,525),t.rotate([0,-h/2,h,h/2][i]),t.translate(-525,-525),t.fillStyle="#ffcc33",t.beginPath(),t.moveTo(e,a),t.lineTo(452,a),t.quadraticCurveTo(450,a,450,632),t.lineTo(450,632),t.quadraticCurveTo(450,634,452,634),t.lineTo(598,634),t.quadraticCurveTo(600,634,600,632),t.lineTo(600,632),t.quadraticCurveTo(600,a,598,a),t.lineTo(e,a),t.fill(),t.stroke(),t.restore(),t.save(),t.translate(525,525),t.rotate(s),t.translate(-525,-525),t.fillStyle="#e0e0f0",t.beginPath(),t.moveTo(425,e),t.lineTo(425,595),t.quadraticCurveTo(425,625,455,625),t.lineTo(595,625),t.quadraticCurveTo(625,625,625,595),t.lineTo(625,455),t.quadraticCurveTo(625,425,595,425),t.lineTo(455,425),t.quadraticCurveTo(425,425,425,455),t.lineTo(425,e),t.fill(),t.stroke(),t.fillStyle="#000000",t.font="40px garamond",t.fillText("Est",505,615),t.save(),t.translate(525,525),t.rotate(-1.570796),t.translate(-525,-525),t.fillText("Sud",500,615),t.restore(),t.save(),t.translate(525,525),t.rotate(3.141592),t.translate(-525,-525),t.fillText("Ouest",480,615),t.restore(),t.save(),t.translate(525,525),t.rotate(1.570796),t.translate(-525,-525),t.fillText("Nord",485,615),t.restore(),t.restore()}(this.staticCtx,this.turn,M/2*this.windPlayer),this.drawDiscardSize(),this.drawResult(),this.drawHands(),this.drawGroups(E.GROUP_SIZE);for(let t=0;t<4;t++)this.drawDiscard(t,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0);this.chooseChii?function(t,i){const s=[...i].reverse();I(t,850,835,T);let e=1;for(const i of s)w(t,850-e*f,835,i),e++}(this.staticCtx,this.getChii(0)):function(t,i,s){const e=[[i,(t,i,s)=>I(t,i,s,p)],[s,(t,i,s)=>I(t,i,s,v)],[!1,(t,i,s)=>I(t,i,s,y)],[!1,(t,i,s)=>I(t,i,s,C)],[!1,(t,i,s)=>I(t,i,s,x)]];if(!e.some((([t])=>t)))return;e.unshift([!0,(t,i,s)=>I(t,i,s,m)]);let h=0;for(const[i,s]of e)i&&(s(t,850-h*f,835),h++)}(this.staticCtx,this.canDoAChii().length>0,this.canDoAPon())}drawHands(){const{HAND_SIZE:t,HIDDEN_HAND_SIZE:i}=E;this.hands[0].drawHand(this.staticCtx,140.625,1e3-150*t,5*t,.75,this.selectedTile,!1,0),this.hands[1].drawHand(this.staticCtx,1e3-150*i,1e3-375*i,5*i,i,void 0,!0,-M/2),this.hands[2].drawHand(this.staticCtx,1e3-375*i,150*i,5*i,i,void 0,!0,-M),this.hands[3].drawHand(this.staticCtx,150*i,375*i,5*i,i,void 0,!0,M/2)}drawGroups(t){for(let i=0;i<4;i++){const s=this.rotations[i],e=this.groups[i];if(e.length>0)for(let i=e.length-1;i>=0;i--)e[i].drawGroup(this.staticCtx,810-285*t*i,988,5,.6,s,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0)}}drawDiscard(t,i){const s=E.DISCARD_SIZE;this.staticCtx.save(),this.staticCtx.translate(525,525),this.staticCtx.rotate(this.rotations[t]);const e=475*-s/2,h=242.5*s;for(let a=0;at.preload())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}}var R=function(t,i,s,e){return new(s||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}))};class V{constructor(){this.CANVAS_ID="myCanvas",this.BG_RECT={x:0,y:0,w:1050,h:1050},this.FPS=60,this.FRAME_INTERVAL=1e3/this.FPS,this.mouse={x:0,y:0},this.game=null,this.decks=[],this.hands=[],this.animationFrameId=null,this.lastFrameTime=0,this.isInitialized=!1,this.cleanupCallbacks=[];const t=document.getElementById(this.CANVAS_ID);if(!t)throw new Error(`Canvas avec ID '${this.CANVAS_ID}' introuvable`);this.canvas=t,this.canvas.width=this.BG_RECT.w,this.canvas.height=this.BG_RECT.h;const i=t.getContext("2d",{alpha:!1});if(!i)throw new Error("Impossible d'obtenir le contexte 2D du canvas");this.ctx=i,this.staticCanvas=document.createElement("canvas"),this.staticCanvas.width=t.width,this.staticCanvas.height=t.height;const s=this.staticCanvas.getContext("2d",{alpha:!1});if(!s)throw new Error("Impossible d'obtenir le contexte du canvas statique");this.staticCtx=s}static getInstance(){return V.instance||(V.instance=new V),V.instance}drawFrame(){this.game&&this.game.draw(this.mouse)}startAnimationLoop(){const t=i=>{this.animationFrameId=requestAnimationFrame(t);const s=i-this.lastFrameTime;s{const i=this.canvas.getBoundingClientRect();this.mouse.x=t.clientX-i.left,this.mouse.y=t.clientY-i.top},i=t=>{this.game&&this.game.click(t)};this.canvas.addEventListener("mousemove",t),this.canvas.addEventListener("mousedown",i),this.cleanupCallbacks.push((()=>{this.canvas.removeEventListener("mousemove",t),this.canvas.removeEventListener("mousedown",i)}))}preloadDeck(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}preloadHand(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}initialize(){return R(this,void 0,void 0,(function*(){if(!this.isInitialized)try{console.log("Chargement en cours..."),this.game=new A(this.ctx,this.canvas,this.staticCtx,this.staticCanvas),yield Promise.all([...this.decks.map((t=>this.preloadDeck(t))),...this.hands.map((t=>this.preloadHand(t))),this.game.preload()]),console.log("Chargement terminé"),this.initEventListeners(),this.startAnimationLoop(),this.isInitialized=!0,window.cleanup=this.cleanup.bind(this)}catch(t){console.error("Erreur lors de l'initialisation:",t)}}))}cleanup(){var t,i;null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.cleanupCallbacks.forEach((t=>t())),this.cleanupCallbacks=[],this.game&&(null===(i=(t=this.game).cleanup)||void 0===i||i.call(t),this.game=null),this.decks.forEach((t=>t.cleanup())),this.hands.forEach((t=>t.cleanup())),this.decks=[],this.hands=[],this.isInitialized=!1,this.lastFrameTime=0,this.mouse={x:0,y:0},this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.staticCtx.clearRect(0,0,this.staticCanvas.width,this.staticCanvas.height)}}"undefined"!=typeof window&&function(){return R(this,void 0,void 0,(function*(){yield V.getInstance().initialize()}))}().catch(console.error)})(); \ No newline at end of file diff --git a/build/dp6.js b/build/dp6.js new file mode 100644 index 0000000..2ec5960 --- /dev/null +++ b/build/dp6.js @@ -0,0 +1 @@ +(()=>{"use strict";class t{constructor(t,i,s){this.family=t,this.value=i,this.red=s,this.imgFront=new Image,this.imgBack=new Image,this.imgGray=new Image,this.img=new Image,this.imgSrc="",this.tilt=0,this.setImgSrc()}getFamily(){return this.family}getValue(){return this.value}isEqual(t,i){return this.family===t&&this.value===i}isRed(){return this.red}compare(t){return this.family!==t.family?this.family{t.onload=null,t.onerror=null}))}preloadImg(){return t=this,i=void 0,e=function*(){const t=[{img:this.imgFront,src:"img/Regular/Front.svg"},{img:this.imgBack,src:"img/Regular/Back.svg"},{img:this.imgGray,src:"img/Regular/Gray.svg"},{img:this.img,src:this.imgSrc}];yield Promise.all(t.map((({img:t,src:i})=>this.loadImg(t,i))))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}loadImg(t,i){return new Promise(((s,e)=>{t.onload=()=>s(),t.onerror=()=>e(),t.src=i}))}setImgSrc(){if(this.imgSrc="img/Regular/",this.family<=3){const t=["","Man","Pin","Sou"];this.imgSrc+=t[this.family]+String(this.value),this.red&&(this.imgSrc+="-Dora")}else if(4===this.family){const t=["","Ton","Nan","Shaa","Pei"];this.imgSrc+=t[this.value]}else if(5===this.family){const t=["","Chun","Hatsu","Haku"];this.imgSrc+=t[this.value]}this.imgSrc+=".svg"}}class i{constructor(t,i,s){this.tiles=t,this.stolenFrom=i,this.belongsTo=s}push(t){this.tiles.push(t)}pop(){return this.tiles.pop()}getTiles(){return this.tiles}compare(t){const i=this.tiles[0].compare(t.tiles[0]);return 0!==i?i:this.tiles[1].compare(t.tiles[1])}drawGroup(t,i,s,e,h,a,n){t.save(),t.translate(525,525),t.rotate(a),t.translate(-525,-525);const l=75*h,r=90*h,o=25*h/2,c=(this.belongsTo-this.stolenFrom-1+4)%4,d=void 0===n?-1:n.getFamily(),u=void 0===n?0:n.getValue(),f=(i,s,e,a)=>{i.drawTile(t,s,e,h,!1,a,i.isEqual(d,u))},g=Math.PI/2;switch(c){case 0:f(this.tiles[0],i,s+o,g),f(this.tiles[1],i+r,s,0),f(this.tiles[2],i+r+l+e*h,s,0);break;case 1:f(this.tiles[0],i,s,0),f(this.tiles[1],i+r,s+o,-g),f(this.tiles[2],i+r+l+3*e*h,s,0);break;case 2:f(this.tiles[0],i,s,0),f(this.tiles[1],i+l+e*h,s,0),f(this.tiles[2],i+r+l+e*h,s+o,-g);break;default:console.error(`Position non prise en charge: ${c}`)}t.restore()}}const s="m",e=1,h="p",a=2,n="s",l=3,r="w",o=4,c="d",d=5;class u{constructor(t=""){this.isolate=!1,this.tiles=[],this.initializeFromString(t)}initializeFromString(t){for(let i=0;is.getFamily()===t&&s.getValue()===i))}eject(t){if(t<0||t>=this.tiles.length)throw new Error("Invalid tile index");[this.tiles[0],this.tiles[t]]=[this.tiles[t],this.tiles[0]];const i=this.tiles.shift();return this.sort(),i}get(t){if(!(void 0===t||t<0||t>=this.tiles.length))return this.tiles[t]}sort(){this.tiles.sort(((t,i)=>t.isLessThan(i)?-1:1))}count(t,i){return this.tiles.filter((s=>s.getFamily()===t&&s.getValue()===i)).length}toGroup(t=!1){if(0===this.tiles.length)return[];const i=this.tiles.pop(),s=i.getFamily(),e=i.getValue();if(this.count(s,e)>=1&&!t){const t=this.tryFormPair(i);if(t)return t}if(this.count(s,e)>=2){const s=this.tryFormTriplet(i,t);if(s)return s}const h=this.count(s,e-1)>0,a=this.count(s,e-2)>0;if(h&&a){const s=this.tryFormSequence(i,t);if(s)return s}this.tiles.push(i),this.sort()}tryFormPair(t){const s=this.find(t.getFamily(),t.getValue()),e=this.toGroup(!0);if(this.tiles.push(s),this.sort(),void 0!==e)return this.tiles.push(t),this.sort(),e.push(new i([t,s],0,0)),e}tryFormTriplet(t,s){const e=this.find(t.getFamily(),t.getValue()),h=this.find(t.getFamily(),t.getValue()),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([t,e,h],0,0)),this.tiles.push(t),this.sort(),a}tryFormSequence(t,s){const e=this.find(t.getFamily(),t.getValue()-1),h=this.find(t.getFamily(),t.getValue()-2),a=this.toGroup(s);if(this.tiles.push(e),this.tiles.push(h),this.sort(),void 0!==a)return a.push(new i([h,e,t],0,0)),this.tiles.push(t),this.sort(),a}drawHand(t,i,s,e,h,a=void 0,n=!1,l=0){const r=(75+e)*h,o=Math.cos(l)*r,c=Math.sin(l)*r;for(let e=0;et.preloadImg())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[]}}class f{constructor(t=!1){this.tiles=[],this.tileIndexMap=new Map,this.initTiles(t)}displayFamilies(t,i,s,e,h=0,a=0){let n=i,l=s;const r=[{family:1,start:1,end:9},{family:2,start:1,end:9},{family:3,start:1,end:9},{family:4,start:1,end:4},{family:5,start:1,end:3}];for(const s of r){for(let i=s.start;i<=s.end;i++){const a=this.find(s.family,i);a&&(a.drawTile(t,n,l,e,!1,0,!1),n+=(75+h)*e)}n=i,l+=(100+a)*e}}length(){return this.tiles.length}pop(){if(0===this.tiles.length)throw new Error("Cannot pop from an empty deck");const t=this.tiles.pop(),i=this.getTileKey(t.getFamily(),t.getValue()),s=this.tileIndexMap.get(i);return s&&s.length>0&&(s.pop(),0===s.length?this.tileIndexMap.delete(i):this.tileIndexMap.set(i,s)),t}push(t){const i=this.tiles.length;this.tiles.push(t);const s=this.getTileKey(t.getFamily(),t.getValue()),e=this.tileIndexMap.get(s)||[];e.push(i),this.tileIndexMap.set(s,e)}find(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);if(!e||0===e.length)return;const h=e[0];if(h>=this.tiles.length)return this.rebuildIndexMap(),this.find(t,i);[this.tiles[h],this.tiles[0]]=[this.tiles[0],this.tiles[h]],this.updateIndicesAfterSwap(0,h);const a=this.tiles.shift();return this.decrementIndicesAfterShift(),a}count(t,i){const s=this.getTileKey(t,i),e=this.tileIndexMap.get(s);return e?e.length:0}shuffle(){for(let t=this.tiles.length-1;t>0;t--){const i=Math.floor(Math.random()*(t+1));[this.tiles[t],this.tiles[i]]=[this.tiles[i],this.tiles[t]]}this.rebuildIndexMap()}getRandomHand(){const t=new u;if(this.shuffle(),this.tiles.length<13)throw new Error("Not enough tiles in deck to create a hand");for(let i=0;i<13;i++)t.push(this.pop());return t}cleanup(){this.tiles.forEach((t=>t.cleanup())),this.tiles=[],this.tileIndexMap.clear()}preload(){return t=this,i=void 0,e=function*(){const t=this.tiles.map((t=>t.preloadImg()));yield Promise.all(t)},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}getTileKey(t,i){return`${t}-${i}`}updateIndicesAfterSwap(t,i){if(t===i)return;const s=this.tiles[t],e=this.tiles[i],h=this.getTileKey(s.getFamily(),s.getValue()),a=this.getTileKey(e.getFamily(),e.getValue()),n=this.tileIndexMap.get(h)||[],l=this.tileIndexMap.get(a)||[],r=n.indexOf(i),o=l.indexOf(t);-1!==r&&(n[r]=t),-1!==o&&(l[o]=i),this.tileIndexMap.set(h,n),this.tileIndexMap.set(a,l)}decrementIndicesAfterShift(){var t;for(const[i,s]of this.tileIndexMap.entries())this.tileIndexMap.set(i,s.filter((t=>0!==t)).map((t=>t>0?t-1:t))),0===(null===(t=this.tileIndexMap.get(i))||void 0===t?void 0:t.length)&&this.tileIndexMap.delete(i)}rebuildIndexMap(){this.tileIndexMap.clear();for(let t=0;tt)).map((([,t])=>t));return a.length>0&&a.push(m.action),a}(s,e,h,a,n);if(0===l.length)return-1;const r=960-l.length*g;if(!(838=0&&o10?l[o]:-1}(t.x-i.x,t.y-i.y,this.canDoAChii().length>0,this.canDoAPon(),!1,!1,!1);this.canCall&&-1!==s?this.handleCallAction(s):0===this.turn&&void 0!==this.selectedTile&&this.handlePlayerDiscard()}handleChiiSelection(t,i){const s=this.getChii(0),e=function(t,i,s){if(0===s.length)return-1;const e=960-(s.length+1)*g;if(!(838=0&&h10?h===s.length?0:s[h][0].getValue():-1}(t.x-i.x,t.y-i.y,s);0===e?this.chooseChii=!1:(this.chooseChii=!1,this.chii(e,0))}handleCallAction(t){if(0===t)this.canCall=!1,this.advanceTurn();else if(1===t){const t=this.canDoAChii();1===t.length?this.chii(t[0],0):(this.chooseChii=!0,this.drawGame())}else 2===t&&this.pon(this.turn)}handlePlayerDiscard(){if(this.discard(0,this.selectedTile),!this.checkPon()){const t=this.canDoAChii(1);if(t.length>0){const i=Math.floor(Math.random()*t.length);this.chii(t[i],1)}else this.advanceTurn()}}advanceTurn(){this.updateWaitingTime(),this.turn=(this.turn+1)%4,this.pick(this.turn),this.hasPicked=!0,this.hasPlayed=!1}getSelected(t){this.cv.getBoundingClientRect();const i=M.HAND_SIZE,s=t.x-140.625,e=t.y,h=Math.floor(s/(83.9*i));s-83.9*h*i<=80.9*i&&h>=0&&h=900&&e<=900+100*i?this.selectedTile=h:this.selectedTile=void 0}updateWaitingTime(){this.waitingTime=Math.floor(Math.random()*(S-P)+P)}play(){0===this.turn||this.end||(this.hasPicked?this.hasPlayed?this.canCall||this.advanceBotTurn():this.handleBotTurn():(this.lastPlayed=Date.now(),this.pick(this.turn),this.hasPicked=!0))}handleBotTurn(){if(this.hasWin(this.turn))return this.end=!0,void(this.result=2);if(Date.now()-this.lastPlayed>this.waitingTime){this.lastPlayed=Date.now();const t=Math.floor(this.hands[this.turn].length()*Math.random());if(this.discard(this.turn,t),this.hasPlayed=!0,this.deck.length()<=0)return this.result=0,void(this.end=!0);this.end||(this.checkPon(),this.checkForChii(),this.canCall=this.canDoAChii().length>0||this.canDoAPon())}}checkForChii(){if(3===this.turn)return;const t=this.turn+1,i=this.canDoAChii(t);if(i.length>0){const s=Math.floor(Math.random()*i.length);this.chii(i[s],t)}}advanceBotTurn(){3===this.turn?(this.turn=0,this.pick(0),this.hasWin(0)&&(this.end=!0,this.result=1)):this.turn++,this.updateWaitingTime(),this.hasPicked=!1,this.hasPlayed=!1}pick(t){this.hands[t].push(this.deck.pop()),this.hands[t].isolate=!0}discard(t,i){const s=this.hands[t].eject(i);this.hands[t].sort(),s.setTilt(),this.discards[t].push(s),this.hands[t].isolate=!1,this.hands[t].sort(),this.lastDiscard=t,this.lastPlayed=Date.now()}canDoAChii(t=0){const i=[];if(void 0===this.lastDiscard||(this.lastDiscard+1)%4!==t||(this.turn+1)%4!==t||this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1].getFamily()>=4)return i;const s=this.discards[this.lastDiscard][this.discards[this.lastDiscard].length-1],e=this.hands[t],h=s.getFamily(),a=s.getValue();return e.count(h,a-2)>0&&e.count(h,a-1)>0&&i.push(a-2),e.count(h,a-1)>0&&e.count(h,a+1)>0&&i.push(a-1),e.count(h,a+1)>0&&e.count(h,a+2)>0&&i.push(a),i}chii(t,s){const e=0===s?3:s-1,h=this.discards[e].pop();this.lastDiscard=void 0;const a=[];let n=0,l=t;for(;n<2;)l===h.getValue()||(a[n]=this.hands[s].find(h.getFamily(),l),n++),l++;[h,a[0],a[1]].forEach((t=>t.setTilt())),this.groups[s].push(new i([h,a[0],a[1]],e,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}getChii(t){const i=[],s=this.canDoAChii(),e=0===t?3:t-1,h=this.discards[e],a=h[h.length-1];for(let e=0;e=2}pon(t,s=0){const e=this.discards[t].pop();this.lastDiscard=void 0;const h=this.hands[s].find(e.getFamily(),e.getValue()),a=this.hands[s].find(e.getFamily(),e.getValue());[e,h,a].forEach((t=>t.setTilt())),this.groups[s].push(new i([e,h,a],t,s)),this.hasWin(s)&&(this.end=!0,this.result=0===s?1:2),this.updateWaitingTime(),this.turn=s,this.hasPicked=!0,this.hasPlayed=!1}hasWin(t){return void 0!==this.hands[t].toGroup()}drawGame(){this.play(),function(t,i=0,s=0){let e=525,h=Math.PI,a=630;t.save(),t.translate(525,525),t.rotate([0,-h/2,h,h/2][i]),t.translate(-525,-525),t.fillStyle="#ffcc33",t.beginPath(),t.moveTo(e,a),t.lineTo(452,a),t.quadraticCurveTo(450,a,450,632),t.lineTo(450,632),t.quadraticCurveTo(450,634,452,634),t.lineTo(598,634),t.quadraticCurveTo(600,634,600,632),t.lineTo(600,632),t.quadraticCurveTo(600,a,598,a),t.lineTo(e,a),t.fill(),t.stroke(),t.restore(),t.save(),t.translate(525,525),t.rotate(s),t.translate(-525,-525),t.fillStyle="#e0e0f0",t.beginPath(),t.moveTo(425,e),t.lineTo(425,595),t.quadraticCurveTo(425,625,455,625),t.lineTo(595,625),t.quadraticCurveTo(625,625,625,595),t.lineTo(625,455),t.quadraticCurveTo(625,425,595,425),t.lineTo(455,425),t.quadraticCurveTo(425,425,425,455),t.lineTo(425,e),t.fill(),t.stroke(),t.fillStyle="#000000",t.font="40px garamond",t.fillText("Est",505,615),t.save(),t.translate(525,525),t.rotate(-1.570796),t.translate(-525,-525),t.fillText("Sud",500,615),t.restore(),t.save(),t.translate(525,525),t.rotate(3.141592),t.translate(-525,-525),t.fillText("Ouest",480,615),t.restore(),t.save(),t.translate(525,525),t.rotate(1.570796),t.translate(-525,-525),t.fillText("Nord",485,615),t.restore(),t.restore()}(this.staticCtx,this.turn,E/2*this.windPlayer),this.drawDiscardSize(),this.drawResult(),this.drawHands(),this.drawGroups(M.GROUP_SIZE);for(let t=0;t<4;t++)this.drawDiscard(t,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0);this.chooseChii?function(t,i){const s=[...i].reverse();I(t,850,835,T);let e=1;for(const i of s)w(t,850-e*g,835,i),e++}(this.staticCtx,this.getChii(0)):function(t,i,s){const e=[[i,(t,i,s)=>I(t,i,s,p)],[s,(t,i,s)=>I(t,i,s,v)],[!1,(t,i,s)=>I(t,i,s,y)],[!1,(t,i,s)=>I(t,i,s,C)],[!1,(t,i,s)=>I(t,i,s,x)]];if(!e.some((([t])=>t)))return;e.unshift([!0,(t,i,s)=>I(t,i,s,m)]);let h=0;for(const[i,s]of e)i&&(s(t,850-h*g,835),h++)}(this.staticCtx,this.canDoAChii().length>0,this.canDoAPon())}drawHands(){const{HAND_SIZE:t,HIDDEN_HAND_SIZE:i}=M;this.hands[0].drawHand(this.staticCtx,140.625,1e3-150*t,5*t,.75,this.selectedTile,!1,0),this.hands[1].drawHand(this.staticCtx,1e3-150*i,1e3-375*i,5*i,i,void 0,!0,-E/2),this.hands[2].drawHand(this.staticCtx,1e3-375*i,150*i,5*i,i,void 0,!0,-E),this.hands[3].drawHand(this.staticCtx,150*i,375*i,5*i,i,void 0,!0,E/2)}drawGroups(t){for(let i=0;i<4;i++){const s=this.rotations[i],e=this.groups[i];if(e.length>0)for(let i=e.length-1;i>=0;i--)e[i].drawGroup(this.staticCtx,810-285*t*i,988,5,.6,s,void 0!==this.selectedTile?this.hands[0].get(this.selectedTile):void 0)}}drawDiscard(t,i){const s=M.DISCARD_SIZE;this.staticCtx.save(),this.staticCtx.translate(525,525),this.staticCtx.rotate(this.rotations[t]);const e=475*-s/2,h=242.5*s;for(let a=0;at.preload())))},new((s=void 0)||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}));var t,i,s,e}}var R=function(t,i,s,e){return new(s||(s=Promise))((function(h,a){function n(t){try{r(e.next(t))}catch(t){a(t)}}function l(t){try{r(e.throw(t))}catch(t){a(t)}}function r(t){var i;t.done?h(t.value):(i=t.value,i instanceof s?i:new s((function(t){t(i)}))).then(n,l)}r((e=e.apply(t,i||[])).next())}))};class V{constructor(){this.CANVAS_ID="myCanvas",this.BG_RECT={x:0,y:0,w:1050,h:1050},this.FPS=60,this.FRAME_INTERVAL=1e3/this.FPS,this.mouse={x:0,y:0},this.game=null,this.decks=[],this.hands=[],this.animationFrameId=null,this.lastFrameTime=0,this.isInitialized=!1,this.cleanupCallbacks=[];const t=document.getElementById(this.CANVAS_ID);if(!t)throw new Error(`Canvas avec ID '${this.CANVAS_ID}' introuvable`);this.canvas=t,this.canvas.width=this.BG_RECT.w,this.canvas.height=this.BG_RECT.h;const i=t.getContext("2d",{alpha:!1});if(!i)throw new Error("Impossible d'obtenir le contexte 2D du canvas");this.ctx=i,this.staticCanvas=document.createElement("canvas"),this.staticCanvas.width=t.width,this.staticCanvas.height=t.height;const s=this.staticCanvas.getContext("2d",{alpha:!1});if(!s)throw new Error("Impossible d'obtenir le contexte du canvas statique");this.staticCtx=s}static getInstance(){return V.instance||(V.instance=new V),V.instance}drawFrame(){this.game&&this.game.draw(this.mouse)}startAnimationLoop(){const t=i=>{this.animationFrameId=requestAnimationFrame(t);const s=i-this.lastFrameTime;s{const i=this.canvas.getBoundingClientRect();this.mouse.x=t.clientX-i.left,this.mouse.y=t.clientY-i.top},i=t=>{this.game&&this.game.click(t)};this.canvas.addEventListener("mousemove",t),this.canvas.addEventListener("mousedown",i),this.cleanupCallbacks.push((()=>{this.canvas.removeEventListener("mousemove",t),this.canvas.removeEventListener("mousedown",i)}))}preloadDeck(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}preloadHand(t){return R(this,void 0,void 0,(function*(){yield t.preload()}))}initialize(){return R(this,void 0,void 0,(function*(){if(!this.isInitialized)try{console.log("Chargement en cours..."),this.game=new A(this.ctx,this.canvas,this.staticCtx,this.staticCanvas,!1,0,Math.floor(4*Math.random())),yield Promise.all([...this.decks.map((t=>this.preloadDeck(t))),...this.hands.map((t=>this.preloadHand(t))),this.game.preload()]),console.log("Chargement terminé"),this.initEventListeners(),this.startAnimationLoop(),this.isInitialized=!0,window.cleanup=this.cleanup.bind(this)}catch(t){console.error("Erreur lors de l'initialisation:",t)}}))}cleanup(){var t,i;null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.cleanupCallbacks.forEach((t=>t())),this.cleanupCallbacks=[],this.game&&(null===(i=(t=this.game).cleanup)||void 0===i||i.call(t),this.game=null),this.decks.forEach((t=>t.cleanup())),this.hands.forEach((t=>t.cleanup())),this.decks=[],this.hands=[],this.isInitialized=!1,this.lastFrameTime=0,this.mouse={x:0,y:0},this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.staticCtx.clearRect(0,0,this.staticCanvas.width,this.staticCanvas.height)}}"undefined"!=typeof window&&function(){return R(this,void 0,void 0,(function*(){yield V.getInstance().initialize()}))}().catch(console.error)})(); \ No newline at end of file diff --git a/build/txt6.js b/build/txt6.js new file mode 100644 index 0000000..afac35d --- /dev/null +++ b/build/txt6.js @@ -0,0 +1 @@ +(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,20+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt6.txt",n).catch((t=>console.error(t)))})(); \ No newline at end of file diff --git a/index.html b/index.html index c6fed88..ad64589 100644 --- a/index.html +++ b/index.html @@ -79,10 +79,10 @@ Chap 3: Les groupes Chap 4: Avoir une main valide en jeu Chap 5: Annoncer la victoire - Chap 6: Le furiten - Chap 7: Le vent du joueur - Chap 8: Les yakus - Chap 9: Le riichi + Chap 6: Le vent du joueur + Chap 7: Les yakus + Chap 8: Le riichi + Chap 9: Le furiten Chap 10: Première partie complète Chap 11: Score: les Hans Chap 12: Score: les doras diff --git a/src/display/dp6.ts b/src/display/dp6.ts new file mode 100644 index 0000000..a3472e8 --- /dev/null +++ b/src/display/dp6.ts @@ -0,0 +1,256 @@ +import { Deck } from "../deck"; +import { Hand } from "../hand"; +import { Game } from "../game"; + +class RiichiGameManager { + // Configuration globale + private readonly CANVAS_ID: string = "myCanvas"; + private readonly BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 }; + private readonly FPS: number = 60; + private readonly FRAME_INTERVAL: number = 1000 / this.FPS; + + // Singleton instance + private static instance: RiichiGameManager; + + // Canvas et contextes + private canvas: HTMLCanvasElement; + private ctx: CanvasRenderingContext2D; + private staticCanvas: HTMLCanvasElement; + private staticCtx: CanvasRenderingContext2D; + + // État du jeu + private mouse = { x: 0, y: 0 }; + private game: Game | null = null; + private decks: Array = []; + private hands: Array = []; + + // Animation et boucle de jeu + private animationFrameId: number | null = null; + private lastFrameTime: number = 0; + private isInitialized: boolean = false; + + // Nettoyage + private cleanupCallbacks: Array<() => void> = []; + + /** + * Constructeur privé pour le Singleton + */ + private constructor() { + // Initialisation du canvas principal + const canvas = document.getElementById(this.CANVAS_ID) as HTMLCanvasElement; + if (!canvas) { + throw new Error(`Canvas avec ID '${this.CANVAS_ID}' introuvable`); + } + this.canvas = canvas; + this.canvas.width = this.BG_RECT.w; + this.canvas.height = this.BG_RECT.h; + + // Récupération du contexte 2D + const ctx = canvas.getContext("2d", { alpha: false }); + if (!ctx) { + throw new Error("Impossible d'obtenir le contexte 2D du canvas"); + } + this.ctx = ctx; + + // Initialisation du canvas pour le double-buffering + this.staticCanvas = document.createElement('canvas'); + this.staticCanvas.width = canvas.width; + this.staticCanvas.height = canvas.height; + + const staticCtx = this.staticCanvas.getContext("2d", { alpha: false }); + if (!staticCtx) { + throw new Error("Impossible d'obtenir le contexte du canvas statique"); + } + this.staticCtx = staticCtx; + } + + /** + * Accès à l'instance Singleton + */ + public static getInstance(): RiichiGameManager { + if (!RiichiGameManager.instance) { + RiichiGameManager.instance = new RiichiGameManager(); + } + return RiichiGameManager.instance; + } + + /** + * Dessine une frame du jeu + */ + private drawFrame(): void { + if (this.game) { + this.game.draw(this.mouse); + } + } + + /** + * Démarre la boucle d'animation du jeu + */ + private startAnimationLoop(): void { + const animationLoop = (currentTime: number) => { + this.animationFrameId = requestAnimationFrame(animationLoop); + + const deltaTime = currentTime - this.lastFrameTime; + if (deltaTime < this.FRAME_INTERVAL) return; + + // Correction du time drift + this.lastFrameTime = currentTime - (deltaTime % this.FRAME_INTERVAL); + + // Rendu d'une frame + this.drawFrame(); + }; + + this.animationFrameId = requestAnimationFrame(animationLoop); + } + + /** + * Initialise les écouteurs d'événements + */ + private initEventListeners(): void { + // Handler pour le déplacement de la souris + const mouseMoveHandler = (e: MouseEvent) => { + // Calcul des coordonnées relatives au canvas + const rect = this.canvas.getBoundingClientRect(); + this.mouse.x = e.clientX - rect.left; + this.mouse.y = e.clientY - rect.top; + }; + + // Handler pour le clic de souris + const mouseDownHandler = (e: MouseEvent) => { + if (this.game) { + this.game.click(e); + } + }; + + // Enregistrement des écouteurs + this.canvas.addEventListener('mousemove', mouseMoveHandler); + this.canvas.addEventListener('mousedown', mouseDownHandler); + + // Enregistrement des callbacks de nettoyage + this.cleanupCallbacks.push(() => { + this.canvas.removeEventListener('mousemove', mouseMoveHandler); + this.canvas.removeEventListener('mousedown', mouseDownHandler); + }); + } + + /** + * Précharge un deck + */ + private async preloadDeck(deck: Deck): Promise { + await deck.preload(); + } + + /** + * Précharge une main + */ + private async preloadHand(hand: Hand): Promise { + await hand.preload(); + } + + /** + * Initialise le jeu + */ + public async initialize(): Promise { + if (this.isInitialized) return; + + try { + console.log("Chargement en cours..."); + + // Création du jeu + this.game = new Game( + this.ctx, + this.canvas, + this.staticCtx, + this.staticCanvas, + false, + 0, + Math.floor(Math.random() * 4) + ); + + // Préchargement parallèle des ressources + await Promise.all([ + ...this.decks.map(deck => this.preloadDeck(deck)), + ...this.hands.map(hand => this.preloadHand(hand)), + this.game.preload() + ]); + + console.log("Chargement terminé"); + + // Initialisation des écouteurs d'événements + this.initEventListeners(); + + // Démarrage de la boucle d'animation + this.startAnimationLoop(); + + // Marquer comme initialisé + this.isInitialized = true; + + // Définir la fonction de nettoyage global + window.cleanup = this.cleanup.bind(this); + + } catch (error) { + console.error("Erreur lors de l'initialisation:", error); + } + } + + /** + * Nettoie les ressources + */ + public cleanup(): void { + // Arrêter la boucle d'animation + if (this.animationFrameId !== null) { + cancelAnimationFrame(this.animationFrameId); + this.animationFrameId = null; + } + + // Exécuter tous les callbacks de nettoyage + this.cleanupCallbacks.forEach(callback => callback()); + this.cleanupCallbacks = []; + + // Nettoyer les ressources du jeu + if (this.game) { + // Supposons que Game a une méthode cleanup + (this.game as any).cleanup?.(); + this.game = null; + } + + // Nettoyer les ressources des decks et des mains + this.decks.forEach(deck => deck.cleanup()); + this.hands.forEach(hand => hand.cleanup()); + + // Vider les collections + this.decks = []; + this.hands = []; + + // Réinitialiser l'état + this.isInitialized = false; + this.lastFrameTime = 0; + this.mouse = { x: 0, y: 0 }; + + // Effacer les canvas + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.staticCtx.clearRect(0, 0, this.staticCanvas.width, this.staticCanvas.height); + } +} + +// Fonction de nettoyage globale exportée +export function cleanup(): void { + RiichiGameManager.getInstance().cleanup(); +} + +// Fonction d'initialisation globale exportée +export async function initDisplay(): Promise { + await RiichiGameManager.getInstance().initialize(); +} + +// 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); +} diff --git a/src/game.ts b/src/game.ts index 9d8b3aa..03c9a12 100644 --- a/src/game.ts +++ b/src/game.ts @@ -37,6 +37,7 @@ export class Game { // Game state private level: number; private turn = 0; + private windPlayer: number; private waitingTime = GAME_CONSTANTS.WAITING_TIME.DEFAULT; private selectedTile: number | undefined = undefined; private canCall: boolean = false; @@ -63,13 +64,16 @@ export class Game { staticCtx: CanvasRenderingContext2D, staticCv: HTMLCanvasElement, red: boolean = false, - level: number = 0 + level: number = 0, + windPlayer: number = 0 ) { this.ctx = ctx; this.cv = cv; this.staticCtx = staticCtx; this.staticCv = staticCv; this.level = level; + this.windPlayer = windPlayer; + this.turn = windPlayer % 2 === 0 ? windPlayer : 4 - windPlayer; // Initialize game elements this.deck = new Deck(red); @@ -497,7 +501,7 @@ export class Game { this.play(); // Draw game elements - drawState(this.staticCtx, this.turn); + drawState(this.staticCtx, this.turn, GAME_CONSTANTS.PI / 2 * this.windPlayer); this.drawDiscardSize(); this.drawResult(); this.drawHands(); diff --git a/src/text/txt6.ts b/src/text/txt6.ts new file mode 100644 index 0000000..16baba0 --- /dev/null +++ b/src/text/txt6.ts @@ -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; +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 + "txt6.txt", ctx).catch(error => console.error(error)); + +export {}; diff --git a/src/text/txt6.txt b/src/text/txt6.txt new file mode 100644 index 0000000..805dce8 --- /dev/null +++ b/src/text/txt6.txt @@ -0,0 +1,28 @@ +~Chapitre 6: Le vent du joueur~ + +Normalement, vous auriez dû remarquer la présence de directions +cardinales affichées au centre. L'explication est simple: chaque +joueur se voit assigner un vent durant une manche. L'assignation +se fait simplement: un premier joueur est l'#ff00ff{Est}, le joueur +à sa droite est donc le #ff00ff{Sud}, en face l'#ff00ff{Ouest} et à sa +gauche le #ff00ff{Nord} + +L'#ff00ff{Est} est celui qui joue en tout premier durant sa manche. +À la fin de cette dernière, les vents tournent dans le sens +anti-horaire + +La partie s'arrête lorsque *tous* les joueurs ont été *Est* + + +#ff0000{Remarque}: Contrairement à une boussole, l'*Est* et +l'*Ouest* sont inversés. La logique qui se cache derrière est +qu'il faut interpréter le jeu comme une carte céleste + +#ff0000{Remarque 2}: Si le joueur *Est* gagne la manche, il y a alors +une #ff00f{répétition}, et les vents ne tournent pas + + + + +~Dans le jeu ci-contre, le vent est tiré aléatoirement, et change +à chaque manche en fonction du résultat~