diff --git a/.github/ISSUE TEMPLATE/Bug_report_template.md b/.github/ISSUE TEMPLATE/Bug_report_template.md deleted file mode 100644 index b41b495..0000000 --- a/.github/ISSUE TEMPLATE/Bug_report_template.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: 🪲 Bug Report -about: Create a bug report to help us resolving the bug -title: '🪲[Bug]: ' -labels: 'bug' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE TEMPLATE/Feature_request_template.md b/.github/ISSUE TEMPLATE/Feature_request_template.md deleted file mode 100644 index 16775e1..0000000 --- a/.github/ISSUE TEMPLATE/Feature_request_template.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: 💡 Feature Request -about: Suggest an interesting feature idea for this project -title: '💡[FEATURE]: ' -labels: 'enhancement' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/ISSUE TEMPLATE/docs.md b/.github/ISSUE TEMPLATE/docs.md deleted file mode 100644 index 7f4160a..0000000 --- a/.github/ISSUE TEMPLATE/docs.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: 📝 Documentation -about: Propose changes and improvements to Rubik-Cube Docs. -title: '📝[Docs]: ' -labels: 'enhancement' -assignees: '' ---- - -**What Docs changes are you proposing?** -Provide a clear description of the changes you're proposing for the documentation. Are you suggesting corrections, clarifications, additions, or updates? - -**Why do the Docs need this improvement? What is the motivation for this change? How will this change benefit the community?** -Explain the motivation behind the proposed changes and how they will benefit the community or users of the documentation. - -**Describe the solution you'd like** -Provide a clear and concise description of the changes you'd like to see implemented in the documentation. - -**Describe alternatives you've considered** -If you've thought about alternative approaches or solutions, briefly describe them here. - -**Additional context** -Add any additional context, examples, or screenshots related to the proposed documentation changes. diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..2bc3c61 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,45 @@ +name: "Bug ​🐞" +description: "Report an issue to help us improve the project." +title: "[BUG] " +labels: ["bug", "goal: fix"] + +body: + - type: textarea + attributes: + label: Description + description: A brief description of the issue or bug you are facing, also include what you tried and what didn't work. + validations: + required: false + - type: textarea + attributes: + label: Screenshots + description: Please add screenshots if applicable + validations: + required: false + - type: textarea + attributes: + label: Any additional information? + description: Any additional information or Is there anything we should know about this bug? + validations: + required: false + - type: dropdown + attributes: + label: What browser are you seeing the problem on? + multiple: true + options: + - Firefox + - Chrome + - Safari + - Microsoft Edge + - type: checkboxes + id: terms_checklist_docs + attributes: + label: "Checklist" + description: "By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CODE_OF_CONDUCT.md)" + options: + - label: "I have checked the existing [issues](https://github.com/Dev-tanay/Rubik-Cube/issues)" + required: true + - label: "I have read the [Contributing Guidelines](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CONTRIBUTING.md)" + required: true + - label: "I am willing to work on this issue (optional)" + required: false diff --git a/.github/ISSUE_TEMPLATE/document.yml b/.github/ISSUE_TEMPLATE/document.yml new file mode 100644 index 0000000..8bb45a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/document.yml @@ -0,0 +1,38 @@ +name: "Documentation 📋" +description: "Suggestions on how to improve our docs" +title: "[DOCS] " +labels: ["documenation", "goal: enhancement"] + +body: + - type: textarea + id: docs_description + attributes: + label: "Issue Description" + description: "Please provide a brief summary of the documentation issue you would like to address." + validations: + required: true + + - type: textarea + id: screenshots_examples_docs + attributes: + label: "Screenshots or Examples (if applicable)" + description: "Include relevant screenshots or examples to help illustrate the problem." + + - type: textarea + id: proposed_solution_docs + attributes: + label: "Proposed Solution (optional)" + description: "If you have a proposed solution for the documentation issue, please provide it here." + - type: checkboxes + id: terms_checklist_docs + attributes: + label: "Checklist" + description: "By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CODE_OF_CONDUCT.md)" + options: + - label: "I have checked the existing [issues](https://github.com/Dev-tanay/Rubik-Cube/issues)" + required: true + - label: "I have read the [Contributing Guidelines](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CONTRIBUTING.md)" + required: true + - label: "I am willing to work on this issue (optional)" + required: false + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..89eef57 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,37 @@ +name: Feature Request 💡 +description: Have any new idea or new feature for RubikCube? Do suggest! +title: "[Feature] " +labels: ["enhancement"] +body: + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the feature you'd like to see implemented. + validations: + required: true + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: Please add screenshots if applicable + validations: + required: false + - type: textarea + id: solution + attributes: + label: "Proposed Solution (optional)" + description: "If you have a proposed solution for the documentation issue, please provide it here." + - type: checkboxes + id: terms_checklist_docs + attributes: + label: "Checklist" + description: "By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CODE_OF_CONDUCT.md)" + options: + - label: "I have checked the existing [issues](https://github.com/Dev-tanay/Rubik-Cube/issues)" + required: true + - label: "I have read the [Contributing Guidelines](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CONTRIBUTING.md)" + required: true + - label: "I am willing to work on this issue (optional)" + required: false + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/other.yml b/.github/ISSUE_TEMPLATE/other.yml new file mode 100644 index 0000000..ca6ff5b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.yml @@ -0,0 +1,25 @@ +name: Other +description: Use this for any other question or issue. Please do not create blank issues. +title: "[OTHER]" + +body: + - type: textarea + id: issuedescription + attributes: + label: What would you like to share or ask? + description: Provide a clear and concise explanation of your issue. + validations: + required: true + - type: checkboxes + id: terms_checklist_docs + attributes: + label: "Checklist" + description: "By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CODE_OF_CONDUCT.md)" + options: + - label: "I have checked the existing [issues](https://github.com/Dev-tanay/Rubik-Cube/issues)" + required: true + - label: "I have read the [Contributing Guidelines](https://github.com/Dev-tanay/Rubik-Cube/blob/main/CONTRIBUTING.md)" + required: true + - label: "I am willing to work on this issue (optional)" + required: false + \ No newline at end of file diff --git a/.github/Pull-request_template.yml b/.github/pull_request_template.yml similarity index 100% rename from .github/Pull-request_template.yml rename to .github/pull_request_template.yml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6b665aa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} diff --git a/AboutUs/About.css b/AboutUs/About.css new file mode 100644 index 0000000..709fb18 --- /dev/null +++ b/AboutUs/About.css @@ -0,0 +1,327 @@ + +::-webkit-scrollbar-track +{ + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); + background-color: #F5F5F5; +} + +::-webkit-scrollbar +{ + width: 10px; + background-color: #F5F5F5; +} + +::-webkit-scrollbar-thumb +{ + background-color:#FF9900; + background-image: -webkit-linear-gradient(45deg, + rgba(255, 255, 255, .2) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, .2) 50%, + rgba(255, 255, 255, .2) 75%, + transparent 75%, + transparent) +} + + + +html { + -webkit-tap-highlight-color: transparent; + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + overflow: hidden; + height: 100%; +} + +body { + margin: 0; + padding: 0; + height: 100%; + font-family: "BungeeFont"; + font-weight: normal; + font-style: normal; + background-color: #fff; + cursor: default; + overflow: scroll; + overflow-x: hidden; +} + +.header { + padding: 10px; +} + +.content { + text-align: center; + padding: 20px; +} + +@font-face { + font-family: "BungeeFont"; + font-weight: normal; + font-style: normal; + src: url("data:font/truetype;charset=utf-8;base64,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") + format("woff2"), + url("data:font/truetype;charset=utf-8;base64,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") + format("woff"); +} +*, +*:before, +*:after { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + box-sizing: border-box; + cursor: inherit; + margin: 0; + padding: 0; + outline: none; + font-size: inherit; + font-family: inherit; + font-weight: inherit; + font-style: inherit; + text-transform: uppercase; +} +*:focus { + outline: none; +} + +/*banner*/ +.banner { + width: 80%; + height: 200px; + margin: 0 auto 20px; /* Center the banner and add margin-bottom */ + background-image: url("./AboutUs_images/AboutUsBanner.png"); /* Path to your banner image */ + background-size: cover; + background-position: center; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.text--title { + margin-top: 20px; + font-size: 2em; + color: #333; +} + +.text { + margin: 0 auto; + max-width: 800px; + text-align: left; +} + +.about-text--content p, +.about-text--content h1, +.about-text--content h2, +.about-text--content ul { + margin-bottom: 20px; + line-height: 1.6; +} + +.subheading { + font-size: 1.5em; + color: #444; +} + +.subtopic { + margin-left: 20px; +} + +.formula h3 { + margin-top: 10px; + font-size: 1.2em; + color: #555; +} + +.formula p { + font-family: monospace; + color: #333; +} + +.footer { + text-align: center; +} + +.text--footer { + margin: 0; + font-size: 0.9em; + color: #666; +} + +#contributors { + list-style-type: none; + padding: 0; + display: flex; + flex-wrap: wrap; +} +.contributor-item { + margin-right: 10px; + margin-bottom: 10px; + transition: transform 0.3s ease; +} +.contributor-item:hover { + transform: translateY(-5px); +} +.contributor-item img { + width: 50px; + height: 50px; + border-radius: 50%; + cursor: pointer; + transition: transform 0.3s ease; +} +.contributor-item img:hover { + transform: scale(1.1); + + /*For Btn */ +} +.btn { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; + border-radius: 0; + border-width: 0; + position: absolute; + pointer-events: none; + font-size: 2em; /* Increased font size */ + color: rgba(0, 0, 0, 0.25); + opacity: 0; +} + +.btn:hover { + color: rgb(245, 245, 245); + transition: color 400ms ease-out; + background-color: #541a8be7; +} + +.btn:after { + cursor: pointer; + position: absolute; + content: ""; + width: 3.5em; + height: 3.5em; + left: 50%; + top: 50%; + margin-left: 1.75em; + margin-top: 1.75em; + border-radius: 100%; +} + +.btn--bg { + top: 1.2em; + left: 1.2em; + opacity: 1; + pointer-events: auto; + height: 60px; + width: 60px; + border-radius: 8px; + cursor: pointer; + background-color: #541a8b24; + padding-left: 11px; + padding-top: 9px; +} + +.btn--gh { + top: 1.2em; + right: 1.2em; + opacity: 1; + pointer-events: auto; + height: 60px; + width: 60px; + border-radius: 8px; + cursor: pointer; + background-color: #541a8b24; + padding-left: 10px; + padding-top: 9px; +} + +@media screen and (max-width: 768px) { + .banner { + width: 100%; + } + + .text--title { + font-size: 1.5em; + } + + .text { + padding: 0 10px; + } + + .btn { + font-size: 1.5em; /* Adjust font size */ + } + + .btn:after { + width: 3em; + height: 3em; + margin-left: -1.5em; + margin-top: -1.5em; + } + + .btn--bg { + position: fixed; + top: 0.5em; + left: 1em; + } + + .btn--gh { + position: fixed; + top: 0.5em; + right: 1em; + } +} + +.icons { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + gap: 25px; +} + +.icons .icon i { + margin-top: 15px; + font-size: 30px; + transition: all 0.9s ease; +} + +.icons .icon:hover i { + transform: scale(1.2); +} + +.icons .icon:hover .fab.fa-facebook { + color: #3b5998; +} + +.icons .icon:hover .fab.fa-twitter { + color: #090e11; +} + +.icons .icon:hover .fab.fa-instagram { + background: radial-gradient( + circle at 30% 107%, + #fdf497 0%, + #fdf497 5%, + #fd5949 45%, + #d6249f 60%, + #285aeb 90% + ); + background-clip: text; + border-radius: 20%; + transform: scale(1.5); + color: transparent; +} + +.icons .icon:hover .fab.fa-youtube { + color: #c31a1e; +} +.icons .icon:hover .fab.fa-github { + color: #333; +} +.icons .icon:hover .fab.fa-linkedin { + color: #0077b5; +} diff --git a/AboutUs/About.html b/AboutUs/About.html new file mode 100644 index 0000000..6b6d01b --- /dev/null +++ b/AboutUs/About.html @@ -0,0 +1,131 @@ + + + + + + About The Game - Rubik Cube + + + + + + +
+ + +
+ +
+ + About The Game + +
+ +
+
+
+

Welcome to our website dedicated to providing you with the best Rubik's Cube solving experience! Our team is passionate about puzzles and games, and we strive to create tools and resources that enhance your enjoyment and skill development.

+

Whether you're a beginner or an experienced solver, we hope you find our website helpful and enjoyable. Feel free to explore the various features and settings of our Rubik's Cube solver and let us know if you have any feedback or suggestions!

+

Our project is open source, and we are proud to collaborate with a diverse group of contributors. Here is the list of our amazing contributors who have made this project possible:

+

Contributors

+
    + +
+ +

History

+

The Rubik's Cube, invented by Ernő Rubik in 1974, is a 3D combination puzzle that has fascinated millions of people worldwide. Originally called the "Magic Cube," it gained immense popularity after being licensed to the Ideal Toy Corporation in 1980 and rebranded as the "Rubik's Cube."

+ +

Basic Concepts

+

A standard Rubik's Cube consists of six faces, each covered by nine stickers of one of six solid colors: white, red, blue, orange, green, and yellow. The goal is to solve the cube by rotating the faces until each face is a solid color.

+ +

Solving Methods

+

There are several methods for solving Rubik's Cube, including:

+
    +
  • Fridrich Method (CFOP)
  • +
  • BEGINNER Method
  • +
  • Roux Method
  • +
  • ZB Method
  • +
+ +

Advanced Techniques

+

Advanced cubers often utilize techniques such as:

+
    +
  • OLL (Orientation of Last Layer)
  • +
  • PLL (Permutation of Last Layer)
  • +
  • F2L (First Two Layers)
  • +
  • X-Cross
  • +
  • OLLCP (OLL plus Corner Permutation)
  • +
+
+ +

Formulas and Algorithms

+
+
+

The Cross

+

Formula: F R U R' U' F'

+
+
+

First Layer Corners

+

Formula: R U R' U R U2 R'

+
+
+

Second Layer Edges (F2L)

+

Formula: R U R' U' R U' R'

+
+
+

Orientation of Last Layer (OLL)

+

Formula: F R U R' U' F'

+
+
+

Permutation of Last Layer (PLL)

+

Formula: R U2 R' U' R U R' U' R U' R'

