baptiste
468
package-lock.json
generated
@@ -12,376 +12,450 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^5.0.8"
|
"vite": "^7.2.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
||||||
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"aix"
|
"aix"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
|
||||||
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
|
||||||
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
|
||||||
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
|
||||||
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
|
||||||
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
|
||||||
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
|
||||||
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
|
||||||
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
|
||||||
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
|
||||||
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
|
||||||
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
|
||||||
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
|
||||||
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
|
||||||
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
|
||||||
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
|
||||||
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
|
||||||
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"netbsd"
|
"netbsd"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
|
||||||
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
|
"version": "0.25.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
|
||||||
|
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"openbsd"
|
"openbsd"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
|
||||||
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openharmony-arm64": {
|
||||||
|
"version": "0.25.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
|
||||||
|
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.25.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
|
||||||
|
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"sunos"
|
"sunos"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
|
||||||
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
|
||||||
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
|
||||||
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
@@ -392,6 +466,7 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
@@ -405,6 +480,7 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
@@ -418,6 +494,7 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -431,6 +508,7 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -444,6 +522,7 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
@@ -457,6 +536,7 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
@@ -470,6 +550,7 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -483,6 +564,7 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -496,6 +578,7 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -509,6 +592,7 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -522,6 +606,7 @@
|
|||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -535,6 +620,7 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -548,6 +634,7 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -561,6 +648,7 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -574,6 +662,7 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -587,6 +676,7 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -600,6 +690,7 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -613,6 +704,7 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"openharmony"
|
"openharmony"
|
||||||
@@ -626,6 +718,7 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -639,6 +732,7 @@
|
|||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -652,6 +746,7 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -665,6 +760,7 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -674,59 +770,84 @@
|
|||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.19.25",
|
"version": "20.19.27",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz",
|
||||||
"integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==",
|
"integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.21.5",
|
"version": "0.25.12",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
||||||
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/aix-ppc64": "0.21.5",
|
"@esbuild/aix-ppc64": "0.25.12",
|
||||||
"@esbuild/android-arm": "0.21.5",
|
"@esbuild/android-arm": "0.25.12",
|
||||||
"@esbuild/android-arm64": "0.21.5",
|
"@esbuild/android-arm64": "0.25.12",
|
||||||
"@esbuild/android-x64": "0.21.5",
|
"@esbuild/android-x64": "0.25.12",
|
||||||
"@esbuild/darwin-arm64": "0.21.5",
|
"@esbuild/darwin-arm64": "0.25.12",
|
||||||
"@esbuild/darwin-x64": "0.21.5",
|
"@esbuild/darwin-x64": "0.25.12",
|
||||||
"@esbuild/freebsd-arm64": "0.21.5",
|
"@esbuild/freebsd-arm64": "0.25.12",
|
||||||
"@esbuild/freebsd-x64": "0.21.5",
|
"@esbuild/freebsd-x64": "0.25.12",
|
||||||
"@esbuild/linux-arm": "0.21.5",
|
"@esbuild/linux-arm": "0.25.12",
|
||||||
"@esbuild/linux-arm64": "0.21.5",
|
"@esbuild/linux-arm64": "0.25.12",
|
||||||
"@esbuild/linux-ia32": "0.21.5",
|
"@esbuild/linux-ia32": "0.25.12",
|
||||||
"@esbuild/linux-loong64": "0.21.5",
|
"@esbuild/linux-loong64": "0.25.12",
|
||||||
"@esbuild/linux-mips64el": "0.21.5",
|
"@esbuild/linux-mips64el": "0.25.12",
|
||||||
"@esbuild/linux-ppc64": "0.21.5",
|
"@esbuild/linux-ppc64": "0.25.12",
|
||||||
"@esbuild/linux-riscv64": "0.21.5",
|
"@esbuild/linux-riscv64": "0.25.12",
|
||||||
"@esbuild/linux-s390x": "0.21.5",
|
"@esbuild/linux-s390x": "0.25.12",
|
||||||
"@esbuild/linux-x64": "0.21.5",
|
"@esbuild/linux-x64": "0.25.12",
|
||||||
"@esbuild/netbsd-x64": "0.21.5",
|
"@esbuild/netbsd-arm64": "0.25.12",
|
||||||
"@esbuild/openbsd-x64": "0.21.5",
|
"@esbuild/netbsd-x64": "0.25.12",
|
||||||
"@esbuild/sunos-x64": "0.21.5",
|
"@esbuild/openbsd-arm64": "0.25.12",
|
||||||
"@esbuild/win32-arm64": "0.21.5",
|
"@esbuild/openbsd-x64": "0.25.12",
|
||||||
"@esbuild/win32-ia32": "0.21.5",
|
"@esbuild/openharmony-arm64": "0.25.12",
|
||||||
"@esbuild/win32-x64": "0.21.5"
|
"@esbuild/sunos-x64": "0.25.12",
|
||||||
|
"@esbuild/win32-arm64": "0.25.12",
|
||||||
|
"@esbuild/win32-ia32": "0.25.12",
|
||||||
|
"@esbuild/win32-x64": "0.25.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eventemitter3": {
|
"node_modules/eventemitter3": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/fdir": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"picomatch": "^3 || ^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"picomatch": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
@@ -734,6 +855,7 @@
|
|||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -753,6 +875,7 @@
|
|||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.cjs"
|
||||||
},
|
},
|
||||||
@@ -764,6 +887,7 @@
|
|||||||
"version": "3.90.0",
|
"version": "3.90.0",
|
||||||
"resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz",
|
"resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz",
|
||||||
"integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==",
|
"integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eventemitter3": "^5.0.1"
|
"eventemitter3": "^5.0.1"
|
||||||
}
|
}
|
||||||
@@ -772,7 +896,21 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.6",
|
"version": "8.5.6",
|
||||||
@@ -793,6 +931,7 @@
|
|||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -807,6 +946,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
|
||||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
},
|
},
|
||||||
@@ -848,15 +988,34 @@
|
|||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tinyglobby": {
|
||||||
|
"version": "0.2.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
|
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fdir": "^6.5.0",
|
||||||
|
"picomatch": "^4.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -869,23 +1028,28 @@
|
|||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.4.21",
|
"version": "7.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz",
|
||||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.25.0",
|
||||||
"postcss": "^8.4.43",
|
"fdir": "^6.5.0",
|
||||||
"rollup": "^4.20.0"
|
"picomatch": "^4.0.3",
|
||||||
|
"postcss": "^8.5.6",
|
||||||
|
"rollup": "^4.43.0",
|
||||||
|
"tinyglobby": "^0.2.15"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.0.0 || >=20.0.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
@@ -894,19 +1058,25 @@
|
|||||||
"fsevents": "~2.3.3"
|
"fsevents": "~2.3.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/node": "^18.0.0 || >=20.0.0",
|
"@types/node": "^20.19.0 || >=22.12.0",
|
||||||
"less": "*",
|
"jiti": ">=1.21.0",
|
||||||
|
"less": "^4.0.0",
|
||||||
"lightningcss": "^1.21.0",
|
"lightningcss": "^1.21.0",
|
||||||
"sass": "*",
|
"sass": "^1.70.0",
|
||||||
"sass-embedded": "*",
|
"sass-embedded": "^1.70.0",
|
||||||
"stylus": "*",
|
"stylus": ">=0.54.8",
|
||||||
"sugarss": "*",
|
"sugarss": "^5.0.0",
|
||||||
"terser": "^5.4.0"
|
"terser": "^5.16.0",
|
||||||
|
"tsx": "^4.8.1",
|
||||||
|
"yaml": "^2.4.2"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"jiti": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"less": {
|
"less": {
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
@@ -927,6 +1097,12 @@
|
|||||||
},
|
},
|
||||||
"terser": {
|
"terser": {
|
||||||
"optional": true
|
"optional": true
|
||||||
|
},
|
||||||
|
"tsx": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^5.0.8"
|
"vite": "^7.2.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/assets/audio/01. Ground Theme.mp3
Normal file
BIN
public/assets/audio/1-up.mp3
Normal file
BIN
public/assets/audio/champignon.mp3
Normal file
BIN
public/assets/audio/game-over.mp3
Normal file
BIN
public/assets/audio/hurry-up.mp3
Normal file
BIN
public/assets/audio/monde-termine.mp3
Normal file
BIN
public/assets/audio/niveau-termine.mp3
Normal file
BIN
public/assets/audio/piece.mp3
Normal file
BIN
public/assets/audio/power-up.mp3
Normal file
BIN
public/assets/audio/saut.mp3
Normal file
BIN
public/assets/audio/saute_champi.mp3
Normal file
BIN
public/assets/audio/super_tresor.mp3
Normal file
BIN
public/assets/audio/tuyau.mp3
Normal file
BIN
public/assets/sprites/champignon.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
public/assets/sprites/champignon_ecrasé.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
public/assets/sprites/jump_1.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
public/assets/sprites/jump_2.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
public/assets/sprites/jump_3.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
public/assets/sprites/jump_4.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
public/assets/sprites/jump_5.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/assets/sprites/jumping.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
public/assets/sprites/player_spritesheet.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/assets/sprites/walk_1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/assets/sprites/walk_2.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/assets/sprites/walk_3.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/assets/sprites/walk_4.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/assets/video/intro (Copie 2).mp4
Normal file
BIN
public/assets/video/intro (Copie).mp4
Normal file
BIN
public/assets/video/intro.mp4
Normal file
@@ -13,17 +13,33 @@ export class DirectionalButtons {
|
|||||||
|
|
||||||
private isLeftPressed: boolean = false;
|
private isLeftPressed: boolean = false;
|
||||||
private isRightPressed: boolean = false;
|
private isRightPressed: boolean = false;
|
||||||
|
private isMobile: boolean = false;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene) {
|
constructor(scene: Phaser.Scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.createButtons();
|
this.detectDevice();
|
||||||
|
|
||||||
|
// Ne créer les boutons QUE si on n'est PAS sur mobile
|
||||||
|
if (!this.isMobile) {
|
||||||
|
this.createButtons();
|
||||||
|
} else {
|
||||||
|
console.log('📱 Smartphone détecté → boutons directionnels désactivés (gyroscope utilisé)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Détecte si on est sur mobile
|
||||||
|
*/
|
||||||
|
private detectDevice(): void {
|
||||||
|
const userAgent = navigator.userAgent.toLowerCase();
|
||||||
|
this.isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
||||||
|
console.log('[DirectionalButtons] Mobile détecté?', this.isMobile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crée les boutons gauche et droite
|
* Crée les boutons gauche et droite
|
||||||
*/
|
*/
|
||||||
private createButtons(): void {
|
private createButtons(): void {
|
||||||
const width = this.scene.cameras.main.width;
|
|
||||||
const height = this.scene.cameras.main.height;
|
const height = this.scene.cameras.main.height;
|
||||||
const buttonSize = 80;
|
const buttonSize = 80;
|
||||||
const padding = 30;
|
const padding = 30;
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import { GYRO_DEADZONE, GYRO_MAX_TILT, GYRO_SENSITIVITY } from '../utils/constan
|
|||||||
export class GyroControl {
|
export class GyroControl {
|
||||||
private tiltValue: number = 0;
|
private tiltValue: number = 0;
|
||||||
private isActive: boolean = false;
|
private isActive: boolean = false;
|
||||||
private baseOrientation: number | null = null;
|
|
||||||
private calibrationMode: boolean = false;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.setupGyroscope();
|
this.setupGyroscope();
|
||||||
@@ -46,17 +44,20 @@ export class GyroControl {
|
|||||||
private handleOrientation(event: DeviceOrientationEvent): void {
|
private handleOrientation(event: DeviceOrientationEvent): void {
|
||||||
if (!this.isActive) return;
|
if (!this.isActive) return;
|
||||||
|
|
||||||
// Utiliser gamma (inclinaison gauche/droite)
|
// Déterminer l'axe horizontal en fonction de l'orientation écran
|
||||||
// gamma: -90 à 90 degrés
|
const angle = (window.screen.orientation?.angle ?? (window as any).orientation ?? 0) as number;
|
||||||
let gamma = event.gamma || 0;
|
const beta = event.beta ?? 0; // inclinaison avant/arrière
|
||||||
|
const gamma = event.gamma ?? 0; // inclinaison gauche/droite
|
||||||
|
|
||||||
// Calibration : définir l'orientation de base au premier appel
|
// En paysage, la gauche/droite correspond à beta; en portrait, à gamma
|
||||||
if (this.baseOrientation === null && !this.calibrationMode) {
|
let horizontalTiltDeg = gamma;
|
||||||
this.baseOrientation = gamma;
|
if (angle === 90) {
|
||||||
|
horizontalTiltDeg = -beta;
|
||||||
|
} else if (angle === -90 || angle === 270) {
|
||||||
|
horizontalTiltDeg = beta;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculer l'inclinaison relative à l'orientation de base
|
const relativeTilt = horizontalTiltDeg;
|
||||||
let relativeTilt = gamma - (this.baseOrientation || 0);
|
|
||||||
|
|
||||||
// Appliquer la deadzone
|
// Appliquer la deadzone
|
||||||
if (Math.abs(relativeTilt) < GYRO_DEADZONE) {
|
if (Math.abs(relativeTilt) < GYRO_DEADZONE) {
|
||||||
@@ -70,7 +71,8 @@ export class GyroControl {
|
|||||||
// Clamper entre -1 et 1
|
// Clamper entre -1 et 1
|
||||||
normalizedTilt = Math.max(-1, Math.min(1, normalizedTilt));
|
normalizedTilt = Math.max(-1, Math.min(1, normalizedTilt));
|
||||||
|
|
||||||
this.tiltValue = normalizedTilt;
|
// Inversion gauche/droite (plus naturel selon retour)
|
||||||
|
this.tiltValue = -normalizedTilt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,11 +93,8 @@ export class GyroControl {
|
|||||||
* Calibre le gyroscope (définit l'orientation actuelle comme neutre)
|
* Calibre le gyroscope (définit l'orientation actuelle comme neutre)
|
||||||
*/
|
*/
|
||||||
public calibrate(): void {
|
public calibrate(): void {
|
||||||
this.calibrationMode = true;
|
// Calibration simplifiée : pas de base dynamique, on recentre juste à 0
|
||||||
this.baseOrientation = null;
|
this.tiltValue = 0;
|
||||||
setTimeout(() => {
|
|
||||||
this.calibrationMode = false;
|
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ import {
|
|||||||
* Gère le mouvement, les animations, et les collisions
|
* Gère le mouvement, les animations, et les collisions
|
||||||
*/
|
*/
|
||||||
export class Player extends Phaser.Physics.Arcade.Sprite {
|
export class Player extends Phaser.Physics.Arcade.Sprite {
|
||||||
private isJumping: boolean = false;
|
|
||||||
private velocityX: number = 0;
|
private velocityX: number = 0;
|
||||||
private jumpCount: number = 0; // Compteur de sauts (pour double saut)
|
private jumpCount: number = 0; // Compteur de sauts (pour double saut)
|
||||||
private isInvincible: boolean = false; // Invincibilité temporaire après respawn
|
private isInvincible: boolean = false; // Invincibilité temporaire après respawn
|
||||||
private invincibilityTimer?: Phaser.Time.TimerEvent;
|
private invincibilityTimer?: Phaser.Time.TimerEvent;
|
||||||
|
private animationsCreated: boolean = false;
|
||||||
|
private wasAir: boolean = false;
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||||
// Pour l'instant, utiliser un sprite simple
|
// Pour l'instant, utiliser un sprite simple
|
||||||
@@ -35,6 +36,8 @@ export class Player extends Phaser.Physics.Arcade.Sprite {
|
|||||||
body.onWorldBounds = true; // Active les événements de collision
|
body.onWorldBounds = true; // Active les événements de collision
|
||||||
body.setSize(40, 70); // Hitbox
|
body.setSize(40, 70); // Hitbox
|
||||||
body.setMaxVelocity(PLAYER_MAX_SPEED, 1000);
|
body.setMaxVelocity(PLAYER_MAX_SPEED, 1000);
|
||||||
|
// Aligner la hitbox en bas du sprite (80x169 par défaut)
|
||||||
|
body.setOffset((this.width - 40) / 2, (this.height || 169) - 70);
|
||||||
|
|
||||||
// Temporaire : créer un rectangle coloré si pas de texture
|
// Temporaire : créer un rectangle coloré si pas de texture
|
||||||
if (!scene.textures.exists('player')) {
|
if (!scene.textures.exists('player')) {
|
||||||
@@ -42,6 +45,10 @@ export class Player extends Phaser.Physics.Arcade.Sprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setOrigin(0.5, 1); // Origine en bas au centre
|
this.setOrigin(0.5, 1); // Origine en bas au centre
|
||||||
|
body.setOffset((this.width - 40) / 2, (this.height || 169) - 70); // aligner la hitbox en bas
|
||||||
|
|
||||||
|
this.ensureAnimations();
|
||||||
|
this.setTexture('player_walk_1'); // frame par défaut
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,7 +115,15 @@ export class Player extends Phaser.Physics.Arcade.Sprite {
|
|||||||
if (this.jumpCount < PLAYER_MAX_JUMPS) {
|
if (this.jumpCount < PLAYER_MAX_JUMPS) {
|
||||||
body.setVelocityY(PLAYER_JUMP_VELOCITY);
|
body.setVelocityY(PLAYER_JUMP_VELOCITY);
|
||||||
this.jumpCount++;
|
this.jumpCount++;
|
||||||
this.isJumping = true;
|
|
||||||
|
// Déclencher l'anim de saut immédiatement si disponible
|
||||||
|
if (this.animationsCreated && this.anims.get('player-jump')) {
|
||||||
|
this.anims.play('player-jump', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Effet sonore de saut (volume global SFX depuis le registry)
|
||||||
|
const sfxVolume = (this.scene.registry.get('sfxVolume') as number | undefined) ?? 1;
|
||||||
|
this.scene.sound.play('sfx_jump', { volume: 0.5 * sfxVolume });
|
||||||
|
|
||||||
// TODO: Jouer son de saut (différent pour double saut)
|
// TODO: Jouer son de saut (différent pour double saut)
|
||||||
console.log(`Saut ${this.jumpCount}/${PLAYER_MAX_JUMPS}`);
|
console.log(`Saut ${this.jumpCount}/${PLAYER_MAX_JUMPS}`);
|
||||||
@@ -123,14 +138,39 @@ export class Player extends Phaser.Physics.Arcade.Sprite {
|
|||||||
|
|
||||||
// Réinitialiser le flag de saut et le compteur si on touche le sol
|
// Réinitialiser le flag de saut et le compteur si on touche le sol
|
||||||
if (body.touching.down) {
|
if (body.touching.down) {
|
||||||
this.isJumping = false;
|
|
||||||
this.jumpCount = 0;
|
this.jumpCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Jouer les animations appropriées
|
// TODO: Jouer les animations appropriées
|
||||||
// - idle si velocityX proche de 0 et au sol
|
if (!this.animationsCreated) return;
|
||||||
// - walk/run si velocityX > 0 et au sol
|
|
||||||
// - jump si isJumping
|
const isMoving = Math.abs(this.velocityX) > 10;
|
||||||
|
const bodyState = this.body as Phaser.Physics.Arcade.Body;
|
||||||
|
const isAir = !bodyState.touching.down;
|
||||||
|
|
||||||
|
// Transition sol -> air : jouer l'anim jump une fois
|
||||||
|
if (isAir && !this.wasAir) {
|
||||||
|
const jumpAnim = this.anims.get('player-jump');
|
||||||
|
if (jumpAnim) {
|
||||||
|
this.anims.play('player-jump', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sol : choisir walk ou idle
|
||||||
|
if (!isAir) {
|
||||||
|
if (isMoving) {
|
||||||
|
this.anims.play('player-walk', true);
|
||||||
|
} else {
|
||||||
|
this.anims.play('player-idle', true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// En l'air : si pas d'anim jump, forcer dernière frame jump
|
||||||
|
if (this.anims.currentAnim?.key !== 'player-jump' && this.anims.get('player-jump')) {
|
||||||
|
this.anims.play('player-jump', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wasAir = isAir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,6 +231,48 @@ export class Player extends Phaser.Physics.Arcade.Sprite {
|
|||||||
// Logique supplémentaire si nécessaire
|
// Logique supplémentaire si nécessaire
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Création des animations (walk + idle)
|
||||||
|
*/
|
||||||
|
private ensureAnimations(): void {
|
||||||
|
if (this.animationsCreated) return;
|
||||||
|
|
||||||
|
const scene = this.scene;
|
||||||
|
const walkFrames = ['player_walk_1', 'player_walk_2', 'player_walk_3', 'player_walk_4']
|
||||||
|
.filter((key) => scene.textures.exists(key))
|
||||||
|
.map((key) => ({ key }));
|
||||||
|
const jumpFrames = ['player_jump_1', 'player_jump_2', 'player_jump_3', 'player_jump_4', 'player_jump_5']
|
||||||
|
.filter((key) => scene.textures.exists(key))
|
||||||
|
.map((key) => ({ key }));
|
||||||
|
|
||||||
|
if (walkFrames.length >= 2) {
|
||||||
|
scene.anims.create({
|
||||||
|
key: 'player-walk',
|
||||||
|
frames: walkFrames,
|
||||||
|
frameRate: 10,
|
||||||
|
repeat: -1,
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.anims.create({
|
||||||
|
key: 'player-idle',
|
||||||
|
frames: [{ key: walkFrames[0].key }],
|
||||||
|
frameRate: 1,
|
||||||
|
repeat: -1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jumpFrames.length >= 2) {
|
||||||
|
scene.anims.create({
|
||||||
|
key: 'player-jump',
|
||||||
|
frames: jumpFrames,
|
||||||
|
frameRate: 12,
|
||||||
|
repeat: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.animationsCreated = walkFrames.length >= 2 || jumpFrames.length >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nettoie les ressources
|
* Nettoie les ressources
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ export class SuperTreasure extends Phaser.Physics.Arcade.Sprite {
|
|||||||
const star = scene.add.circle(x, y, 3, 0xFFFFFF, 0.8);
|
const star = scene.add.circle(x, y, 3, 0xFFFFFF, 0.8);
|
||||||
star.setDepth(this.depth - 1);
|
star.setDepth(this.depth - 1);
|
||||||
|
|
||||||
const angle = (i * 120) * (Math.PI / 180);
|
|
||||||
const radius = 40;
|
const radius = 40;
|
||||||
|
|
||||||
scene.tweens.add({
|
scene.tweens.add({
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import Phaser from 'phaser';
|
|||||||
export class TreasureChest extends Phaser.Physics.Arcade.Sprite {
|
export class TreasureChest extends Phaser.Physics.Arcade.Sprite {
|
||||||
private isOpen: boolean = false;
|
private isOpen: boolean = false;
|
||||||
private requiredGifts: number;
|
private requiredGifts: number;
|
||||||
private particles?: Phaser.GameObjects.Particles.ParticleEmitter;
|
|
||||||
|
|
||||||
constructor(scene: Phaser.Scene, x: number, y: number, requiredGifts: number = 15) {
|
constructor(scene: Phaser.Scene, x: number, y: number, requiredGifts: number = 15) {
|
||||||
super(scene, x, y, 'chest');
|
super(scene, x, y, 'chest');
|
||||||
@@ -220,19 +219,6 @@ export class TreasureChest extends Phaser.Physics.Arcade.Sprite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Met à jour le texte du requirement
|
|
||||||
*/
|
|
||||||
public updateRequirementText(scene: Phaser.Scene, giftsCollected: number): void {
|
|
||||||
if (this.isOpen) return;
|
|
||||||
|
|
||||||
// Trouver le texte et le mettre à jour
|
|
||||||
const remaining = this.requiredGifts - giftsCollected;
|
|
||||||
if (remaining > 0) {
|
|
||||||
// Le texte sera mis à jour par GameScene
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vérifie si le coffre est ouvert
|
* Vérifie si le coffre est ouvert
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { GAME_WIDTH, GAME_HEIGHT } from './utils/constants';
|
|||||||
import { BootScene } from './scenes/BootScene';
|
import { BootScene } from './scenes/BootScene';
|
||||||
import { MenuScene } from './scenes/MenuScene';
|
import { MenuScene } from './scenes/MenuScene';
|
||||||
import { GameScene } from './scenes/GameScene';
|
import { GameScene } from './scenes/GameScene';
|
||||||
|
import { IntroScene } from './scenes/IntroScene';
|
||||||
|
|
||||||
// Configuration Phaser
|
// Configuration Phaser
|
||||||
const config: Phaser.Types.Core.GameConfig = {
|
const config: Phaser.Types.Core.GameConfig = {
|
||||||
@@ -21,7 +22,7 @@ const config: Phaser.Types.Core.GameConfig = {
|
|||||||
debug: false, // Mettre à true pour voir les hitboxes
|
debug: false, // Mettre à true pour voir les hitboxes
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scene: [BootScene, MenuScene, GameScene],
|
scene: [BootScene, IntroScene, MenuScene, GameScene],
|
||||||
backgroundColor: '#87CEEB',
|
backgroundColor: '#87CEEB',
|
||||||
render: {
|
render: {
|
||||||
pixelArt: false,
|
pixelArt: false,
|
||||||
|
|||||||
@@ -42,14 +42,49 @@ export class BootScene extends Phaser.Scene {
|
|||||||
loadingText.destroy();
|
loadingText.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Charger les assets de base ici
|
// Sprites du joueur (80x169, 1 frame pour l'instant)
|
||||||
// Exemple : this.load.image('logo', 'assets/logo.png');
|
this.load.spritesheet('player', 'assets/sprites/player_spritesheet.png', {
|
||||||
|
frameWidth: 80,
|
||||||
|
frameHeight: 169,
|
||||||
|
});
|
||||||
|
// Frames de marche (sprite individuel)
|
||||||
|
this.load.image('player_walk_1', 'assets/sprites/walk_1.png');
|
||||||
|
this.load.image('player_walk_2', 'assets/sprites/walk_2.png');
|
||||||
|
this.load.image('player_walk_3', 'assets/sprites/walk_3.png');
|
||||||
|
this.load.image('player_walk_4', 'assets/sprites/walk_4.png');
|
||||||
|
this.load.image('player_jump_1', 'assets/sprites/jump_1.png');
|
||||||
|
this.load.image('player_jump_2', 'assets/sprites/jump_2.png');
|
||||||
|
this.load.image('player_jump_3', 'assets/sprites/jump_3.png');
|
||||||
|
this.load.image('player_jump_4', 'assets/sprites/jump_4.png');
|
||||||
|
this.load.image('player_jump_5', 'assets/sprites/jump_5.png');
|
||||||
|
|
||||||
// TODO: Charger sprites, backgrounds, sons, etc.
|
// Musique de fond
|
||||||
|
this.load.audio('bgm', 'assets/audio/01. Ground Theme.mp3');
|
||||||
|
|
||||||
|
// Effets sonores
|
||||||
|
this.load.audio('sfx_jump', 'assets/audio/saut.mp3');
|
||||||
|
this.load.audio('sfx_piece', 'assets/audio/piece.mp3');
|
||||||
|
this.load.audio('sfx_powerup', 'assets/audio/power-up.mp3');
|
||||||
|
this.load.audio('sfx_gameover', 'assets/audio/game-over.mp3');
|
||||||
|
this.load.audio('sfx_levelcomplete', 'assets/audio/niveau-termine.mp3');
|
||||||
|
this.load.audio('sfx_tuyau', 'assets/audio/tuyau.mp3');
|
||||||
|
// Charger en priorité le MP3, mais accepter AIFF en fallback si présent
|
||||||
|
this.load.audio('sfx_hit', ['assets/audio/champignon.mp3', 'assets/audio/champignon.aiff']);
|
||||||
|
this.load.audio('sfx_super', 'assets/audio/super_tresor.mp3');
|
||||||
|
this.load.audio('sfx_saute_champi', 'assets/audio/saute_champi.mp3');
|
||||||
|
|
||||||
|
// Sprites obstacles
|
||||||
|
this.load.image('obstacle_mushroom', 'assets/sprites/champignon.png');
|
||||||
|
|
||||||
|
// Vidéo d'intro (mp4 uniquement)
|
||||||
|
// Le 3e paramètre 'noAudio' est à false pour garder l'audio si présent
|
||||||
|
this.load.video('intro', 'assets/video/intro.mp4', false);
|
||||||
|
|
||||||
|
// TODO: Charger d'autres sprites, backgrounds, sons, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
create(): void {
|
create(): void {
|
||||||
// Passer à la scène Menu
|
// Passer par l'intro vidéo puis le menu
|
||||||
this.scene.start('MenuScene');
|
this.scene.start('IntroScene');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { LEVEL_DURATION, PLAYER_STARTING_LIVES, CHEST_REQUIRED_GIFTS } from '../
|
|||||||
import { Player } from '../entities/Player';
|
import { Player } from '../entities/Player';
|
||||||
import { GyroControl } from '../controls/GyroControl';
|
import { GyroControl } from '../controls/GyroControl';
|
||||||
import { JumpButton } from '../controls/JumpButton';
|
import { JumpButton } from '../controls/JumpButton';
|
||||||
|
import { DirectionalButtons } from '../controls/DirectionalButtons';
|
||||||
import { SuperTreasure } from '../entities/SuperTreasure';
|
import { SuperTreasure } from '../entities/SuperTreasure';
|
||||||
import { TreasureChest } from '../entities/TreasureChest';
|
import { TreasureChest } from '../entities/TreasureChest';
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private cursors?: Phaser.Types.Input.Keyboard.CursorKeys;
|
private cursors?: Phaser.Types.Input.Keyboard.CursorKeys;
|
||||||
private gyroControl?: GyroControl;
|
private gyroControl?: GyroControl;
|
||||||
private jumpButton?: JumpButton;
|
private jumpButton?: JumpButton;
|
||||||
|
private directionalButtons?: DirectionalButtons;
|
||||||
|
|
||||||
// Plateformes et groupes
|
// Plateformes et groupes
|
||||||
private platforms?: Phaser.Physics.Arcade.StaticGroup;
|
private platforms?: Phaser.Physics.Arcade.StaticGroup;
|
||||||
@@ -22,6 +24,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private gifts?: Phaser.Physics.Arcade.Group;
|
private gifts?: Phaser.Physics.Arcade.Group;
|
||||||
private superTreasures?: Phaser.Physics.Arcade.Group;
|
private superTreasures?: Phaser.Physics.Arcade.Group;
|
||||||
private treasureChest?: TreasureChest;
|
private treasureChest?: TreasureChest;
|
||||||
|
private bgMusic?: Phaser.Sound.BaseSound;
|
||||||
|
private platformRects: { x: number; y: number; w: number; h: number }[] = [];
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
private background?: Phaser.GameObjects.TileSprite;
|
private background?: Phaser.GameObjects.TileSprite;
|
||||||
@@ -32,6 +36,8 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private controlInfoText?: Phaser.GameObjects.Text;
|
private controlInfoText?: Phaser.GameObjects.Text;
|
||||||
private livesText?: Phaser.GameObjects.Text;
|
private livesText?: Phaser.GameObjects.Text;
|
||||||
private giftsCollectedText?: Phaser.GameObjects.Text;
|
private giftsCollectedText?: Phaser.GameObjects.Text;
|
||||||
|
private volumeText?: Phaser.GameObjects.Text;
|
||||||
|
private sfxVolumeText?: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
// Game state
|
// Game state
|
||||||
private score: number = 0;
|
private score: number = 0;
|
||||||
@@ -41,6 +47,11 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private lives: number = PLAYER_STARTING_LIVES;
|
private lives: number = PLAYER_STARTING_LIVES;
|
||||||
private giftsCollected: number = 0;
|
private giftsCollected: number = 0;
|
||||||
private lastCheckpointX: number = 200; // Position du dernier checkpoint
|
private lastCheckpointX: number = 200; // Position du dernier checkpoint
|
||||||
|
private musicVolume: number = 0.5;
|
||||||
|
private sfxVolume: number = 0.6;
|
||||||
|
private snowPileTimer?: Phaser.Time.TimerEvent;
|
||||||
|
private snowHeights: number[] = [];
|
||||||
|
private snowPileGraphics?: Phaser.GameObjects.Graphics;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ key: 'GameScene' });
|
super({ key: 'GameScene' });
|
||||||
@@ -50,17 +61,24 @@ export class GameScene extends Phaser.Scene {
|
|||||||
const width = this.cameras.main.width;
|
const width = this.cameras.main.width;
|
||||||
const height = this.cameras.main.height;
|
const height = this.cameras.main.height;
|
||||||
|
|
||||||
|
// Réinitialiser l'état de partie
|
||||||
|
this.score = 0;
|
||||||
|
this.giftsCollected = 0;
|
||||||
|
this.lives = PLAYER_STARTING_LIVES;
|
||||||
|
this.lastCheckpointX = 200;
|
||||||
|
|
||||||
this.gameStartTime = this.time.now;
|
this.gameStartTime = this.time.now;
|
||||||
|
|
||||||
// Détecter si mobile
|
// Détecter si mobile
|
||||||
this.isMobile = this.sys.game.device.os.android || this.sys.game.device.os.iOS;
|
this.isMobile = this.sys.game.device.os.android || this.sys.game.device.os.iOS;
|
||||||
|
|
||||||
// Configurer les limites du monde physique (IMPORTANT pour permettre mouvement infini)
|
// Configurer les limites du monde physique (IMPORTANT pour permettre mouvement infini)
|
||||||
const levelWidth = width * 6; // Niveau 6x plus grand
|
// Étendre un peu plus pour inclure la plateforme finale et le coffre
|
||||||
|
const levelWidth = Math.max(width * 7, 8000);
|
||||||
this.physics.world.setBounds(0, 0, levelWidth, height);
|
this.physics.world.setBounds(0, 0, levelWidth, height);
|
||||||
|
|
||||||
// Créer le background qui défile
|
// Créer le background qui défile
|
||||||
this.createBackground();
|
this.createBackground(levelWidth);
|
||||||
|
|
||||||
// Créer les plateformes
|
// Créer les plateformes
|
||||||
this.createPlatforms();
|
this.createPlatforms();
|
||||||
@@ -89,6 +107,15 @@ export class GameScene extends Phaser.Scene {
|
|||||||
// UI
|
// UI
|
||||||
this.createUI();
|
this.createUI();
|
||||||
|
|
||||||
|
// Effet neige
|
||||||
|
this.createSnow();
|
||||||
|
|
||||||
|
// Musique
|
||||||
|
this.setupMusic();
|
||||||
|
|
||||||
|
// Volume SFX initial
|
||||||
|
this.registry.set('sfxVolume', this.sfxVolume);
|
||||||
|
|
||||||
// Générer quelques obstacles et cadeaux de test
|
// Générer quelques obstacles et cadeaux de test
|
||||||
this.spawnTestObjects();
|
this.spawnTestObjects();
|
||||||
|
|
||||||
@@ -98,7 +125,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
/**
|
/**
|
||||||
* Crée le background qui défile
|
* Crée le background qui défile
|
||||||
*/
|
*/
|
||||||
private createBackground(): void {
|
private createBackground(levelWidth: number): void {
|
||||||
const width = this.cameras.main.width;
|
const width = this.cameras.main.width;
|
||||||
const height = this.cameras.main.height;
|
const height = this.cameras.main.height;
|
||||||
|
|
||||||
@@ -122,7 +149,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
graphics.generateTexture('sky', width, height);
|
graphics.generateTexture('sky', width, height);
|
||||||
graphics.destroy();
|
graphics.destroy();
|
||||||
|
|
||||||
this.background = this.add.tileSprite(0, 0, width * 6, height, 'sky');
|
this.background = this.add.tileSprite(0, 0, levelWidth, height, 'sky');
|
||||||
this.background.setOrigin(0, 0);
|
this.background.setOrigin(0, 0);
|
||||||
this.background.setScrollFactor(0.3); // Effet parallaxe
|
this.background.setScrollFactor(0.3); // Effet parallaxe
|
||||||
}
|
}
|
||||||
@@ -141,6 +168,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
const ground = this.add.rectangle(groundWidth / 2, height - 25, groundWidth, 50, 0x8B4513);
|
const ground = this.add.rectangle(groundWidth / 2, height - 25, groundWidth, 50, 0x8B4513);
|
||||||
this.physics.add.existing(ground, true);
|
this.physics.add.existing(ground, true);
|
||||||
this.platforms.add(ground);
|
this.platforms.add(ground);
|
||||||
|
this.platformRects.push({ x: groundWidth / 2, y: height - 25, w: groundWidth, h: 50 });
|
||||||
|
|
||||||
// BEAUCOUP plus de plateformes réparties sur toute la longueur
|
// BEAUCOUP plus de plateformes réparties sur toute la longueur
|
||||||
const platformPositions = [
|
const platformPositions = [
|
||||||
@@ -187,6 +215,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
const platform = this.add.rectangle(pos.x, pos.y, pos.w, pos.h, 0x6B8E23);
|
const platform = this.add.rectangle(pos.x, pos.y, pos.w, pos.h, 0x6B8E23);
|
||||||
this.physics.add.existing(platform, true);
|
this.physics.add.existing(platform, true);
|
||||||
this.platforms!.add(platform);
|
this.platforms!.add(platform);
|
||||||
|
this.platformRects.push({ x: pos.x, y: pos.y, w: pos.w, h: pos.h });
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`${platformPositions.length} plateformes créées sur ${groundWidth}px`);
|
console.log(`${platformPositions.length} plateformes créées sur ${groundWidth}px`);
|
||||||
@@ -254,9 +283,77 @@ export class GameScene extends Phaser.Scene {
|
|||||||
6800, 7100, 7400,
|
6800, 7100, 7400,
|
||||||
];
|
];
|
||||||
|
|
||||||
obstaclePositions.forEach((x) => {
|
// Obstacles sur plateformes (x, y)
|
||||||
const obstacle = this.add.rectangle(x, height - 80, 40, 60, 0xF44336);
|
const obstaclePlatforms = [
|
||||||
this.physics.add.existing(obstacle);
|
{ x: 700, y: height - 280 },
|
||||||
|
{ x: 1300, y: height - 330 },
|
||||||
|
{ x: 1900, y: height - 350 },
|
||||||
|
{ x: 2500, y: height - 410 },
|
||||||
|
{ x: 3350, y: height - 380 },
|
||||||
|
{ x: 3900, y: height - 430 },
|
||||||
|
{ x: 4800, y: height - 360 },
|
||||||
|
{ x: 5400, y: height - 440 },
|
||||||
|
{ x: 6250, y: height - 400 },
|
||||||
|
{ x: 6800, y: height - 500 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Ajouter quelques champignons directement sur les plateformes existantes
|
||||||
|
const platformPlaced: Array<{ x: number; y: number }> = [];
|
||||||
|
this.platformRects
|
||||||
|
.filter((rect) => rect.y < height - 80 && rect.w >= 120) // ignorer le sol et les très petites plateformes
|
||||||
|
.forEach((rect, idx) => {
|
||||||
|
// Placer un champignon sur ~1 plateforme sur 2 pour ne pas surcharger
|
||||||
|
if (idx % 2 !== 0) return;
|
||||||
|
const x = rect.x + Phaser.Math.Between(Math.round(-rect.w / 4), Math.round(rect.w / 4));
|
||||||
|
const y = rect.y - rect.h / 2;
|
||||||
|
platformPlaced.push({ x, y });
|
||||||
|
});
|
||||||
|
|
||||||
|
obstaclePositions.filter((_, idx) => idx % 2 === 0).forEach((x) => {
|
||||||
|
const obstacle = this.physics.add.sprite(x, height - 50, 'obstacle_mushroom');
|
||||||
|
obstacle.setOrigin(0.5, 1); // ancré sur les pieds
|
||||||
|
obstacle.setImmovable(true);
|
||||||
|
obstacle.setPushable(false);
|
||||||
|
obstacle.setScale(0.9);
|
||||||
|
|
||||||
|
const body = obstacle.body as Phaser.Physics.Arcade.Body;
|
||||||
|
body.setAllowGravity(false);
|
||||||
|
body.setSize(45, 81); // hitbox 10% plus petite avec le scale
|
||||||
|
body.setOffset((obstacle.displayWidth - 45) / 2, obstacle.displayHeight - 81);
|
||||||
|
|
||||||
|
this.obstacles!.add(obstacle);
|
||||||
|
});
|
||||||
|
|
||||||
|
obstaclePlatforms.filter((_, idx) => idx % 2 === 0).forEach((pos) => {
|
||||||
|
// Trouver la plateforme la plus proche à cet x
|
||||||
|
const target = this.platformRects.find((rect) => Math.abs(pos.x - rect.x) <= rect.w / 2);
|
||||||
|
const topY = target ? target.y - target.h / 2 : pos.y;
|
||||||
|
const obstacle = this.physics.add.sprite(pos.x, topY, 'obstacle_mushroom');
|
||||||
|
obstacle.setOrigin(0.5, 1);
|
||||||
|
obstacle.setImmovable(true);
|
||||||
|
obstacle.setPushable(false);
|
||||||
|
obstacle.setScale(0.9);
|
||||||
|
|
||||||
|
const body = obstacle.body as Phaser.Physics.Arcade.Body;
|
||||||
|
body.setAllowGravity(false);
|
||||||
|
body.setSize(45, 81);
|
||||||
|
body.setOffset((obstacle.displayWidth - 45) / 2, obstacle.displayHeight - 81);
|
||||||
|
|
||||||
|
this.obstacles!.add(obstacle);
|
||||||
|
});
|
||||||
|
|
||||||
|
platformPlaced.filter((_, idx) => idx % 2 === 0).forEach((pos) => {
|
||||||
|
const obstacle = this.physics.add.sprite(pos.x, pos.y, 'obstacle_mushroom');
|
||||||
|
obstacle.setOrigin(0.5, 1);
|
||||||
|
obstacle.setImmovable(true);
|
||||||
|
obstacle.setPushable(false);
|
||||||
|
obstacle.setScale(0.9);
|
||||||
|
|
||||||
|
const body = obstacle.body as Phaser.Physics.Arcade.Body;
|
||||||
|
body.setAllowGravity(false);
|
||||||
|
body.setSize(45, 81);
|
||||||
|
body.setOffset((obstacle.displayWidth - 45) / 2, obstacle.displayHeight - 81);
|
||||||
|
|
||||||
this.obstacles!.add(obstacle);
|
this.obstacles!.add(obstacle);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -276,7 +373,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// COFFRE FINAL au bout du niveau
|
// COFFRE FINAL au bout du niveau
|
||||||
this.treasureChest = new TreasureChest(this, 7600, height - 300, CHEST_REQUIRED_GIFTS);
|
this.treasureChest = new TreasureChest(this, 7700, height - 300, CHEST_REQUIRED_GIFTS);
|
||||||
this.physics.add.overlap(this.player!, this.treasureChest, this.openChest, undefined, this);
|
this.physics.add.overlap(this.player!, this.treasureChest, this.openChest, undefined, this);
|
||||||
|
|
||||||
console.log(`${giftPositions.length} cadeaux, ${obstaclePositions.length} obstacles, ${superTreasurePositions.length} SUPER TRÉSORS et 1 COFFRE FINAL créés`);
|
console.log(`${giftPositions.length} cadeaux, ${obstaclePositions.length} obstacles, ${superTreasurePositions.length} SUPER TRÉSORS et 1 COFFRE FINAL créés`);
|
||||||
@@ -293,6 +390,9 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.jumpButton = new JumpButton(this, () => {
|
this.jumpButton = new JumpButton(this, () => {
|
||||||
this.player?.jump();
|
this.player?.jump();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Boutons gauche/droite (en bas à gauche)
|
||||||
|
this.directionalButtons = new DirectionalButtons(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -375,6 +475,36 @@ export class GameScene extends Phaser.Scene {
|
|||||||
this.cleanup();
|
this.cleanup();
|
||||||
this.scene.start('MenuScene');
|
this.scene.start('MenuScene');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Volume musique
|
||||||
|
this.volumeText = this.add.text(this.cameras.main.width - 20, 60, this.getVolumeLabel(), {
|
||||||
|
fontSize: '18px',
|
||||||
|
color: '#ffffff',
|
||||||
|
backgroundColor: '#000000',
|
||||||
|
padding: { x: 8, y: 4 },
|
||||||
|
});
|
||||||
|
this.volumeText.setOrigin(1, 0);
|
||||||
|
this.volumeText.setScrollFactor(0);
|
||||||
|
this.volumeText.setDepth(100);
|
||||||
|
this.volumeText.setInteractive({ useHandCursor: true });
|
||||||
|
this.volumeText.on('pointerdown', () => {
|
||||||
|
this.cycleMusicVolume();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Volume SFX
|
||||||
|
this.sfxVolumeText = this.add.text(this.cameras.main.width - 20, 90, this.getSfxVolumeLabel(), {
|
||||||
|
fontSize: '18px',
|
||||||
|
color: '#ffffff',
|
||||||
|
backgroundColor: '#000000',
|
||||||
|
padding: { x: 8, y: 4 },
|
||||||
|
});
|
||||||
|
this.sfxVolumeText.setOrigin(1, 0);
|
||||||
|
this.sfxVolumeText.setScrollFactor(0);
|
||||||
|
this.sfxVolumeText.setDepth(100);
|
||||||
|
this.sfxVolumeText.setInteractive({ useHandCursor: true });
|
||||||
|
this.sfxVolumeText.on('pointerdown', () => {
|
||||||
|
this.cycleSfxVolume();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update(time: number): void {
|
update(time: number): void {
|
||||||
@@ -405,10 +535,15 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mobile : Gyroscope
|
// Mobile : boutons directionnels priment, sinon gyroscope
|
||||||
if (this.gyroControl && this.isMobile) {
|
if (this.isMobile) {
|
||||||
const tiltValue = this.gyroControl.getTiltValue();
|
const dirButtons = this.directionalButtons?.getDirection() ?? 0;
|
||||||
direction = tiltValue; // -1 à 1
|
if (dirButtons !== 0) {
|
||||||
|
direction = dirButtons;
|
||||||
|
} else if (this.gyroControl) {
|
||||||
|
const tiltValue = this.gyroControl.getTiltValue();
|
||||||
|
direction = tiltValue; // -1 à 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Déplacer le joueur
|
// Déplacer le joueur
|
||||||
@@ -460,6 +595,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
gift.destroy();
|
gift.destroy();
|
||||||
this.giftsCollected++;
|
this.giftsCollected++;
|
||||||
this.addScore(100);
|
this.addScore(100);
|
||||||
|
this.playSfx('sfx_powerup', 0.5);
|
||||||
|
|
||||||
// Mettre à jour l'UI
|
// Mettre à jour l'UI
|
||||||
this.giftsCollectedText?.setText(`🎁 Cadeaux: ${this.giftsCollected}/${CHEST_REQUIRED_GIFTS}`);
|
this.giftsCollectedText?.setText(`🎁 Cadeaux: ${this.giftsCollected}/${CHEST_REQUIRED_GIFTS}`);
|
||||||
@@ -509,6 +645,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
obstacle.destroy();
|
obstacle.destroy();
|
||||||
this.addScore(50); // Bonus pour avoir sauté dessus
|
this.addScore(50); // Bonus pour avoir sauté dessus
|
||||||
player.jump(); // Petit rebond
|
player.jump(); // Petit rebond
|
||||||
|
this.playSfx('sfx_saute_champi', 0.6);
|
||||||
|
|
||||||
// Effet visuel
|
// Effet visuel
|
||||||
const explosion = this.add.circle(obstacleBody.x, obstacleBody.y, 20, 0x00FF00, 0.5);
|
const explosion = this.add.circle(obstacleBody.x, obstacleBody.y, 20, 0x00FF00, 0.5);
|
||||||
@@ -530,6 +667,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.playSfx('sfx_hit', 0.6);
|
||||||
this.loseLife();
|
this.loseLife();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,6 +684,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
// GROS BONUS de points!
|
// GROS BONUS de points!
|
||||||
this.addScore(500);
|
this.addScore(500);
|
||||||
|
this.playSfx('sfx_super', 0.6);
|
||||||
|
|
||||||
// Message spécial
|
// Message spécial
|
||||||
const bonusText = this.add.text(
|
const bonusText = this.add.text(
|
||||||
@@ -583,18 +722,40 @@ export class GameScene extends Phaser.Scene {
|
|||||||
/**
|
/**
|
||||||
* Ouvre le coffre au trésor final
|
* Ouvre le coffre au trésor final
|
||||||
*/
|
*/
|
||||||
private openChest(_player: any, _chest: any): void {
|
private openChest(_player: any, chest: any): void {
|
||||||
if (!this.treasureChest) return;
|
if (chest.canOpen(this.giftsCollected)) {
|
||||||
|
const bonus = chest.open(this);
|
||||||
// Vérifier si on peut ouvrir
|
this.addScore(bonus);
|
||||||
if (this.treasureChest.canOpen(this.giftsCollected)) {
|
|
||||||
const megaBonus = this.treasureChest.open(this);
|
|
||||||
this.addScore(megaBonus);
|
|
||||||
|
|
||||||
// VICTOIRE ! Lancer l'animation de fin
|
// VICTOIRE ! Lancer l'animation de fin
|
||||||
this.time.delayedCall(2000, () => {
|
this.time.delayedCall(2000, () => {
|
||||||
this.levelComplete();
|
this.levelComplete();
|
||||||
});
|
});
|
||||||
|
} else if (!chest.getIsOpen()) {
|
||||||
|
// Pas assez de cadeaux
|
||||||
|
const remaining = chest.getRequiredGifts() - this.giftsCollected;
|
||||||
|
|
||||||
|
const warning = this.add.text(
|
||||||
|
this.cameras.main.scrollX + this.cameras.main.width / 2,
|
||||||
|
this.cameras.main.height / 2,
|
||||||
|
`❌ Encore ${remaining} cadeaux nécessaires! ❌`,
|
||||||
|
{
|
||||||
|
fontSize: '28px',
|
||||||
|
color: '#FF0000',
|
||||||
|
stroke: '#000000',
|
||||||
|
strokeThickness: 4,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
warning.setOrigin(0.5);
|
||||||
|
warning.setScrollFactor(0);
|
||||||
|
warning.setDepth(1000);
|
||||||
|
|
||||||
|
this.tweens.add({
|
||||||
|
targets: warning,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 2000,
|
||||||
|
onComplete: () => warning.destroy(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,6 +767,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
// Arrêter la physique
|
// Arrêter la physique
|
||||||
this.physics.pause();
|
this.physics.pause();
|
||||||
|
this.playSfx('sfx_levelcomplete', 0.6);
|
||||||
|
|
||||||
// Flash doré géant
|
// Flash doré géant
|
||||||
this.cameras.main.flash(1000, 255, 215, 0, true);
|
this.cameras.main.flash(1000, 255, 215, 0, true);
|
||||||
@@ -737,21 +899,18 @@ export class GameScene extends Phaser.Scene {
|
|||||||
* Fait perdre une vie au joueur
|
* Fait perdre une vie au joueur
|
||||||
*/
|
*/
|
||||||
private loseLife(): void {
|
private loseLife(): void {
|
||||||
// Flash rouge pour indiquer les dégâts
|
|
||||||
this.cameras.main.flash(200, 255, 0, 0, true);
|
|
||||||
|
|
||||||
// Décrémenter les vies
|
|
||||||
this.lives--;
|
this.lives--;
|
||||||
this.livesText?.setText(`❤️ Vies: ${this.lives}`);
|
this.livesText?.setText(`❤️ Vies: ${this.lives}`);
|
||||||
|
|
||||||
// Effet sonore (TODO: ajouter un vrai son)
|
// Flash rouge
|
||||||
console.log(`💔 Vie perdue ! Vies restantes: ${this.lives}`);
|
this.cameras.main.flash(200, 255, 0, 0, true);
|
||||||
|
this.cameras.main.shake(200, 0.01);
|
||||||
|
|
||||||
|
console.log(`💔 Vie perdue! Vies restantes: ${this.lives}`);
|
||||||
|
|
||||||
if (this.lives <= 0) {
|
if (this.lives <= 0) {
|
||||||
// Game Over
|
|
||||||
this.gameOver();
|
this.gameOver();
|
||||||
} else {
|
} else {
|
||||||
// Respawn au dernier checkpoint
|
|
||||||
this.respawnPlayer();
|
this.respawnPlayer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -762,55 +921,81 @@ export class GameScene extends Phaser.Scene {
|
|||||||
private respawnPlayer(): void {
|
private respawnPlayer(): void {
|
||||||
if (!this.player) return;
|
if (!this.player) return;
|
||||||
|
|
||||||
// Téléporter au checkpoint
|
// Téléporter au dernier checkpoint
|
||||||
this.player.setPosition(this.lastCheckpointX, this.cameras.main.height - 150);
|
this.player.setPosition(this.lastCheckpointX, this.cameras.main.height - 200);
|
||||||
this.player.setVelocity(0, 0);
|
this.player.setVelocity(0, 0);
|
||||||
|
|
||||||
// Activer l'invincibilité temporaire
|
// Activer l'invincibilité
|
||||||
this.player.makeInvincible(this);
|
this.player.makeInvincible(this);
|
||||||
|
|
||||||
console.log(`🔄 Respawn au checkpoint x=${this.lastCheckpointX}`);
|
// Message
|
||||||
|
const respawnText = this.add.text(
|
||||||
|
this.cameras.main.scrollX + this.cameras.main.width / 2,
|
||||||
|
this.cameras.main.height / 2,
|
||||||
|
`💫 RESPAWN! ${this.lives} ❤️ restantes`,
|
||||||
|
{
|
||||||
|
fontSize: '36px',
|
||||||
|
color: '#00FF00',
|
||||||
|
stroke: '#000000',
|
||||||
|
strokeThickness: 6,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
respawnText.setOrigin(0.5);
|
||||||
|
respawnText.setScrollFactor(0);
|
||||||
|
respawnText.setDepth(1000);
|
||||||
|
|
||||||
|
this.tweens.add({
|
||||||
|
targets: respawnText,
|
||||||
|
alpha: 0,
|
||||||
|
duration: 2000,
|
||||||
|
onComplete: () => respawnText.destroy(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Game Over - plus de vies
|
* Game Over - plus de vies
|
||||||
*/
|
*/
|
||||||
private gameOver(): void {
|
private gameOver(): void {
|
||||||
console.log('💀 GAME OVER - Plus de vies!');
|
console.log('💀 GAME OVER');
|
||||||
|
|
||||||
// Arrêter la physique
|
// Arrêter la physique
|
||||||
this.physics.pause();
|
this.physics.pause();
|
||||||
|
this.playSfx('sfx_gameover', 0.6);
|
||||||
|
|
||||||
// Message Game Over
|
// Écran de game over
|
||||||
const gameOverText = this.add.text(
|
const gameOverText = this.add.text(
|
||||||
this.cameras.main.scrollX + this.cameras.main.width / 2,
|
this.cameras.main.scrollX + this.cameras.main.width / 2,
|
||||||
this.cameras.main.height / 2,
|
this.cameras.main.height / 2 - 50,
|
||||||
`GAME OVER\n\nScore Final: ${this.score}\n\nRetour au menu dans 5s...`,
|
'GAME OVER',
|
||||||
{
|
{
|
||||||
fontSize: '48px',
|
fontSize: '72px',
|
||||||
color: '#FF0000',
|
color: '#FF0000',
|
||||||
stroke: '#000000',
|
stroke: '#000000',
|
||||||
strokeThickness: 8,
|
strokeThickness: 8,
|
||||||
fontStyle: 'bold',
|
fontStyle: 'bold',
|
||||||
align: 'center',
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
gameOverText.setOrigin(0.5);
|
gameOverText.setOrigin(0.5);
|
||||||
gameOverText.setScrollFactor(0);
|
gameOverText.setScrollFactor(0);
|
||||||
gameOverText.setDepth(2000);
|
gameOverText.setDepth(2000);
|
||||||
|
|
||||||
// Animation du texte
|
const scoreText = this.add.text(
|
||||||
this.tweens.add({
|
this.cameras.main.scrollX + this.cameras.main.width / 2,
|
||||||
targets: gameOverText,
|
this.cameras.main.height / 2 + 50,
|
||||||
scaleX: 1.1,
|
`Score Final: ${this.score}`,
|
||||||
scaleY: 1.1,
|
{
|
||||||
duration: 500,
|
fontSize: '36px',
|
||||||
yoyo: true,
|
color: '#FFFFFF',
|
||||||
repeat: -1,
|
stroke: '#000000',
|
||||||
});
|
strokeThickness: 4,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
scoreText.setOrigin(0.5);
|
||||||
|
scoreText.setScrollFactor(0);
|
||||||
|
scoreText.setDepth(2000);
|
||||||
|
|
||||||
// Retour au menu après 5 secondes
|
// Retour au menu après 3 secondes
|
||||||
this.time.delayedCall(5000, () => {
|
this.time.delayedCall(3000, () => {
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
this.scene.start('MenuScene');
|
this.scene.start('MenuScene');
|
||||||
});
|
});
|
||||||
@@ -851,6 +1036,163 @@ export class GameScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure et lance la musique de fond
|
||||||
|
*/
|
||||||
|
private setupMusic(): void {
|
||||||
|
if (this.bgMusic && this.bgMusic.isPlaying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bgMusic = this.sound.add('bgm', {
|
||||||
|
loop: true,
|
||||||
|
volume: this.musicVolume,
|
||||||
|
});
|
||||||
|
this.bgMusic.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour le volume musique (cycle 0%, 20%, 40%, 60%, 80%, 100%)
|
||||||
|
*/
|
||||||
|
private cycleMusicVolume(): void {
|
||||||
|
const steps = [0, 0.2, 0.4, 0.6, 0.8, 1];
|
||||||
|
const currentIndex = steps.findIndex((v) => v === this.musicVolume);
|
||||||
|
const nextIndex = (currentIndex + 1) % steps.length;
|
||||||
|
this.musicVolume = steps[nextIndex];
|
||||||
|
|
||||||
|
if (this.bgMusic) {
|
||||||
|
const webAudio = this.bgMusic as Phaser.Sound.WebAudioSound;
|
||||||
|
const htmlAudio = this.bgMusic as Phaser.Sound.HTML5AudioSound;
|
||||||
|
if (typeof webAudio.setVolume === 'function') {
|
||||||
|
webAudio.setVolume(this.musicVolume);
|
||||||
|
} else if (typeof htmlAudio.setVolume === 'function') {
|
||||||
|
htmlAudio.setVolume(this.musicVolume);
|
||||||
|
}
|
||||||
|
if (this.musicVolume === 0 && this.bgMusic.isPlaying) {
|
||||||
|
this.bgMusic.pause();
|
||||||
|
} else if (this.musicVolume > 0 && this.bgMusic.isPaused) {
|
||||||
|
this.bgMusic.resume();
|
||||||
|
} else if (this.musicVolume > 0 && !this.bgMusic.isPlaying) {
|
||||||
|
this.bgMusic.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.volumeText?.setText(this.getVolumeLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
private getVolumeLabel(): string {
|
||||||
|
return `🔊 Volume: ${Math.round(this.musicVolume * 100)}% (clic)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour le volume des SFX (cycle 0%, 20%, 40%, 60%, 80%, 100%)
|
||||||
|
*/
|
||||||
|
private cycleSfxVolume(): void {
|
||||||
|
const steps = [0, 0.2, 0.4, 0.6, 0.8, 1];
|
||||||
|
const currentIndex = steps.findIndex((v) => v === this.sfxVolume);
|
||||||
|
const nextIndex = (currentIndex + 1) % steps.length;
|
||||||
|
this.sfxVolume = steps[nextIndex];
|
||||||
|
this.registry.set('sfxVolume', this.sfxVolume);
|
||||||
|
this.sfxVolumeText?.setText(this.getSfxVolumeLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSfxVolumeLabel(): string {
|
||||||
|
return `🎚️ Bruitages: ${Math.round(this.sfxVolume * 100)}% (clic)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lecture SFX avec volume global appliqué
|
||||||
|
*/
|
||||||
|
private playSfx(key: string, baseVolume: number = 0.5): void {
|
||||||
|
const globalSfxVolume = (this.registry.get('sfxVolume') as number | undefined) ?? this.sfxVolume;
|
||||||
|
// Vérifier que l'asset audio existe pour éviter un blocage si le fichier est absent
|
||||||
|
const audioCache = this.cache.audio;
|
||||||
|
if (!audioCache.exists(key)) {
|
||||||
|
console.warn(`SFX manquant: ${key}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sound.play(key, { volume: baseVolume * globalSfxVolume });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effet de neige qui tombe en overlay
|
||||||
|
*/
|
||||||
|
private createSnow(): void {
|
||||||
|
// Texture flocon si absente
|
||||||
|
if (!this.textures.exists('snowflake')) {
|
||||||
|
const g = this.add.graphics();
|
||||||
|
g.fillStyle(0xffffff, 1);
|
||||||
|
g.fillCircle(3, 3, 3);
|
||||||
|
g.generateTexture('snowflake', 6, 6);
|
||||||
|
g.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emetteur unique attaché à un manager en overlay
|
||||||
|
const emitterManager = this.add.particles(0, 0, 'snowflake', {
|
||||||
|
x: { min: 0, max: this.cameras.main.width },
|
||||||
|
y: -10,
|
||||||
|
lifespan: 8000,
|
||||||
|
speedY: { min: 50, max: 100 },
|
||||||
|
speedX: { min: -20, max: 20 },
|
||||||
|
scale: { min: 0.5, max: 1 },
|
||||||
|
quantity: 4,
|
||||||
|
frequency: 80,
|
||||||
|
alpha: { start: 0.9, end: 0.15 },
|
||||||
|
rotate: { min: -30, max: 30 },
|
||||||
|
blendMode: 'ADD',
|
||||||
|
emitZone: { type: 'random', source: new Phaser.Geom.Rectangle(0, 0, this.cameras.main.width, 1) } as any,
|
||||||
|
});
|
||||||
|
emitterManager.setScrollFactor(0);
|
||||||
|
emitterManager.setDepth(2000);
|
||||||
|
|
||||||
|
// Neige qui s'accumule au sol (colonne par colonne)
|
||||||
|
const initSnowBuffer = () => {
|
||||||
|
const cols = Math.max(10, Math.ceil(this.cameras.main.width / 8));
|
||||||
|
this.snowHeights = new Array(cols).fill(0);
|
||||||
|
if (!this.snowPileGraphics) {
|
||||||
|
this.snowPileGraphics = this.add.graphics();
|
||||||
|
this.snowPileGraphics.setScrollFactor(0);
|
||||||
|
this.snowPileGraphics.setDepth(5);
|
||||||
|
}
|
||||||
|
this.renderSnowPile();
|
||||||
|
};
|
||||||
|
|
||||||
|
initSnowBuffer();
|
||||||
|
this.scale.on('resize', initSnowBuffer);
|
||||||
|
|
||||||
|
const maxHeight = 160;
|
||||||
|
this.snowPileTimer = this.time.addEvent({
|
||||||
|
delay: 120,
|
||||||
|
loop: true,
|
||||||
|
callback: () => {
|
||||||
|
if (this.snowHeights.length === 0) return;
|
||||||
|
const idx = Phaser.Math.Between(0, this.snowHeights.length - 1);
|
||||||
|
const add = Phaser.Math.Between(2, 6);
|
||||||
|
this.snowHeights[idx] = Math.min(maxHeight, this.snowHeights[idx] + add);
|
||||||
|
this.renderSnowPile();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dessine l'accumulation de neige au sol
|
||||||
|
*/
|
||||||
|
private renderSnowPile(): void {
|
||||||
|
if (!this.snowPileGraphics || this.snowHeights.length === 0) return;
|
||||||
|
const gfx = this.snowPileGraphics;
|
||||||
|
const colWidth = this.cameras.main.width / this.snowHeights.length;
|
||||||
|
const baseY = this.cameras.main.height;
|
||||||
|
|
||||||
|
gfx.clear();
|
||||||
|
gfx.fillStyle(0xffffff, 0.9);
|
||||||
|
|
||||||
|
this.snowHeights.forEach((h, i) => {
|
||||||
|
if (h <= 0) return;
|
||||||
|
const x = i * colWidth;
|
||||||
|
gfx.fillRect(x, baseY - h, colWidth + 1, h);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nettoyage avant de quitter la scène
|
* Nettoyage avant de quitter la scène
|
||||||
*/
|
*/
|
||||||
@@ -861,5 +1203,15 @@ export class GameScene extends Phaser.Scene {
|
|||||||
if (this.jumpButton) {
|
if (this.jumpButton) {
|
||||||
this.jumpButton.destroy();
|
this.jumpButton.destroy();
|
||||||
}
|
}
|
||||||
|
if (this.directionalButtons) {
|
||||||
|
this.directionalButtons.destroy();
|
||||||
|
}
|
||||||
|
if (this.bgMusic) {
|
||||||
|
this.bgMusic.stop();
|
||||||
|
this.bgMusic.destroy();
|
||||||
|
}
|
||||||
|
if (this.snowPileTimer) {
|
||||||
|
this.snowPileTimer.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
306
src/scenes/IntroScene.ts
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
import Phaser from 'phaser';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scène d'introduction : lit une vidéo en mode paysage puis passe au menu
|
||||||
|
*/
|
||||||
|
export class IntroScene extends Phaser.Scene {
|
||||||
|
private video?: Phaser.GameObjects.Video;
|
||||||
|
private hasFinished: boolean = false;
|
||||||
|
private rotationText?: Phaser.GameObjects.Text;
|
||||||
|
private playButton?: Phaser.GameObjects.Container;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super({ key: 'IntroScene' });
|
||||||
|
}
|
||||||
|
|
||||||
|
create(): void {
|
||||||
|
const { width, height } = this.cameras.main;
|
||||||
|
this.cameras.main.setBackgroundColor('#000000');
|
||||||
|
|
||||||
|
console.log('[IntroScene] Création de la scène d\'intro');
|
||||||
|
console.log('[IntroScene] Dimensions:', width, 'x', height);
|
||||||
|
|
||||||
|
// Vérifier si on est en mode paysage
|
||||||
|
const isLandscape = window.innerWidth > window.innerHeight;
|
||||||
|
|
||||||
|
if (!isLandscape) {
|
||||||
|
// Mode portrait → demander de tourner en paysage
|
||||||
|
console.log('[IntroScene] Mode portrait détecté → demande de rotation paysage');
|
||||||
|
this.showRotateToLandscapeScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode paysage → afficher le bouton Play
|
||||||
|
console.log('[IntroScene] Mode paysage → affichage du bouton Play');
|
||||||
|
this.showPlayButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Affiche le bouton Play pour démarrer la vidéo
|
||||||
|
*/
|
||||||
|
private showPlayButton(): void {
|
||||||
|
const { width, height } = this.cameras.main;
|
||||||
|
|
||||||
|
// Créer un container pour le bouton
|
||||||
|
this.playButton = this.add.container(width / 2, height / 2);
|
||||||
|
|
||||||
|
// Cercle du bouton
|
||||||
|
const circle = this.add.circle(0, 0, 60, 0x4CAF50, 1);
|
||||||
|
circle.setStrokeStyle(4, 0xFFFFFF);
|
||||||
|
|
||||||
|
// Triangle "Play"
|
||||||
|
const triangle = this.add.triangle(
|
||||||
|
5, // Décalé légèrement à droite pour centrer visuellement
|
||||||
|
0,
|
||||||
|
-15, -20, // Point gauche haut
|
||||||
|
-15, 20, // Point gauche bas
|
||||||
|
20, 0, // Point droit
|
||||||
|
0xFFFFFF
|
||||||
|
);
|
||||||
|
|
||||||
|
// Texte "Cliquez pour démarrer"
|
||||||
|
const text = this.add.text(0, 100, 'Cliquez pour démarrer la vidéo', {
|
||||||
|
fontSize: '24px',
|
||||||
|
color: '#ffffff',
|
||||||
|
align: 'center',
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
});
|
||||||
|
text.setOrigin(0.5);
|
||||||
|
|
||||||
|
this.playButton.add([circle, triangle, text]);
|
||||||
|
this.playButton.setDepth(1000);
|
||||||
|
|
||||||
|
// Rendre le bouton interactif
|
||||||
|
circle.setInteractive({ useHandCursor: true });
|
||||||
|
|
||||||
|
// Animation hover
|
||||||
|
circle.on('pointerover', () => {
|
||||||
|
circle.setFillStyle(0x66BB6A);
|
||||||
|
this.tweens.add({
|
||||||
|
targets: this.playButton,
|
||||||
|
scale: 1.1,
|
||||||
|
duration: 200,
|
||||||
|
ease: 'Power2',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
circle.on('pointerout', () => {
|
||||||
|
circle.setFillStyle(0x4CAF50);
|
||||||
|
this.tweens.add({
|
||||||
|
targets: this.playButton,
|
||||||
|
scale: 1.0,
|
||||||
|
duration: 200,
|
||||||
|
ease: 'Power2',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clic sur le bouton
|
||||||
|
circle.on('pointerdown', () => {
|
||||||
|
console.log('[IntroScene] Bouton Play cliqué → démarrage vidéo');
|
||||||
|
|
||||||
|
// Effet de clic
|
||||||
|
circle.setFillStyle(0x2E7D32);
|
||||||
|
this.tweens.add({
|
||||||
|
targets: this.playButton,
|
||||||
|
scale: 0.9,
|
||||||
|
duration: 100,
|
||||||
|
yoyo: true,
|
||||||
|
onComplete: () => {
|
||||||
|
// Détruire le bouton
|
||||||
|
this.playButton?.destroy();
|
||||||
|
this.playButton = undefined;
|
||||||
|
|
||||||
|
// Lancer la vidéo
|
||||||
|
this.playIntroVideo();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[IntroScene] Bouton Play affiché');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Affiche l'écran demandant de tourner en paysage
|
||||||
|
*/
|
||||||
|
private showRotateToLandscapeScreen(): void {
|
||||||
|
const { width, height } = this.cameras.main;
|
||||||
|
|
||||||
|
this.rotationText = this.add.text(width / 2, height / 2, '🔄\n\nTournez votre téléphone\nen mode paysage\npour voir la vidéo', {
|
||||||
|
fontSize: '32px',
|
||||||
|
color: '#ffffff',
|
||||||
|
align: 'center',
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
});
|
||||||
|
this.rotationText.setOrigin(0.5);
|
||||||
|
|
||||||
|
// Surveiller l'orientation
|
||||||
|
this.listenForLandscapeOrientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Écoute les changements d'orientation
|
||||||
|
*/
|
||||||
|
private listenForLandscapeOrientation(): void {
|
||||||
|
// Méthode 1: Utiliser l'événement resize
|
||||||
|
this.scale.on('resize', this.checkOrientationForVideo, this);
|
||||||
|
|
||||||
|
// Méthode 2: orientationchange event
|
||||||
|
window.addEventListener('orientationchange', () => {
|
||||||
|
console.log('[IntroScene] orientationchange event détecté');
|
||||||
|
setTimeout(() => this.checkOrientationForVideo(), 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Méthode 3: polling manuel toutes les 500ms
|
||||||
|
const checkInterval = setInterval(() => {
|
||||||
|
this.checkOrientationForVideo();
|
||||||
|
if (this.video || this.hasFinished) {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Vérifier immédiatement
|
||||||
|
this.checkOrientationForVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie l'orientation pour afficher le bouton Play
|
||||||
|
*/
|
||||||
|
private checkOrientationForVideo(): void {
|
||||||
|
const isNowLandscape = window.innerWidth > window.innerHeight;
|
||||||
|
|
||||||
|
console.log('[IntroScene] Vérification orientation - Paysage?', isNowLandscape, 'Dimensions:', window.innerWidth, 'x', window.innerHeight);
|
||||||
|
|
||||||
|
if (isNowLandscape && !this.playButton && !this.video) {
|
||||||
|
console.log('[IntroScene] Paysage détecté → affichage bouton Play');
|
||||||
|
|
||||||
|
// Effacer le message
|
||||||
|
if (this.rotationText) {
|
||||||
|
this.rotationText.destroy();
|
||||||
|
this.rotationText = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scale.off('resize', this.checkOrientationForVideo, this);
|
||||||
|
this.showPlayButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lit la vidéo d'intro
|
||||||
|
*/
|
||||||
|
private playIntroVideo(): void {
|
||||||
|
const { width, height } = this.cameras.main;
|
||||||
|
|
||||||
|
console.log('[IntroScene] Vidéo dans cache?', this.cache.video.exists('intro'));
|
||||||
|
|
||||||
|
if (!this.cache.video.exists('intro')) {
|
||||||
|
console.warn('[IntroScene] Vidéo intro non trouvée → passage au menu.');
|
||||||
|
this.gotoMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[IntroScene] Création de l\'objet vidéo');
|
||||||
|
this.video = this.add.video(width / 2, height / 2, 'intro');
|
||||||
|
this.video.setOrigin(0.5);
|
||||||
|
this.video.setDepth(1000);
|
||||||
|
|
||||||
|
// Attendre que les métadonnées soient chargées
|
||||||
|
this.video.on('metadata', () => {
|
||||||
|
if (!this.video) return;
|
||||||
|
|
||||||
|
const videoWidth = this.video.video?.videoWidth || 324;
|
||||||
|
const videoHeight = this.video.video?.videoHeight || 720;
|
||||||
|
|
||||||
|
console.log('[IntroScene] Métadonnées vidéo chargées:', videoWidth, 'x', videoHeight);
|
||||||
|
|
||||||
|
this.video.setSize(videoWidth, videoHeight);
|
||||||
|
this.updateVideoSize();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Forcer mute pour éviter les blocages autoplay
|
||||||
|
this.video.setMute(true);
|
||||||
|
this.video.setLoop(false);
|
||||||
|
|
||||||
|
console.log('[IntroScene] Démarrage de la lecture');
|
||||||
|
const started = this.video.play(false);
|
||||||
|
console.log('[IntroScene] Lecture démarrée?', started);
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
console.warn('[IntroScene] Lecture vidéo bloquée → passage au menu.');
|
||||||
|
this.gotoMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.video.once('complete', () => {
|
||||||
|
console.log('[IntroScene] Vidéo terminée → passage au menu');
|
||||||
|
this.gotoMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.video.once('error', (err: any) => {
|
||||||
|
console.error('[IntroScene] Erreur lecture vidéo:', err);
|
||||||
|
this.gotoMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ajuster si resize
|
||||||
|
this.scale.on('resize', (gameSize: Phaser.Structs.Size) => {
|
||||||
|
if (this.video && this.video.isPlaying()) {
|
||||||
|
this.updateVideoSize(gameSize.width, gameSize.height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajuste la vidéo à l'écran en mode paysage en respectant le ratio
|
||||||
|
* Vidéo 324×720 (portrait) affichée en paysage → mode "contain" pour tout voir
|
||||||
|
*/
|
||||||
|
private updateVideoSize(targetW?: number, targetH?: number): void {
|
||||||
|
if (!this.video) return;
|
||||||
|
const w = targetW ?? this.cameras.main.width;
|
||||||
|
const h = targetH ?? this.cameras.main.height;
|
||||||
|
|
||||||
|
// Dimensions natives de la vidéo (324×720 portrait)
|
||||||
|
const nativeW = this.video.video?.videoWidth || 324;
|
||||||
|
const nativeH = this.video.video?.videoHeight || 720;
|
||||||
|
|
||||||
|
console.log('[IntroScene] updateVideoSize - Écran:', w, 'x', h);
|
||||||
|
console.log('[IntroScene] updateVideoSize - Vidéo native:', nativeW, 'x', nativeH);
|
||||||
|
|
||||||
|
// Ratio vidéo = 324/720 = 0.45 (portrait étroit)
|
||||||
|
// Ratio écran paysage = 1280/720 = 1.78 (paysage large)
|
||||||
|
const videoRatio = nativeW / nativeH;
|
||||||
|
const screenRatio = w / h;
|
||||||
|
|
||||||
|
let scale: number;
|
||||||
|
|
||||||
|
// Mode "contain" : adapter pour que toute la vidéo soit visible
|
||||||
|
if (screenRatio > videoRatio) {
|
||||||
|
// L'écran est plus large que la vidéo → adapter à la HAUTEUR
|
||||||
|
scale = h / nativeH;
|
||||||
|
console.log('[IntroScene] Adaptation à la hauteur');
|
||||||
|
} else {
|
||||||
|
// L'écran est plus étroit que la vidéo → adapter à la LARGEUR
|
||||||
|
scale = w / nativeW;
|
||||||
|
console.log('[IntroScene] Adaptation à la largeur');
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalWidth = nativeW * scale;
|
||||||
|
const finalHeight = nativeH * scale;
|
||||||
|
|
||||||
|
console.log('[IntroScene] updateVideoSize - Ratio vidéo:', videoRatio.toFixed(2), '- Ratio écran:', screenRatio.toFixed(2));
|
||||||
|
console.log('[IntroScene] updateVideoSize - Scale:', scale.toFixed(2));
|
||||||
|
console.log('[IntroScene] updateVideoSize - Taille finale:', Math.round(finalWidth), 'x', Math.round(finalHeight));
|
||||||
|
|
||||||
|
this.video.setScale(scale);
|
||||||
|
this.video.setPosition(w / 2, h / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passe au menu
|
||||||
|
*/
|
||||||
|
private gotoMenu(): void {
|
||||||
|
if (this.hasFinished) return;
|
||||||
|
this.hasFinished = true;
|
||||||
|
this.video?.stop();
|
||||||
|
this.video?.destroy();
|
||||||
|
this.scene.start('MenuScene');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import Phaser from 'phaser';
|
|||||||
export class MenuScene extends Phaser.Scene {
|
export class MenuScene extends Phaser.Scene {
|
||||||
private startButton?: Phaser.GameObjects.Text;
|
private startButton?: Phaser.GameObjects.Text;
|
||||||
private gyroStatus?: Phaser.GameObjects.Text;
|
private gyroStatus?: Phaser.GameObjects.Text;
|
||||||
|
private hasStarted: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ key: 'MenuScene' });
|
super({ key: 'MenuScene' });
|
||||||
@@ -79,47 +80,17 @@ export class MenuScene extends Phaser.Scene {
|
|||||||
* Demande la permission gyroscope (iOS) et lance le jeu
|
* Demande la permission gyroscope (iOS) et lance le jeu
|
||||||
*/
|
*/
|
||||||
private requestGyroPermission(): void {
|
private requestGyroPermission(): void {
|
||||||
let debugInfo = '🔍 Vérification...\n';
|
// Simplifié : on part immédiatement en jeu, sans attendre la permission.
|
||||||
debugInfo += `DeviceOrientation: ${!!window.DeviceOrientationEvent}\n`;
|
this.gyroStatus?.setText('Lancement du jeu...');
|
||||||
debugInfo += `requestPermission: ${typeof (DeviceOrientationEvent as any).requestPermission}\n`;
|
this.startGame();
|
||||||
debugInfo += `HTTPS: ${window.location.protocol === 'https:'}\n`;
|
|
||||||
debugInfo += `UserAgent: ${navigator.userAgent.substring(0, 50)}...`;
|
|
||||||
|
|
||||||
this.gyroStatus?.setText(debugInfo);
|
|
||||||
|
|
||||||
// iOS 13+ nécessite une permission explicite
|
|
||||||
if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.gyroStatus?.setText('📱 iOS détecté\nDemande permission...');
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
(DeviceOrientationEvent as any).requestPermission()
|
|
||||||
.then((response: string) => {
|
|
||||||
if (response === 'granted') {
|
|
||||||
this.gyroStatus?.setText('✅ Permission accordée\nLancement du jeu...');
|
|
||||||
setTimeout(() => this.startGame(), 500);
|
|
||||||
} else {
|
|
||||||
this.gyroStatus?.setText(`❌ Permission: ${response}\nLancement quand même...`);
|
|
||||||
setTimeout(() => this.startGame(), 2000);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error: Error) => {
|
|
||||||
this.gyroStatus?.setText(`❌ Erreur:\n${error.message}\n${error.name}\nLancement quand même...`);
|
|
||||||
setTimeout(() => this.startGame(), 3000);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Android ou navigateurs sans permission requise
|
|
||||||
setTimeout(() => {
|
|
||||||
this.gyroStatus?.setText('✅ Android/Desktop\nGyroscope disponible\nLancement...');
|
|
||||||
setTimeout(() => this.startGame(), 500);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lance la scène de jeu
|
* Lance la scène de jeu
|
||||||
*/
|
*/
|
||||||
private startGame(): void {
|
private startGame(): void {
|
||||||
|
if (this.hasStarted || this.scene.isActive('GameScene')) return;
|
||||||
|
this.hasStarted = true;
|
||||||
this.scene.start('GameScene');
|
this.scene.start('GameScene');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export const PLAYER_ACCELERATION = 50;
|
|||||||
export const PLAYER_MAX_JUMPS = 2; // Nombre de sauts autorisés (double saut)
|
export const PLAYER_MAX_JUMPS = 2; // Nombre de sauts autorisés (double saut)
|
||||||
|
|
||||||
// Gyroscope
|
// Gyroscope
|
||||||
export const GYRO_DEADZONE = 5; // Degrés de zone morte
|
export const GYRO_DEADZONE = 10; // Degrés de zone morte (plus large pour éviter les faux mouvements)
|
||||||
export const GYRO_MAX_TILT = 30; // Tilt maximum pris en compte (degrés)
|
export const GYRO_MAX_TILT = 35; // Tilt max pris en compte (pense à incliner franchement)
|
||||||
export const GYRO_SENSITIVITY = 10; // Multiplicateur de sensibilité
|
export const GYRO_SENSITIVITY = 10; // Multiplicateur de sensibilité
|
||||||
|
|
||||||
// Niveau
|
// Niveau
|
||||||
@@ -25,7 +25,7 @@ export const PLAYER_STARTING_LIVES = 3; // Nombre de vies au départ
|
|||||||
export const RESPAWN_INVINCIBILITY_TIME = 2000; // Temps d'invincibilité après respawn (ms)
|
export const RESPAWN_INVINCIBILITY_TIME = 2000; // Temps d'invincibilité après respawn (ms)
|
||||||
|
|
||||||
// Coffre final
|
// Coffre final
|
||||||
export const CHEST_REQUIRED_GIFTS = 5; // Nombre de cadeaux requis pour ouvrir le coffre
|
export const CHEST_REQUIRED_GIFTS = 15; // Nombre de cadeaux requis pour ouvrir le coffre
|
||||||
|
|
||||||
// Couleurs
|
// Couleurs
|
||||||
export const COLOR_PRIMARY = 0x4CAF50;
|
export const COLOR_PRIMARY = 0x4CAF50;
|
||||||
|
|||||||