+
+
+
+ + + +
+ +
+ + + + + + \ No newline at end of file diff --git a/AboutUs/About.js b/AboutUs/About.js new file mode 100644 index 0000000..9d556e0 --- /dev/null +++ b/AboutUs/About.js @@ -0,0 +1,22 @@ +async function fetchContributors() { + const repoOwner = 'Dev-tanay'; + const repoName = 'Rubik-Cube'; + const response = await fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/contributors`); + const contributors = await response.json(); + + const contributorsList = document.getElementById('contributors'); + contributorsList.innerHTML = ''; + + contributors.forEach(contributor => { + const listItem = document.createElement('li'); + listItem.classList.add('contributor-item'); + const profilePic = document.createElement('img'); + profilePic.src = contributor.avatar_url; + profilePic.alt = contributor.login; + profilePic.onclick = () => window.open(contributor.html_url, '_blank'); + listItem.appendChild(profilePic); + contributorsList.appendChild(listItem); + }); + } + + fetchContributors(); \ No newline at end of file diff --git a/AboutUs/AboutUs_images/AboutUsBanner.png b/AboutUs/AboutUs_images/AboutUsBanner.png new file mode 100644 index 0000000..30f325b Binary files /dev/null and b/AboutUs/AboutUs_images/AboutUsBanner.png differ diff --git a/Music/8 Bit Space Groove! Chill Pretty Chiptune Game Music by HeatleyBros.mp3 b/Music/8 Bit Space Groove! Chill Pretty Chiptune Game Music by HeatleyBros.mp3 new file mode 100644 index 0000000..47865c7 Binary files /dev/null and b/Music/8 Bit Space Groove! Chill Pretty Chiptune Game Music by HeatleyBros.mp3 differ diff --git a/README.md b/README.md index cfa3af9..7de9b5d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ +# Rubik-Cube + #

✨Rubik-Cube✨

+

- - + GitHub issues @@ -18,10 +20,92 @@ Website status

+ + +## 🌐 Link to Game Demo +https://dev-tanay.github.io/Rubik-Cube/ + + + +## 📖 About the Game +This is a simple and elegant game where you can play Rubik's Cube from your own web browser. + +- Each of the six faces of the Rubik's cube is in one of the six colors - red, green, yellow, blue, white, and orange. +- The visible part of the cube is divided into 26 pieces: + - 6 central pieces (one colored side) + - 12 edge pieces (two colored sides) + - 8 corner pieces (three colored sides) +- The 26 pieces are "scrambled" to have different colors on each face. + +## 🕹️ How to Play +The objective of this game is to get each face of the cube to have a single color. + +This can be done in two ways: + +1. **Twisting the Segments** + - This can be done by dragging the cursor over the segment you want to rotate. + + + +2. **Changing the View of the Cube** + - This can be done by dragging the cursor in an arrow around the cube. + + +## 🧩 Installation Guide for Rubik-Cube + +Follow the steps below to install and set up the Rubik-Cube project on your local machine. + +### 📋 Prerequisites +- Ensure you have `git` installed on your system. If not, download and install it from [here](https://git-scm.com/). +- A modern web browser (e.g., Chrome, Firefox, Edge). + +### 🛠️ Steps + +1. **🔗 Clone the Repository** + Open your terminal (or Command Prompt on Windows) and run the following command to clone the repository: + ```sh + git clone https://github.com/Dev-tanay/Rubik-Cube.git + ``` + +2. **📂 Navigate to the Cloned Repository** + Change directory to the cloned repository: + ```sh + cd Rubik-Cube + ``` + +3. **🌐 Open the Index.html File** + Locate the `index.html` file in the repository and open it in your web browser. You can do this by either: + - **Double-clicking the `index.html` file** in your file manager. This will open the file in your default web browser. + - **Running a simple HTTP server** (if you prefer): + + **Using Python (if installed):** + ```sh + python -m http.server + ``` + Open your web browser and go to `http://localhost:8000`. + + **Using Node.js (if installed) with http-server package:** + ```sh + npx http-server + ``` + Open your web browser and go to the provided local server URL (usually `http://127.0.0.1:8080` or similar). + +4. **🎮 Play the Game** + Once the `index.html` file is opened in the web browser, the game screen will be displayed. You can now start playing the Rubik-Cube game. + +### ℹ️ Notes +- Ensure your browser allows running JavaScript, as the game likely depends on it. +- If you encounter any issues, check the browser console for error messages and ensure all assets are properly loaded. + +Enjoy your Rubik-Cube game! 🕹️✨ + +## 🧭 Navigating the Game Screen +- **Home** +

About the Game

This is a simple and elegant game where you can play Rubik's Cube from your own web browser.

Each of the six faces of the Rubik's cube is in one of the six colors - red, green, yellow, blue, white, and orange.

@@ -43,12 +127,21 @@

This can be done in two ways:

  1. Twisting the segments
  2. -

    This can be done by dragging the cursor over the segment you want to rotate.

    +

    This can be done by dragging the cursor over the segment you want to rotate or letters(r l u b d f) for the move(Hold Shift for prime/anticlockwise moves) +

  3. Changing the view of the cube
  4. -

    This can be done by dragging the cursor in an arrow around the cube.

    +

    This can be done by dragging the cursor in an arrow or arrowkeys (up,down,right,left) around the cube.

    +
  5. Using keyboard shortcuts
  6. +

    You can also use keyboard shortcuts to rotate the cube:

    +
      +
    • Press w or Arrow Up to rotate the cube upwards.
    • +
    • Press s or Arrow Down to rotate the cube downwards.
    • +
    • Press a or Arrow Left to rotate the cube to the left.
    • +
    • Press d or Arrow Right to rotate the cube to the right.
    • +
@@ -74,38 +167,62 @@

Navigating the Game Screen

  • Home
  • + -

    You'll see two icons, each on the two bottom corners of the screen. They're (1) Leaderboard and (2) Preferences.

    -

    You can start the game by double-clicking on the text "DOUBLE TAP TO START"

    -
  • Game start
  • + - You'll see two icons at the bottom corners of the screen: (1) Leaderboard and (2) Preferences. + - You can start the game by double-clicking on the text "DOUBLE TAP TO START". + +- **Game Start** -

    Once you start the game, a timer will show above the cube.

    -

    Now you'll see only one icon, that is Back.

    -
  • Preferences
  • + - Once you start the game, a timer will show above the cube. + - Now you'll see only one icon, which is Back. + +- **Preferences** -

    On clicking Preferences from Home screen, you'll be led to the settings page.

    -

    The options you can change here are:

    -
      -
    • Cube size
    • -
    • Flip Type
    • -
    • Scramble
    • -
    • Camera Angle
    • -
    • Color Scheme
    • -
    -

    Also, there are two new icons in the bottom corners - (3) Back and (4) Theme.

    -
  • Theme
  • + - Clicking Preferences from the Home screen will lead you to the settings page. + - Options available for customization: + - Cube size + - Flip Type + - Scramble + - Camera Angle + - Color Scheme + - Two new icons in the bottom corners: (3) Back and (4) Theme. + +- **Theme** -

    Here, you can adjust the hue, saturation, and lightness of the screen to your liking.

    -

    The new icons in the bottom corners are - (5) Back and (6) Reset.

    -
  • Stats
  • + - Here, you can adjust the hue, saturation, and lightness of the screen. + - New icons in the bottom corners: (5) Back and (6) Reset. + +- **Stats** -

    These display the statistics of your Rubik's Cube game. The stats include:

    -
      -
    • Total Number of Solves
    • -
    • Best time
    • -
    • Average of 5, 12, 25
    • -
    -
+ - Displays statistics of your Rubik's Cube game, including: + - Total Number of Solves + - Best time + - Average of 5, 12, 25 +

LICENSE

This project is licensed under the MIT License - see the LICENSE file for details.

+ + +
+

Red Heart Contributors

+
+
+ +- This project thanking all the contributors for having your valuable contribution to our project +- Make sure you show some love by giving ⭐ to our repository + +
+ +
+ + + +
+
+

Back to top

+ +## 📜 License +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + diff --git a/changeview.png b/changeview.png new file mode 100644 index 0000000..f37e611 Binary files /dev/null and b/changeview.png differ diff --git a/check-progress.html b/check-progress.html new file mode 100644 index 0000000..46362ef --- /dev/null +++ b/check-progress.html @@ -0,0 +1,78 @@ + + + + + Check Progress + + + + +
+ About Us + +
+ +
+ +

Your Rubik's Cube Progress

+

+
+
+

Total Solves

+

0

+
+
+

Best Time

+

NA

+
+
+

Average Time

+

NA

+
+
+

Types of Cubes Solved

+

3x3

+
+
+

Latest Solves

+
    + +
+
+ +

Your Rubik's Cube Progress

+
+
+
+
Total Solves: --
+
Best Time: --
+
Average Time: --
+
+ Types of Cubes Solved: 3x3, 4x4, 5x5 +
+
Latest Solves:
+
    + +
+
+
+ + + + + diff --git a/edit-profile.html b/edit-profile.html new file mode 100644 index 0000000..aa00e52 --- /dev/null +++ b/edit-profile.html @@ -0,0 +1,50 @@ + + + + + Profile Dropdown and Edit Profile + + + + + +
+ About Us + +
+ + +
+

Edit Profile

+

You are playing as a guest now, add details to identify you.

+
+ + + + + + + + + + + + + +
+
+ + + + diff --git a/images/profilepic.jpg b/images/profilepic.jpg new file mode 100644 index 0000000..1d6602b Binary files /dev/null and b/images/profilepic.jpg differ diff --git a/index.html b/index.html index 567eecf..9bbc06e 100644 --- a/index.html +++ b/index.html @@ -1,37 +1,104 @@ - + + Rubik Cube + + + + + + + + +
+ +
+ + + + + + +
+ + About Us + +
+ + + +
+
+ -
-
- - +
+
+ + - + - - + + - - + + -
+
+ + -
+ -
+

@@ -43,75 +110,356 @@

Time limit 05:00 + +
+ + +
+

+ Let's + Solve! +

+
+ Double tap to start +
+
+ +
+

+ Let's + GO +

+
+ Click here to start +
+ +
+

TIME START NOW

+ + 0:00 +
+
+ Complete! +
+
+ + Best Time! +
+ +
-
- Complete! + +
+ + + + +
-
- - Best Time! + +
+ + +
-
-
- - - - - -
+
-
- - - -
+
+ Cube:3x3x3 +
+
+ Total solves:- +
+
+ Best time:- +
+
+ Worst time:- +
+
+ Average of 5:- +
+
+ Average of 12:- +
+
+ Average of 25:- +
-
-
- Cube:3x3x3
-
- Total solves:- + +
+ + + + +
-
- Best time:- + + + + +
-
- Worst time:- + +
+ + + + +
-
- Average of 5:- + +
+ + +
-
- Average of 12:- + +
+
+ Cube:3x3x3 +
+
+ Total solves:- +
+
+ Best time:- +
+
+ Worst time:- +
+
+ Average of 5:- +
+
+ Average of 12:- +
+
+ Average of 25:- +
-
- Average of 25:- + +
+ + +
+
+
-
-
- - - - - + + + +
+ + +
+ + + + + + + + + + + +
+ + +
+

1. Twisting the segments

+
+ This can be done by dragging the cursor over the segment you want to rotate. +
+
+ +

2. Changing the view of the cube

+
+ This can be done by dragging the cursor in an arrow around the cube. +
+
+ + + + +
+ + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..da8fa65 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Rubik-Cube", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/profile.css b/profile.css new file mode 100644 index 0000000..0a17ceb --- /dev/null +++ b/profile.css @@ -0,0 +1,181 @@ +/* Your existing CSS styles */ +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap"); + +body { + font-family: "Space Grotesk", sans-serif; + background-image: url("https://png.pngtree.com/thumb_back/fh260/background/20210728/pngtree-purple-rubiks-cube-holographic-floating-geometric-abstract-background-image_752595.jpg"); + background-size: cover; + background-repeat: no-repeat; + background-position: center; + margin: 0; + padding: 0; + height: 100vh; + display: flex; + flex-direction: column; + align-items: center; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + color: #333; + z-index: 10; + background-color: rgba(194, 190, 190, 0.4); + width: 100%; + height: 60px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); + position: fixed; + top: 0; + left: 0; +} + +/* Button style for About Us link */ +.header a { + display: inline-block; + padding: 10px 20px; + background: linear-gradient(rgb(231, 231, 246), rgb(180, 180, 249)); + color: rgb(7, 6, 6); + text-decoration: none; + border: 2px solid #000000; + border-radius: 10px; + font-weight: bold; + transition: background-color 0.3s, transform 0.3s, box-shadow 0.3s; + box-shadow: 3px 3px 0px 0px #b3aaf7; +} + +.header a:hover { + background-color: #2b74bc; + transform: translateY(-2px); + box-shadow: 5px 5px 0px 0px #090915; +} + +.header a:active { + transform: translateY(1px); + box-shadow: 2px 2px 0px 0px #e99f4c; +} + +.profile-menu { + position: relative; + display: inline-block; + z-index: 11; +} + +.profile-btn { + background-color: transparent; + border: none; + color: rgb(10, 10, 10); + padding: 10px; + border-radius: 50%; + cursor: pointer; + outline: none; + width: 4rem; + height: 4rem; + margin-right: 80px; +} + +.profile-pic { + width: 100%; + height: 100%; + border-radius: 50%; +} + +.dropdown-content { + display: none; + position: absolute; + right: 0; + background-color: transparent; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + z-index: 12; + border-radius: 5px; + margin-right: 30px; +} + +.dropdown-content a { + color: black; + padding: 10px 30px; + text-decoration: none; + display: block; +} + +.dropdown-content a:hover { + + background-color: #2f302d; + + background-color: #f1f1f1; + +} + +.profile-menu:hover .dropdown-content { + display: block; +} + +/* Styles for the edit profile form */ + +#edit-profile-form { + display: flex; + flex-direction: column; + align-items: center; + padding: 20px; + opacity: 0.6; + background-image: url("https://t4.ftcdn.net/jpg/03/30/27/41/360_F_330274196_C6mjMdPaTggBr4Jg45faWAwViexh7DoA.jpg"); + background-size: cover; + background-repeat: no-repeat; + background-position: center; + border-radius: 5px; + max-width: 400px; + margin-top: 180px; /* Adjust margin-top to create space below the header */ + border: 2px solid #0a0b0b; + border-radius: 20px; + box-shadow: 10px 8px 0px 2px #0a0808; + backdrop-filter: blur(10px); +} + +#edit-profile-form label { + display: block; + margin-bottom: 5px; + font-weight: bold; + color: #000000; +} + +#edit-profile-form input[type="text"], +#edit-profile-form input[type="file"] { + width: 85%; + padding: 8px; + background: #ebedf0; + margin-bottom: 10px; + border: 1.5px solid #000000; + transform: translateY(4px); + box-shadow: 1px 2px 0px 0px #080604; + color: #000000; + border-radius: 3px; +} + +#edit-profile-form button { + + padding: 10px 20px; + background-color: #007bff; + color: rgb(75, 231, 8); + border: none; + border-radius: 3px; + cursor: pointer; + + padding: 10px 20px; + background-color: #2d8ef6; + border: 1.5px solid #000000; + color: rgb(13, 13, 13); + border-radius: 10px; + cursor: pointer; + margin-top: 15px; + font-weight: 800; + box-shadow: 3px 3px 0px 0px #0f0b07; +} + +#edit-profile-form button:hover { + background-color: rgba(4, 4, 4, 0.8); + color: white; + border: 1.5px solid #000000; + box-shadow: 6px 6px 0px 0px #0f0b05; + +} diff --git a/profile.js b/profile.js new file mode 100644 index 0000000..d22e56e --- /dev/null +++ b/profile.js @@ -0,0 +1,220 @@ +// Function to handle user sign-in +function handleCredentialResponse(response) { + const data = jwt_decode(response.credential); + const profileContainer = document.querySelector(".profile-container"); + const profileName = document.querySelector(".profile-name"); + const profileImage = document.querySelector(".profile-container img"); + + profileName.textContent = data.name; + profileImage.src = data.picture; + + profileContainer.style.opacity = 1; + profileContainer.style.pointerEvents = "auto"; + } + + // Function to handle user sign-out + function signOut() { + const profileContainer = document.querySelector(".profile-container"); + + profileContainer.style.opacity = 0; + profileContainer.style.pointerEvents = "none"; + } + + // Add sign-out button event listener + document.querySelector(".sign-out-btn").addEventListener("click", signOut); + + function handleCredentialResponse(response) { + const responsePayload = parseJwt(response.credential); + document.getElementById("user-name").innerText = `Hello, ${responsePayload.name}`; + document.getElementById("user-image").src = responsePayload.picture; + document.getElementById("user-image").style.display = "block"; + document.getElementById("sign-out").style.display = "block"; + + // Save user profile in local storage + localStorage.setItem("userProfile", JSON.stringify(responsePayload)); + + // Load user progress + loadUserProgress(); + } + + function parseJwt(token) { + const base64Url = token.split(".")[1]; + const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); + const jsonPayload = decodeURIComponent( + atob(base64) + .split("") + .map(function (c) { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join("") + ); + + return JSON.parse(jsonPayload); + } + + document.getElementById("sign-out").addEventListener("click", function () { + document.getElementById("user-name").innerText = ""; + document.getElementById("user-image").style.display = "none"; + document.getElementById("sign-out").style.display = "none"; + localStorage.removeItem("userProfile"); + localStorage.removeItem("userProgress"); + location.reload(); + }); + + function loadUserProfile() { + const userProfile = JSON.parse(localStorage.getItem("userProfile")); + if (userProfile) { + document.getElementById("user-name").innerText = `Hello, ${userProfile.name}`; + document.getElementById("user-image").src = userProfile.picture; + document.getElementById("user-image").style.display = "block"; + document.getElementById("sign-out").style.display = "block"; + + // Load additional profile info + document.getElementById("user-email").innerText = `Email: ${userProfile.email}`; + document.getElementById("user-additional-info").innerText = `Info: ${userProfile.additionalInfo}`; + + loadUserProgress(); + } + } + + function saveUserProgress(stats) { + localStorage.setItem("userProgress", JSON.stringify(stats)); + } + + function loadUserProgress() { + const userProgress = JSON.parse(localStorage.getItem("userProgress")); + if (userProgress) { + document.querySelector('.stats[name="cube-size"] b').innerText = userProgress.cubeSize; + document.querySelector('.stats[name="total-solves"] b').innerText = userProgress.totalSolves; + document.querySelector('.stats[name="best-time"] b').innerText = userProgress.bestTime; + document.querySelector('.stats[name="worst-time"] b').innerText = userProgress.worstTime; + document.querySelector('.stats[name="average-5"] b').innerText = userProgress.average5; + document.querySelector('.stats[name="average-12"] b').innerText = userProgress.average12; + document.querySelector('.stats[name="average-25"] b').innerText = userProgress.average25; + } + } + + // Call loadUserProfile on page load + window.onload = function () { + loadUserProfile(); + }; + + function changeProfilePic() { + // Implement functionality to change profile picture + alert("Change Profile Picture"); + } + + function editName() { + document.getElementById("profile-name").style.display = "none"; + document.getElementById("name-input").style.display = "inline"; + document.getElementById("save-name-btn").style.display = "inline"; + document.getElementById("name-input").value = document.getElementById("profile-name").textContent; + } + + function saveName() { + const newName = document.getElementById("name-input").value; + document.getElementById("profile-name").textContent = newName; + document.getElementById("profile-name").style.display = "inline"; + document.getElementById("name-input").style.display = "none"; + document.getElementById("save-name-btn").style.display = "none"; + + // Optionally, save the new name to local storage or server + } + + document.getElementById("sign-out-btn").addEventListener("click", function () { + // Sign out logic here + console.log("User signed out"); + // Hide profile elements + document.getElementById("profile-container").style.opacity = 0; + document.getElementById("profile-container").style.pointerEvents = "none"; + // Clear user data from localStorage or state + }); + + function toggleProfileOptions() { + const options = document.getElementById("profile-options"); + options.classList.toggle("hidden"); // Toggle visibility + } + + function editProfileName() { + const newName = prompt("Enter new name:"); + if (newName) { + document.getElementById("user-name").textContent = newName; + // Update this in your local storage or server as needed + } + } + + function viewProgress() { + alert("Viewing progress..."); // Replace with actual progress viewing logic + } + + // Update profile info + function updateProfile(event) { + event.preventDefault(); + + const name = document.getElementById("name").value.trim(); + const email = document.getElementById("email").value.trim(); + const additionalInfo = document.getElementById("additional-info").value.trim(); + const profilePic = document.getElementById("profile-pic").files[0]; + + if (name === "" || email === "" || additionalInfo === "") { + Swal.fire({ + icon: "error", + title: "Oops...", + text: "Please complete all fields before submitting.", + customClass: { + popup: "swal-popup", + title: "swal-title", + content: "swal-content", + confirmButton: "swal-confirm-button", + }, + }); + return; + } + + if (!profilePic) { + Swal.fire({ + icon: "error", + title: "Oops...", + text: "Please select a profile picture before submitting.", + customClass: { + popup: "swal-popup", + title: "swal-title", + content: "swal-content", + confirmButton: "swal-confirm-button", + }, + }); + return; + } + + const reader = new FileReader(); + reader.onload = function (e) { + const userProfile = { + name: name, + email: email, + additionalInfo: additionalInfo, + picture: e.target.result, + }; + localStorage.setItem("userProfile", JSON.stringify(userProfile)); + + Swal.fire({ + icon: "success", + title: "Success!", + text: "Your profile has been updated.", + customClass: { + popup: "swal-popup", + title: "swal-title", + content: "swal-content", + confirmButton: "swal-confirm-button", + }, + }).then((result) => { + if (result.isConfirmed || result.isDismissed) { + document.getElementById("edit-profile").reset(); + document.getElementById("main-profile-pic").src = userProfile.picture; + } + }); + }; + reader.readAsDataURL(profilePic); + } + + document.getElementById("edit-profile").addEventListener("submit", updateProfile); + diff --git a/progress.css b/progress.css new file mode 100644 index 0000000..8ad21fb --- /dev/null +++ b/progress.css @@ -0,0 +1,257 @@ +/* Import Google Fonts */ +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap"); + +/* Body styling */ +body { + + font-family: 'Arial', sans-serif; + /* Use a clean, modern font */ + font-size: 30px; + /* Base font size */ + color: #f7f4f4; + /* Dark gray text for readability */ + background-color: #f4f4f4; + background-image:url('https://i2.pickpik.com/photos/502/642/517/rubik-s-cube-game-cube-strategy-13845d21bb0c925716567a8a3ee7a0fd.jpg'); + background-size: cover; + background-repeat: no-repeat; + background-position: center center; + /* Light gray background */ + padding: 20px; + line-height: 1.6; +} + + +/* Header styles */ + + + font-family: "Space Grotesk", sans-serif; + background-image: url("https://png.pngtree.com/thumb_back/fh260/background/20210728/pngtree-purple-rubiks-cube-holographic-floating-geometric-abstract-background-image_752595.jpg"); + background-size: cover; + background-repeat: no-repeat; + background-position: center; + margin: 0; + padding: 0; + height: 100vh; + display: flex; + flex-direction: column; + align-items: center; +} + +/* Header styling */ + +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + color: #333; + z-index: 10; + background-color: rgba(194, 190, 190, 0.4); + width: 100%; + height: 60px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); + position: fixed; + top: 0; + left: 0; +} + +/* Button style for About Us link */ +.header a { + display: inline-block; + padding: 10px 20px; + background: linear-gradient(rgb(231, 231, 246), rgb(180, 180, 249)); + color: rgb(7, 6, 6); + text-decoration: none; + border: 2px solid #000000; + border-radius: 10px; + font-weight: bold; + transition: background-color 0.3s, transform 0.3s, box-shadow 0.3s; + box-shadow: 3px 3px 0px 0px #b3aaf7; +} + +.header a:hover { + background-color: #2b74bc; + transform: translateY(-2px); + box-shadow: 5px 5px 0px 0px #090915; +} + +.header a:active { + transform: translateY(1px); + box-shadow: 2px 2px 0px 0px #e99f4c; +} + +/* Profile menu styling */ +.profile-menu { + position: relative; + display: inline-block; + z-index: 11; +} + +.profile-btn { + background-color: transparent; + border: none; + color: rgb(10, 10, 10); + padding: 10px; + border-radius: 50%; + cursor: pointer; + outline: none; + width: 4rem; + height: 4rem; + margin-right: 80px; +} + +.profile-pic { + width: 100%; + height: 100%; + border-radius: 50%; +} + +.dropdown-content { + display: none; + position: absolute; + right: 0; + background-color: transparent; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + z-index: 12; + border-radius: 5px; + margin-right: 30px; +} + +.dropdown-content a { + color: black; + padding: 10px 30px; + text-decoration: none; + display: block; +} + +.dropdown-content a:hover { + background-color: #f1f1f1; +} + +.profile-menu:hover .dropdown-content { + display: block; +} + + + +/* Progress container styles */ + + + +.progress-container { + + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + margin-top: 20px; + max-width: 900px; /* Set a maximum width */ + height: 500px; + display: flex; + flex-direction: column; + justify-content: center; + padding: 20px; + +} + +.progress-stats { + display: grid; + grid-template-columns: repeat(3, 2fr); + gap: 20px; + margin: 0; + +} + +.progress-box { + background-color: #f0e5e5; + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + margin:0; + +} + +.progress-box h3 { + font-size: 20px; + margin-bottom: 8px; + text-align: center; + color: #333; +} + +.progress-box p { + font-size: 16px; + color: #666; + text-align: center; +} + +ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +/* Additional styles for the layout of the two boxes below */ +.progress-stats .bottom-box { + grid-column: span 2; /* Span across two columns */ +} + + +#no-progress{ + color: red; + +/* Progress container styling */ +.progress-container { + opacity: 0.6; + background-image: url("https://t4.ftcdn.net/jpg/03/30/27/41/360_F_330274196_C6mjMdPaTggBr4Jg45faWAwViexh7DoA.jpg"); + background-size: cover; + background-repeat: no-repeat; + background-position: center; + margin-top: 150px; + border-radius: 10px; + padding: 15px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.7); + width: 50%; + max-width: 800px; + text-align: center; +} + +.progress-container:hover { + opacity: 0.9; + transform: translateY(-10px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.9); +} + +.progress-container h1 { + font-size: 2em; + margin-bottom: 20px; + color: #0b0a0a; +} + +.progress-stats { + font-size: 1.2em; + color: #080606; +} + +#recent-solves li { + background: white; + margin-top: 5px; + padding: 10px; + border-radius: 4px; + +.progress-stats div { + margin-bottom: 10px; + +} + +.progress-stats ul { + list-style-type: none; + padding: 0; +} + +.progress-stats ul li { + background: rgba(231, 231, 246, 0.8); + border: 1px solid #ccc; + border-radius: 5px; + padding: 10px; + margin-bottom: 5px; + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.1); +} diff --git a/progress.js b/progress.js new file mode 100644 index 0000000..b92e623 --- /dev/null +++ b/progress.js @@ -0,0 +1,47 @@ +document.addEventListener('DOMContentLoaded', function() { + // Example function to fetch data + fetchProgressData(); + + function fetchProgressData() { + // This function should fetch data from your server or local storage + // Here we simulate data fetching with a static example + const progressData = { + totalSolves: 0, + bestTime: 'NA', + averageTime: 'NA', + cubeTypes: '3x3', + recentSolves: [ + // { time: '1:30', cubeType: '3x3' }, + // { time: '2:15', cubeType: '4x4' } + ] + }; + + displayProgressData(progressData); + } + // Store data in localStorage if it doesn't already exist + if (!localStorage.getItem('progressData')) { + localStorage.setItem('progressData', JSON.stringify(initialData)); + } + + // Retrieve data from localStorage + const progressData = JSON.parse(localStorage.getItem('progressData')); + + + function displayProgressData(data) { + document.getElementById('total-solves').textContent = data.totalSolves; + document.getElementById('best-time').textContent = data.bestTime; + document.getElementById('average-time').textContent = data.averageTime; + document.getElementById('cube-types').textContent = data.cubeTypes; + + if(data.totalSolves == 0) document.getElementById('no-progress').textContent = "Please attempt atleast once to display the progress!!" + const recentSolvesList = document.getElementById('recent-solves'); + recentSolvesList.innerHTML = ''; // Clear previous entries + data.recentSolves.forEach(solve => { + const listItem = document.createElement('li'); + listItem.textContent = `${solve.time} - ${solve.cubeType}`; + recentSolvesList.appendChild(listItem); + }); + } + // Fetch and display data on page load + fetchProgressData(); +}); \ No newline at end of file diff --git a/script.js b/script.js index 0575481..a1a6da9 100644 --- a/script.js +++ b/script.js @@ -1,4 +1,4 @@ -const animationEngine = ( () => { +const animationEngine = (() => { let uniqueID = 0; @@ -8,12 +8,13 @@ const animationEngine = ( () => { this.ids = []; this.animations = {}; - this.update = this.update.bind( this ); + this.update = this.update.bind(this); this.raf = 0; this.time = 0; } + update() { const now = performance.now(); @@ -22,85 +23,90 @@ const animationEngine = ( () => { let i = this.ids.length; - this.raf = i ? requestAnimationFrame( this.update ) : 0; + this.raf = i ? requestAnimationFrame(this.update) : 0; - while ( i-- ) - this.animations[ this.ids[ i ] ] && this.animations[ this.ids[ i ] ].update( delta ); + while (i--) + this.animations[this.ids[i]] && this.animations[this.ids[i]].update(delta); } - add( animation ) { + add(animation) { - animation.id = uniqueID ++; + animation.id = uniqueID++; - this.ids.push( animation.id ); - this.animations[ animation.id ] = animation; + this.ids.push(animation.id); + this.animations[animation.id] = animation; - if ( this.raf !== 0 ) return; + if (this.raf !== 0) return; this.time = performance.now(); - this.raf = requestAnimationFrame( this.update ); + this.raf = requestAnimationFrame(this.update); } - remove( animation ) { + remove(animation) { - const index = this.ids.indexOf( animation.id ); + const index = this.ids.indexOf(animation.id); - if ( index < 0 ) return; + if (index < 0) return; + this.ids.splice( index, 1 ); delete this.animations[ animation.id ]; animation =newGame; + this.ids.splice(index, 1); + delete this.animations[animation.id]; + animation = null; + } } return new AnimationEngine(); -} )(); +})(); class Animation { - constructor( start ) { + constructor(start) { - if ( start === true ) this.start(); + if (start === true) this.start(); } start() { - animationEngine.add( this ); + animationEngine.add(this); } stop() { - animationEngine.remove( this ); + animationEngine.remove(this); } - update( delta ) {} + update(delta) { } } class World extends Animation { - constructor( game ) { + constructor(game) { - super( true ); + super(true); this.game = game; this.container = this.game.dom.game; this.scene = new THREE.Scene(); - this.renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } ); - this.renderer.setPixelRatio( window.devicePixelRatio ); - this.container.appendChild( this.renderer.domElement ); + this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + this.renderer.setPixelRatio(window.devicePixelRatio); + this.container.appendChild(this.renderer.domElement); - this.camera = new THREE.PerspectiveCamera( 2, 1, 0.1, 10000 ); + this.camera = new THREE.PerspectiveCamera(2, 1, 0.1, 10000); this.stage = { width: 2, height: 3 }; this.fov = 10; @@ -110,13 +116,13 @@ class World extends Animation { this.onResize = []; this.resize(); - window.addEventListener( 'resize', () => this.resize(), false ); + window.addEventListener('resize', () => this.resize(), false); } update() { - this.renderer.render( this.scene, this.camera ); + this.renderer.render(this.scene, this.camera); } @@ -125,7 +131,7 @@ class World extends Animation { this.width = this.container.offsetWidth; this.height = this.container.offsetHeight; - this.renderer.setSize( this.width, this.height ); + this.renderer.setSize(this.width, this.height); this.camera.fov = this.fov; this.camera.aspect = this.width / this.height; @@ -133,62 +139,90 @@ class World extends Animation { const aspect = this.stage.width / this.stage.height; const fovRad = this.fov * THREE.Math.DEG2RAD; - let distance = ( aspect < this.camera.aspect ) - ? ( this.stage.height / 2 ) / Math.tan( fovRad / 2 ) - : ( this.stage.width / this.camera.aspect ) / ( 2 * Math.tan( fovRad / 2 ) ); + let distance = (aspect < this.camera.aspect) + ? (this.stage.height / 2) / Math.tan(fovRad / 2) + : (this.stage.width / this.camera.aspect) / (2 * Math.tan(fovRad / 2)); distance *= 0.5; - this.camera.position.set( distance, distance, distance); - this.camera.lookAt( this.scene.position ); + this.camera.position.set(distance, distance, distance); + this.camera.lookAt(this.scene.position); this.camera.updateProjectionMatrix(); - const docFontSize = ( aspect < this.camera.aspect ) - ? ( this.height / 100 ) * aspect + const docFontSize = (aspect < this.camera.aspect) + ? (this.height / 100) * aspect : this.width / 100; document.documentElement.style.fontSize = docFontSize + 'px'; - if ( this.onResize ) this.onResize.forEach( cb => cb() ); + if (this.onResize) this.onResize.forEach(cb => cb()); } createLights() { this.lights = { - holder: new THREE.Object3D, - ambient: new THREE.AmbientLight( 0xffffff, 0.69 ), - front: new THREE.DirectionalLight( 0xffffff, 0.36 ), - back: new THREE.DirectionalLight( 0xffffff, 0.19 ), + holder: new THREE.Object3D, + ambient: new THREE.AmbientLight(0xffffff, 0.69), + front: new THREE.DirectionalLight(0xffffff, 0.36), + back: new THREE.DirectionalLight(0xffffff, 0.19), }; - this.lights.front.position.set( 1.5, 5, 3 ); - this.lights.back.position.set( -1.5, -5, -3 ); + this.lights.front.position.set(1.5, 5, 3); + this.lights.back.position.set(-1.5, -5, -3); - this.lights.holder.add( this.lights.ambient ); - this.lights.holder.add( this.lights.front ); - this.lights.holder.add( this.lights.back ); + this.lights.holder.add(this.lights.ambient); + this.lights.holder.add(this.lights.front); + this.lights.holder.add(this.lights.back); - this.scene.add( this.lights.holder ); + this.scene.add(this.lights.holder); } } -function RoundedBoxGeometry( size, radius, radiusSegments ) { - THREE.BufferGeometry.call( this ); +// script.js + +document.addEventListener('DOMContentLoaded', () => { + const themeSelector = document.getElementById('theme'); + + // Load saved theme from localStorage if it exists + const savedTheme = localStorage.getItem('theme'); + if (savedTheme) { + document.body.classList.add(`${savedTheme}-theme`); + themeSelector.value = savedTheme; + } else { + document.body.classList.add('default-theme'); + } + + // Change theme when the user selects a different option + themeSelector.addEventListener('change', (event) => { + document.body.className = ''; // Remove all existing classes + const selectedTheme = event.target.value; + document.body.classList.add(`${selectedTheme}-theme`); + + // Save selected theme to localStorage + localStorage.setItem('theme', selectedTheme); + }); +}); + + +function RoundedBoxGeometry(size, radius, radiusSegments) { + + + THREE.BufferGeometry.call(this); this.type = 'RoundedBoxGeometry'; - radiusSegments = ! isNaN( radiusSegments ) ? Math.max( 1, Math.floor( radiusSegments ) ) : 1; + radiusSegments = !isNaN(radiusSegments) ? Math.max(1, Math.floor(radiusSegments)) : 1; var width, height, depth; width = height = depth = size; radius = size * radius; - radius = Math.min( radius, Math.min( width, Math.min( height, Math.min( depth ) ) ) / 2 ); + radius = Math.min(radius, Math.min(width, Math.min(height, Math.min(depth))) / 2); var edgeHalfWidth = width / 2 - radius; var edgeHalfHeight = height / 2 - radius; @@ -203,10 +237,10 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { }; var rs1 = radiusSegments + 1; - var totalVertexCount = ( rs1 * radiusSegments + 1 ) << 3; + var totalVertexCount = (rs1 * radiusSegments + 1) << 3; - var positions = new THREE.BufferAttribute( new Float32Array( totalVertexCount * 3 ), 3 ); - var normals = new THREE.BufferAttribute( new Float32Array( totalVertexCount * 3 ), 3 ); + var positions = new THREE.BufferAttribute(new Float32Array(totalVertexCount * 3), 3); + var normals = new THREE.BufferAttribute(new Float32Array(totalVertexCount * 3), 3); var cornerVerts = [], @@ -216,12 +250,12 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { vertexPool = [], normalPool = [], indices = [] - ; + ; var lastVertex = rs1 * radiusSegments, cornerVertNumber = rs1 * radiusSegments + 1 - ; + ; doVertices(); doFaces(); @@ -233,77 +267,77 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { function doVertices() { var cornerLayout = [ - new THREE.Vector3( 1, 1, 1 ), - new THREE.Vector3( 1, 1, - 1 ), - new THREE.Vector3( - 1, 1, - 1 ), - new THREE.Vector3( - 1, 1, 1 ), - new THREE.Vector3( 1, - 1, 1 ), - new THREE.Vector3( 1, - 1, - 1 ), - new THREE.Vector3( - 1, - 1, - 1 ), - new THREE.Vector3( - 1, - 1, 1 ) + new THREE.Vector3(1, 1, 1), + new THREE.Vector3(1, 1, - 1), + new THREE.Vector3(- 1, 1, - 1), + new THREE.Vector3(- 1, 1, 1), + new THREE.Vector3(1, - 1, 1), + new THREE.Vector3(1, - 1, - 1), + new THREE.Vector3(- 1, - 1, - 1), + new THREE.Vector3(- 1, - 1, 1) ]; - for ( var j = 0; j < 8; j ++ ) { + for (var j = 0; j < 8; j++) { - cornerVerts.push( [] ); - cornerNormals.push( [] ); + cornerVerts.push([]); + cornerNormals.push([]); } var PIhalf = Math.PI / 2; - var cornerOffset = new THREE.Vector3( edgeHalfWidth, edgeHalfHeight, edgeHalfDepth ); + var cornerOffset = new THREE.Vector3(edgeHalfWidth, edgeHalfHeight, edgeHalfDepth); - for ( var y = 0; y <= radiusSegments; y ++ ) { + for (var y = 0; y <= radiusSegments; y++) { var v = y / radiusSegments; var va = v * PIhalf; - var cosVa = Math.cos( va ); - var sinVa = Math.sin( va ); + var cosVa = Math.cos(va); + var sinVa = Math.sin(va); - if ( y == radiusSegments ) { + if (y == radiusSegments) { - vertex.set( 0, 1, 0 ); - var vert = vertex.clone().multiplyScalar( radius ).add( cornerOffset ); - cornerVerts[ 0 ].push( vert ); - vertexPool.push( vert ); + vertex.set(0, 1, 0); + var vert = vertex.clone().multiplyScalar(radius).add(cornerOffset); + cornerVerts[0].push(vert); + vertexPool.push(vert); var norm = vertex.clone(); - cornerNormals[ 0 ].push( norm ); - normalPool.push( norm ); + cornerNormals[0].push(norm); + normalPool.push(norm); continue; } - for ( var x = 0; x <= radiusSegments; x ++ ) { + for (var x = 0; x <= radiusSegments; x++) { var u = x / radiusSegments; var ha = u * PIhalf; - vertex.x = cosVa * Math.cos( ha ); + vertex.x = cosVa * Math.cos(ha); vertex.y = sinVa; - vertex.z = cosVa * Math.sin( ha ); + vertex.z = cosVa * Math.sin(ha); - var vert = vertex.clone().multiplyScalar( radius ).add( cornerOffset ); - cornerVerts[ 0 ].push( vert ); - vertexPool.push( vert ); + var vert = vertex.clone().multiplyScalar(radius).add(cornerOffset); + cornerVerts[0].push(vert); + vertexPool.push(vert); var norm = vertex.clone().normalize(); - cornerNormals[ 0 ].push( norm ); - normalPool.push( norm ); + cornerNormals[0].push(norm); + normalPool.push(norm); } } - for ( var i = 1; i < 8; i ++ ) { + for (var i = 1; i < 8; i++) { - for ( var j = 0; j < cornerVerts[ 0 ].length; j ++ ) { + for (var j = 0; j < cornerVerts[0].length; j++) { - var vert = cornerVerts[ 0 ][ j ].clone().multiply( cornerLayout[ i ] ); - cornerVerts[ i ].push( vert ); - vertexPool.push( vert ); + var vert = cornerVerts[0][j].clone().multiply(cornerLayout[i]); + cornerVerts[i].push(vert); + vertexPool.push(vert); - var norm = cornerNormals[ 0 ][ j ].clone().multiply( cornerLayout[ i ] ); - cornerNormals[ i ].push( norm ); - normalPool.push( norm ); + var norm = cornerNormals[0][j].clone().multiply(cornerLayout[i]); + cornerNormals[i].push(norm); + normalPool.push(norm); } @@ -324,18 +358,18 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { true ]; - var lastRowOffset = rs1 * ( radiusSegments - 1 ); + var lastRowOffset = rs1 * (radiusSegments - 1); - for ( var i = 0; i < 8; i ++ ) { + for (var i = 0; i < 8; i++) { var cornerOffset = cornerVertNumber * i; - for ( var v = 0; v < radiusSegments - 1; v ++ ) { + for (var v = 0; v < radiusSegments - 1; v++) { var r1 = v * rs1; - var r2 = ( v + 1 ) * rs1; + var r2 = (v + 1) * rs1; - for ( var u = 0; u < radiusSegments; u ++ ) { + for (var u = 0; u < radiusSegments; u++) { var u1 = u + 1; var a = cornerOffset + r1 + u; @@ -343,25 +377,25 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { var c = cornerOffset + r2 + u; var d = cornerOffset + r2 + u1; - if ( ! flips[ i ] ) { + if (!flips[i]) { - indices.push( a ); - indices.push( b ); - indices.push( c ); + indices.push(a); + indices.push(b); + indices.push(c); - indices.push( b ); - indices.push( d ); - indices.push( c ); + indices.push(b); + indices.push(d); + indices.push(c); } else { - indices.push( a ); - indices.push( c ); - indices.push( b ); + indices.push(a); + indices.push(c); + indices.push(b); - indices.push( b ); - indices.push( c ); - indices.push( d ); + indices.push(b); + indices.push(c); + indices.push(d); } @@ -369,23 +403,23 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { } - for ( var u = 0; u < radiusSegments; u ++ ) { + for (var u = 0; u < radiusSegments; u++) { var a = cornerOffset + lastRowOffset + u; var b = cornerOffset + lastRowOffset + u + 1; var c = cornerOffset + lastVertex; - if ( ! flips[ i ] ) { + if (!flips[i]) { - indices.push( a ); - indices.push( b ); - indices.push( c ); + indices.push(a); + indices.push(b); + indices.push(c); } else { - indices.push( a ); - indices.push( c ); - indices.push( b ); + indices.push(a); + indices.push(c); + indices.push(b); } @@ -402,84 +436,84 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { var c = lastVertex + cornerVertNumber * 2; var d = lastVertex + cornerVertNumber * 3; - indices.push( a ); - indices.push( b ); - indices.push( c ); - indices.push( a ); - indices.push( c ); - indices.push( d ); + indices.push(a); + indices.push(b); + indices.push(c); + indices.push(a); + indices.push(c); + indices.push(d); a = lastVertex + cornerVertNumber * 4; b = lastVertex + cornerVertNumber * 5; c = lastVertex + cornerVertNumber * 6; d = lastVertex + cornerVertNumber * 7; - indices.push( a ); - indices.push( c ); - indices.push( b ); - indices.push( a ); - indices.push( d ); - indices.push( c ); + indices.push(a); + indices.push(c); + indices.push(b); + indices.push(a); + indices.push(d); + indices.push(c); a = 0; b = cornerVertNumber; c = cornerVertNumber * 4; d = cornerVertNumber * 5; - indices.push( a ); - indices.push( c ); - indices.push( b ); - indices.push( b ); - indices.push( c ); - indices.push( d ); + indices.push(a); + indices.push(c); + indices.push(b); + indices.push(b); + indices.push(c); + indices.push(d); a = cornerVertNumber * 2; b = cornerVertNumber * 3; c = cornerVertNumber * 6; d = cornerVertNumber * 7; - indices.push( a ); - indices.push( c ); - indices.push( b ); - indices.push( b ); - indices.push( c ); - indices.push( d ); + indices.push(a); + indices.push(c); + indices.push(b); + indices.push(b); + indices.push(c); + indices.push(d); a = radiusSegments; b = radiusSegments + cornerVertNumber * 3; c = radiusSegments + cornerVertNumber * 4; d = radiusSegments + cornerVertNumber * 7; - indices.push( a ); - indices.push( b ); - indices.push( c ); - indices.push( b ); - indices.push( d ); - indices.push( c ); + indices.push(a); + indices.push(b); + indices.push(c); + indices.push(b); + indices.push(d); + indices.push(c); a = radiusSegments + cornerVertNumber; b = radiusSegments + cornerVertNumber * 2; c = radiusSegments + cornerVertNumber * 5; d = radiusSegments + cornerVertNumber * 6; - indices.push( a ); - indices.push( c ); - indices.push( b ); - indices.push( b ); - indices.push( c ); - indices.push( d ); + indices.push(a); + indices.push(c); + indices.push(b); + indices.push(b); + indices.push(c); + indices.push(d); } function doHeightEdges() { - for ( var i = 0; i < 4; i ++ ) { + for (var i = 0; i < 4; i++) { var cOffset = i * cornerVertNumber; var cRowOffset = 4 * cornerVertNumber + cOffset; var needsFlip = i & 1 === 1; - for ( var u = 0; u < radiusSegments; u ++ ) { + for (var u = 0; u < radiusSegments; u++) { var u1 = u + 1; var a = cOffset + u; @@ -487,23 +521,23 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { var c = cRowOffset + u; var d = cRowOffset + u1; - if ( ! needsFlip ) { + if (!needsFlip) { - indices.push( a ); - indices.push( b ); - indices.push( c ); - indices.push( b ); - indices.push( d ); - indices.push( c ); + indices.push(a); + indices.push(b); + indices.push(c); + indices.push(b); + indices.push(d); + indices.push(c); } else { - indices.push( a ); - indices.push( c ); - indices.push( b ); - indices.push( b ); - indices.push( c ); - indices.push( d ); + indices.push(a); + indices.push(c); + indices.push(b); + indices.push(b); + indices.push(c); + indices.push(d); } @@ -515,43 +549,43 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { function doDepthEdges() { - var cStarts = [ 0, 2, 4, 6 ]; - var cEnds = [ 1, 3, 5, 7 ]; + var cStarts = [0, 2, 4, 6]; + var cEnds = [1, 3, 5, 7]; - for ( var i = 0; i < 4; i ++ ) { + for (var i = 0; i < 4; i++) { - var cStart = cornerVertNumber * cStarts[ i ]; - var cEnd = cornerVertNumber * cEnds[ i ]; + var cStart = cornerVertNumber * cStarts[i]; + var cEnd = cornerVertNumber * cEnds[i]; var needsFlip = 1 >= i; - for ( var u = 0; u < radiusSegments; u ++ ) { + for (var u = 0; u < radiusSegments; u++) { var urs1 = u * rs1; - var u1rs1 = ( u + 1 ) * rs1; + var u1rs1 = (u + 1) * rs1; var a = cStart + urs1; var b = cStart + u1rs1; var c = cEnd + urs1; var d = cEnd + u1rs1; - if ( needsFlip ) { + if (needsFlip) { - indices.push( a ); - indices.push( c ); - indices.push( b ); - indices.push( b ); - indices.push( c ); - indices.push( d ); + indices.push(a); + indices.push(c); + indices.push(b); + indices.push(b); + indices.push(c); + indices.push(d); } else { - indices.push( a ); - indices.push( b ); - indices.push( c ); - indices.push( b ); - indices.push( d ); - indices.push( c ); + indices.push(a); + indices.push(b); + indices.push(c); + indices.push(b); + indices.push(d); + indices.push(c); } @@ -565,40 +599,40 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { var end = radiusSegments - 1; - var cStarts = [ 0, 1, 4, 5 ]; - var cEnds = [ 3, 2, 7, 6 ]; - var needsFlip = [ 0, 1, 1, 0 ]; + var cStarts = [0, 1, 4, 5]; + var cEnds = [3, 2, 7, 6]; + var needsFlip = [0, 1, 1, 0]; - for ( var i = 0; i < 4; i ++ ) { + for (var i = 0; i < 4; i++) { - var cStart = cStarts[ i ] * cornerVertNumber; - var cEnd = cEnds[ i ] * cornerVertNumber; + var cStart = cStarts[i] * cornerVertNumber; + var cEnd = cEnds[i] * cornerVertNumber; - for ( var u = 0; u <= end; u ++ ) { + for (var u = 0; u <= end; u++) { var a = cStart + radiusSegments + u * rs1; - var b = cStart + ( u != end ? radiusSegments + ( u + 1 ) * rs1 : cornerVertNumber - 1 ); + var b = cStart + (u != end ? radiusSegments + (u + 1) * rs1 : cornerVertNumber - 1); var c = cEnd + radiusSegments + u * rs1; - var d = cEnd + ( u != end ? radiusSegments + ( u + 1 ) * rs1 : cornerVertNumber - 1 ); + var d = cEnd + (u != end ? radiusSegments + (u + 1) * rs1 : cornerVertNumber - 1); - if ( ! needsFlip[ i ] ) { + if (!needsFlip[i]) { - indices.push( a ); - indices.push( b ); - indices.push( c ); - indices.push( b ); - indices.push( d ); - indices.push( c ); + indices.push(a); + indices.push(b); + indices.push(c); + indices.push(b); + indices.push(d); + indices.push(c); } else { - indices.push( a ); - indices.push( c ); - indices.push( b ); - indices.push( b ); - indices.push( c ); - indices.push( d ); + indices.push(a); + indices.push(c); + indices.push(b); + indices.push(b); + indices.push(c); + indices.push(d); } @@ -610,36 +644,36 @@ function RoundedBoxGeometry( size, radius, radiusSegments ) { var index = 0; - for ( var i = 0; i < vertexPool.length; i ++ ) { + for (var i = 0; i < vertexPool.length; i++) { positions.setXYZ( index, - vertexPool[ i ].x, - vertexPool[ i ].y, - vertexPool[ i ].z + vertexPool[i].x, + vertexPool[i].y, + vertexPool[i].z ); normals.setXYZ( index, - normalPool[ i ].x, - normalPool[ i ].y, - normalPool[ i ].z + normalPool[i].x, + normalPool[i].y, + normalPool[i].z ); - index ++; + index++; } - this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) ); - this.addAttribute( 'position', positions ); - this.addAttribute( 'normal', normals ); + this.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1)); + this.addAttribute('position', positions); + this.addAttribute('normal', normals); } -RoundedBoxGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +RoundedBoxGeometry.prototype = Object.create(THREE.BufferGeometry.prototype); RoundedBoxGeometry.constructor = RoundedBoxGeometry; -function RoundedPlaneGeometry( size, radius, depth ) { +function RoundedPlaneGeometry(size, radius, depth) { var x, y, width, height; @@ -649,15 +683,15 @@ function RoundedPlaneGeometry( size, radius, depth ) { const shape = new THREE.Shape(); - shape.moveTo( x, y + radius ); - shape.lineTo( x, y + height - radius ); - shape.quadraticCurveTo( x, y + height, x + radius, y + height ); - shape.lineTo( x + width - radius, y + height ); - shape.quadraticCurveTo( x + width, y + height, x + width, y + height - radius ); - shape.lineTo( x + width, y + radius ); - shape.quadraticCurveTo( x + width, y, x + width - radius, y ); - shape.lineTo( x + radius, y ); - shape.quadraticCurveTo( x, y, x, y + radius ); + shape.moveTo(x, y + radius); + shape.lineTo(x, y + height - radius); + shape.quadraticCurveTo(x, y + height, x + radius, y + height); + shape.lineTo(x + width - radius, y + height); + shape.quadraticCurveTo(x + width, y + height, x + width, y + height - radius); + shape.lineTo(x + width, y + radius); + shape.quadraticCurveTo(x + width, y, x + width - radius, y); + shape.lineTo(x + radius, y); + shape.quadraticCurveTo(x, y, x, y + radius); const geometry = new THREE.ExtrudeBufferGeometry( shape, @@ -670,7 +704,7 @@ function RoundedPlaneGeometry( size, radius, depth ) { class Cube { - constructor( game ) { + constructor(game) { this.game = game; this.size = 3; @@ -686,10 +720,10 @@ class Cube { this.object = new THREE.Object3D(); this.animator = new THREE.Object3D(); - this.holder.add( this.animator ); - this.animator.add( this.object ); + this.holder.add(this.animator); + this.animator.add(this.object); - this.game.world.scene.add( this.holder ); + this.game.world.scene.add(this.holder); } @@ -697,42 +731,42 @@ class Cube { this.cubes = []; this.object.children = []; - this.object.add( this.game.controls.group ); + this.object.add(this.game.controls.group); - if ( this.size === 2 ) this.scale = 1.25; - else if ( this.size === 3 ) this.scale = 1; - else if ( this.size > 3 ) this.scale = 3 / this.size; + if (this.size === 2) this.scale = 1.25; + else if (this.size === 3) this.scale = 1; + else if (this.size > 3) this.scale = 3 / this.size; - this.object.scale.set( this.scale, this.scale, this.scale ); + this.object.scale.set(this.scale, this.scale, this.scale); const controlsScale = this.size === 2 ? 0.825 : 1; - this.game.controls.edges.scale.set( controlsScale, controlsScale, controlsScale ); - + this.game.controls.edges.scale.set(controlsScale, controlsScale, controlsScale); + this.generatePositions(); this.generateModel(); - this.pieces.forEach( piece => { + this.pieces.forEach(piece => { - this.cubes.push( piece.userData.cube ); - this.object.add( piece ); + this.cubes.push(piece.userData.cube); + this.object.add(piece); - } ); + }); - this.holder.traverse( node => { + this.holder.traverse(node => { - if ( node.frustumCulled ) node.frustumCulled = false; + if (node.frustumCulled) node.frustumCulled = false; - } ); + }); - this.updateColors( this.game.themes.getColors() ); + this.updateColors(this.game.themes.getColors()); this.sizeGenerated = this.size; } - resize( force = false ) { + resize(force = false) { - if ( this.size !== this.sizeGenerated || force ) { + if (this.size !== this.sizeGenerated || force) { this.size = this.game.preferences.ranges.size.value; @@ -749,11 +783,11 @@ class Cube { reset() { - this.game.controls.edges.rotation.set( 0, 0, 0 ); + this.game.controls.edges.rotation.set(0, 0, 0); - this.holder.rotation.set( 0, 0, 0 ); - this.object.rotation.set( 0, 0, 0 ); - this.animator.rotation.set( 0, 0, 0 ); + this.holder.rotation.set(0, 0, 0); + this.object.rotation.set(0, 0, 0); + this.animator.rotation.set(0, 0, 0); } @@ -768,22 +802,22 @@ class Cube { this.positions = []; - for ( x = 0; x < this.size; x ++ ) { - for ( y = 0; y < this.size; y ++ ) { - for ( z = 0; z < this.size; z ++ ) { + for (x = 0; x < this.size; x++) { + for (y = 0; y < this.size; y++) { + for (z = 0; z < this.size; z++) { let position = new THREE.Vector3(first + x, first + y, first + z); let edges = []; - if ( x == 0 ) edges.push(0); - if ( x == m ) edges.push(1); - if ( y == 0 ) edges.push(2); - if ( y == m ) edges.push(3); - if ( z == 0 ) edges.push(4); - if ( z == m ) edges.push(5); + if (x == 0) edges.push(0); + if (x == m) edges.push(1); + if (y == 0) edges.push(2); + if (y == m) edges.push(3); + if (z == 0) edges.push(4); + if (z == m) edges.push(5); position.edges = edges; - this.positions.push( position ); + this.positions.push(position); } } @@ -801,7 +835,7 @@ class Cube { const mainMaterial = new THREE.MeshLambertMaterial(); const pieceMesh = new THREE.Mesh( - new RoundedBoxGeometry( pieceSize, this.geometry.pieceCornerRadius, 3 ), + new RoundedBoxGeometry(pieceSize, this.geometry.pieceCornerRadius, 3), mainMaterial.clone() ); @@ -811,32 +845,32 @@ class Cube { this.geometry.edgeDepth ); - this.positions.forEach( ( position, index ) => { + this.positions.forEach((position, index) => { const piece = new THREE.Object3D(); const pieceCube = pieceMesh.clone(); const pieceEdges = []; - piece.position.copy( position.clone().divideScalar( 3 ) ); - piece.add( pieceCube ); + piece.position.copy(position.clone().divideScalar(3)); + piece.add(pieceCube); piece.name = index; piece.edgesName = ''; - position.edges.forEach( position => { + position.edges.forEach(position => { - const edge = new THREE.Mesh( edgeGeometry, mainMaterial.clone() ); - const name = [ 'L', 'R', 'D', 'U', 'B', 'F' ][ position ]; + const edge = new THREE.Mesh(edgeGeometry, mainMaterial.clone()); + const name = ['L', 'R', 'D', 'U', 'B', 'F'][position]; const distance = pieceSize / 2; edge.position.set( - distance * [ - 1, 1, 0, 0, 0, 0 ][ position ], - distance * [ 0, 0, - 1, 1, 0, 0 ][ position ], - distance * [ 0, 0, 0, 0, - 1, 1 ][ position ] + distance * [- 1, 1, 0, 0, 0, 0][position], + distance * [0, 0, - 1, 1, 0, 0][position], + distance * [0, 0, 0, 0, - 1, 1][position] ); edge.rotation.set( - Math.PI / 2 * [ 0, 0, 1, - 1, 0, 0 ][ position ], - Math.PI / 2 * [ - 1, 1, 0, 0, 2, 0 ][ position ], + Math.PI / 2 * [0, 0, 1, - 1, 0, 0][position], + Math.PI / 2 * [- 1, 1, 0, 0, 2, 0][position], 0 ); @@ -848,11 +882,11 @@ class Cube { edge.name = name; - piece.add( edge ); - pieceEdges.push( name ); - this.edges.push( edge ); + piece.add(edge); + pieceEdges.push(name); + this.edges.push(edge); - } ); + }); piece.userData.edges = pieceEdges; piece.userData.cube = pieceCube; @@ -862,39 +896,39 @@ class Cube { rotation: piece.rotation.clone(), }; - this.pieces.push( piece ); + this.pieces.push(piece); - } ); + }); } - updateColors( colors ) { + updateColors(colors) { - if ( typeof this.pieces !== 'object' && typeof this.edges !== 'object' ) return; + if (typeof this.pieces !== 'object' && typeof this.edges !== 'object') return; - this.pieces.forEach( piece => piece.userData.cube.material.color.setHex( colors.P ) ); - this.edges.forEach( edge => edge.material.color.setHex( colors[ edge.name ] ) ); + this.pieces.forEach(piece => piece.userData.cube.material.color.setHex(colors.P)); + this.edges.forEach(edge => edge.material.color.setHex(colors[edge.name])); } - loadFromData( data ) { + loadFromData(data) { this.size = data.size; this.reset(); this.init(); - this.pieces.forEach( piece => { + this.pieces.forEach(piece => { - const index = data.names.indexOf( piece.name ); + const index = data.names.indexOf(piece.name); const position = data.positions[index]; const rotation = data.rotations[index]; - piece.position.set( position.x, position.y, position.z ); - piece.rotation.set( rotation.x, rotation.y, rotation.z ); + piece.position.set(position.x, position.y, position.z); + piece.rotation.set(rotation.x, rotation.y, rotation.z); - } ); + }); } @@ -906,27 +940,27 @@ const Easing = { In: power => { - power = Math.round( power || 1 ); + power = Math.round(power || 1); - return t => Math.pow( t, power ); + return t => Math.pow(t, power); }, Out: power => { - power = Math.round( power || 1 ); + power = Math.round(power || 1); - return t => 1 - Math.abs( Math.pow( t - 1, power ) ); + return t => 1 - Math.abs(Math.pow(t - 1, power)); }, InOut: power => { - power = Math.round( power || 1 ); + power = Math.round(power || 1); - return t => ( t < 0.5 ) - ? Math.pow( t * 2, power ) / 2 - : ( 1 - Math.abs( Math.pow( ( t * 2 - 1 ) - 1, power ) ) ) / 2 + 0.5; + return t => (t < 0.5) + ? Math.pow(t * 2, power) / 2 + : (1 - Math.abs(Math.pow((t * 2 - 1) - 1, power))) / 2 + 0.5; }, @@ -934,11 +968,11 @@ const Easing = { Sine: { - In: () => t => 1 + Math.sin( Math.PI / 2 * t - Math.PI / 2 ), + In: () => t => 1 + Math.sin(Math.PI / 2 * t - Math.PI / 2), - Out: () => t => Math.sin( Math.PI / 2 * t ), + Out: () => t => Math.sin(Math.PI / 2 * t), - InOut: () => t => ( 1 + Math.sin( Math.PI * t - Math.PI / 2 ) ) / 2, + InOut: () => t => (1 + Math.sin(Math.PI * t - Math.PI / 2)) / 2, }, @@ -948,7 +982,7 @@ const Easing = { s = s || 1.70158; - return t => { return ( t -= 1 ) * t * ( ( s + 1 ) * t + s ) + 1; }; + return t => { return (t -= 1) * t * ((s + 1) * t + s) + 1; }; }, @@ -956,7 +990,7 @@ const Easing = { s = s || 1.70158; - return t => { return t * t * ( ( s + 1 ) * t - s ); }; + return t => { return t * t * ((s + 1) * t - s); }; } @@ -964,17 +998,17 @@ const Easing = { Elastic: { - Out: ( amplitude, period ) => { + Out: (amplitude, period) => { let PI2 = Math.PI * 2; - let p1 = ( amplitude >= 1 ) ? amplitude : 1; - let p2 = ( period || 0.3 ) / ( amplitude < 1 ? amplitude : 1 ); - let p3 = p2 / PI2 * ( Math.asin( 1 / p1 ) || 0 ); + let p1 = (amplitude >= 1) ? amplitude : 1; + let p2 = (period || 0.3) / (amplitude < 1 ? amplitude : 1); + let p3 = p2 / PI2 * (Math.asin(1 / p1) || 0); p2 = PI2 / p2; - return t => { return p1 * Math.pow( 2, -10 * t ) * Math.sin( ( t - p3 ) * p2 ) + 1 } + return t => { return p1 * Math.pow(2, -10 * t) * Math.sin((t - p3) * p2) + 1 } }, @@ -984,14 +1018,14 @@ const Easing = { class Tween extends Animation { - constructor( options ) { + constructor(options) { - super( false ); + super(false); this.duration = options.duration || 500; - this.easing = options.easing || ( t => t ); - this.onUpdate = options.onUpdate || ( () => {} ); - this.onComplete = options.onComplete || ( () => {} ); + this.easing = options.easing || (t => t); + this.onUpdate = options.onUpdate || (() => { }); + this.onComplete = options.onComplete || (() => { }); this.delay = options.delay || false; this.yoyo = options.yoyo ? false : null; @@ -1000,36 +1034,36 @@ class Tween extends Animation { this.value = 0; this.delta = 0; - this.getFromTo( options ); + this.getFromTo(options); - if ( this.delay ) setTimeout( () => super.start(), this.delay ); + if (this.delay) setTimeout(() => super.start(), this.delay); else super.start(); - this.onUpdate( this ); + this.onUpdate(this); } - update( delta ) { + update(delta) { const old = this.value * 1; - const direction = ( this.yoyo === true ) ? - 1 : 1; + const direction = (this.yoyo === true) ? - 1 : 1; - this.progress += ( delta / this.duration ) * direction; + this.progress += (delta / this.duration) * direction; - this.value = this.easing( this.progress ); + this.value = this.easing(this.progress); this.delta = this.value - old; - if ( this.values !== null ) this.updateFromTo(); + if (this.values !== null) this.updateFromTo(); - if ( this.yoyo !== null ) this.updateYoyo(); - else if ( this.progress <= 1 ) this.onUpdate( this ); + if (this.yoyo !== null) this.updateYoyo(); + else if (this.progress <= 1) this.onUpdate(this); else { this.progress = 1; this.value = 1; - this.onUpdate( this ); - this.onComplete( this ); - super.stop(); + this.onUpdate(this); + this.onComplete(this); + super.stop(); } @@ -1037,30 +1071,30 @@ class Tween extends Animation { updateYoyo() { - if ( this.progress > 1 || this.progress < 0 ) { + if (this.progress > 1 || this.progress < 0) { - this.value = this.progress = ( this.progress > 1 ) ? 1 : 0; - this.yoyo = ! this.yoyo; + this.value = this.progress = (this.progress > 1) ? 1 : 0; + this.yoyo = !this.yoyo; } - this.onUpdate( this ); + this.onUpdate(this); } updateFromTo() { - this.values.forEach( key => { + this.values.forEach(key => { - this.target[ key ] = this.from[ key ] + ( this.to[ key ] - this.from[ key ] ) * this.value; + this.target[key] = this.from[key] + (this.to[key] - this.from[key]) * this.value; - } ); + }); } - getFromTo( options ) { + getFromTo(options) { - if ( ! options.target || ! options.to ) { + if (!options.target || !options.to) { this.values = null; return; @@ -1072,21 +1106,21 @@ class Tween extends Animation { this.to = options.to || null; this.values = []; - if ( Object.keys( this.from ).length < 1 ) - Object.keys( this.to ).forEach( key => { this.from[ key ] = this.target[ key ]; } ); + if (Object.keys(this.from).length < 1) + Object.keys(this.to).forEach(key => { this.from[key] = this.target[key]; }); - Object.keys( this.to ).forEach( key => { this.values.push( key ); } ); + Object.keys(this.to).forEach(key => { this.values.push(key); }); } } -window.addEventListener( 'touchmove', () => {} ); -document.addEventListener( 'touchmove', event => { event.preventDefault(); }, { passive: false } ); +window.addEventListener('touchmove', () => { }); +document.addEventListener('touchmove', event => { event.preventDefault(); }, { passive: false }); class Draggable { - constructor( element, options ) { + constructor(element, options) { this.position = { current: new THREE.Vector2(), @@ -1096,76 +1130,76 @@ class Draggable { drag: new THREE.Vector2(), }; - this.options = Object.assign( { + this.options = Object.assign({ calcDelta: false, - }, options || {} ); + }, options || {}); this.element = element; this.touch = null; this.drag = { - start: ( event ) => { + start: (event) => { - if ( event.type == 'mousedown' && event.which != 1 ) return; - if ( event.type == 'touchstart' && event.touches.length > 1 ) return; + if (event.type == 'mousedown' && !(event.which == 1 || event.which == 3)) return; + if (event.type == 'touchstart' && event.touches.length > 1) return; - this.getPositionCurrent( event ); + this.getPositionCurrent(event); - if ( this.options.calcDelta ) { + if (this.options.calcDelta) { this.position.start = this.position.current.clone(); - this.position.delta.set( 0, 0 ); - this.position.drag.set( 0, 0 ); + this.position.delta.set(0, 0); + this.position.drag.set(0, 0); } - this.touch = ( event.type == 'touchstart' ); + this.touch = (event.type == 'touchstart'); - this.onDragStart( this.position ); - - window.addEventListener( ( this.touch ) ? 'touchmove' : 'mousemove', this.drag.move, false ); - window.addEventListener( ( this.touch ) ? 'touchend' : 'mouseup', this.drag.end, false ); + this.onDragStart(this.position); + window.addEventListener((this.touch) ? 'touchmove' : 'mousemove', this.drag.move, false); + window.addEventListener((this.touch) ? 'touchend' : 'mouseup', this.drag.end, false); + window.addEventListener("contextmenu", (event) => { event.preventDefault() }); }, - move: ( event ) => { + move: (event) => { - if ( this.options.calcDelta ) { + if (this.options.calcDelta) { this.position.old = this.position.current.clone(); } - this.getPositionCurrent( event ); + this.getPositionCurrent(event); - if ( this.options.calcDelta ) { + if (this.options.calcDelta) { - this.position.delta = this.position.current.clone().sub( this.position.old ); - this.position.drag = this.position.current.clone().sub( this.position.start ); + this.position.delta = this.position.current.clone().sub(this.position.old); + this.position.drag = this.position.current.clone().sub(this.position.start); } - this.onDragMove( this.position ); + this.onDragMove(this.position); }, - end: ( event ) => { + end: (event) => { - this.getPositionCurrent( event ); + this.getPositionCurrent(event); - this.onDragEnd( this.position ); + this.onDragEnd(this.position); - window.removeEventListener( ( this.touch ) ? 'touchmove' : 'mousemove', this.drag.move, false ); - window.removeEventListener( ( this.touch ) ? 'touchend' : 'mouseup', this.drag.end, false ); + window.removeEventListener((this.touch) ? 'touchmove' : 'mousemove', this.drag.move, false); + window.removeEventListener((this.touch) ? 'touchend' : 'mouseup', this.drag.end, false); }, }; - this.onDragStart = () => {}; - this.onDragMove = () => {}; - this.onDragEnd = () => {}; + this.onDragStart = () => { }; + this.onDragMove = () => { }; + this.onDragEnd = () => { }; this.enable(); @@ -1175,8 +1209,8 @@ class Draggable { enable() { - this.element.addEventListener( 'touchstart', this.drag.start, false ); - this.element.addEventListener( 'mousedown', this.drag.start, false ); + this.element.addEventListener('touchstart', this.drag.start, false); + this.element.addEventListener('mousedown', this.drag.start, false); return this; @@ -1184,27 +1218,27 @@ class Draggable { disable() { - this.element.removeEventListener( 'touchstart', this.drag.start, false ); - this.element.removeEventListener( 'mousedown', this.drag.start, false ); + this.element.removeEventListener('touchstart', this.drag.start, false); + this.element.removeEventListener('mousedown', this.drag.start, false); return this; } - getPositionCurrent( event ) { + getPositionCurrent(event) { const dragEvent = event.touches - ? ( event.touches[ 0 ] || event.changedTouches[ 0 ] ) + ? (event.touches[0] || event.changedTouches[0]) : event; - this.position.current.set( dragEvent.pageX, dragEvent.pageY ); + this.position.current.set(dragEvent.pageX, dragEvent.pageY); } - convertPosition( position ) { + convertPosition(position) { - position.x = ( position.x / this.element.offsetWidth ) * 2 - 1; - position.y = - ( ( position.y / this.element.offsetHeight ) * 2 - 1 ); + position.x = (position.x / this.element.offsetWidth) * 2 - 1; + position.y = - ((position.y / this.element.offsetHeight) * 2 - 1); return position; @@ -1219,40 +1253,40 @@ const ANIMATING = 3; class Controls { - constructor( game ) { + constructor(game) { this.game = game; this.flipConfig = 0; - this.flipEasings = [ Easing.Power.Out( 3 ), Easing.Sine.Out(), Easing.Back.Out( 1.5 ) ]; - this.flipSpeeds = [ 125, 200, 300 ]; + this.flipEasings = [Easing.Power.Out(3), Easing.Sine.Out(), Easing.Back.Out(1.5)]; + this.flipSpeeds = [125, 200, 300]; this.raycaster = new THREE.Raycaster(); - const helperMaterial = new THREE.MeshBasicMaterial( { depthWrite: false, transparent: true, opacity: 0, color: 0x0033ff } ); + const helperMaterial = new THREE.MeshBasicMaterial({ depthWrite: false, transparent: true, opacity: 0, color: 0x0033ff }); this.group = new THREE.Object3D(); this.group.name = 'controls'; - this.game.cube.object.add( this.group ); + this.game.cube.object.add(this.group); this.helper = new THREE.Mesh( - new THREE.PlaneBufferGeometry( 200, 200 ), + new THREE.PlaneBufferGeometry(200, 200), helperMaterial.clone() ); - this.helper.rotation.set( 0, Math.PI / 4, 0 ); - this.game.world.scene.add( this.helper ); + this.helper.rotation.set(0, Math.PI / 4, 0); + this.game.world.scene.add(this.helper); this.edges = new THREE.Mesh( - new THREE.BoxBufferGeometry( 1, 1, 1 ), + new THREE.BoxBufferGeometry(1, 1, 1), helperMaterial.clone(), ); - this.game.world.scene.add( this.edges ); + this.game.world.scene.add(this.edges); - this.onSolved = () => {}; - this.onMove = () => {}; + this.onSolved = () => { }; + this.onMove = () => { }; this.momentum = []; @@ -1280,128 +1314,126 @@ class Controls { initDraggable() { - this.draggable = new Draggable( this.game.dom.game ); + this.draggable = new Draggable(this.game.dom.game); this.draggable.onDragStart = position => { - if ( this.scramble !== null ) return; - if ( this.state === PREPARING || this.state === ROTATING ) return; + if (this.scramble !== null) return; + if (this.state === PREPARING || this.state === ROTATING) return; this.gettingDrag = this.state === ANIMATING; - const edgeIntersect = this.getIntersect( position.current, this.edges, false ); + const edgeIntersect = this.getIntersect(position.current, this.edges, false); - if ( edgeIntersect !== false ) { + if (edgeIntersect !== false) { - this.dragIntersect = this.getIntersect( position.current, this.game.cube.cubes, true ); + this.dragIntersect = this.getIntersect(position.current, this.game.cube.cubes, true); } - if ( edgeIntersect !== false && this.dragIntersect !== false ) { + if (edgeIntersect !== false && this.dragIntersect !== false && event.which != 3) { this.dragNormal = edgeIntersect.face.normal.round(); this.flipType = 'layer'; - this.attach( this.helper, this.edges ); + this.attach(this.helper, this.edges); - this.helper.rotation.set( 0, 0, 0 ); - this.helper.position.set( 0, 0, 0 ); - this.helper.lookAt( this.dragNormal ); - this.helper.translateZ( 0.5 ); + this.helper.rotation.set(0, 0, 0); + this.helper.position.set(0, 0, 0); + this.helper.lookAt(this.dragNormal); + this.helper.translateZ(0.5); this.helper.updateMatrixWorld(); - this.detach( this.helper, this.edges ); + this.detach(this.helper, this.edges); } else { - this.dragNormal = new THREE.Vector3( 0, 0, 1 ); + this.dragNormal = new THREE.Vector3(0, 0, 1); this.flipType = 'cube'; - this.helper.position.set( 0, 0, 0 ); - this.helper.rotation.set( 0, Math.PI / 4, 0 ); + this.helper.position.set(0, 0, 0); + this.helper.rotation.set(0, Math.PI / 4, 0); this.helper.updateMatrixWorld(); } - let planeIntersect = this.getIntersect( position.current, this.helper, false ); - if ( planeIntersect === false ) return; + let planeIntersect = this.getIntersect(position.current, this.helper, false); + if (planeIntersect === false) return; - this.dragCurrent = this.helper.worldToLocal( planeIntersect.point ); + this.dragCurrent = this.helper.worldToLocal(planeIntersect.point); this.dragTotal = new THREE.Vector3(); - this.state = ( this.state === STILL ) ? PREPARING : this.state; + this.state = (this.state === STILL) ? PREPARING : this.state; }; this.draggable.onDragMove = position => { - if ( this.scramble !== null ) return; - if ( this.state === STILL || ( this.state === ANIMATING && this.gettingDrag === false ) ) return; + if (this.scramble !== null) return; + if (this.state === STILL || (this.state === ANIMATING && this.gettingDrag === false)) return; - const planeIntersect = this.getIntersect( position.current, this.helper, false ); - if ( planeIntersect === false ) return; + const planeIntersect = this.getIntersect(position.current, this.helper, false); + if (planeIntersect === false) return; - const point = this.helper.worldToLocal( planeIntersect.point.clone() ); + const point = this.helper.worldToLocal(planeIntersect.point.clone()); - this.dragDelta = point.clone().sub( this.dragCurrent ).setZ( 0 ); - this.dragTotal.add( this.dragDelta ); + this.dragDelta = point.clone().sub(this.dragCurrent).setZ(0); + this.dragTotal.add(this.dragDelta); this.dragCurrent = point; - this.addMomentumPoint( this.dragDelta ); + this.addMomentumPoint(this.dragDelta); - if ( this.state === PREPARING && this.dragTotal.length() > 0.05 ) { + if (this.state === PREPARING && this.dragTotal.length() > 0.05) { - this.dragDirection = this.getMainAxis( this.dragTotal ); + this.dragDirection = this.getMainAxis(this.dragTotal); - if ( this.flipType === 'layer' ) { + if (this.flipType === 'layer') { const direction = new THREE.Vector3(); - direction[ this.dragDirection ] = 1; + direction[this.dragDirection] = 1; - const worldDirection = this.helper.localToWorld( direction ).sub( this.helper.position ); - const objectDirection = this.edges.worldToLocal( worldDirection ).round(); + const worldDirection = this.helper.localToWorld(direction).sub(this.helper.position); + const objectDirection = this.edges.worldToLocal(worldDirection).round(); - this.flipAxis = objectDirection.cross( this.dragNormal ).negate(); + this.flipAxis = objectDirection.cross(this.dragNormal).negate(); - this.selectLayer( this.getLayer( false ) ); + this.selectLayer(this.getLayer(false)); } else { - const axis = ( this.dragDirection != 'x' ) - ? ( ( this.dragDirection == 'y' && position.current.x > this.game.world.width / 2 ) ? 'z' : 'x' ) + const axis = (this.dragDirection != 'x') + ? ((this.dragDirection == 'y' && position.current.x > this.game.world.width / 2) ? 'z' : 'x') : 'y'; this.flipAxis = new THREE.Vector3(); - this.flipAxis[ axis ] = 1 * ( ( axis == 'x' ) ? - 1 : 1 ); + this.flipAxis[axis] = 1 * ((axis == 'x') ? - 1 : 1); } this.flipAngle = 0; this.state = ROTATING; - } else if ( this.state === ROTATING ) { + } else if (this.state === ROTATING) { - const rotation = this.dragDelta[ this.dragDirection ]; + const rotation = this.dragDelta[this.dragDirection]; - if ( this.flipType === 'layer' ) { + if (this.flipType === 'layer') { - this.group.rotateOnAxis( this.flipAxis, rotation ); + this.group.rotateOnAxis(this.flipAxis, rotation); this.flipAngle += rotation; } else { - this.edges.rotateOnWorldAxis( this.flipAxis, rotation ); - this.game.cube.object.rotation.copy( this.edges.rotation ); + this.edges.rotateOnWorldAxis(this.flipAxis, rotation); + this.game.cube.object.rotation.copy(this.edges.rotation); this.flipAngle += rotation; - } - } }; this.draggable.onDragEnd = position => { - if ( this.scramble !== null ) return; - if ( this.state !== ROTATING ) { + if (this.scramble !== null) return; + if (this.state !== ROTATING) { this.gettingDrag = false; this.state = STILL; @@ -1411,36 +1443,36 @@ class Controls { this.state = ANIMATING; - const momentum = this.getMomentum()[ this.dragDirection ]; - const flip = ( Math.abs( momentum ) > 0.05 && Math.abs( this.flipAngle ) < Math.PI / 2 ); + const momentum = this.getMomentum()[this.dragDirection]; + const flip = (Math.abs(momentum) > 0.05 && Math.abs(this.flipAngle) < Math.PI / 2); const angle = flip - ? this.roundAngle( this.flipAngle + Math.sign( this.flipAngle ) * ( Math.PI / 4 ) ) - : this.roundAngle( this.flipAngle ); + ? this.roundAngle(this.flipAngle + Math.sign(this.flipAngle) * (Math.PI / 4)) + : this.roundAngle(this.flipAngle); const delta = angle - this.flipAngle; - if ( this.flipType === 'layer' ) { + if (this.flipType === 'layer') { - this.rotateLayer( delta, false, layer => { + this.rotateLayer(delta, false, layer => { this.game.storage.saveGame(); - + this.state = this.gettingDrag ? PREPARING : STILL; this.gettingDrag = false; this.checkIsSolved(); - } ); + }); } else { - this.rotateCube( delta, () => { + this.rotateCube(delta, () => { this.state = this.gettingDrag ? PREPARING : STILL; this.gettingDrag = false; - } ); + }); } @@ -1448,38 +1480,38 @@ class Controls { } - rotateLayer( rotation, scramble, callback ) { + rotateLayer(rotation, scramble, callback) { const config = scramble ? 0 : this.flipConfig; - const easing = this.flipEasings[ config ]; - const duration = this.flipSpeeds[ config ]; - const bounce = ( config == 2 ) ? this.bounceCube() : ( () => {} ); + const easing = this.flipEasings[config]; + const duration = this.flipSpeeds[config]; + const bounce = (config == 2) ? this.bounceCube() : (() => { }); - this.rotationTween = new Tween( { + this.rotationTween = new Tween({ easing: easing, duration: duration, onUpdate: tween => { let deltaAngle = tween.delta * rotation; - this.group.rotateOnAxis( this.flipAxis, deltaAngle ); - bounce( tween.value, deltaAngle, rotation ); + this.group.rotateOnAxis(this.flipAxis, deltaAngle); + bounce(tween.value, deltaAngle, rotation); }, onComplete: () => { - if ( ! scramble ) this.onMove(); + if (!scramble) this.onMove(); - const layer = this.flipLayer.slice( 0 ); + const layer = this.flipLayer.slice(0); - this.game.cube.object.rotation.setFromVector3( this.snapRotation( this.game.cube.object.rotation.toVector3() ) ); - this.group.rotation.setFromVector3( this.snapRotation( this.group.rotation.toVector3() ) ); - this.deselectLayer( this.flipLayer ); + this.game.cube.object.rotation.setFromVector3(this.snapRotation(this.game.cube.object.rotation.toVector3())); + this.group.rotation.setFromVector3(this.snapRotation(this.group.rotation.toVector3())); + this.deselectLayer(this.flipLayer); - callback( layer ); + callback(layer); }, - } ); + }); } @@ -1487,149 +1519,149 @@ class Controls { let fixDelta = true; - return ( progress, delta, rotation ) => { + return (progress, delta, rotation) => { - if ( progress >= 1 ) { + if (progress >= 1) { - if ( fixDelta ) { + if (fixDelta) { - delta = ( progress - 1 ) * rotation; - fixDelta = false; + delta = (progress - 1) * rotation; + fixDelta = false; - } + } - this.game.cube.object.rotateOnAxis( this.flipAxis, delta ); + this.game.cube.object.rotateOnAxis(this.flipAxis, delta); - } + } } } - rotateCube( rotation, callback ) { + rotateCube(rotation, callback) { const config = this.flipConfig; - const easing = [ Easing.Power.Out( 4 ), Easing.Sine.Out(), Easing.Back.Out( 2 ) ][ config ]; - const duration = [ 100, 150, 350 ][ config ]; + const easing = [Easing.Power.Out(4), Easing.Sine.Out(), Easing.Back.Out(2)][config]; + const duration = [100, 150, 350][config]; - this.rotationTween = new Tween( { + this.rotationTween = new Tween({ easing: easing, duration: duration, onUpdate: tween => { - this.edges.rotateOnWorldAxis( this.flipAxis, tween.delta * rotation ); - this.game.cube.object.rotation.copy( this.edges.rotation ); + this.edges.rotateOnWorldAxis(this.flipAxis, tween.delta * rotation); + this.game.cube.object.rotation.copy(this.edges.rotation); }, onComplete: () => { - this.edges.rotation.setFromVector3( this.snapRotation( this.edges.rotation.toVector3() ) ); - this.game.cube.object.rotation.copy( this.edges.rotation ); + this.edges.rotation.setFromVector3(this.snapRotation(this.edges.rotation.toVector3())); + this.game.cube.object.rotation.copy(this.edges.rotation); callback(); }, - } ); + }); } - selectLayer( layer ) { + selectLayer(layer) { - this.group.rotation.set( 0, 0, 0 ); - this.movePieces( layer, this.game.cube.object, this.group ); + this.group.rotation.set(0, 0, 0); + this.movePieces(layer, this.game.cube.object, this.group); this.flipLayer = layer; } - deselectLayer( layer ) { + deselectLayer(layer) { - this.movePieces( layer, this.group, this.game.cube.object ); + this.movePieces(layer, this.group, this.game.cube.object); this.flipLayer = null; } - movePieces( layer, from, to ) { + movePieces(layer, from, to) { from.updateMatrixWorld(); to.updateMatrixWorld(); - layer.forEach( index => { + layer.forEach(index => { - const piece = this.game.cube.pieces[ index ]; + const piece = this.game.cube.pieces[index]; - piece.applyMatrix( from.matrixWorld ); - from.remove( piece ); - piece.applyMatrix( new THREE.Matrix4().getInverse( to.matrixWorld ) ); - to.add( piece ); + piece.applyMatrix(from.matrixWorld); + from.remove(piece); + piece.applyMatrix(new THREE.Matrix4().getInverse(to.matrixWorld)); + to.add(piece); - } ); + }); } - getLayer( position ) { + getLayer(position) { - const scalar = { 2: 6, 3: 3, 4: 4, 5: 3 }[ this.game.cube.size ]; + const scalar = { 2: 6, 3: 3, 4: 4, 5: 3 }[this.game.cube.size]; const layer = []; let axis; - if ( position === false ) { + if (position === false) { const piece = this.dragIntersect.object.parent; - axis = this.getMainAxis( this.flipAxis ); - position = piece.position.clone() .multiplyScalar( scalar ) .round(); + axis = this.getMainAxis(this.flipAxis); + position = piece.position.clone().multiplyScalar(scalar).round(); } else { - axis = this.getMainAxis( position ); + axis = this.getMainAxis(position); } - this.game.cube.pieces.forEach( piece => { + this.game.cube.pieces.forEach(piece => { - const piecePosition = piece.position.clone().multiplyScalar( scalar ).round(); + const piecePosition = piece.position.clone().multiplyScalar(scalar).round(); - if ( piecePosition[ axis ] == position[ axis ] ) layer.push( piece.name ); + if (piecePosition[axis] == position[axis]) layer.push(piece.name); - } ); + }); return layer; } - keyboardMove( type, move, callback ) { + keyboardMove(type, move, callback) { - if ( this.state !== STILL ) return; - if ( this.enabled !== true ) return; + if (this.state !== STILL) return; + if (this.enabled !== true) return; - if ( type === 'LAYER' ) { + if (type === 'LAYER') { - const layer = this.getLayer( move.position ); + const layer = this.getLayer(move.position); this.flipAxis = new THREE.Vector3(); - this.flipAxis[ move.axis ] = 1; + this.flipAxis[move.axis] = 1; this.state = ROTATING; - this.selectLayer( layer ); - this.rotateLayer( move.angle, false, layer => { + this.selectLayer(layer); + this.rotateLayer(move.angle, false, layer => { this.game.storage.saveGame(); this.state = STILL; this.checkIsSolved(); - } ); + }); - } else if ( type === 'CUBE' ) { + } else if (type === 'CUBE') { this.flipAxis = new THREE.Vector3(); - this.flipAxis[ move.axis ] = 1; + this.flipAxis[move.axis] = 1; this.state = ROTATING; - this.rotateCube( move.angle, () => { + this.rotateCube(move.angle, () => { this.state = STILL; - } ); + }); } @@ -1637,26 +1669,26 @@ class Controls { scrambleCube() { - if ( this.scramble == null ) { + if (this.scramble == null) { this.scramble = this.game.scrambler; - this.scramble.callback = ( typeof callback !== 'function' ) ? () => {} : callback; + this.scramble.callback = (typeof callback !== 'function') ? () => { } : callback; } const converted = this.scramble.converted; - const move = converted[ 0 ]; - const layer = this.getLayer( move.position ); + const move = converted[0]; + const layer = this.getLayer(move.position); this.flipAxis = new THREE.Vector3(); - this.flipAxis[ move.axis ] = 1; + this.flipAxis[move.axis] = 1; - this.selectLayer( layer ); - this.rotateLayer( move.angle, true, () => { + this.selectLayer(layer); + this.rotateLayer(move.angle, true, () => { converted.shift(); - if ( converted.length > 0 ) { + if (converted.length > 0) { this.scrambleCube(); @@ -1667,56 +1699,56 @@ class Controls { } - } ); + }); } - getIntersect( position, object, multiple ) { + getIntersect(position, object, multiple) { this.raycaster.setFromCamera( - this.draggable.convertPosition( position.clone() ), + this.draggable.convertPosition(position.clone()), this.game.world.camera ); - const intersect = ( multiple ) - ? this.raycaster.intersectObjects( object ) - : this.raycaster.intersectObject( object ); + const intersect = (multiple) + ? this.raycaster.intersectObjects(object) + : this.raycaster.intersectObject(object); - return ( intersect.length > 0 ) ? intersect[ 0 ] : false; + return (intersect.length > 0) ? intersect[0] : false; } - getMainAxis( vector ) { + getMainAxis(vector) { - return Object.keys( vector ).reduce( - ( a, b ) => Math.abs( vector[ a ] ) > Math.abs( vector[ b ] ) ? a : b + return Object.keys(vector).reduce( + (a, b) => Math.abs(vector[a]) > Math.abs(vector[b]) ? a : b ); } - detach( child, parent ) { + detach(child, parent) { - child.applyMatrix( parent.matrixWorld ); - parent.remove( child ); - this.game.world.scene.add( child ); + child.applyMatrix(parent.matrixWorld); + parent.remove(child); + this.game.world.scene.add(child); } - attach( child, parent ) { + attach(child, parent) { - child.applyMatrix( new THREE.Matrix4().getInverse( parent.matrixWorld ) ); - this.game.world.scene.remove( child ); - parent.add( child ); + child.applyMatrix(new THREE.Matrix4().getInverse(parent.matrixWorld)); + this.game.world.scene.remove(child); + parent.add(child); } - addMomentumPoint( delta ) { + addMomentumPoint(delta) { const time = Date.now(); - this.momentum = this.momentum.filter( moment => time - moment.time < 500 ); + this.momentum = this.momentum.filter(moment => time - moment.time < 500); - if ( delta !== false ) this.momentum.push( { delta, time } ); + if (delta !== false) this.momentum.push({ delta, time }); } @@ -1725,31 +1757,31 @@ class Controls { const points = this.momentum.length; const momentum = new THREE.Vector2(); - this.addMomentumPoint( false ); + this.addMomentumPoint(false); - this.momentum.forEach( ( point, index ) => { + this.momentum.forEach((point, index) => { - momentum.add( point.delta.multiplyScalar( index / points ) ); + momentum.add(point.delta.multiplyScalar(index / points)); - } ); + }); return momentum; } - roundAngle( angle ) { + roundAngle(angle) { const round = Math.PI / 2; - return Math.sign( angle ) * Math.round( Math.abs( angle) / round ) * round; + return Math.sign(angle) * Math.round(Math.abs(angle) / round) * round; } - snapRotation( angle ) { + snapRotation(angle) { return angle.set( - this.roundAngle( angle.x ), - this.roundAngle( angle.y ), - this.roundAngle( angle.z ) + this.roundAngle(angle.x), + this.roundAngle(angle.y), + this.roundAngle(angle.z) ); } @@ -1761,26 +1793,26 @@ class Controls { let solved = true; const sides = { 'x-': [], 'x+': [], 'y-': [], 'y+': [], 'z-': [], 'z+': [] }; - this.game.cube.edges.forEach( edge => { + this.game.cube.edges.forEach(edge => { const position = edge.parent - .localToWorld( edge.position.clone() ) - .sub( this.game.cube.object.position ); + .localToWorld(edge.position.clone()) + .sub(this.game.cube.object.position); - const mainAxis = this.getMainAxis( position ); - const mainSign = position.multiplyScalar( 2 ).round()[ mainAxis ] < 1 ? '-' : '+'; + const mainAxis = this.getMainAxis(position); + const mainSign = position.multiplyScalar(2).round()[mainAxis] < 1 ? '-' : '+'; - sides[ mainAxis + mainSign ].push( edge.name ); + sides[mainAxis + mainSign].push(edge.name); - } ); + }); - Object.keys( sides ).forEach( side => { + Object.keys(sides).forEach(side => { - if ( ! sides[ side ].every( value => value === sides[ side ][ 0 ] ) ) solved = false; + if (!sides[side].every(value => value === sides[side][0])) solved = false; - } ); + }); - if ( solved ) this.onSolved(); + if (solved) this.onSolved(); } @@ -1788,17 +1820,17 @@ class Controls { class Scrambler { - constructor( game ) { + constructor(game) { this.game = game; this.dificulty = 0; this.scrambleLength = { - 2: [ 7, 9, 11 ], - 3: [ 20, 25, 30 ], - 4: [ 30, 40, 50 ], - 5: [ 40, 60, 80 ], + 2: [7, 9, 11], + 3: [20, 25, 30], + 4: [30, 40, 50], + 5: [40, 60, 80], }; this.moves = []; @@ -1807,73 +1839,73 @@ class Scrambler { } - scramble( scramble ) { + scramble(scramble) { let count = 0; - this.moves = ( typeof scramble !== 'undefined' ) ? scramble.split( ' ' ) : []; + this.moves = (typeof scramble !== 'undefined') ? scramble.split(' ') : []; - if ( this.moves.length < 1 ) { + if (this.moves.length < 1) { - const scrambleLength = this.scrambleLength[ this.game.cube.size ][ this.dificulty ]; + const scrambleLength = this.scrambleLength[this.game.cube.size][this.dificulty]; const faces = this.game.cube.size < 4 ? 'UDLRFB' : 'UuDdLlRrFfBb'; - const modifiers = [ "", "'", "2" ]; - const total = ( typeof scramble === 'undefined' ) ? scrambleLength : scramble; + const modifiers = ["", "'", "2"]; + const total = (typeof scramble === 'undefined') ? scrambleLength : scramble; - while ( count < total ) { + while (count < total) { const move = - faces[ Math.floor( Math.random() * faces.length ) ] + - modifiers[ Math.floor( Math.random() * 3 ) ]; + faces[Math.floor(Math.random() * faces.length)] + + modifiers[Math.floor(Math.random() * 3)]; - if ( count > 0 && move.charAt( 0 ) == this.moves[ count - 1 ].charAt( 0 ) ) continue; - if ( count > 1 && move.charAt( 0 ) == this.moves[ count - 2 ].charAt( 0 ) ) continue; + if (count > 0 && move.charAt(0) == this.moves[count - 1].charAt(0)) continue; + if (count > 1 && move.charAt(0) == this.moves[count - 2].charAt(0)) continue; - this.moves.push( move ); - count ++; + this.moves.push(move); + count++; } } - this.callback = () => {}; + this.callback = () => { }; this.convert(); - this.print = this.moves.join( ' ' ); + this.print = this.moves.join(' '); return this; } - convert( moves ) { + convert(moves) { this.converted = []; - this.moves.forEach( move => { + this.moves.forEach(move => { - const convertedMove = this.convertMove( move ); - const modifier = move.charAt( 1 ); + const convertedMove = this.convertMove(move); + const modifier = move.charAt(1); - this.converted.push( convertedMove ); - if ( modifier == "2" ) this.converted.push( convertedMove ); + this.converted.push(convertedMove); + if (modifier == "2") this.converted.push(convertedMove); - } ); + }); } - convertMove( move ) { + convertMove(move) { - const face = move.charAt( 0 ); - const modifier = move.charAt( 1 ); + const face = move.charAt(0); + const modifier = move.charAt(1); - const axis = { D: 'y', U: 'y', L: 'x', R: 'x', F: 'z', B: 'z' }[ face.toUpperCase() ]; - let row = { D: -1, U: 1, L: -1, R: 1, F: 1, B: -1 }[ face.toUpperCase() ]; + const axis = { D: 'y', U: 'y', L: 'x', R: 'x', F: 'z', B: 'z' }[face.toUpperCase()]; + let row = { D: -1, U: 1, L: -1, R: 1, F: 1, B: -1 }[face.toUpperCase()]; - if ( this.game.cube.size > 3 && face !== face.toLowerCase() ) row = row * 2; + if (this.game.cube.size > 3 && face !== face.toLowerCase()) row = row * 2; const position = new THREE.Vector3(); - position[ { D: 'y', U: 'y', L: 'x', R: 'x', F: 'z', B: 'z' }[ face.toUpperCase() ] ] = row; + position[{ D: 'y', U: 'y', L: 'x', R: 'x', F: 'z', B: 'z' }[face.toUpperCase()]] = row; - const angle = ( Math.PI / 2 ) * - row * ( ( modifier == "'" ) ? - 1 : 1 ); + const angle = (Math.PI / 2) * - row * ((modifier == "'") ? - 1 : 1); return { position, axis, angle, name: move }; @@ -1883,7 +1915,7 @@ class Scrambler { class Transition { - constructor( game ) { + constructor(game) { this.game = game; @@ -1919,14 +1951,14 @@ class Transition { } - buttons( show, hide ) { + buttons(show, hide) { - const buttonTween = ( button, show ) => { + const buttonTween = (button, show) => { - return new Tween( { + return new Tween({ target: button.style, duration: 300, - easing: show ? Easing.Power.Out( 2 ) : Easing.Power.In( 3 ), + easing: show ? Easing.Power.Out(2) : Easing.Power.In(3), from: { opacity: show ? 0 : 1 }, to: { opacity: show ? 1 : 0 }, onUpdate: tween => { @@ -1936,61 +1968,61 @@ class Transition { }, onComplete: () => button.style.pointerEvents = show ? 'all' : 'none' - } ); + }); }; - hide.forEach( button => - this.tweens.buttons[ button ] = buttonTween( this.game.dom.buttons[ button ], false ) + hide.forEach(button => + this.tweens.buttons[button] = buttonTween(this.game.dom.buttons[button], false) ); - setTimeout( () => show.forEach( button => { + setTimeout(() => show.forEach(button => { - this.tweens.buttons[ button ] = buttonTween( this.game.dom.buttons[ button ], true ); + this.tweens.buttons[button] = buttonTween(this.game.dom.buttons[button], true); - } ), hide ? 500 : 0 ); + }), hide ? 500 : 0); } - cube( show, theming = false ) { + cube(show, theming = false) { this.activeTransitions++; - try { this.tweens.cube.stop(); } catch(e) {} + try { this.tweens.cube.stop(); } catch (e) { } const currentY = this.game.cube.animator.position.y; const currentRotation = this.game.cube.animator.rotation.x; - this.tweens.cube = new Tween( { + this.tweens.cube = new Tween({ duration: show ? 3000 : 1250, - easing: show ? Easing.Elastic.Out( 0.8, 0.6 ) : Easing.Back.In( 1 ), + easing: show ? Easing.Elastic.Out(0.8, 0.6) : Easing.Back.In(1), onUpdate: tween => { this.game.cube.animator.position.y = show - ? ( theming ? 0.9 + ( 1 - tween.value ) * 3.5 : ( 1 - tween.value ) * 4 ) + ? (theming ? 0.9 + (1 - tween.value) * 3.5 : (1 - tween.value) * 4) : currentY + tween.value * 4; this.game.cube.animator.rotation.x = show - ? ( 1 - tween.value ) * Math.PI / 3 + ? (1 - tween.value) * Math.PI / 3 : currentRotation + tween.value * - Math.PI / 3; }, - } ); + }); - if ( theming ) { + if (theming) { - if ( show ) { + if (show) { this.game.world.camera.zoom = 0.75; this.game.world.camera.updateProjectionMatrix(); } else { - setTimeout( () => { + setTimeout(() => { this.game.world.camera.zoom = this.data.cameraZoom; this.game.world.camera.updateProjectionMatrix(); - }, 1500 ); + }, 1500); } @@ -1998,20 +2030,20 @@ class Transition { this.durations.cube = show ? 1500 : 1500; - setTimeout( () => this.activeTransitions--, this.durations.cube ); + setTimeout(() => this.activeTransitions--, this.durations.cube); } float() { - try { this.tweens.float.stop(); } catch(e) {} - this.tweens.float = new Tween( { + try { this.tweens.float.stop(); } catch (e) { } + this.tweens.float = new Tween({ duration: 1500, easing: Easing.Sine.InOut(), yoyo: true, onUpdate: tween => { - this.game.cube.holder.position.y = (- 0.02 + tween.value * 0.04); + this.game.cube.holder.position.y = (- 0.02 + tween.value * 0.04); this.game.cube.holder.rotation.x = 0.005 - tween.value * 0.01; this.game.cube.holder.rotation.z = - this.game.cube.holder.rotation.x; this.game.cube.holder.rotation.y = this.game.cube.holder.rotation.x; @@ -2020,198 +2052,198 @@ class Transition { this.game.cube.holder.position.y + this.game.cube.object.position.y; }, - } ); + }); } - zoom( play, time ) { + zoom(play, time) { this.activeTransitions++; - const zoom = ( play ) ? 1 : this.data.cameraZoom; - const duration = ( time > 0 ) ? Math.max( time, 1500 ) : 1500; - const rotations = ( time > 0 ) ? Math.round( duration / 1500 ) : 1; - const easing = Easing.Power.InOut( ( time > 0 ) ? 2 : 3 ); + const zoom = (play) ? 1 : this.data.cameraZoom; + const duration = (time > 0) ? Math.max(time, 1500) : 1500; + const rotations = (time > 0) ? Math.round(duration / 1500) : 1; + const easing = Easing.Power.InOut((time > 0) ? 2 : 3); - this.tweens.zoom = new Tween( { + this.tweens.zoom = new Tween({ target: this.game.world.camera, duration: duration, easing: easing, to: { zoom: zoom }, onUpdate: () => { this.game.world.camera.updateProjectionMatrix(); }, - } ); + }); - this.tweens.rotate = new Tween( { + this.tweens.rotate = new Tween({ target: this.game.cube.animator.rotation, duration: duration, easing: easing, to: { y: - Math.PI * 2 * rotations }, onComplete: () => { this.game.cube.animator.rotation.y = 0; }, - } ); + }); this.durations.zoom = duration; - setTimeout( () => this.activeTransitions--, this.durations.zoom ); + setTimeout(() => this.activeTransitions--, this.durations.zoom); } - elevate( complete ) { + elevate(complete) { this.activeTransitions++; - const cubeY = + const cubeY = - this.tweens.elevate = new Tween( { - target: this.game.cube.object.position, - duration: complete ? 1500 : 0, - easing: Easing.Power.InOut( 3 ), - to: { y: complete ? -0.05 : this.data.cubeY } - } ); + this.tweens.elevate = new Tween({ + target: this.game.cube.object.position, + duration: complete ? 1500 : 0, + easing: Easing.Power.InOut(3), + to: { y: complete ? -0.05 : this.data.cubeY } + }); this.durations.elevate = 1500; - setTimeout( () => this.activeTransitions--, this.durations.elevate ); + setTimeout(() => this.activeTransitions--, this.durations.elevate); } - complete( show, best ) { + complete(show, best) { this.activeTransitions++; const text = best ? this.game.dom.texts.best : this.game.dom.texts.complete; - if ( text.querySelector( 'span i' ) === null ) - text.querySelectorAll( 'span' ).forEach( span => this.splitLetters( span ) ); + if (text.querySelector('span i') === null) + text.querySelectorAll('span').forEach(span => this.splitLetters(span)); - const letters = text.querySelectorAll( '.icon, i' ); + const letters = text.querySelectorAll('.icon, i'); - this.flipLetters( best ? 'best' : 'complete', letters, show ); + this.flipLetters(best ? 'best' : 'complete', letters, show); text.style.opacity = 1; - const duration = this.durations[ best ? 'best' : 'complete' ]; + const duration = this.durations[best ? 'best' : 'complete']; - if ( ! show ) setTimeout( () => this.game.dom.texts.timer.style.transform = '', duration ); + if (!show) setTimeout(() => this.game.dom.texts.timer.style.transform = '', duration); - setTimeout( () => this.activeTransitions--, duration ); + setTimeout(() => this.activeTransitions--, duration); - } + } - stats( show ) { + stats(show) { - if ( show ) this.game.scores.calcStats(); + if (show) this.game.scores.calcStats(); this.activeTransitions++; - this.tweens.stats.forEach( tween => { tween.stop(); tween = null; } ); + this.tweens.stats.forEach(tween => { tween.stop(); tween = null; }); let tweenId = -1; - const stats = this.game.dom.stats.querySelectorAll( '.stats' ); - const easing = show ? Easing.Power.Out( 2 ) : Easing.Power.In( 3 ); + const stats = this.game.dom.stats.querySelectorAll('.stats'); + const easing = show ? Easing.Power.Out(2) : Easing.Power.In(3); - stats.forEach( ( stat, index ) => { + stats.forEach((stat, index) => { - const delay = index * ( show ? 80 : 60 ); + const delay = index * (show ? 80 : 60); - this.tweens.stats[ tweenId++ ] = new Tween( { + this.tweens.stats[tweenId++] = new Tween({ delay: delay, duration: 400, easing: easing, onUpdate: tween => { - const translate = show ? ( 1 - tween.value ) * 2 : tween.value; - const opacity = show ? tween.value : ( 1 - tween.value ); + const translate = show ? (1 - tween.value) * 2 : tween.value; + const opacity = show ? tween.value : (1 - tween.value); stat.style.transform = `translate3d(0, ${translate}em, 0)`; stat.style.opacity = opacity; } - } ); + }); - } ); + }); this.durations.stats = 0; - setTimeout( () => this.activeTransitions--, this.durations.stats ); + setTimeout(() => this.activeTransitions--, this.durations.stats); } - preferences( show ) { + preferences(show) { - this.ranges( this.game.dom.prefs.querySelectorAll( '.range' ), 'prefs', show ); + this.ranges(this.game.dom.prefs.querySelectorAll('.range'), 'prefs', show); } - theming( show ) { + theming(show) { - this.ranges( this.game.dom.theme.querySelectorAll( '.range' ), 'prefs', show ); + this.ranges(this.game.dom.theme.querySelectorAll('.range'), 'prefs', show); } - ranges( ranges, type, show ) { + ranges(ranges, type, show) { this.activeTransitions++; - this.tweens[ type ].forEach( tween => { tween.stop(); tween = null; } ); + this.tweens[type].forEach(tween => { tween.stop(); tween = null; }); const easing = show ? Easing.Power.Out(2) : Easing.Power.In(3); let tweenId = -1; let listMax = 0; - ranges.forEach( ( range, rangeIndex ) => { - - const label = range.querySelector( '.range__label' ); - const track = range.querySelector( '.range__track-line' ); - const handle = range.querySelector( '.range__handle' ); - const list = range.querySelectorAll( '.range__list div' ); + ranges.forEach((range, rangeIndex) => { + + const label = range.querySelector('.range__label'); + const track = range.querySelector('.range__track-line'); + const handle = range.querySelector('.range__handle'); + const list = range.querySelectorAll('.range__list div'); - const delay = rangeIndex * ( show ? 120 : 100 ); + const delay = rangeIndex * (show ? 120 : 100); label.style.opacity = show ? 0 : 1; track.style.opacity = show ? 0 : 1; handle.style.opacity = show ? 0 : 1; handle.style.pointerEvents = show ? 'all' : 'none'; - this.tweens[ type ][ tweenId++ ] = new Tween( { + this.tweens[type][tweenId++] = new Tween({ delay: show ? delay : delay, duration: 400, easing: easing, onUpdate: tween => { - const translate = show ? ( 1 - tween.value ) : tween.value; - const opacity = show ? tween.value : ( 1 - tween.value ); + const translate = show ? (1 - tween.value) : tween.value; + const opacity = show ? tween.value : (1 - tween.value); label.style.transform = `translate3d(0, ${translate}em, 0)`; label.style.opacity = opacity; } - } ); + }); - this.tweens[ type ][ tweenId++ ] = new Tween( { + this.tweens[type][tweenId++] = new Tween({ delay: show ? delay + 100 : delay, duration: 400, easing: easing, onUpdate: tween => { - const translate = show ? ( 1 - tween.value ) : tween.value; - const scale = show ? tween.value : ( 1 - tween.value ); + const translate = show ? (1 - tween.value) : tween.value; + const scale = show ? tween.value : (1 - tween.value); const opacity = scale; track.style.transform = `translate3d(0, ${translate}em, 0) scale3d(${scale}, 1, 1)`; track.style.opacity = opacity; } - } ); + }); - this.tweens[ type ][ tweenId++ ] = new Tween( { + this.tweens[type][tweenId++] = new Tween({ delay: show ? delay + 100 : delay, duration: 400, easing: easing, onUpdate: tween => { - const translate = show ? ( 1 - tween.value ) : tween.value; + const translate = show ? (1 - tween.value) : tween.value; const opacity = 1 - translate; const scale = 0.5 + opacity * 0.5; @@ -2219,74 +2251,74 @@ class Transition { handle.style.opacity = opacity; } - } ); + }); - list.forEach( ( listItem, labelIndex ) => { + list.forEach((listItem, labelIndex) => { listItem.style.opacity = show ? 0 : 1; - this.tweens[ type ][ tweenId++ ] = new Tween( { + this.tweens[type][tweenId++] = new Tween({ delay: show ? delay + 200 + labelIndex * 50 : delay, duration: 400, easing: easing, onUpdate: tween => { - const translate = show ? ( 1 - tween.value ) : tween.value; - const opacity = show ? tween.value : ( 1 - tween.value ); + const translate = show ? (1 - tween.value) : tween.value; + const opacity = show ? tween.value : (1 - tween.value); listItem.style.transform = `translate3d(0, ${translate}em, 0)`; listItem.style.opacity = opacity; } - } ); + }); - } ); + }); listMax = list.length > listMax ? list.length - 1 : listMax; range.style.opacity = 1; - } ); + }); - this.durations[ type ] = show - ? ( ( ranges.length - 1 ) * 100 ) + 200 + listMax * 50 + 400 - : ( ( ranges.length - 1 ) * 100 ) + 400; + this.durations[type] = show + ? ((ranges.length - 1) * 100) + 200 + listMax * 50 + 400 + : ((ranges.length - 1) * 100) + 400; - setTimeout( () => this.activeTransitions--, this.durations[ type ] ); + setTimeout(() => this.activeTransitions--, this.durations[type]); } - title( show ) { + title(show) { this.activeTransitions++; const title = this.game.dom.texts.title; - if ( title.querySelector( 'span i' ) === null ) - title.querySelectorAll( 'span' ).forEach( span => this.splitLetters( span ) ); + if (title.querySelector('span i') === null) + title.querySelectorAll('span').forEach(span => this.splitLetters(span)); - const letters = title.querySelectorAll( 'i' ); + const letters = title.querySelectorAll('i'); - this.flipLetters( 'title', letters, show ); + this.flipLetters('title', letters, show); title.style.opacity = 1; const note = this.game.dom.texts.note; - this.tweens.title[ letters.length ] = new Tween( { + this.tweens.title[letters.length] = new Tween({ target: note.style, easing: Easing.Sine.InOut(), duration: show ? 800 : 400, yoyo: show ? true : null, - from: { opacity: show ? 0 : ( parseFloat( getComputedStyle( note ).opacity ) ) }, + from: { opacity: show ? 0 : (parseFloat(getComputedStyle(note).opacity)) }, to: { opacity: show ? 1 : 0 }, - } ); + }); - setTimeout( () => this.activeTransitions--, this.durations.title ); + setTimeout(() => this.activeTransitions--, this.durations.title); } - timer( show ) { + timer(show) { this.activeTransitions++; @@ -2296,58 +2328,58 @@ class Transition { this.game.timer.convert(); this.game.timer.setText(); - this.splitLetters( timer ); - const letters = timer.querySelectorAll( 'i' ); - this.flipLetters( 'timer', letters, show ); + this.splitLetters(timer); + const letters = timer.querySelectorAll('i'); + this.flipLetters('timer', letters, show); timer.style.opacity = 1; - setTimeout( () => this.activeTransitions--, this.durations.timer ); + setTimeout(() => this.activeTransitions--, this.durations.timer); } - splitLetters( element ) { + splitLetters(element) { const text = element.innerHTML; element.innerHTML = ''; - text.split( '' ).forEach( letter => { + text.split('').forEach(letter => { - const i = document.createElement( 'i' ); + const i = document.createElement('i'); i.innerHTML = letter; - element.appendChild( i ); + element.appendChild(i); - } ); + }); } - flipLetters( type, letters, show ) { + flipLetters(type, letters, show) { - try { this.tweens[ type ].forEach( tween => tween.stop() ); } catch(e) {} - letters.forEach( ( letter, index ) => { + try { this.tweens[type].forEach(tween => tween.stop()); } catch (e) { } + letters.forEach((letter, index) => { letter.style.opacity = show ? 0 : 1; - this.tweens[ type ][ index ] = new Tween( { + this.tweens[type][index] = new Tween({ easing: Easing.Sine.Out(), duration: show ? 800 : 400, delay: index * 50, onUpdate: tween => { - const rotation = show ? ( 1 - tween.value ) * -80 : tween.value * 80; + const rotation = show ? (1 - tween.value) * -80 : tween.value * 80; letter.style.transform = `rotate3d(0, 1, 0, ${rotation}deg)`; - letter.style.opacity = show ? tween.value : ( 1 - tween.value ); + letter.style.opacity = show ? tween.value : (1 - tween.value); }, - } ); + }); - } ); + }); - this.durations[ type ] = ( letters.length - 1 ) * 50 + ( show ? 800 : 400 ); + this.durations[type] = (letters.length - 1) * 50 + (show ? 800 : 400); } @@ -2355,18 +2387,18 @@ class Transition { class Timer extends Animation { - constructor( game ) { + constructor(game) { - super( false ); + super(false); this.game = game; this.reset(); - + } - start( continueGame ) { + start(continueGame) { - this.startTime = continueGame ? ( Date.now() - this.deltaTime ) : Date.now(); + this.startTime = continueGame ? (Date.now() - this.deltaTime) : Date.now(); this.deltaTime = 0; this.converted = this.convert(); @@ -2403,9 +2435,9 @@ class Timer extends Animation { this.deltaTime = this.currentTime - this.startTime; this.convert(); - if ( this.converted != old ) { + if (this.converted != old) { - localStorage.setItem( 'theCube_time', this.deltaTime ); + localStorage.setItem('theCube_time', this.deltaTime); this.setText(); } @@ -2414,10 +2446,10 @@ class Timer extends Animation { convert() { - const seconds = parseInt( ( this.deltaTime / 1000 ) % 60 ); - const minutes = parseInt( ( this.deltaTime / ( 1000 * 60 ) ) ); + const seconds = parseInt((this.deltaTime / 1000) % 60); + const minutes = parseInt((this.deltaTime / (1000 * 60))); - this.converted = minutes + ':' + ( seconds < 10 ? '0' : '' ) + seconds; + this.converted = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; } @@ -2432,67 +2464,67 @@ class Timer extends Animation { const RangeHTML = [ '
', - '
', - '
', - '
', - '
', - '
', - '
', + '
', + '
', + '
', + '
', + '
', + '
', '
', -].join( '\n' ); +].join('\n'); -document.querySelectorAll( 'range' ).forEach( el => { +document.querySelectorAll('range').forEach(el => { - const temp = document.createElement( 'div' ); + const temp = document.createElement('div'); temp.innerHTML = RangeHTML; - const range = temp.querySelector( '.range' ); - const rangeLabel = range.querySelector( '.range__label' ); - const rangeList = range.querySelector( '.range__list' ); + const range = temp.querySelector('.range'); + const rangeLabel = range.querySelector('.range__label'); + const rangeList = range.querySelector('.range__list'); - range.setAttribute( 'name', el.getAttribute( 'name' ) ); - rangeLabel.innerHTML = el.getAttribute( 'title' ); + range.setAttribute('name', el.getAttribute('name')); + rangeLabel.innerHTML = el.getAttribute('title'); - if ( el.hasAttribute( 'color' ) ) { + if (el.hasAttribute('color')) { - range.classList.add( 'range--type-color' ); - range.classList.add( 'range--color-' + el.getAttribute( 'name' ) ); + range.classList.add('range--type-color'); + range.classList.add('range--color-' + el.getAttribute('name')); } - if ( el.hasAttribute( 'list' ) ) { + if (el.hasAttribute('list')) { - el.getAttribute( 'list' ).split( ',' ).forEach( listItemText => { + el.getAttribute('list').split(',').forEach(listItemText => { - const listItem = document.createElement( 'div' ); + const listItem = document.createElement('div'); listItem.innerHTML = listItemText; - rangeList.appendChild( listItem ); + rangeList.appendChild(listItem); - } ); + }); } - el.parentNode.replaceChild( range, el ); + el.parentNode.replaceChild(range, el); -} ); +}); class Range { - constructor( name, options ) { + constructor(name, options) { - options = Object.assign( { - range: [ 0, 1 ], + options = Object.assign({ + range: [0, 1], value: 0, step: 0, - onUpdate: () => {}, - onComplete: () => {}, - }, options || {} ); + onUpdate: () => { }, + onComplete: () => { }, + }, options || {}); - this.element = document.querySelector( '.range[name="' + name + '"]' ); - this.track = this.element.querySelector( '.range__track' ); - this.handle = this.element.querySelector( '.range__handle' ); - this.list = [].slice.call( this.element.querySelectorAll( '.range__list div' ) ); + this.element = document.querySelector('.range[name="' + name + '"]'); + this.track = this.element.querySelector('.range__track'); + this.handle = this.element.querySelector('.range__handle'); + this.list = [].slice.call(this.element.querySelectorAll('.range__list div')); this.value = options.value; this.min = options.range[0]; @@ -2502,15 +2534,15 @@ class Range { this.onUpdate = options.onUpdate; this.onComplete = options.onComplete; - this.setValue( this.value ); + this.setValue(this.value); this.initDraggable(); } - setValue( value ) { + setValue(value) { - this.value = this.round( this.limitValue( value ) ); + this.value = this.round(this.limitValue(value)); this.setHandlePosition(); } @@ -2519,77 +2551,77 @@ class Range { let current; - this.draggable = new Draggable( this.handle, { calcDelta: true } ); + this.draggable = new Draggable(this.handle, { calcDelta: true }); this.draggable.onDragStart = position => { - current = this.positionFromValue( this.value ); + current = this.positionFromValue(this.value); this.handle.style.left = current + 'px'; }; this.draggable.onDragMove = position => { - current = this.limitPosition( current + position.delta.x ); - this.value = this.round( this.valueFromPosition( current ) ); + current = this.limitPosition(current + position.delta.x); + this.value = this.round(this.valueFromPosition(current)); this.setHandlePosition(); - - this.onUpdate( this.value ); + + this.onUpdate(this.value); }; this.draggable.onDragEnd = position => { - this.onComplete( this.value ); + this.onComplete(this.value); }; } - round( value ) { + round(value) { - if ( this.step < 1 ) return value; + if (this.step < 1) return value; - return Math.round( ( value - this.min ) / this.step ) * this.step + this.min; + return Math.round((value - this.min) / this.step) * this.step + this.min; } - limitValue( value ) { + limitValue(value) { - const max = Math.max( this.max, this.min ); - const min = Math.min( this.max, this.min ); + const max = Math.max(this.max, this.min); + const min = Math.min(this.max, this.min); - return Math.min( Math.max( value, min ), max ); + return Math.min(Math.max(value, min), max); } - limitPosition( position ) { + limitPosition(position) { - return Math.min( Math.max( position, 0 ), this.track.offsetWidth ); + return Math.min(Math.max(position, 0), this.track.offsetWidth); } - percentsFromValue( value ) { + percentsFromValue(value) { - return ( value - this.min ) / ( this.max - this.min ); + return (value - this.min) / (this.max - this.min); } - valueFromPosition( position ) { + valueFromPosition(position) { - return this.min + ( this.max - this.min ) * ( position / this.track.offsetWidth ); + return this.min + (this.max - this.min) * (position / this.track.offsetWidth); } - positionFromValue( value ) { + positionFromValue(value) { - return this.percentsFromValue( value ) * this.track.offsetWidth; + return this.percentsFromValue(value) * this.track.offsetWidth; } setHandlePosition() { - this.handle.style.left = this.percentsFromValue( this.value ) * 100 + '%'; + this.handle.style.left = this.percentsFromValue(this.value) * 100 + '%'; } @@ -2597,7 +2629,7 @@ class Range { class Preferences { - constructor( game ) { + constructor(game) { this.game = game; @@ -2607,27 +2639,27 @@ class Preferences { this.ranges = { - size: new Range( 'size', { + size: new Range('size', { value: this.game.cube.size, - range: [ 2, 5 ], + range: [2, 5], step: 1, onUpdate: value => { this.game.cube.size = value; - this.game.preferences.ranges.scramble.list.forEach( ( item, i ) => { + this.game.preferences.ranges.scramble.list.forEach((item, i) => { - item.innerHTML = this.game.scrambler.scrambleLength[ this.game.cube.size ][ i ]; + item.innerHTML = this.game.scrambler.scrambleLength[this.game.cube.size][i]; - } ); + }); }, onComplete: () => this.game.storage.savePreferences(), - } ), + }), - flip: new Range( 'flip', { + flip: new Range('flip', { value: this.game.controls.flipConfig, - range: [ 0, 2 ], + range: [0, 2], step: 1, onUpdate: value => { @@ -2635,11 +2667,11 @@ class Preferences { }, onComplete: () => this.game.storage.savePreferences(), - } ), + }), - scramble: new Range( 'scramble', { + scramble: new Range('scramble', { value: this.game.scrambler.dificulty, - range: [ 0, 2 ], + range: [0, 2], step: 1, onUpdate: value => { @@ -2647,11 +2679,11 @@ class Preferences { }, onComplete: () => this.game.storage.savePreferences() - } ), + }), - fov: new Range( 'fov', { + fov: new Range('fov', { value: this.game.world.fov, - range: [ 2, 45 ], + range: [2, 45], onUpdate: value => { this.game.world.fov = value; @@ -2659,57 +2691,57 @@ class Preferences { }, onComplete: () => this.game.storage.savePreferences() - } ), + }), - theme: new Range( 'theme', { - value: { cube: 0, erno: 1, dust: 2, camo: 3, rain: 4 }[ this.game.themes.theme ], - range: [ 0, 4 ], + theme: new Range('theme', { + value: { cube: 0, erno: 1, dust: 2, camo: 3, rain: 4 }[this.game.themes.theme], + range: [0, 4], step: 1, onUpdate: value => { - const theme = [ 'cube', 'erno', 'dust', 'camo', 'rain' ][ value ]; - this.game.themes.setTheme( theme ); + const theme = ['cube', 'erno', 'dust', 'camo', 'rain'][value]; + this.game.themes.setTheme(theme); }, onComplete: () => this.game.storage.savePreferences() - } ), + }), - hue: new Range( 'hue', { + hue: new Range('hue', { value: 0, - range: [ 0, 360 ], + range: [0, 360], onUpdate: value => this.game.themeEditor.updateHSL(), onComplete: () => this.game.storage.savePreferences(), - } ), + }), - saturation: new Range( 'saturation', { + saturation: new Range('saturation', { value: 100, - range: [ 0, 100 ], + range: [0, 100], onUpdate: value => this.game.themeEditor.updateHSL(), onComplete: () => this.game.storage.savePreferences(), - } ), + }), - lightness: new Range( 'lightness', { + lightness: new Range('lightness', { value: 50, - range: [ 0, 100 ], + range: [0, 100], onUpdate: value => this.game.themeEditor.updateHSL(), onComplete: () => this.game.storage.savePreferences(), - } ), + }), }; - this.ranges.scramble.list.forEach( ( item, i ) => { + this.ranges.scramble.list.forEach((item, i) => { + + item.innerHTML = this.game.scrambler.scrambleLength[this.game.cube.size][i]; - item.innerHTML = this.game.scrambler.scrambleLength[ this.game.cube.size ][ i ]; + }); - } ); - } } class Confetti { - constructor( game ) { + constructor(game) { this.game = game; this.started = 0; @@ -2718,61 +2750,61 @@ class Confetti { speed: { min: 0.0011, max: 0.0022 }, revolution: { min: 0.01, max: 0.05 }, size: { min: 0.1, max: 0.15 }, - colors: [ 0x41aac8, 0x82ca38, 0xffef48, 0xef3923, 0xff8c0a ], + colors: [0x41aac8, 0x82ca38, 0xffef48, 0xef3923, 0xff8c0a], }; - this.geometry = new THREE.PlaneGeometry( 1, 1 ); - this.material = new THREE.MeshLambertMaterial( { side: THREE.DoubleSide } ); + this.geometry = new THREE.PlaneGeometry(1, 1); + this.material = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide }); this.holders = [ - new ConfettiStage( this.game, this, 1, 20 ), - new ConfettiStage( this.game, this, -1, 30 ), + new ConfettiStage(this.game, this, 1, 20), + new ConfettiStage(this.game, this, -1, 30), ]; } start() { - if ( this.started > 0 ) return; + if (this.started > 0) return; - this.holders.forEach( holder => { + this.holders.forEach(holder => { - this.game.world.scene.add( holder.holder ); + this.game.world.scene.add(holder.holder); holder.start(); - this.started ++; + this.started++; - } ); + }); } stop() { - if ( this.started == 0 ) return; + if (this.started == 0) return; - this.holders.forEach( holder => { + this.holders.forEach(holder => { - holder.stop( () => { + holder.stop(() => { - this.game.world.scene.remove( holder.holder ); - this.started --; + this.game.world.scene.remove(holder.holder); + this.started--; - } ); + }); - } ); + }); } - updateColors( colors ) { + updateColors(colors) { - this.holders.forEach( holder => { + this.holders.forEach(holder => { - holder.options.colors.forEach( ( color, index ) => { + holder.options.colors.forEach((color, index) => { - holder.options.colors[ index ] = colors[ [ 'D', 'F', 'R', 'B', 'L' ][ index ] ]; + holder.options.colors[index] = colors[['D', 'F', 'R', 'B', 'L'][index]]; - } ); + }); - } ); + }); } @@ -2780,9 +2812,9 @@ class Confetti { class ConfettiStage extends Animation { - constructor( game, parent, distance, count ) { + constructor(game, parent, distance, count) { - super( false ); + super(false); this.game = game; this.parent = parent; @@ -2793,14 +2825,14 @@ class ConfettiStage extends Animation { this.particles = []; this.holder = new THREE.Object3D(); - this.holder.rotation.copy( this.game.world.camera.rotation ); + this.holder.rotation.copy(this.game.world.camera.rotation); this.object = new THREE.Object3D(); - this.holder.add( this.object ); + this.holder.add(this.object); - this.resizeViewport = this.resizeViewport.bind( this ); - this.game.world.onResize.push( this.resizeViewport ); - this.resizeViewport(); + this.resizeViewport = this.resizeViewport.bind(this); + this.game.world.onResize.push(this.resizeViewport); + this.resizeViewport(); this.geometry = this.parent.geometry; this.material = this.parent.material; @@ -2808,7 +2840,7 @@ class ConfettiStage extends Animation { this.options = this.parent.options; let i = this.count; - while ( i-- ) this.particles.push( new Particle( this ) ); + while (i--) this.particles.push(new Particle(this)); } @@ -2818,13 +2850,13 @@ class ConfettiStage extends Animation { this.playing = true; let i = this.count; - while ( i-- ) this.particles[ i ].reset(); + while (i--) this.particles[i].reset(); super.start(); } - stop( callback ) { + stop(callback) { this.playing = false; this.completed = 0; @@ -2848,10 +2880,10 @@ class ConfettiStage extends Animation { let i = this.count; - while ( i-- ) - if ( ! this.particles[ i ].completed ) this.particles[ i ].update( delta ); + while (i--) + if (!this.particles[i].completed) this.particles[i].update(delta); - if ( ! this.playing && this.completed == this.count ) this.reset(); + if (!this.playing && this.completed == this.count) this.reset(); } @@ -2859,7 +2891,7 @@ class ConfettiStage extends Animation { const fovRad = this.game.world.camera.fov * THREE.Math.DEG2RAD; - this.height = 2 * Math.tan( fovRad / 2 ) * ( this.game.world.camera.position.length() - this.distanceFromCube ); + this.height = 2 * Math.tan(fovRad / 2) * (this.game.world.camera.position.length() - this.distanceFromCube); this.width = this.height * this.game.world.camera.aspect; const scale = 1 / this.game.transition.data.cameraZoom; @@ -2871,12 +2903,12 @@ class ConfettiStage extends Animation { this.object.position.y = this.height / 2; } - + } class Particle { - constructor( confetti ) { + constructor(confetti) { this.confetti = confetti; this.options = this.confetti.options; @@ -2884,49 +2916,49 @@ class Particle { this.velocity = new THREE.Vector3(); this.force = new THREE.Vector3(); - this.mesh = new THREE.Mesh( this.confetti.geometry, this.confetti.material.clone() ); - this.confetti.object.add( this.mesh ); + this.mesh = new THREE.Mesh(this.confetti.geometry, this.confetti.material.clone()); + this.confetti.object.add(this.mesh); - this.size = THREE.Math.randFloat( this.options.size.min, this.options.size.max ); - this.mesh.scale.set( this.size, this.size, this.size ); + this.size = THREE.Math.randFloat(this.options.size.min, this.options.size.max); + this.mesh.scale.set(this.size, this.size, this.size); return this; } - reset( randomHeight = true ) { + reset(randomHeight = true) { this.completed = false; - this.color = new THREE.Color( this.options.colors[ Math.floor( Math.random() * this.options.colors.length ) ] ); - this.mesh.material.color.set( this.color ); + this.color = new THREE.Color(this.options.colors[Math.floor(Math.random() * this.options.colors.length)]); + this.mesh.material.color.set(this.color); - this.speed = THREE.Math.randFloat( this.options.speed.min, this.options.speed.max ) * - 1; - this.mesh.position.x = THREE.Math.randFloat( - this.confetti.width / 2, this.confetti.width / 2 ); - this.mesh.position.y = ( randomHeight ) - ? THREE.Math.randFloat( this.size, this.confetti.height + this.size ) + this.speed = THREE.Math.randFloat(this.options.speed.min, this.options.speed.max) * - 1; + this.mesh.position.x = THREE.Math.randFloat(- this.confetti.width / 2, this.confetti.width / 2); + this.mesh.position.y = (randomHeight) + ? THREE.Math.randFloat(this.size, this.confetti.height + this.size) : this.size; - this.revolutionSpeed = THREE.Math.randFloat( this.options.revolution.min, this.options.revolution.max ); - this.revolutionAxis = [ 'x', 'y', 'z' ][ Math.floor( Math.random() * 3 ) ]; - this.mesh.rotation.set( Math.random() * Math.PI / 3, Math.random() * Math.PI / 3, Math.random() * Math.PI / 3 ); + this.revolutionSpeed = THREE.Math.randFloat(this.options.revolution.min, this.options.revolution.max); + this.revolutionAxis = ['x', 'y', 'z'][Math.floor(Math.random() * 3)]; + this.mesh.rotation.set(Math.random() * Math.PI / 3, Math.random() * Math.PI / 3, Math.random() * Math.PI / 3); } stop() { this.completed = true; - this.confetti.completed ++; + this.confetti.completed++; } - update( delta ) { + update(delta) { this.mesh.position.y += this.speed * delta; - this.mesh.rotation[ this.revolutionAxis ] += this.revolutionSpeed; + this.mesh.rotation[this.revolutionAxis] += this.revolutionSpeed; - if ( this.mesh.position.y < - this.confetti.height - this.size ) - ( this.confetti.playing ) ? this.reset( false ) : this.stop(); + if (this.mesh.position.y < - this.confetti.height - this.size) + (this.confetti.playing) ? this.reset(false) : this.stop(); } @@ -2934,7 +2966,7 @@ class Particle { class Scores { - constructor( game ) { + constructor(game) { this.game = game; @@ -2967,25 +2999,25 @@ class Scores { } - addScore( time ) { + addScore(time) { - const data = this.data[ this.game.cube.sizeGenerated ]; + const data = this.data[this.game.cube.sizeGenerated]; - data.scores.push( time ); + data.scores.push(time); data.solves++; - if ( data.scores.lenght > 100 ) data.scores.shift(); + if (data.scores.lenght > 100) data.scores.shift(); - let bestTime = false; + let bestTime = false; - if ( time < data.best || data.best === 0 ) { + if (time < data.best || data.best === 0) { data.best = time; bestTime = true; } - if ( time > data.worst ) data.worst = time; + if (time > data.worst) data.worst = time; this.game.storage.saveScores(); @@ -2996,44 +3028,44 @@ class Scores { calcStats() { const s = this.game.cube.sizeGenerated; - const data = this.data[ s ]; + const data = this.data[s]; - this.setStat( 'cube-size', `${s}x${s}x${s}` ); - this.setStat( 'total-solves', data.solves ); - this.setStat( 'best-time', this.convertTime( data.best ) ); - this.setStat( 'worst-time', this.convertTime( data.worst ) ); - this.setStat( 'average-5', this.getAverage( 5 ) ); - this.setStat( 'average-12', this.getAverage( 12 ) ); - this.setStat( 'average-25', this.getAverage( 25 ) ); + this.setStat('cube-size', `${s}x${s}x${s}`); + this.setStat('total-solves', data.solves); + this.setStat('best-time', this.convertTime(data.best)); + this.setStat('worst-time', this.convertTime(data.worst)); + this.setStat('average-5', this.getAverage(5)); + this.setStat('average-12', this.getAverage(12)); + this.setStat('average-25', this.getAverage(25)); } - setStat( name, value ) { + setStat(name, value) { - if ( value === 0 ) value = '-'; + if (value === 0) value = '-'; - this.game.dom.stats.querySelector( `.stats[name="${name}"] b` ).innerHTML = value; + this.game.dom.stats.querySelector(`.stats[name="${name}"] b`).innerHTML = value; } - getAverage( count ) { + getAverage(count) { - const data = this.data[ this.game.cube.sizeGenerated ]; + const data = this.data[this.game.cube.sizeGenerated]; - if ( data.scores.length < count ) return 0; + if (data.scores.length < count) return 0; - return this.convertTime( data.scores.slice( -count ).reduce( ( a, b ) => a + b, 0 ) / count ); + return this.convertTime(data.scores.slice(-count).reduce((a, b) => a + b, 0) / count); } - convertTime( time ) { + convertTime(time) { - if ( time <= 0 ) return 0; + if (time <= 0) return 0; - const seconds = parseInt( ( time / 1000 ) % 60 ); - const minutes = parseInt( ( time / ( 1000 * 60 ) ) ); + const seconds = parseInt((time / 1000) % 60); + const minutes = parseInt((time / (1000 * 60))); - return minutes + ':' + ( seconds < 10 ? '0' : '' ) + seconds; + return minutes + ':' + (seconds < 10 ? '0' : '') + seconds; } @@ -3041,18 +3073,18 @@ class Scores { class Storage { - constructor( game ) { + constructor(game) { this.game = game; - const userVersion = localStorage.getItem( 'theCube_version' ); + const userVersion = localStorage.getItem('theCube_version'); - if ( ! userVersion || userVersion !== window.gameVersion ) { + if (!userVersion || userVersion !== window.gameVersion) { this.clearGame(); this.clearPreferences(); this.migrateScores(); - localStorage.setItem( 'theCube_version', window.gameVersion ); + localStorage.setItem('theCube_version', window.gameVersion); } @@ -3069,23 +3101,23 @@ class Storage { try { - const gameInProgress = localStorage.getItem( 'theCube_playing' ) === 'true'; + const gameInProgress = localStorage.getItem('theCube_playing') === 'true'; - if ( ! gameInProgress ) throw new Error(); + if (!gameInProgress) throw new Error(); - const gameCubeData = JSON.parse( localStorage.getItem( 'theCube_savedState' ) ); - const gameTime = parseInt( localStorage.getItem( 'theCube_time' ) ); + const gameCubeData = JSON.parse(localStorage.getItem('theCube_savedState')); + const gameTime = parseInt(localStorage.getItem('theCube_time')); - if ( ! gameCubeData || gameTime === null ) throw new Error(); - if ( gameCubeData.size !== this.game.cube.sizeGenerated ) throw new Error(); + if (!gameCubeData || gameTime === null) throw new Error(); + if (gameCubeData.size !== this.game.cube.sizeGenerated) throw new Error(); - this.game.cube.loadFromData( gameCubeData ); + this.game.cube.loadFromData(gameCubeData); this.game.timer.deltaTime = gameTime; this.game.saved = true; - } catch( e ) { + } catch (e) { this.game.saved = false; @@ -3101,25 +3133,25 @@ class Storage { gameCubeData.size = this.game.cube.sizeGenerated; - this.game.cube.pieces.forEach( piece => { + this.game.cube.pieces.forEach(piece => { - gameCubeData.names.push( piece.name ); - gameCubeData.positions.push( piece.position ); - gameCubeData.rotations.push( piece.rotation.toVector3() ); + gameCubeData.names.push(piece.name); + gameCubeData.positions.push(piece.position); + gameCubeData.rotations.push(piece.rotation.toVector3()); - } ); + }); - localStorage.setItem( 'theCube_playing', gameInProgress ); - localStorage.setItem( 'theCube_savedState', JSON.stringify( gameCubeData ) ); - localStorage.setItem( 'theCube_time', gameTime ); + localStorage.setItem('theCube_playing', gameInProgress); + localStorage.setItem('theCube_savedState', JSON.stringify(gameCubeData)); + localStorage.setItem('theCube_time', gameTime); } clearGame() { - localStorage.removeItem( 'theCube_playing' ); - localStorage.removeItem( 'theCube_savedState' ); - localStorage.removeItem( 'theCube_time' ); + localStorage.removeItem('theCube_playing'); + localStorage.removeItem('theCube_savedState'); + localStorage.removeItem('theCube_time'); } @@ -3127,13 +3159,13 @@ class Storage { try { - const scoresData = JSON.parse( localStorage.getItem( 'theCube_scores' ) ); + const scoresData = JSON.parse(localStorage.getItem('theCube_scores')); - if ( ! scoresData ) throw new Error(); + if (!scoresData) throw new Error(); this.game.scores.data = scoresData; - } catch( e ) {} + } catch (e) { } } @@ -3141,13 +3173,13 @@ class Storage { const scoresData = this.game.scores.data; - localStorage.setItem( 'theCube_scores', JSON.stringify( scoresData ) ); + localStorage.setItem('theCube_scores', JSON.stringify(scoresData)); } clearScores() { - localStorage.removeItem( 'theCube_scores' ); + localStorage.removeItem('theCube_scores'); } @@ -3155,24 +3187,24 @@ class Storage { try { - const scoresData = JSON.parse( localStorage.getItem( 'theCube_scoresData' ) ); - const scoresBest = parseInt( localStorage.getItem( 'theCube_scoresBest' ) ); - const scoresWorst = parseInt( localStorage.getItem( 'theCube_scoresWorst' ) ); - const scoresSolves = parseInt( localStorage.getItem( 'theCube_scoresSolves' ) ); + const scoresData = JSON.parse(localStorage.getItem('theCube_scoresData')); + const scoresBest = parseInt(localStorage.getItem('theCube_scoresBest')); + const scoresWorst = parseInt(localStorage.getItem('theCube_scoresWorst')); + const scoresSolves = parseInt(localStorage.getItem('theCube_scoresSolves')); - if ( ! scoresData || ! scoresBest || ! scoresSolves || ! scoresWorst ) return false; + if (!scoresData || !scoresBest || !scoresSolves || !scoresWorst) return false; - this.game.scores.data[ 3 ].scores = scoresData; - this.game.scores.data[ 3 ].best = scoresBest; - this.game.scores.data[ 3 ].solves = scoresSolves; - this.game.scores.data[ 3 ].worst = scoresWorst; + this.game.scores.data[3].scores = scoresData; + this.game.scores.data[3].best = scoresBest; + this.game.scores.data[3].solves = scoresSolves; + this.game.scores.data[3].worst = scoresWorst; - localStorage.removeItem( 'theCube_scoresData' ); - localStorage.removeItem( 'theCube_scoresBest' ); - localStorage.removeItem( 'theCube_scoresWorst' ); - localStorage.removeItem( 'theCube_scoresSolves' ); + localStorage.removeItem('theCube_scoresData'); + localStorage.removeItem('theCube_scoresBest'); + localStorage.removeItem('theCube_scoresWorst'); + localStorage.removeItem('theCube_scoresSolves'); - } catch( e ) {} + } catch (e) { } } @@ -3180,19 +3212,19 @@ class Storage { try { - const preferences = JSON.parse( localStorage.getItem( 'theCube_preferences' ) ); + const preferences = JSON.parse(localStorage.getItem('theCube_preferences')); - if ( ! preferences ) throw new Error(); + if (!preferences) throw new Error(); - this.game.cube.size = parseInt( preferences.cubeSize ); - this.game.controls.flipConfig = parseInt( preferences.flipConfig ); - this.game.scrambler.dificulty = parseInt( preferences.dificulty ); + this.game.cube.size = parseInt(preferences.cubeSize); + this.game.controls.flipConfig = parseInt(preferences.flipConfig); + this.game.scrambler.dificulty = parseInt(preferences.dificulty); - this.game.world.fov = parseFloat( preferences.fov ); + this.game.world.fov = parseFloat(preferences.fov); this.game.world.resize(); this.game.themes.colors = preferences.colors; - this.game.themes.setTheme( preferences.theme ); + this.game.themes.setTheme(preferences.theme); return true; @@ -3205,7 +3237,7 @@ class Storage { this.game.world.fov = 10; this.game.world.resize(); - this.game.themes.setTheme( 'cube' ); + this.game.themes.setTheme('cube'); this.savePreferences(); @@ -3226,13 +3258,13 @@ class Storage { colors: this.game.themes.colors, }; - localStorage.setItem( 'theCube_preferences', JSON.stringify( preferences ) ); + localStorage.setItem('theCube_preferences', JSON.stringify(preferences)); } clearPreferences() { - localStorage.removeItem( 'theCube_preferences' ); + localStorage.removeItem('theCube_preferences'); } @@ -3240,7 +3272,7 @@ class Storage { class Themes { - constructor( game ) { + constructor(game) { this.game = game; this.theme = null; @@ -3298,32 +3330,32 @@ class Themes { }, }; - this.colors = JSON.parse( JSON.stringify( this.defaults ) ); + this.colors = JSON.parse(JSON.stringify(this.defaults)); } getColors() { - return this.colors[ this.theme ]; + return this.colors[this.theme]; } - setTheme( theme = false, force = false ) { + setTheme(theme = false, force = false) { - if ( theme === this.theme && force === false ) return; - if ( theme !== false ) this.theme = theme; + if (theme === this.theme && force === false) return; + if (theme !== false) this.theme = theme; const colors = this.getColors(); - this.game.dom.prefs.querySelectorAll( '.range__handle div' ).forEach( range => { + this.game.dom.prefs.querySelectorAll('.range__handle div').forEach(range => { range.style.background = '#' + colors.R.toString(16).padStart(6, '0'); - } ); + }); - this.game.cube.updateColors( colors ); + this.game.cube.updateColors(colors); - this.game.confetti.updateColors( colors ); + this.game.confetti.updateColors(colors); this.game.dom.back.style.background = '#' + colors.G.toString(16).padStart(6, '0'); @@ -3333,67 +3365,67 @@ class Themes { class ThemeEditor { - constructor( game ) { + constructor(game) { this.game = game; this.editColor = 'R'; - this.getPieceColor = this.getPieceColor.bind( this ); + this.getPieceColor = this.getPieceColor.bind(this); } - colorFromHSL( h, s, l ) { + colorFromHSL(h, s, l) { - h = Math.round( h ); - s = Math.round( s ); - l = Math.round( l ); + h = Math.round(h); + s = Math.round(s); + l = Math.round(l); - return new THREE.Color( `hsl(${h}, ${s}%, ${l}%)` ); + return new THREE.Color(`hsl(${h}, ${s}%, ${l}%)`); } - setHSL( color = null, animate = false ) { + setHSL(color = null, animate = false) { - this.editColor = ( color === null) ? 'R' : color; + this.editColor = (color === null) ? 'R' : color; - const hsl = new THREE.Color( this.game.themes.getColors()[ this.editColor ] ); + const hsl = new THREE.Color(this.game.themes.getColors()[this.editColor]); - const { h, s, l } = hsl.getHSL( hsl ); + const { h, s, l } = hsl.getHSL(hsl); const { hue, saturation, lightness } = this.game.preferences.ranges; - if ( animate ) { + if (animate) { const ho = hue.value / 360; const so = saturation.value / 100; const lo = lightness.value / 100; - const colorOld = this.colorFromHSL( hue.value, saturation.value, lightness.value ); + const colorOld = this.colorFromHSL(hue.value, saturation.value, lightness.value); - if ( this.tweenHSL ) this.tweenHSL.stop(); + if (this.tweenHSL) this.tweenHSL.stop(); - this.tweenHSL = new Tween( { + this.tweenHSL = new Tween({ duration: 200, easing: Easing.Sine.Out(), onUpdate: tween => { - hue.setValue( ( ho + ( h - ho ) * tween.value ) * 360 ); - saturation.setValue( ( so + ( s - so ) * tween.value ) * 100 ); - lightness.setValue( ( lo + ( l - lo ) * tween.value ) * 100 ); + hue.setValue((ho + (h - ho) * tween.value) * 360); + saturation.setValue((so + (s - so) * tween.value) * 100); + lightness.setValue((lo + (l - lo) * tween.value) * 100); - const colorTween = colorOld.clone().lerp( hsl, tween.value ); + const colorTween = colorOld.clone().lerp(hsl, tween.value); const colorTweenStyle = colorTween.getStyle(); - const colorTweenHex = colorTween.getHSL( colorTween ); + const colorTweenHex = colorTween.getHSL(colorTween); hue.handle.style.color = colorTweenStyle; saturation.handle.style.color = colorTweenStyle; lightness.handle.style.color = colorTweenStyle; saturation.track.style.color = - this.colorFromHSL( colorTweenHex.h * 360, 100, 50 ).getStyle(); + this.colorFromHSL(colorTweenHex.h * 360, 100, 50).getStyle(); lightness.track.style.color = - this.colorFromHSL( colorTweenHex.h * 360, colorTweenHex.s * 100, 50 ).getStyle(); + this.colorFromHSL(colorTweenHex.h * 360, colorTweenHex.s * 100, 50).getStyle(); this.game.dom.theme.style.display = 'none'; this.game.dom.theme.offsetHeight; @@ -3406,13 +3438,13 @@ class ThemeEditor { this.game.storage.savePreferences(); }, - } ); + }); } else { - hue.setValue( h * 360 ); - saturation.setValue( s * 100 ); - lightness.setValue( l * 100 ); + hue.setValue(h * 360); + saturation.setValue(s * 100); + lightness.setValue(l * 100); this.updateHSL(); this.game.storage.savePreferences(); @@ -3429,14 +3461,14 @@ class ThemeEditor { const s = saturation.value; const l = lightness.value; - const color = this.colorFromHSL( h, s, l ).getStyle(); + const color = this.colorFromHSL(h, s, l).getStyle(); hue.handle.style.color = color; saturation.handle.style.color = color; lightness.handle.style.color = color; - saturation.track.style.color = this.colorFromHSL( h, 100, 50 ).getStyle(); - lightness.track.style.color = this.colorFromHSL( h, s, 50 ).getStyle(); + saturation.track.style.color = this.colorFromHSL(h, 100, 50).getStyle(); + lightness.track.style.color = this.colorFromHSL(h, s, 50).getStyle(); this.game.dom.theme.style.display = 'none'; this.game.dom.theme.offsetHeight; @@ -3444,64 +3476,64 @@ class ThemeEditor { const theme = this.game.themes.theme; - this.game.themes.colors[ theme ][ this.editColor ] = this.colorFromHSL( h, s, l ).getHex(); + this.game.themes.colors[theme][this.editColor] = this.colorFromHSL(h, s, l).getHex(); this.game.themes.setTheme(); } - colorPicker( enable ) { + colorPicker(enable) { - if ( enable ) { + if (enable) { - this.game.dom.game.addEventListener( 'click', this.getPieceColor, false ); + this.game.dom.game.addEventListener('click', this.getPieceColor, false); } else { - this.game.dom.game.removeEventListener( 'click', this.getPieceColor, false ); + this.game.dom.game.removeEventListener('click', this.getPieceColor, false); } } - getPieceColor( event ) { + getPieceColor(event) { const clickEvent = event.touches - ? ( event.touches[ 0 ] || event.changedTouches[ 0 ] ) + ? (event.touches[0] || event.changedTouches[0]) : event; - const clickPosition = new THREE.Vector2( clickEvent.pageX, clickEvent.pageY ); + const clickPosition = new THREE.Vector2(clickEvent.pageX, clickEvent.pageY); - let edgeIntersect = this.game.controls.getIntersect( clickPosition, this.game.cube.edges, true ); - let pieceIntersect = this.game.controls.getIntersect( clickPosition, this.game.cube.cubes, true ); + let edgeIntersect = this.game.controls.getIntersect(clickPosition, this.game.cube.edges, true); + let pieceIntersect = this.game.controls.getIntersect(clickPosition, this.game.cube.cubes, true); - if ( edgeIntersect !== false ) { + if (edgeIntersect !== false) { const edge = edgeIntersect.object; const position = edge.parent - .localToWorld( edge.position.clone() ) - .sub( this.game.cube.object.position ) - .sub( this.game.cube.animator.position ); + .localToWorld(edge.position.clone()) + .sub(this.game.cube.object.position) + .sub(this.game.cube.animator.position); - const mainAxis = this.game.controls.getMainAxis( position ); - if ( position.multiplyScalar( 2 ).round()[ mainAxis ] < 1 ) edgeIntersect = false; + const mainAxis = this.game.controls.getMainAxis(position); + if (position.multiplyScalar(2).round()[mainAxis] < 1) edgeIntersect = false; } const name = edgeIntersect ? edgeIntersect.object.name : pieceIntersect ? 'P' : 'G'; - this.setHSL( name, true ); + this.setHSL(name, true); } resetTheme() { - this.game.themes.colors[ this.game.themes.theme ] = - JSON.parse( JSON.stringify( this.game.themes.defaults[ this.game.themes.theme ] ) ); + this.game.themes.colors[this.game.themes.theme] = + JSON.parse(JSON.stringify(this.game.themes.defaults[this.game.themes.theme])); this.game.themes.setTheme(); - this.setHSL( this.editColor, true ); + this.setHSL(this.editColor, true); } @@ -3510,35 +3542,35 @@ class ThemeEditor { const States = { 3: { checkerboard: { - names: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 ], + names: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26], positions: [ - { "x": 1/3, "y": -1/3, "z": 1/3 }, - { "x": -1/3, "y": 1/3, "z": 0 }, - { "x": 1/3, "y": -1/3, "z": -1/3 }, - { "x": -1/3, "y": 0, "z": -1/3 }, - { "x": 1/3, "y": 0, "z": 0 }, - { "x": -1/3, "y": 0, "z": 1/3 }, - { "x": 1/3, "y": 1/3, "z": 1/3 }, - { "x": -1/3, "y": -1/3, "z": 0 }, - { "x": 1/3, "y": 1/3, "z": -1/3 }, - { "x": 0, "y": 1/3, "z": -1/3 }, - { "x": 0, "y": -1/3, "z": 0 }, - { "x": 0, "y": 1/3, "z": 1/3 }, - { "x": 0, "y": 0, "z": 1/3 }, + { "x": 1 / 3, "y": -1 / 3, "z": 1 / 3 }, + { "x": -1 / 3, "y": 1 / 3, "z": 0 }, + { "x": 1 / 3, "y": -1 / 3, "z": -1 / 3 }, + { "x": -1 / 3, "y": 0, "z": -1 / 3 }, + { "x": 1 / 3, "y": 0, "z": 0 }, + { "x": -1 / 3, "y": 0, "z": 1 / 3 }, + { "x": 1 / 3, "y": 1 / 3, "z": 1 / 3 }, + { "x": -1 / 3, "y": -1 / 3, "z": 0 }, + { "x": 1 / 3, "y": 1 / 3, "z": -1 / 3 }, + { "x": 0, "y": 1 / 3, "z": -1 / 3 }, + { "x": 0, "y": -1 / 3, "z": 0 }, + { "x": 0, "y": 1 / 3, "z": 1 / 3 }, + { "x": 0, "y": 0, "z": 1 / 3 }, { "x": 0, "y": 0, "z": 0 }, - { "x": 0, "y": 0, "z": -1/3 }, - { "x": 0, "y": -1/3, "z": -1/3 }, - { "x": 0, "y": 1/3, "z": 0 }, - { "x": 0, "y": -1/3, "z": 1/3 }, - { "x": -1/3, "y": -1/3, "z": 1/3 }, - { "x": 1/3, "y": 1/3, "z": 0 }, - { "x": -1/3, "y": -1/3, "z": -1/3 }, - { "x": 1/3, "y": 0, "z": -1/3 }, - { "x": -1/3, "y": 0, "z": 0 }, - { "x": 1/3, "y": 0, "z": 1/3 }, - { "x": -1/3, "y": 1/3, "z": 1/3 }, - { "x": 1/3, "y": -1/3, "z": 0 }, - { "x": -1/3, "y": 1/3, "z": -1/3 } + { "x": 0, "y": 0, "z": -1 / 3 }, + { "x": 0, "y": -1 / 3, "z": -1 / 3 }, + { "x": 0, "y": 1 / 3, "z": 0 }, + { "x": 0, "y": -1 / 3, "z": 1 / 3 }, + { "x": -1 / 3, "y": -1 / 3, "z": 1 / 3 }, + { "x": 1 / 3, "y": 1 / 3, "z": 0 }, + { "x": -1 / 3, "y": -1 / 3, "z": -1 / 3 }, + { "x": 1 / 3, "y": 0, "z": -1 / 3 }, + { "x": -1 / 3, "y": 0, "z": 0 }, + { "x": 1 / 3, "y": 0, "z": 1 / 3 }, + { "x": -1 / 3, "y": 1 / 3, "z": 1 / 3 }, + { "x": 1 / 3, "y": -1 / 3, "z": 0 }, + { "x": -1 / 3, "y": 1 / 3, "z": -1 / 3 } ], rotations: [ { "x": -Math.PI, "y": 0, "z": Math.PI, }, @@ -3576,32 +3608,32 @@ const States = { class IconsConverter { - constructor( options ) { + constructor(options) { - options = Object.assign( { + options = Object.assign({ tagName: 'icon', className: 'icon', styles: false, icons: {}, observe: false, convert: false, - }, options || {} ); + }, options || {}); this.tagName = options.tagName; this.className = options.className; this.icons = options.icons; - this.svgTag = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); - this.svgTag.setAttribute( 'class', this.className ); + this.svgTag = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + this.svgTag.setAttribute('class', this.className); - if ( options.styles ) this.addStyles(); - if ( options.convert ) this.convertAllIcons(); + if (options.styles) this.addStyles(); + if (options.convert) this.convertAllIcons(); - if ( options.observe ) { + if (options.observe) { const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; - this.observer = new MutationObserver( mutations => { this.convertAllIcons(); } ); - this.observer.observe( document.documentElement, { childList: true, subtree: true } ); + this.observer = new MutationObserver(mutations => { this.convertAllIcons(); }); + this.observer.observe(document.documentElement, { childList: true, subtree: true }); } @@ -3611,39 +3643,39 @@ class IconsConverter { convertAllIcons() { - document.querySelectorAll( this.tagName ).forEach( icon => { this.convertIcon( icon ); } ); + document.querySelectorAll(this.tagName).forEach(icon => { this.convertIcon(icon); }); } - convertIcon( icon ) { + convertIcon(icon) { - const svgData = this.icons[ icon.attributes[0].localName ]; + const svgData = this.icons[icon.attributes[0].localName]; - if ( typeof svgData === 'undefined' ) return; + if (typeof svgData === 'undefined') return; - const svg = this.svgTag.cloneNode( true ); - const viewBox = svgData.viewbox.split( ' ' ); + const svg = this.svgTag.cloneNode(true); + const viewBox = svgData.viewbox.split(' '); - svg.setAttributeNS( null, 'viewBox', svgData.viewbox ); + svg.setAttributeNS(null, 'viewBox', svgData.viewbox); svg.style.width = viewBox[2] / viewBox[3] + 'em'; svg.style.height = '1em'; svg.innerHTML = svgData.content; - icon.parentNode.replaceChild( svg, icon ); + icon.parentNode.replaceChild(svg, icon); } addStyles() { - const style = document.createElement( 'style' ); + const style = document.createElement('style'); style.innerHTML = `.${this.className} { display: inline-block; font-size: inherit; overflow: visible; vertical-align: -0.125em; preserveAspectRatio: none; }`; - document.head.appendChild( style ); + document.head.appendChild(style); } } -const Icons = new IconsConverter( { +const Icons = new IconsConverter({ icons: { settings: { @@ -3678,7 +3710,7 @@ const Icons = new IconsConverter( { convert: true, -} ); +}); const STATE = { Menu: 0, @@ -3690,12 +3722,12 @@ const STATE = { }; const BUTTONS = { - Menu: [ 'stats', 'prefs' ], - Playing: [ 'back' ], + Menu: ['stats', 'prefs'], + Playing: ['back'], Complete: [], - Stats: [ 'back' ], - Prefs: [ 'back', 'theme' ], - Theme: [ 'back', 'reset' ], + Stats: ['back'], + Prefs: ['back', 'theme'], + Theme: ['back', 'reset'], None: [], }; @@ -3706,42 +3738,43 @@ class Game { constructor() { + this.audio = document.getElementById('background-music'); this.dom = { - ui: document.querySelector( '.ui' ), - game: document.querySelector( '.ui__game' ), - back: document.querySelector( '.ui__background' ), - prefs: document.querySelector( '.ui__prefs' ), - theme: document.querySelector( '.ui__theme' ), - stats: document.querySelector( '.ui__stats' ), + ui: document.querySelector('.ui'), + game: document.querySelector('.ui__game'), + back: document.querySelector('.ui__background'), + prefs: document.querySelector('.ui__prefs'), + theme: document.querySelector('.ui__theme'), + stats: document.querySelector('.ui__stats'), texts: { - title: document.querySelector( '.text--title' ), - note: document.querySelector( '.text--note' ), - timer: document.querySelector( '.text--timer' ), - complete: document.querySelector( '.text--complete' ), - best: document.querySelector( '.text--best-time' ), - theme: document.querySelector( '.text--theme' ), + title: document.querySelector('.text--title'), + note: document.querySelector('.text--note'), + timer: document.querySelector('.text--timer'), + complete: document.querySelector('.text--complete'), + best: document.querySelector('.text--best-time'), + theme: document.querySelector('.text--theme'), }, buttons: { - prefs: document.querySelector( '.btn--prefs' ), - back: document.querySelector( '.btn--back' ), - stats: document.querySelector( '.btn--stats' ), - reset: document.querySelector( '.btn--reset' ), - theme: document.querySelector( '.btn--theme' ), + prefs: document.querySelector('.btn--prefs'), + back: document.querySelector('.btn--back'), + stats: document.querySelector('.btn--stats'), + reset: document.querySelector('.btn--reset'), + theme: document.querySelector('.btn--theme'), }, }; - this.world = new World( this ); - this.cube = new Cube( this ); - this.controls = new Controls( this ); - this.scrambler = new Scrambler( this ); - this.transition = new Transition( this ); - this.timer = new Timer( this ); - this.preferences = new Preferences( this ); - this.scores = new Scores( this ); - this.storage = new Storage( this ); - this.confetti = new Confetti( this ); - this.themes = new Themes( this ); - this.themeEditor = new ThemeEditor( this ); + this.world = new World(this); + this.cube = new Cube(this); + this.controls = new Controls(this); + this.scrambler = new Scrambler(this); + this.transition = new Transition(this); + this.timer = new Timer(this); + this.preferences = new Preferences(this); + this.scores = new Scores(this); + this.storage = new Storage(this); + this.confetti = new Confetti(this); + this.themes = new Themes(this); + this.themeEditor = new ThemeEditor(this); this.initActions(); @@ -3757,15 +3790,15 @@ class Game { this.storage.loadGame(); this.scores.calcStats(); - setTimeout( () => { + setTimeout(() => { this.transition.float(); - this.transition.cube( SHOW ); + this.transition.cube(SHOW); - setTimeout( () => this.transition.title( SHOW ), 700 ); - setTimeout( () => this.transition.buttons( BUTTONS.Menu, BUTTONS.None ), 1000 ); + setTimeout(() => this.transition.title(SHOW), 700); + setTimeout(() => this.transition.buttons(BUTTONS.Menu, BUTTONS.None), 1000); - }, 500 ); + }, 500); } @@ -3773,26 +3806,26 @@ class Game { let tappedTwice = false; - this.dom.game.addEventListener( 'click', event => { + this.dom.game.addEventListener('click', event => { - if ( this.transition.activeTransitions > 0 ) return; - if ( this.state === STATE.Playing ) return; + if (this.transition.activeTransitions > 0) return; + if (this.state === STATE.Playing) return; - if ( this.state === STATE.Menu ) { + if (this.state === STATE.Menu) { - if ( ! tappedTwice ) { + if (!tappedTwice) { tappedTwice = true; - setTimeout( () => tappedTwice = false, 300 ); + setTimeout(() => tappedTwice = false, 300); return false; } - this.game( SHOW ); + this.game(SHOW); - } else if ( this.state === STATE.Complete ) { + } else if (this.state === STATE.Complete) { - this.complete( HIDE ); + this.complete(HIDE); } // else if ( this.state === STATE.Stats ) { @@ -3801,13 +3834,13 @@ class Game { // } - }, false ); + }, false); this.controls.onMove = () => { - if ( this.newGame ) { - - this.timer.start( true ); + if (this.newGame) { + + this.timer.start(true); this.newGame = false; } @@ -3816,23 +3849,23 @@ class Game { this.dom.buttons.back.onclick = event => { - if ( this.transition.activeTransitions > 0 ) return; + if (this.transition.activeTransitions > 0) return; - if ( this.state === STATE.Playing ) { + if (this.state === STATE.Playing) { - this.game( HIDE ); + this.game(HIDE); - } else if ( this.state === STATE.Prefs ) { + } else if (this.state === STATE.Prefs) { - this.prefs( HIDE ); + this.prefs(HIDE); - } else if ( this.state === STATE.Stats ) { + } else if (this.state === STATE.Stats) { - this.stats( HIDE ); + this.stats(HIDE); - } else if ( this.state === STATE.Theme ) { + } else if (this.state === STATE.Theme) { - this.theme( HIDE ); + this.theme(HIDE); } @@ -3840,29 +3873,29 @@ class Game { this.dom.buttons.reset.onclick = event => { - if ( this.state === STATE.Theme ) { + if (this.state === STATE.Theme) { this.themeEditor.resetTheme(); } - + }; - this.dom.buttons.prefs.onclick = event => this.prefs( SHOW ); + this.dom.buttons.prefs.onclick = event => this.prefs(SHOW); - this.dom.buttons.theme.onclick = event => this.theme( SHOW ); + this.dom.buttons.theme.onclick = event => this.theme(SHOW); - this.dom.buttons.stats.onclick = event => this.stats( SHOW ); + this.dom.buttons.stats.onclick = event => this.stats(SHOW); - this.controls.onSolved = () => this.complete( SHOW ); + this.controls.onSolved = () => this.complete(SHOW); } - game( show ) { + game(show) { - if ( show ) { + if (show) { - if ( ! this.saved ) { + if (!this.saved) { this.scrambler.scramble(); this.controls.scrambleCube(); @@ -3871,43 +3904,43 @@ class Game { } const duration = this.saved ? 0 : - this.scrambler.converted.length * ( this.controls.flipSpeeds[0] + 10 ); + this.scrambler.converted.length * (this.controls.flipSpeeds[0] + 10); this.state = STATE.Playing; this.saved = true; - this.transition.buttons( BUTTONS.None, BUTTONS.Menu ); + this.transition.buttons(BUTTONS.None, BUTTONS.Menu); - this.transition.zoom( STATE.Playing, duration ); - this.transition.title( HIDE ); + this.transition.zoom(STATE.Playing, duration); + this.transition.title(HIDE); - setTimeout( () => { + setTimeout(() => { - this.transition.timer( SHOW ); - this.transition.buttons( BUTTONS.Playing, BUTTONS.None ); + this.transition.timer(SHOW); + this.transition.buttons(BUTTONS.Playing, BUTTONS.None); - }, this.transition.durations.zoom - 1000 ); + }, this.transition.durations.zoom - 1000); - setTimeout( () => { + setTimeout(() => { this.controls.enable(); - if ( ! this.newGame ) this.timer.start( true ); + if (!this.newGame) this.timer.start(true); - }, this.transition.durations.zoom ); + }, this.transition.durations.zoom); } else { this.state = STATE.Menu; - this.transition.buttons( BUTTONS.Menu, BUTTONS.Playing ); + this.transition.buttons(BUTTONS.Menu, BUTTONS.Playing); - this.transition.zoom( STATE.Menu, 0 ); + this.transition.zoom(STATE.Menu, 0); this.controls.disable(); - if ( ! this.newGame ) this.timer.stop(); - this.transition.timer( HIDE ); + if (!this.newGame) this.timer.stop(); + this.transition.timer(HIDE); - setTimeout( () => this.transition.title( SHOW ), this.transition.durations.zoom - 1000 ); + setTimeout(() => this.transition.title(SHOW), this.transition.durations.zoom - 1000); this.playing = false; this.controls.disable(); @@ -3916,20 +3949,20 @@ class Game { } - prefs( show ) { + prefs(show) { - if ( show ) { + if (show) { - if ( this.transition.activeTransitions > 0 ) return; + if (this.transition.activeTransitions > 0) return; this.state = STATE.Prefs; - this.transition.buttons( BUTTONS.Prefs, BUTTONS.Menu ); + this.transition.buttons(BUTTONS.Prefs, BUTTONS.Menu); - this.transition.title( HIDE ); - this.transition.cube( HIDE ); + this.transition.title(HIDE); + this.transition.cube(HIDE); - setTimeout( () => this.transition.preferences( SHOW ), 1000 ); + setTimeout(() => this.transition.preferences(SHOW), 1000); } else { @@ -3937,102 +3970,102 @@ class Game { this.state = STATE.Menu; - this.transition.buttons( BUTTONS.Menu, BUTTONS.Prefs ); + this.transition.buttons(BUTTONS.Menu, BUTTONS.Prefs); - this.transition.preferences( HIDE ); + this.transition.preferences(HIDE); - setTimeout( () => this.transition.cube( SHOW ), 500 ); - setTimeout( () => this.transition.title( SHOW ), 1200 ); + setTimeout(() => this.transition.cube(SHOW), 500); + setTimeout(() => this.transition.title(SHOW), 1200); } } - theme( show ) { + theme(show) { + + this.themeEditor.colorPicker(show); - this.themeEditor.colorPicker( show ); - - if ( show ) { + if (show) { - if ( this.transition.activeTransitions > 0 ) return; + if (this.transition.activeTransitions > 0) return; - this.cube.loadFromData( States[ '3' ][ 'checkerboard' ] ); + this.cube.loadFromData(States['3']['checkerboard']); - this.themeEditor.setHSL( null, false ); + this.themeEditor.setHSL(null, false); this.state = STATE.Theme; - this.transition.buttons( BUTTONS.Theme, BUTTONS.Prefs ); + this.transition.buttons(BUTTONS.Theme, BUTTONS.Prefs); - this.transition.preferences( HIDE ); + this.transition.preferences(HIDE); - setTimeout( () => this.transition.cube( SHOW, true ), 500 ); - setTimeout( () => this.transition.theming( SHOW ), 1000 ); + setTimeout(() => this.transition.cube(SHOW, true), 500); + setTimeout(() => this.transition.theming(SHOW), 1000); } else { this.state = STATE.Prefs; - this.transition.buttons( BUTTONS.Prefs, BUTTONS.Theme ); + this.transition.buttons(BUTTONS.Prefs, BUTTONS.Theme); - this.transition.cube( HIDE, true ); - this.transition.theming( HIDE ); + this.transition.cube(HIDE, true); + this.transition.theming(HIDE); - setTimeout( () => this.transition.preferences( SHOW ), 1000 ); - setTimeout( () => { + setTimeout(() => this.transition.preferences(SHOW), 1000); + setTimeout(() => { - const gameCubeData = JSON.parse( localStorage.getItem( 'theCube_savedState' ) ); + const gameCubeData = JSON.parse(localStorage.getItem('theCube_savedState')); - if ( !gameCubeData ) { + if (!gameCubeData) { - this.cube.resize( true ); + this.cube.resize(true); return; } - this.cube.loadFromData( gameCubeData ); + this.cube.loadFromData(gameCubeData); - }, 1500 ); + }, 1500); } } - stats( show ) { + stats(show) { - if ( show ) { + if (show) { - if ( this.transition.activeTransitions > 0 ) return; + if (this.transition.activeTransitions > 0) return; this.state = STATE.Stats; - this.transition.buttons( BUTTONS.Stats, BUTTONS.Menu ); + this.transition.buttons(BUTTONS.Stats, BUTTONS.Menu); - this.transition.title( HIDE ); - this.transition.cube( HIDE ); + this.transition.title(HIDE); + this.transition.cube(HIDE); - setTimeout( () => this.transition.stats( SHOW ), 1000 ); + setTimeout(() => this.transition.stats(SHOW), 1000); } else { this.state = STATE.Menu; - this.transition.buttons( BUTTONS.Menu, BUTTONS.Stats ); + this.transition.buttons(BUTTONS.Menu, BUTTONS.Stats); - this.transition.stats( HIDE ); + this.transition.stats(HIDE); - setTimeout( () => this.transition.cube( SHOW ), 500 ); - setTimeout( () => this.transition.title( SHOW ), 1200 ); + setTimeout(() => this.transition.cube(SHOW), 500); + setTimeout(() => this.transition.title(SHOW), 1200); } } - complete( show ) { + complete(show) { - if ( show ) { + if (show) { - this.transition.buttons( BUTTONS.Complete, BUTTONS.Playing ); + this.transition.buttons(BUTTONS.Complete, BUTTONS.Playing); this.state = STATE.Complete; this.saved = false; @@ -4041,37 +4074,38 @@ class Game { this.timer.stop(); this.storage.clearGame(); - this.bestTime = this.scores.addScore( this.timer.deltaTime ); + this.bestTime = this.scores.addScore(this.timer.deltaTime); - this.transition.zoom( STATE.Menu, 0 ); - this.transition.elevate( SHOW ); + this.transition.zoom(STATE.Menu, 0); + this.transition.elevate(SHOW); - setTimeout( () => { + setTimeout(() => { - this.transition.complete( SHOW, this.bestTime ); + this.transition.complete(SHOW, this.bestTime); this.confetti.start(); - }, 1000 ); + }, 1000); } else { this.state = STATE.Stats; + this.transition.buttons(BUTTONS.Stats, BUTTONS.Complete); this.saved = false; - this.transition.timer( HIDE ); - this.transition.complete( HIDE, this.bestTime ); - this.transition.cube( HIDE ); + this.transition.timer(HIDE); + this.transition.complete(HIDE, this.bestTime); + this.transition.cube(HIDE); this.timer.reset(); - setTimeout( () => { + setTimeout(() => { this.cube.reset(); this.confetti.stop(); - this.transition.stats( SHOW ); - this.transition.elevate( 0 ); + this.transition.stats(SHOW); + this.transition.elevate(0); - }, 1000 ); + }, 1000); return false; @@ -4080,6 +4114,306 @@ class Game { } } +// Volume button functionality +const musicButton = document.getElementById('play-btn'); +const music = document.getElementById('background-music'); + +musicButton.addEventListener('click', () => { + if (music.paused) { + music.play(); + musicButton.classList.remove('fa-volume-xmark'); + musicButton.classList.add('fa-volume-high'); + } else { + music.pause(); + musicButton.classList.remove('fa-volume-high'); + musicButton.classList.add('fa-volume-xmark'); + } +}); + + feature/shortcut-keys +////Add keyboard shortcuts +function rotateView(direction) { + const move = { + 'up': { axis: 'x', angle: -Math.PI / 2 }, + 'down': { axis: 'x', angle: Math.PI / 2 }, + 'left': { axis: 'y', angle: Math.PI / 2 }, + 'right': { axis: 'y', angle: -Math.PI / 2 } + +//rotation functions +function rotateU() { + const move = { axis: 'y', angle: -Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, 1, 0), axis: move.axis, angle: move.angle }); +} +function rotateUPrime() { + const move = { axis: 'y', angle: Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, 1, 0), axis: move.axis, angle: move.angle }); +} + +function rotateR() { + const move = { axis: 'x', angle: -Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(1, 0, 0), axis: move.axis, angle: move.angle }); +} + +function rotateRPrime() { + const move = { axis: 'x', angle: Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(1, 0, 0), axis: move.axis, angle: move.angle }); +} + +function rotateF() { + const move = { axis: 'z', angle: -Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, 0, 1), axis: move.axis, angle: move.angle }); +} + +function rotateFPrime() { + const move = { axis: 'z', angle: Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, 0, 1), axis: move.axis, angle: move.angle }); +} + +function rotateL() { + const move = { axis: 'x', angle: Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(-1, 0, 0), axis: move.axis, angle: move.angle }); +} + +function rotateLPrime() { + const move = { axis: 'x', angle: -Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(-1, 0, 0), axis: move.axis, angle: move.angle }); +} + +function rotateD() { + const move = { axis: 'y', angle: Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, -1, 0), axis: move.axis, angle: move.angle }); +} + +function rotateDPrime() { + const move = { axis: 'y', angle: -Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, -1, 0), axis: move.axis, angle: move.angle }); +} + +function rotateB() { + const move = { axis: 'z', angle: Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, 0, -1), axis: move.axis, angle: move.angle }); +} + +function rotateBPrime() { + const move = { axis: 'z', angle: -Math.PI / 2 }; + game.controls.keyboardMove('LAYER', { position: new THREE.Vector3(0, 0, -1), axis: move.axis, angle: move.angle }); +} + +// Function to handle view rotation based on direction +function rotateView(direction, isPrime = false) { + const move = { + 'up': { axis: 'x', angle: isPrime ? Math.PI / 2 : -Math.PI / 2 }, + 'down': { axis: 'x', angle: isPrime ? -Math.PI / 2 : Math.PI / 2 }, + 'left': { axis: 'y', angle: isPrime ? -Math.PI / 2 : Math.PI / 2 }, + 'right': { axis: 'y', angle: isPrime ? Math.PI / 2 : -Math.PI / 2 } + + }[direction]; + if (move) { + game.controls.keyboardMove('CUBE', move); + } +} + + feature/shortcut-keys +document.addEventListener('keydown', function(event) { + const key = event.key.toLowerCase(); + switch (key) { + case 'w': + case 'arrowup': + rotateView('up'); + break; + case 's': + case 'arrowdown': + rotateView('down'); + break; + case 'a': + case 'arrowleft': + rotateView('left'); + break; + case 'd': + case 'arrowright': + rotateView('right'); + +let isPaused = false; + +function togglePause() { + if (isPaused) { + game.timer.start(true); + game.controls.enable(); + console.log('Game resumed'); + } else { + game.timer.stop(); + game.controls.disable(); + console.log('Game paused'); + } + isPaused = !isPaused; +} + +// Add event listener for keydown +document.addEventListener('keydown', function (event) { + const isPrime = event.shiftKey; + const key = event.key.toLowerCase(); + + if (event.key === 'p') { + togglePause(); + return; + } + + if (isPaused) return; + + switch (key) { + case 'arrowup': + rotateView('up', isPrime); + break; + case 'arrowdown': + rotateView('down', isPrime); + break; + case 'arrowleft': + rotateView('left', isPrime); + break; + case 'arrowright': + rotateView('right', isPrime); + break; + case 'u': + isPrime ? rotateUPrime() : rotateU(); + break; + case 'r': + isPrime ? rotateRPrime() : rotateR(); + break; + case 'f': + isPrime ? rotateFPrime() : rotateF(); + break; + case 'l': + isPrime ? rotateLPrime() : rotateL(); + break; + case 'd': + isPrime ? rotateDPrime() : rotateD(); + break; + case 'b': + isPrime ? rotateBPrime() : rotateB(); + main + break; + default: + break; + } +}); + + feature/shortcut-keys + +document.addEventListener('mousemove', function () { + if (isPaused) { + togglePause(); + } +}); + +let cube; // Global cube variable + +function handleKeyPress(event) { + console.log('Key pressed:', event.key); // Log the pressed key + switch (event.key) { + case 'ArrowLeft': + console.log('Left Arrow pressed'); + rotateLeft(); + break; + case 'ArrowUp': + console.log('Up Arrow pressed'); + rotateUp(); + break; + case 'ArrowRight': + console.log('Right Arrow pressed'); + rotateRight(); + break; + case 'ArrowDown': + console.log('Down Arrow pressed'); + rotateDown(); + break; + default: + console.log('Other key pressed:', event.key); + break; + } +} + +function rotateUp() { + console.log('Rotate up function called'); + if (cube) { + var axis = new THREE.Vector3(1, 0, 0); // Rotate around the x-axis + var angle = Math.PI / 2; // Rotate by 90 degrees + cube.rotateOnAxis(axis, angle); + } +} + +function rotateDown() { + console.log('Rotate down function called'); + if (cube) { + var axis = new THREE.Vector3(1, 0, 0); // Rotate around the x-axis + var angle = -Math.PI / 2; // Rotate by -90 degrees + cube.rotateOnAxis(axis, angle); + } +} + +function rotateLeft() { + console.log('Rotate left function called'); + if (cube) { + var axis = new THREE.Vector3(0, 1, 0); // Rotate around the y-axis + var angle = Math.PI / 2; // Rotate by 90 degrees + cube.rotateOnAxis(axis, angle); + } +} + +function rotateRight() { + console.log('Rotate right function called'); + if (cube) { + var axis = new THREE.Vector3(0, 1, 0); // Rotate around the y-axis + var angle = -Math.PI / 2; // Rotate by -90 degrees + cube.rotateOnAxis(axis, angle); + } +} + +function init() { + console.log('Initializing Three.js'); + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + const renderer = new THREE.WebGLRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); + cube = new THREE.Mesh(geometry, material); + scene.add(cube); + + camera.position.z = 5; + + const animate = function () { + requestAnimationFrame(animate); + renderer.render(scene, camera); + }; + + animate(); +} + +function handleCookieBlocking() { + console.log('Checking for third-party cookie blocking'); + // Implement your logic to check and handle third-party cookie blocking +} + +// Adding CSS dynamically +const head = document.head || document.getElementsByTagName('head')[0]; +if (head) { + const style = document.createElement('style'); + style.textContent = ` + /* Your CSS rules here */ + `; + head.appendChild(style); +} else { + console.error('Head element not found'); +} + +function initThreeJS() { + console.log('Initializing Three.js'); + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window) +} + window.version = '0.99.2'; -window.game = new Game(); \ No newline at end of file +window.game = new Game(); diff --git a/script1.js b/script1.js index f2ba21d..790fac8 100644 --- a/script1.js +++ b/script1.js @@ -1,5 +1,6 @@ + function handleCredentialResponse(response)//is the callback function that handles the ID token received from Google. - { +{ const data = jwt_decode(response.credential); console.log('User data:', data); @@ -15,8 +16,79 @@ function jwt_decode(token) //jwt_decode is a helper function to decode the JWT t { const base64Url = token.split('.')[1]; const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); - const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) { + const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); return JSON.parse(jsonPayload); } + +document.addEventListener('DOMContentLoaded', () => { + console.log('DOM fully loaded and parsed'); + // Initialize Three.js + console.log('Initializing Three.js'); + // Your Three.js initialization code here + + // Check for and handle third-party cookie blocking issues + console.log('Checking for third-party cookie blocking'); + // Your code to handle third-party cookie blocking + + // Check for and handle any failed resource loading issues + console.log('Checking for failed resource loading'); + // Your code to handle failed resource loading +}); + +document.addEventListener('DOMContentLoaded', (event) => { + console.log('DOM fully loaded and parsed'); + + // Check for third-party cookie blocking + checkThirdPartyCookies(); + + // Initialize Three.js + initThreeJS(); + + // Append custom style + appendCustomStyle(); +}); + +function checkThirdPartyCookies() { + console.log('Checking for third-party cookie blocking'); + // Implement the check for third-party cookies here +} + +function appendCustomStyle() { + console.log('Appending custom style'); + var head = document.head || document.getElementsByTagName('head')[0]; + if (head) { + var style = document.createElement('style'); + style.textContent = ` + /* Your CSS rules here */ + `; + head.appendChild(style); + } else { + console.error('Head element not found'); + } +} + +function initThreeJS() { + console.log('Initializing Three.js'); + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + const renderer = new THREE.WebGLRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); + + const geometry = new THREE.BoxGeometry(); + const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); + const cube = new THREE.Mesh(geometry, material); + scene.add(cube); + + camera.position.z = 5; + + const animate = function () { + requestAnimationFrame(animate); + renderer.render(scene, camera); + }; + + animate(); +} + diff --git a/style.css b/style.css index b51f72a..73a0135 100644 --- a/style.css +++ b/style.css @@ -1,10 +1,67 @@ + +/* styles.css */ + +/* Default theme */ +body.default-theme { + background-color: white; + color: black; +} + +/* Dark theme */ +body.dark-theme { + background-color: #333; + color: white; +} + +/* Light theme */ +body.light-theme { + background-color: #f9f9f9; + color: #333; +} + +/* Colorful theme */ +body.colorful-theme { + background: linear-gradient(45deg, #ff6f69, #ffcc5c, #ffeead, #88d8b0, #96ceb4); + color: #333; +} + +/* Style for the theme selector */ +.theme-selector { + position: fixed; + top: 10px; + right: 10px; + z-index: 1000; +} + +#play-btn { + position: fixed; + top: 85px; + right: 50px; /* Adjust as necessary to avoid overlap */ + z-index: 10001; /* Higher than the theme selector */ +} + @font-face { font-family: "BungeeFont"; font-weight: normal; font-style: normal; + + src: url("data:font/truetype;charset=utf-8;base64,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") + format("woff2"), + url("data:font/truetype;charset=utf-8;base64,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") + format("woff"); +} +*, +*:before, +*:after { + src: url("data:font/truetype;charset=utf-8;base64,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") format("woff2"), url("data:font/truetype;charset=utf-8;base64,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") format("woff"); } -*, *:before, *:after { + +*, +*:before, +*:after { + + -webkit-user-select: none; -moz-user-select: none; user-select: none; @@ -18,7 +75,9 @@ font-weight: inherit; font-style: inherit; text-transform: uppercase; + } + *:focus { outline: none; cursor:pointer; @@ -36,6 +95,7 @@ html { } body { + font-family: "BungeeFont", sans-serif; font-weight: normal; font-style: normal; @@ -44,6 +104,7 @@ body { overflow: hidden; height: 100%; font-size: 5rem; + } .icon { @@ -52,7 +113,7 @@ body { font-size: inherit; overflow: visible; vertical-align: -0.125em; - preserveAspectRatio: none; + preserveaspectratio: none; } .range { @@ -62,9 +123,11 @@ body { z-index: 1; opacity: 0; } + .range:not(:last-child) { margin-bottom: 2em; } + .range__label { cursor:pointer; position: relative; @@ -73,6 +136,7 @@ body { padding-bottom: 0.5em; z-index: 2; } + .range__track { cursor:pointer; position: relative; @@ -81,6 +145,7 @@ body { margin-right: 0.5em; z-index: 3; } + .range__track-line { cursor:pointer; position: absolute; @@ -92,6 +157,7 @@ body { right: -0.5em; transform-origin: left center; } + .range__handle { cursor:pointer; position: absolute; @@ -102,6 +168,7 @@ body { cursor: pointer; z-index: 1; } + .range__handle div { cursor:pointer; transition: background 500ms ease; @@ -116,9 +183,11 @@ body { background: #41aac8; border-bottom: 2px solid rgba(0, 0, 0, 0.2); } + .range.is-active .range__handle div { transform: scale(1.25); } + .range__handle:after { content: ""; position: absolute; @@ -129,6 +198,7 @@ body { margin-left: -1.5em; margin-top: -1.5em; } + .range__list { cursor:pointer; display: flex; @@ -140,18 +210,24 @@ body { color: rgba(0, 0, 0, 0.5); z-index: 1; } + .range--type-color:not(:last-child) { margin-bottom: 1em; } + .range--type-color .range__list { display: none; } -.range--type-color .range__handle > div { + +.range--type-color .range__handle>div { background: currentColor !important; + } + .range--type-color .range__track-line { background: transparent; } + .range--type-color .range__track-line:after { position: absolute; top: 0; @@ -162,30 +238,48 @@ body { opacity: 0.5; cursor:pointer; } + .range--color-hue .range__handle { color: red; } + .range--color-hue .range__track { color: red; } + .range--color-hue .range__track-line:after { - background: linear-gradient(to right, red, yellow, lime, cyan, blue, magenta, red); + background: linear-gradient( + to right, + red, + yellow, + lime, + cyan, + blue, + magenta, + red + ); } + .range--color-saturation .range__handle { color: red; } + .range--color-saturation .range__track { color: red; } + .range--color-saturation .range__track-line:after { background: linear-gradient(to right, gray, currentColor); } + .range--color-lightness .range__handle { color: red; } + .range--color-lightness .range__track { color: red; } + .range--color-lightness .range__track-line:after { background: linear-gradient(to right, black, currentColor, white); } @@ -200,22 +294,33 @@ body { justify-content: space-between; opacity: 0; } + .stats:not(:last-child) { margin-bottom: 1.5em; } -.stats > i { + +.stats>i { + display: block; color: rgba(0, 0, 0, 0.5); font-size: 0.9em; } -.stats > b { + +.stats>b { display: block; font-size: 0.9em; } -.stats > b > i { + +.stats>b>i { font-size: 0.75em; + } + +.stats[name="worst-time"] { + + .stats[name=worst-time] { + display: none; } @@ -228,34 +333,44 @@ body { line-height: 0.75; perspective: 100rem; opacity: 0; + } + .text i { display: inline-block; opacity: 0; white-space: pre-wrap; } + .text--title { bottom: 75%; font-size: 4.4em; height: 1.2em; } + .text--title span { display: block; } + .text--title span:first-child { font-size: 0.5em; margin-bottom: 0.2em; + margin-top: 28px; } + .text--note { top: 87%; font-size: 1em; } + .text--timer { bottom: 78%; font-size: 3.5em; line-height: 1; } -.text--complete, .text--best-time { + +.text--complete, +.text--best-time { font-size: 1.5em; top: 83%; line-height: 1em; @@ -275,11 +390,17 @@ body { color: rgba(0, 0, 0, 0.25); opacity: 0; } + .btn:hover { color: rgba(203, 18, 18, 0.5); transition: color 400ms ease-out; cursor:pointer; + + color: rgb(245, 245, 245); + transition: color 400ms ease-out; + background-color: #541a8be7; } + .btn:after { cursor: pointer; position: absolute; @@ -292,25 +413,95 @@ body { margin-top: -1.5em; border-radius: 100%; } + + +/*The back button was changed to bottom because it interrupts the info button.*/ + .btn--tl { top: 1.2em; left: 1.2em; + + height: 60px; + width: 60px; + padding-left: 12px; + border-radius: 8px; + cursor: pointer; + background-color: #541a8b24; + + left: 1.2em; + } + .btn--bl { bottom: 1.2em; left: 1.2em; + height: 60px; + width: 60px; + padding-left: 10px; + border-radius: 8px; + cursor: pointer; + background-color: #541a8b24; } + .btn--br { bottom: 1.2em; right: 1.2em; + height: 60px; + width: 60px; + padding-left: 11px; + border-radius: 8px; + cursor: pointer; + background-color: #541a8b24; } + .btn--bc { bottom: 0.8em; left: calc(50% - 0.5em); } + + +/* For volume button */ + +.btn--tr { + top: 1.2em; + right: 1.2em; + opacity: 1; + pointer-events: auto; + height: 60px; + width: 60px; + border-radius: 8px; + cursor: pointer; + background-color: #541a8b24; +} + + +/* For info button */ + +.btn--in { + top: 1.2em; + left: 1.2em; + opacity: 1; + pointer-events: auto; + height: 60px; + width: 60px; + border-radius: 8px; + cursor: pointer; + background-color: #541a8b24; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px 5px; + color: #333; + z-index: 10; +} + .btn svg { display: block; } + .btn--cancel { display: none !important; } @@ -318,8 +509,17 @@ body { .ui { pointer-events: none; color: #070d15; + } -.ui, .ui__background, .ui__game, .ui__texts, .ui__prefs, .ui__theme, .ui__stats, .ui__buttons { + +.ui, +.ui__background, +.ui__game, +.ui__texts, +.ui__prefs, +.ui__theme, +.ui__stats, +.ui__buttons { position: absolute; top: 0; left: 0; @@ -327,11 +527,13 @@ body { height: 100%; overflow: hidden; } + .ui__background { z-index: 1; transition: background 500ms ease; background: #d1d5db; } + .ui__background:after { position: absolute; top: 0; @@ -339,21 +541,32 @@ body { width: 100%; height: 100%; content: ""; - background-image: linear-gradient(to bottom, white 50%, rgba(255, 255, 255, 0) 100%); + background-image: linear-gradient( + to bottom, + white 50%, + rgba(255, 255, 255, 0) 100% + ); } + .ui__game { pointer-events: all; z-index: 2; + background: linear-gradient(to bottom, #fffffff0, #7dadb7f0); } + .ui__game canvas { display: block; width: 100%; height: 100%; } + .ui__texts { z-index: 3; } -.ui__prefs, .ui__stats, .ui__theme { + +.ui__prefs, +.ui__stats, +.ui__theme { display: flex; flex-flow: column nowrap; justify-content: center; @@ -361,16 +574,293 @@ body { overflow: hidden; z-index: 4; } + .ui__theme { padding-top: 15em; } + .ui__buttons { z-index: 5; } + + +/* Replace -ms-high-constrast styling with the new Forced Colors Mode standard */ + +/* Original styling */ + + +/* Updated styling for Forced Colors Mode */ +@media (forced-colors: active) { + /* Apply styling for Forced Colors Mode */ + @media (forced-colors: active) { + + /* Update styles accordingly */ + *, + *:before, + *:after { + color: ButtonText; + background-color: ButtonFace; + /* Add other properties as needed */ + } + } + + /* Apply styling for high contrast mode in Microsoft Edge */ + @media screen and (-ms-high-constrast: active), + (-ms-high-constrast: none) { + + /* Update styles accordingly */ + *, + *:before, + *:after { + -webkit-text-fill-color: WindowText; + -webkit-background-color: Window; + color: WindowText; + background-color: Window; + /* Add other properties as needed */ + } + } + + /* Styling for Google Sign-in button */ + .g_id_signin { + margin-top: 20px; + } + + /* Styles for Forced Colors Mode */ + /* Update styles accordingly */ +} + /* styling element for the goggle login option to be placed at the top right corner of the webpage. */ + +.navbar .g_id_signin { + position: relative; + z-index: 160; +} + +/* styling the navbar issue Back option on leaderboad page #112 */ + +.navbar{ + border: 1px solid white; + width: 80vw; + position: relative; + justify-content: center; + z-index: 15; + align-items: center; + margin: auto; + border-radius: 40px; + padding: 20px 0; + background: linear-gradient(to bottom, #fffffff0, #7ba9b3f0); +} +.navbar ul{ + display: flex; + align-items: center; + justify-content: space-around; + flex-wrap: wrap; +} +.navbar ul li{ + list-style: none; +} +a{ + text-decoration: none; + color: black; + transition-duration: 0.3s; + width: 100%; + font-size: 0.7em; +} + .g_id_signin { margin-top: 20px; -} \ No newline at end of file +} + + + +/* HOW TO PLAY CSS */ + + +/* Body styling */ +body { + line-height: 1.2; + color: #ffffff; + width: 100%; + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + padding-right: 1300px; + padding-bottom: 500px; +} + +/* Center section styling */ +.sec-center { + position: relative; + max-width: 100%; + text-align: center; + z-index: 200; +} + +/* Hide checkboxes */ +[type="checkbox"] { + position: absolute; + left: -9999px; + opacity: 0; + pointer-events: none; +} + +/* Dropdown button styling */ +.dropdown:checked + label, +.dropdown:not(:checked) + label { + position: relative; + line-height: 2; + height: 50px; + transition: all 200ms linear; + border-radius: 4px; + width: 220px; + letter-spacing: 1px; + display: inline-flex; + align-items: center; + justify-content: center; + text-align: center; + border: none; + background-color: #a188eac4; + cursor: pointer; + color: #102770; + box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px, rgba(10, 37, 64, 0.35) 0px -2px 6px 0px inset; +} + + +/* Dropdown content styling */ +.section-dropdown { + position: absolute; + padding: 5px; + background-color: #5a29edd8; + top: 70px; + left: 0; + width: 400%; + border-radius: 10px; + display: block; + box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px, rgba(10, 37, 64, 0.35) 0px -2px 6px 0px inset; + z-index: 2; + opacity: 0; + pointer-events: none; + transform: translateY(20px); + transition: all 200ms linear; +} + +.dropdown:checked ~ .section-dropdown { + opacity: 1; + pointer-events: auto; + transform: translateY(0); +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + color: #333; + +.profile-menu { + position: relative; + display: inline-block; + z-index: 11; + margin-left: auto; + top: 40px; /* Move lower by 10px */ +} + +.profile-btn { + /* Blue background for the button */ + border: none; + color: white; + padding: 10px; + border-radius: 50%; + /* Make the button circular */ + cursor: pointer; + outline: none; + /* Optional: add a border */ + width: 10rem; + /* Set the width of the button */ + height: 10rem; + /* Set the height of the button */ + background-image: url('images/profilepic.jpg'); + background-size: cover; + /* Cover the entire button */ + background-position: center; + /* Center the background image */ + border-radius: 50%; + /* Makes the button circular */ + cursor: pointer; + margin-right: 25rem; + margin-top: 7rem; +} + +.profile-pic { + width: 40px; + height: 40px; + border-radius: 50%; + /* Ensure the profile picture is circular */ + + z-index: 10; + width: 100%; + height: 100%; + border-radius: 50%; + /* Ensures the image inside is circular */ +} + + +.btn--about { + position: relative; + z-index: 10; /* Ensure it's on top */ +} + + +.dropdown-content { + display: none; + position: absolute; + right: 0; + top: 100%; + background-color: #f9f9f9; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + z-index: 12; + border-radius: 3px; +} + +.dropdown-content a { + color: black; + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown-content a:hover { + background-color: #f1f1f1; +} + +.profile-menu:hover .dropdown-content { + display: block; +} + +@media screen and (max-width: 430px) { + #ui_texts { + font-size: 3.5rem; + } + + #theme_selector_id { + font-size: 3rem; + } + + .profile-btn { + margin-right: 14rem; + margin-top: 12rem; + width: 7rem; + height: 7rem; + } + + .btn--tr { + top: 1.8em; + right: 0.5em; + opacity: 1; + pointer-events: auto; + } +} + diff --git a/twist1.png b/twist1.png new file mode 100644 index 0000000..f3388a5 Binary files /dev/null and b/twist1.png differ diff --git a/twist2.png b/twist2.png new file mode 100644 index 0000000..fce93ee Binary files /dev/null and b/twist2.png differ