diff --git a/.gitignore b/.gitignore index a1c2a23..c73a19c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +.idea +.gradle +/build +.env diff --git a/Project Pitch b/Project Pitch new file mode 100644 index 0000000..46c7db5 --- /dev/null +++ b/Project Pitch @@ -0,0 +1,46 @@ +1. A one-liner (exactly 1 sentence) that describes your project. +Fly forwards and avoid obstacles with your jetpack to get as far as possible in this action-packed 2D game, where you can upgrade your jetpack, save your progress and beat your high score. + +2. A list of points (about 5-10 sentences) that describes how your project meets each of the requirement criteria. + +a. Requirement 1: We will use either the arrow keys or the mouse position to move the player and maneuver around obstacles. +b. Requirement 2: We will incorporate non-blocking concurrent processing to update the player’s position and items in the background as the player moves forward. (communicate with outside server or saving game state, use API) +c. Requirement 3: The player’s state is updated as the game progresses, such as the player’s speed. The player’s speed will become increasingly fast. We can also keep track of the player’s accumulated coins so far. +d. Requirement 4: The self-managing iterable data structure will involve adding obstacles to the collection of obstacles and removing them after the player has passed them. + +3. Answers to the below group discussion items + 1. Meetings times: every Monday 1:30pm - 3:30pm + 2. Communication: Discord group + + 3. Roles: + a. Project Manager & Test Maker: Jeevan + b. System Designer: Michelle + c. Architect: Lisa + d. Backend: Mangat + e. UI/UX: Aditya + 4. Expectations: + a. Communicate clearly and document our code clearly + b. We want a 10/10 project + c. Everyone is responsible for making a part of the project unique + + + + + + + +Ideas: + +Doom game with generated levels + Hotline miami + +Jetpack Joyride + +Code to UML Diagram + + + +Meetings time: +Communication, roles, and expectations + + diff --git a/README.md b/README.md index 39f2fd3..76dbd79 100644 --- a/README.md +++ b/README.md @@ -1,115 +1,54 @@ -# Project - -## Technical Project Requirements - -The minimum requirements for the project are outlined here to give you a starting point. Meeting the minimum requirements alone will not guarantee you a good mark. You are welcome to meet and exceed the minimum requirements if you have good, creative ideas and would like to discuss them with me. - -**Requirement 1**: The project must incorporate some visual interface using Processing.org libraries. All user interaction must be conducted via this interface. - -**Requirement 2**: The project must incorporate some kind of non-blocking concurrent/asynchronous processing that happens at regular intervals. For example, you might push or fetch data from in the background. - -**Requirement 3**: The project must incorporate some kind of non-trivial persistent data state that must be read, processed, and written at regular intervals. For example, you might save a game state in a JSON file. This may or may not be included with Requirement 2. - -**Requirement 4**: The project must incorporate some kind of self-managing custom iterable data structure. For example, you might have a collection of enemies that are added and deleted based on statistics maintained by the data structure. - -**Requirement 5**: The project must be well-documented, complete, and run without errors on final submission. - -## Project Pitch (group, 1%) - -The project pitch will be a short document that describes the kind of interactive application you would like to create with your group. The project pitch must include the following items: - -*One-liner*: One-sentence description of your project. -*Outline*: 1-10 sentences that describe how your project will fulfill the project requirements. -*Communication policies*: A description of how your group will meet, communicate, and make decisions (as per Lab 03). -*Roles and responsibilities*: A description of each team member's jobs in the group. -*Milestones*: A rough outline of the major project milestones that you expect to complete and your own estimated timeline. This can and will change, so do your best to estimate and plan for the milestones to change. - -Draft was due today, final due next lab. Submission here, on GitHub. Make a `.md` file that outlines the above. - -## Initial UML Diagrams (group, 1%) - -The initial UML diagrams will outline the class structure that your group will follow for the first milestone of the project. It must include the following items all classes that will be created by the group and important descriptive interfaces from either the Java library or created by the group. I expect that this will change significantly throughout the project, so it does not have to be perfect but it should be a best effort attempt. This is because you will use this to communicate with your group members about what to make. Therefore, the diagram should be *sufficiently complex* to give you a term's worth of work. - -Draft due next Lab, final due two labs from now. Submission here, on GitHub. Suggestion is to use a tool like [draw.io](https://app.diagrams.net/) but you may use whatever tool is most useful for you. - -## Initial GitHub Issues (group, 1%) - -The initial GitHub issues will be the tasks that are assigned to each of your group members at the beginning of the project. Every team member should have at least five issues to start (20-30 total). You will have to decide within your group how granular you want to make these issues. - -Issues will be tracked here, on GitHub. - -## Final Project Demo (group, 1%) - -The Final Project Demo will be a working version of your project that you will present to your lab section for review. The demo will be in lab, and will include a live demo of the working application, and a short code review. There are no slides required, but you should have practiced your demo to make sure it will run reasonably well. This will be during the last lab of class. - -No submission. - -## Final README.md (group, 1%) - -The Final README.md must give instructions on how to run your program, a list of contributions by each member, and any references/citations for code you may have used from elsewhere. - -Submission is here, on GitHub, in the `README.md` file. - -## Final Product (group, 5%) - -The Final Product will be evaluated for overall code design and documentation and evaluated on the same design principles as individual contributions. If you are below the 1000 line minimum contribution, your mark will be scaled down for this portion. - -Submission is here, on GitHub. - -## Code Contributions (individual, 15%) - -You will be expected to take on a significant individual contribution to the group project (at least 1000 lines of non-trivial code). It may be in a number of forms, but here are some examples: - -**Architect**: you are in charge of the high-level code structuring and organizing. - -**Test maker**: you are in charge of test coverage that supports other group members. - -**UI/UX lead**: you design and implement the user interface. - -**Backend**: you design and implement the data structures. - -**…??**: Make up your own depending on your use case, i.e., collision system designer, animation architect, async code wrangler. - -Contributions must be for functional, working Java code and must be continuous throughout the term. You may not, for example, push all of your changes at the end of term. Code will be marked on following good design principles, i.e., SOLID, design patterns, etc. You are encouraged to work together and use pair programming for components, but you will be marked on your contribution to your own modules individually. - -## Documentation Contributions (individual, 5%) - -Your code must be well-documented with fully-formed method signatures, comments, and necessary README or Wiki pages. This is further broken down into the following. - -### Initial individual pitch (1%) -A description of your individual feature that you plan to implement. - -Due date TBD. - -### Initial individual UML Diagrams (1%) -Any combination of sequence, communcation, or class diagrams that describe your feature's initial planned abilities. - -Due date TBD. - -### Documentation contributions (3%) -Your personal feature documentation, wherever it happens to show up in the final documentation. - -Due with final submission. - -## Issues and Pull Request Contributions (individual, 5%) -You must track your own work in the form of creating and closing GitHub issues, creating and reviewing pull requests, responding to issues that have been assigned to you, and creating issues that you assign to others (all within reason). - -# Errata -The project MUST be managed here, in this GitHub repo. Nothing that happens outside of this GitHub repo will be trackable by me, therefore, it will not be marked or considered for marking. - -You must use the following branching structures: -- `main` branch must always be working, tested, debugged, human-readable code. -- `__` is the format for each ISSUE that you're working on. It should always be a branch off of `main`. -- Every individual branch must regularly merge `main`, and it should be no more than a few days before your branch is either merged into `main` or deleted. - -You must use pull requests to manage your code integration: -- make your branch from `main`, e.g., `git checkout -b pb_71_demo` -- make your commits, e.g., `git add .` and `git commit -m "fixed issue 71 by reloading gradle for the 100th time"` and `git push origin pb_71_demo`. -- merge `main` into your branch, and NOT the other way around. E.g., `git merge main`. YOU fix all the merge conflicts or problems that arise. Commit and push again. -- go to GitHub.com and make a pull request -- one other person (NOT YOU) needs to review the code and either approve or reject your changes with detailed line-by-line comments. -- If needed, make the requested fixes and commit and push again. -- the other person (NOT YOU) will merge your PR into `main` -- the other person (NOT YOU) will delete your branch - - +# Are you the best RocketMan? + +![RocketMan](https://github.com/COMP2522/project-rocketman/blob/main/images/rocket_man_images/rocket_man_2.jpg) + +## How To Play +1. Clone the repository to a folder of your choice. +2. Open the folder in the IDE of your choice. +3. Run `GameManager` to start the game. +4. Use the up arrow key to launch the player upwards. Release the up arrow key to let the player fall back down. +5. Rockets will launch at you - use the arrow keys to avoid them. The longer you survive the higher your score becomes! + +## Tips +- The player's speed will get incrementally faster the longer you survive, so get ready to dodge fast! +- Collect hearts to accumulate extra lives. You lose a life if you hit a rocket. The game is over if you hit a rocket with zero hearts left. +- Collect coins to increase your high score. +- You can check out the high scores on the `Leaderboard` tab on the main menu when you start the game. +- Press 'P' to pause/unpause the game. Pressing 'P' will also bring up the option to restart the game if you would like. + +# Member Contributions +## Mangat Toor +- Window +- Collideable Interface +- Player class +- GameDBManager +- PauseGameUI +- DeadGameUI +- Rocket class +## Aditya Agrawal +- Window class +- Sprite class +- PauseGameUI +- LeaderboardUI +- LeaderboardScore +- Database +## Michelle Hung +- Sprite class +- Background class +- GameManager +- PauseGameUI +## Lisa Zhu +- Coin class +- Heart class +- GameManager +- GameManagerTest +## Jeevan Virk +- Heart class +- HeartTest +- PlayerTest +- RocketTest +- BackgroundTest +- ButtonTest +- CoinTest +## UML Diagram +![Diagram](https://github.com/COMP2522/project-rocketman/blob/main/images/UMLDiagram/FinalUMLDiagram.png) diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5e7fa1d --- /dev/null +++ b/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java' +} + +group 'org.example' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testImplementation 'org.testng:testng:7.1.0' + testImplementation 'junit:junit:4.13.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + implementation fileTree(dir: 'library', include: ['*.jar']) + implementation 'org.mongodb:mongodb-driver-sync:4.9.0' + +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ae04661 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..a69d9cb --- /dev/null +++ b/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f127cfd --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/images/Hearts/download.png b/images/Hearts/download.png new file mode 100644 index 0000000..4bd638b Binary files /dev/null and b/images/Hearts/download.png differ diff --git a/images/Hearts/heart01-fotor-bg-remover-2023032603919.png b/images/Hearts/heart01-fotor-bg-remover-2023032603919.png new file mode 100644 index 0000000..11a1392 Binary files /dev/null and b/images/Hearts/heart01-fotor-bg-remover-2023032603919.png differ diff --git a/images/Hearts/heart01.png b/images/Hearts/heart01.png new file mode 100644 index 0000000..11a1392 Binary files /dev/null and b/images/Hearts/heart01.png differ diff --git a/images/Hearts/heart02.png b/images/Hearts/heart02.png new file mode 100644 index 0000000..6af1d82 Binary files /dev/null and b/images/Hearts/heart02.png differ diff --git a/images/Hearts/heart03.png b/images/Hearts/heart03.png new file mode 100644 index 0000000..f4a0334 Binary files /dev/null and b/images/Hearts/heart03.png differ diff --git a/images/Hearts/heart04.png b/images/Hearts/heart04.png new file mode 100644 index 0000000..93ed8b5 Binary files /dev/null and b/images/Hearts/heart04.png differ diff --git a/images/Hearts/heart05.png b/images/Hearts/heart05.png new file mode 100644 index 0000000..6f6014e Binary files /dev/null and b/images/Hearts/heart05.png differ diff --git a/images/Hearts/heart1.png b/images/Hearts/heart1.png new file mode 100644 index 0000000..a839e0b Binary files /dev/null and b/images/Hearts/heart1.png differ diff --git a/images/Hearts/heart1.webp b/images/Hearts/heart1.webp new file mode 100644 index 0000000..e9db641 Binary files /dev/null and b/images/Hearts/heart1.webp differ diff --git a/images/UMLDiagram/FinalUMLDiagram.png b/images/UMLDiagram/FinalUMLDiagram.png new file mode 100644 index 0000000..c72195e Binary files /dev/null and b/images/UMLDiagram/FinalUMLDiagram.png differ diff --git a/images/rocket_images/rocket_1.png b/images/rocket_images/rocket_1.png new file mode 100644 index 0000000..c13055d Binary files /dev/null and b/images/rocket_images/rocket_1.png differ diff --git a/images/rocket_images/rocket_2.png b/images/rocket_images/rocket_2.png new file mode 100644 index 0000000..24b30af Binary files /dev/null and b/images/rocket_images/rocket_2.png differ diff --git a/images/rocket_images/rocket_3.png b/images/rocket_images/rocket_3.png new file mode 100644 index 0000000..2c20294 Binary files /dev/null and b/images/rocket_images/rocket_3.png differ diff --git a/images/rocket_man_backgrounds/PinkBackground.png b/images/rocket_man_backgrounds/PinkBackground.png new file mode 100644 index 0000000..def8541 Binary files /dev/null and b/images/rocket_man_backgrounds/PinkBackground.png differ diff --git a/images/rocket_man_backgrounds/Start.png b/images/rocket_man_backgrounds/Start.png new file mode 100644 index 0000000..ecba18d Binary files /dev/null and b/images/rocket_man_backgrounds/Start.png differ diff --git a/images/rocket_man_backgrounds/backgroud2.jpg b/images/rocket_man_backgrounds/backgroud2.jpg new file mode 100644 index 0000000..caee482 Binary files /dev/null and b/images/rocket_man_backgrounds/backgroud2.jpg differ diff --git a/images/rocket_man_backgrounds/main.png b/images/rocket_man_backgrounds/main.png new file mode 100644 index 0000000..a9d2c2b Binary files /dev/null and b/images/rocket_man_backgrounds/main.png differ diff --git a/images/rocket_man_coins/Daco_4263670.png b/images/rocket_man_coins/Daco_4263670.png new file mode 100644 index 0000000..becd1f0 Binary files /dev/null and b/images/rocket_man_coins/Daco_4263670.png differ diff --git a/images/rocket_man_coins/star coin rotate 1.png b/images/rocket_man_coins/star coin rotate 1.png new file mode 100644 index 0000000..5512788 Binary files /dev/null and b/images/rocket_man_coins/star coin rotate 1.png differ diff --git a/images/rocket_man_coins/star coin rotate 2.png b/images/rocket_man_coins/star coin rotate 2.png new file mode 100644 index 0000000..3c291f3 Binary files /dev/null and b/images/rocket_man_coins/star coin rotate 2.png differ diff --git a/images/rocket_man_coins/star coin rotate 3.png b/images/rocket_man_coins/star coin rotate 3.png new file mode 100644 index 0000000..07ced52 Binary files /dev/null and b/images/rocket_man_coins/star coin rotate 3.png differ diff --git a/images/rocket_man_coins/star coin rotate 4.png b/images/rocket_man_coins/star coin rotate 4.png new file mode 100644 index 0000000..5404377 Binary files /dev/null and b/images/rocket_man_coins/star coin rotate 4.png differ diff --git a/images/rocket_man_coins/star coin rotate 5.png b/images/rocket_man_coins/star coin rotate 5.png new file mode 100644 index 0000000..f5d8268 Binary files /dev/null and b/images/rocket_man_coins/star coin rotate 5.png differ diff --git a/images/rocket_man_coins/star coin rotate 6.png b/images/rocket_man_coins/star coin rotate 6.png new file mode 100644 index 0000000..b2eafa4 Binary files /dev/null and b/images/rocket_man_coins/star coin rotate 6.png differ diff --git a/images/rocket_man_images/Flameonly.png b/images/rocket_man_images/Flameonly.png new file mode 100644 index 0000000..5373858 Binary files /dev/null and b/images/rocket_man_images/Flameonly.png differ diff --git a/images/rocket_man_images/My project.png b/images/rocket_man_images/My project.png new file mode 100644 index 0000000..395d67d Binary files /dev/null and b/images/rocket_man_images/My project.png differ diff --git a/images/rocket_man_images/My project2.png b/images/rocket_man_images/My project2.png new file mode 100644 index 0000000..91c6c22 Binary files /dev/null and b/images/rocket_man_images/My project2.png differ diff --git a/images/rocket_man_images/bigflame.png b/images/rocket_man_images/bigflame.png new file mode 100644 index 0000000..46548e7 Binary files /dev/null and b/images/rocket_man_images/bigflame.png differ diff --git a/images/rocket_man_images/colorflame.png b/images/rocket_man_images/colorflame.png new file mode 100644 index 0000000..26e15c9 Binary files /dev/null and b/images/rocket_man_images/colorflame.png differ diff --git a/images/rocket_man_images/flame.png b/images/rocket_man_images/flame.png new file mode 100644 index 0000000..79d53b6 Binary files /dev/null and b/images/rocket_man_images/flame.png differ diff --git a/images/rocket_man_images/flames.png b/images/rocket_man_images/flames.png new file mode 100644 index 0000000..72a6240 Binary files /dev/null and b/images/rocket_man_images/flames.png differ diff --git a/images/rocket_man_images/flametrasparene.png b/images/rocket_man_images/flametrasparene.png new file mode 100644 index 0000000..cca178a Binary files /dev/null and b/images/rocket_man_images/flametrasparene.png differ diff --git a/images/rocket_man_images/rocket_man_1.png b/images/rocket_man_images/rocket_man_1.png new file mode 100644 index 0000000..2eb922f Binary files /dev/null and b/images/rocket_man_images/rocket_man_1.png differ diff --git a/images/rocket_man_images/rocket_man_2.jpg b/images/rocket_man_images/rocket_man_2.jpg new file mode 100644 index 0000000..36ecbc7 Binary files /dev/null and b/images/rocket_man_images/rocket_man_2.jpg differ diff --git a/images/rocket_man_images/rocket_man_2.png b/images/rocket_man_images/rocket_man_2.png new file mode 100644 index 0000000..4071946 Binary files /dev/null and b/images/rocket_man_images/rocket_man_2.png differ diff --git a/library/core.jar b/library/core.jar new file mode 100644 index 0000000..5b410ab Binary files /dev/null and b/library/core.jar differ diff --git a/library/gluegen-rt.jar b/library/gluegen-rt.jar new file mode 100644 index 0000000..d92fbcc Binary files /dev/null and b/library/gluegen-rt.jar differ diff --git a/library/javamp3-1.0.4.jar b/library/javamp3-1.0.4.jar new file mode 100644 index 0000000..dbc3217 Binary files /dev/null and b/library/javamp3-1.0.4.jar differ diff --git a/library/jogl-all.jar b/library/jogl-all.jar new file mode 100644 index 0000000..e11abbf Binary files /dev/null and b/library/jogl-all.jar differ diff --git a/library/json-simple-1.1.jar b/library/json-simple-1.1.jar new file mode 100644 index 0000000..f395f41 Binary files /dev/null and b/library/json-simple-1.1.jar differ diff --git a/library/jsyn-20171016.jar b/library/jsyn-20171016.jar new file mode 100644 index 0000000..dfb0ce7 Binary files /dev/null and b/library/jsyn-20171016.jar differ diff --git a/library/linux-aarch64/libgluegen_rt.so b/library/linux-aarch64/libgluegen_rt.so new file mode 100644 index 0000000..512015b Binary files /dev/null and b/library/linux-aarch64/libgluegen_rt.so differ diff --git a/library/linux-aarch64/libjogl_desktop.so b/library/linux-aarch64/libjogl_desktop.so new file mode 100644 index 0000000..7e98c64 Binary files /dev/null and b/library/linux-aarch64/libjogl_desktop.so differ diff --git a/library/linux-aarch64/libjogl_mobile.so b/library/linux-aarch64/libjogl_mobile.so new file mode 100644 index 0000000..05ad1aa Binary files /dev/null and b/library/linux-aarch64/libjogl_mobile.so differ diff --git a/library/linux-aarch64/libnativewindow_awt.so b/library/linux-aarch64/libnativewindow_awt.so new file mode 100644 index 0000000..6e6de87 Binary files /dev/null and b/library/linux-aarch64/libnativewindow_awt.so differ diff --git a/library/linux-aarch64/libnativewindow_drm.so b/library/linux-aarch64/libnativewindow_drm.so new file mode 100644 index 0000000..482d59f Binary files /dev/null and b/library/linux-aarch64/libnativewindow_drm.so differ diff --git a/library/linux-aarch64/libnativewindow_x11.so b/library/linux-aarch64/libnativewindow_x11.so new file mode 100644 index 0000000..f9232a5 Binary files /dev/null and b/library/linux-aarch64/libnativewindow_x11.so differ diff --git a/library/linux-aarch64/libnewt_drm.so b/library/linux-aarch64/libnewt_drm.so new file mode 100644 index 0000000..74001d3 Binary files /dev/null and b/library/linux-aarch64/libnewt_drm.so differ diff --git a/library/linux-aarch64/libnewt_head.so b/library/linux-aarch64/libnewt_head.so new file mode 100644 index 0000000..e96960f Binary files /dev/null and b/library/linux-aarch64/libnewt_head.so differ diff --git a/library/linux-amd64/libgluegen_rt.so b/library/linux-amd64/libgluegen_rt.so new file mode 100644 index 0000000..6222112 Binary files /dev/null and b/library/linux-amd64/libgluegen_rt.so differ diff --git a/library/linux-amd64/libjogl_desktop.so b/library/linux-amd64/libjogl_desktop.so new file mode 100644 index 0000000..12f3b5a Binary files /dev/null and b/library/linux-amd64/libjogl_desktop.so differ diff --git a/library/linux-amd64/libjogl_mobile.so b/library/linux-amd64/libjogl_mobile.so new file mode 100644 index 0000000..1f941cb Binary files /dev/null and b/library/linux-amd64/libjogl_mobile.so differ diff --git a/library/linux-amd64/libnativewindow_awt.so b/library/linux-amd64/libnativewindow_awt.so new file mode 100644 index 0000000..78e7849 Binary files /dev/null and b/library/linux-amd64/libnativewindow_awt.so differ diff --git a/library/linux-amd64/libnativewindow_drm.so b/library/linux-amd64/libnativewindow_drm.so new file mode 100644 index 0000000..567a4ba Binary files /dev/null and b/library/linux-amd64/libnativewindow_drm.so differ diff --git a/library/linux-amd64/libnativewindow_x11.so b/library/linux-amd64/libnativewindow_x11.so new file mode 100644 index 0000000..6cc0922 Binary files /dev/null and b/library/linux-amd64/libnativewindow_x11.so differ diff --git a/library/linux-amd64/libnewt_drm.so b/library/linux-amd64/libnewt_drm.so new file mode 100644 index 0000000..3fe44f6 Binary files /dev/null and b/library/linux-amd64/libnewt_drm.so differ diff --git a/library/linux-amd64/libnewt_head.so b/library/linux-amd64/libnewt_head.so new file mode 100644 index 0000000..76712a5 Binary files /dev/null and b/library/linux-amd64/libnewt_head.so differ diff --git a/library/linux-arm/libgluegen_rt.so b/library/linux-arm/libgluegen_rt.so new file mode 100644 index 0000000..68bdda7 Binary files /dev/null and b/library/linux-arm/libgluegen_rt.so differ diff --git a/library/linux-arm/libjogl_desktop.so b/library/linux-arm/libjogl_desktop.so new file mode 100644 index 0000000..9413d57 Binary files /dev/null and b/library/linux-arm/libjogl_desktop.so differ diff --git a/library/linux-arm/libjogl_mobile.so b/library/linux-arm/libjogl_mobile.so new file mode 100644 index 0000000..8f016ea Binary files /dev/null and b/library/linux-arm/libjogl_mobile.so differ diff --git a/library/linux-arm/libnativewindow_awt.so b/library/linux-arm/libnativewindow_awt.so new file mode 100644 index 0000000..941697a Binary files /dev/null and b/library/linux-arm/libnativewindow_awt.so differ diff --git a/library/linux-arm/libnativewindow_drm.so b/library/linux-arm/libnativewindow_drm.so new file mode 100644 index 0000000..fba0800 Binary files /dev/null and b/library/linux-arm/libnativewindow_drm.so differ diff --git a/library/linux-arm/libnativewindow_x11.so b/library/linux-arm/libnativewindow_x11.so new file mode 100644 index 0000000..bd249bf Binary files /dev/null and b/library/linux-arm/libnativewindow_x11.so differ diff --git a/library/linux-arm/libnewt_drm.so b/library/linux-arm/libnewt_drm.so new file mode 100644 index 0000000..b8cde98 Binary files /dev/null and b/library/linux-arm/libnewt_drm.so differ diff --git a/library/linux-arm/libnewt_head.so b/library/linux-arm/libnewt_head.so new file mode 100644 index 0000000..87c31ed Binary files /dev/null and b/library/linux-arm/libnewt_head.so differ diff --git a/library/macos-aarch64/libgluegen_rt.dylib b/library/macos-aarch64/libgluegen_rt.dylib new file mode 100644 index 0000000..2813350 Binary files /dev/null and b/library/macos-aarch64/libgluegen_rt.dylib differ diff --git a/library/macos-aarch64/libjogl_desktop.dylib b/library/macos-aarch64/libjogl_desktop.dylib new file mode 100644 index 0000000..9a90c21 Binary files /dev/null and b/library/macos-aarch64/libjogl_desktop.dylib differ diff --git a/library/macos-aarch64/libjogl_mobile.dylib b/library/macos-aarch64/libjogl_mobile.dylib new file mode 100644 index 0000000..90fbcc7 Binary files /dev/null and b/library/macos-aarch64/libjogl_mobile.dylib differ diff --git a/library/macos-aarch64/libnativewindow_awt.dylib b/library/macos-aarch64/libnativewindow_awt.dylib new file mode 100644 index 0000000..a417903 Binary files /dev/null and b/library/macos-aarch64/libnativewindow_awt.dylib differ diff --git a/library/macos-aarch64/libnativewindow_macosx.dylib b/library/macos-aarch64/libnativewindow_macosx.dylib new file mode 100644 index 0000000..0c94974 Binary files /dev/null and b/library/macos-aarch64/libnativewindow_macosx.dylib differ diff --git a/library/macos-aarch64/libnewt_head.dylib b/library/macos-aarch64/libnewt_head.dylib new file mode 100644 index 0000000..b23a557 Binary files /dev/null and b/library/macos-aarch64/libnewt_head.dylib differ diff --git a/library/macos-x86_64/libgluegen_rt.dylib b/library/macos-x86_64/libgluegen_rt.dylib new file mode 100644 index 0000000..ba356f6 Binary files /dev/null and b/library/macos-x86_64/libgluegen_rt.dylib differ diff --git a/library/macos-x86_64/libjogl_desktop.dylib b/library/macos-x86_64/libjogl_desktop.dylib new file mode 100644 index 0000000..0133fe9 Binary files /dev/null and b/library/macos-x86_64/libjogl_desktop.dylib differ diff --git a/library/macos-x86_64/libjogl_mobile.dylib b/library/macos-x86_64/libjogl_mobile.dylib new file mode 100644 index 0000000..bc1e6bc Binary files /dev/null and b/library/macos-x86_64/libjogl_mobile.dylib differ diff --git a/library/macos-x86_64/libnativewindow_awt.dylib b/library/macos-x86_64/libnativewindow_awt.dylib new file mode 100644 index 0000000..f74a89e Binary files /dev/null and b/library/macos-x86_64/libnativewindow_awt.dylib differ diff --git a/library/macos-x86_64/libnativewindow_macosx.dylib b/library/macos-x86_64/libnativewindow_macosx.dylib new file mode 100644 index 0000000..14b4769 Binary files /dev/null and b/library/macos-x86_64/libnativewindow_macosx.dylib differ diff --git a/library/macos-x86_64/libnewt_head.dylib b/library/macos-x86_64/libnewt_head.dylib new file mode 100644 index 0000000..32b507f Binary files /dev/null and b/library/macos-x86_64/libnewt_head.dylib differ diff --git a/library/sound.jar b/library/sound.jar new file mode 100644 index 0000000..4074b6d Binary files /dev/null and b/library/sound.jar differ diff --git a/library/windows-amd64/fenster.exe b/library/windows-amd64/fenster.exe new file mode 100644 index 0000000..8108d17 Binary files /dev/null and b/library/windows-amd64/fenster.exe differ diff --git a/library/windows-amd64/gluegen_rt.dll b/library/windows-amd64/gluegen_rt.dll new file mode 100644 index 0000000..65b2f6b Binary files /dev/null and b/library/windows-amd64/gluegen_rt.dll differ diff --git a/library/windows-amd64/jogl_desktop.dll b/library/windows-amd64/jogl_desktop.dll new file mode 100644 index 0000000..5c62f78 Binary files /dev/null and b/library/windows-amd64/jogl_desktop.dll differ diff --git a/library/windows-amd64/jogl_mobile.dll b/library/windows-amd64/jogl_mobile.dll new file mode 100644 index 0000000..fc6dfdc Binary files /dev/null and b/library/windows-amd64/jogl_mobile.dll differ diff --git a/library/windows-amd64/nativewindow_awt.dll b/library/windows-amd64/nativewindow_awt.dll new file mode 100644 index 0000000..8c0c64f Binary files /dev/null and b/library/windows-amd64/nativewindow_awt.dll differ diff --git a/library/windows-amd64/nativewindow_win32.dll b/library/windows-amd64/nativewindow_win32.dll new file mode 100644 index 0000000..d5ed2f3 Binary files /dev/null and b/library/windows-amd64/nativewindow_win32.dll differ diff --git a/library/windows-amd64/newt_head.dll b/library/windows-amd64/newt_head.dll new file mode 100644 index 0000000..25c12f0 Binary files /dev/null and b/library/windows-amd64/newt_head.dll differ diff --git a/music/background.mp3 b/music/background.mp3 new file mode 100644 index 0000000..a321333 Binary files /dev/null and b/music/background.mp3 differ diff --git a/music/background_babbu.mp3 b/music/background_babbu.mp3 new file mode 100644 index 0000000..8b45b1e Binary files /dev/null and b/music/background_babbu.mp3 differ diff --git a/music/coin.wav b/music/coin.wav new file mode 100644 index 0000000..0660456 Binary files /dev/null and b/music/coin.wav differ diff --git a/music/heart.wav b/music/heart.wav new file mode 100644 index 0000000..7eb7b76 Binary files /dev/null and b/music/heart.wav differ diff --git a/music/rocket.wav b/music/rocket.wav new file mode 100644 index 0000000..06548ad Binary files /dev/null and b/music/rocket.wav differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..c89e86e --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'ProjectRocjetman' +include 'src:main:test' +findProject(':src:main:test')?.name = 'test' +include 'UMLDiagram' + diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Background.java b/src/main/java/org/comp2522/ProjectRocketMan/Background.java new file mode 100644 index 0000000..d783098 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Background.java @@ -0,0 +1,123 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.core.PVector; + +/** + * The Background class represents a scrolling background in a game. + * It is used to display images that move behind the game objects. + * + * @author Michelle + * @version 1.0.0 + */ +public class Background extends Sprite { + + /** + * Background image. + * */ + private PImage image; + + /** + * Speed of the image. + * */ + private float speed; + + /** + * Window instance. + * */ + private final Window window; + + /** + * Constructor of Background class. + * + * @param image the image used for the background + * @param speed the speed at which the background moves + * @param position the starting position of the background + * @param direction the direction in which the background moves + */ + public Background(PImage image, float speed, PVector position, PVector direction) { + super(position, direction); + this.window = Window.getInstance(); + this.image = image; + this.speed = speed; + } + + /** + * Constructor of Background class without image. + * + * @param speed the speed at which the background moves + * @param position the starting position of the background + * @param direction the direction in which the background moves + */ + public Background(float speed, PVector position, PVector direction) { + super(position,direction); + this.window = Window.getInstance(); + this.speed = speed; + } + + /** + * Sets the position of this object to the specified PVector position. + * + * @param position the PVector position to set + */ + @Override + public void setPosition(PVector position) { + this.position = position; + } + + /** + * Returns the position of an object as a PVector. + * + * @return the position of the object as a PVector. + */ + @Override + public PVector getPosition() { + return position; + } + + /** + * Overrides the move method from the parent class to move the object in a specific direction. + * Moves the object horizontally to the left by the amount of the speed value. + */ + @Override + public void move() { + position.add(-1 * speed,0); + } + + /** + * Returns the speed of an object as a float. + * + * @return the speed of the object as a float. + */ + @Override + public float getSpeed() { + return speed; + } + + /** + * Sets the speed of this object to the specified number. + * + * @param speed the speed to set + */ + public void setSpeed(float speed) { + this.speed = speed; + } + + /** + * Draws an image on a window with a scrolling effect. + * The image is continuously drawn and scrolled across the window. + */ + public void draw() { + position.y = speed; + position.x -= speed; + + int offsetX = (int) (position.x % image.width - image.width); + int offsetY = (int) (position.y % image.height - image.height); + + for (int x = offsetX; x < image.width; x += image.width) { + for (int y = offsetY; y < image.height; y += image.height) { + window.image(image, x, y); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Button.java b/src/main/java/org/comp2522/ProjectRocketMan/Button.java new file mode 100644 index 0000000..3f9315d --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Button.java @@ -0,0 +1,88 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PVector; + +/** + * Represents a clickable button in a Processing sketch. + * + * @author Aditya Agrawal + * @version 1.0.0 + */ +public class Button { + + /** + * The position of the button on the screen. + */ + private final PVector position; + + /** + * The dimensions of the button (width and height). + */ + private final PVector dimensions; + + /** + * The Processing window in which the button is drawn. + */ + private final Window window; + + /** + * The label text displayed on the button. + */ + private String label; + + /** + * Constructs a new Button object with the given position, dimensions, and label. + * + * @param position The position of the button on the screen. + * @param dimensions The dimensions of the button (width and height). + * @param label The label text displayed on the button. + */ + public Button(PVector position, PVector dimensions, String label) { + this.dimensions = dimensions; + this.position = position; + window = Window.getInstance(); + this.label = label; + } + + /** + * Draws the button on the screen. + */ + public void draw() { + window.stroke(255); + window.fill(0); + window.rect(position.x, position.y, dimensions.x, dimensions.y); + window.fill(255); + window.textAlign(window.CENTER, window.CENTER); + window.text(label, position.x + dimensions.x / 2, position.y + dimensions.y / 2); + } + + /** + * Checks if the button is currently being clicked. + * + * @return true if the button is being clicked, false otherwise. + */ + public boolean isClicked() { + return window.mouseX >= position.x + && window.mouseX <= position.x + dimensions.x + && window.mouseY >= position.y + && window.mouseY <= position.y + dimensions.y; + } + + /** + * Gets the label text displayed on the button. + * + * @return The label text displayed on the button. + */ + public String getLabel() { + return label; + } + + /** + * Sets the label text displayed on the button. + * + * @param label The new label text to display on the button. + */ + public void setLabel(String label) { + this.label = label; + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Coin.java b/src/main/java/org/comp2522/ProjectRocketMan/Coin.java new file mode 100644 index 0000000..936dd5e --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Coin.java @@ -0,0 +1,192 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.core.PVector; + +import static processing.core.PApplet.abs; + +/** + * A class representing a coin in a game. + * Coins are sprites that can move, be destroyed, and collide with other objects in the game. + * + * @author Aditya Agrawal + * @version 1.0.0 + */ +public class Coin extends Sprite implements Collideable { + + /** + * Animations. + */ + private PImage[] animations; + + /** + * Index of the coins. + */ + private int index; + + /** + * Speed. + */ + private float speed; + + /** + * Window instance. + */ + private Window window; + + /** + * Height of the coins. + */ + private float height; + + /** + * Width of the coins. + */ + private float width; + + /** + * Constructor for the Coin object in the game. + * + * @param position the position of the coin in the game world. + * @param direction the direction in which the coin moves. + * @param animations an array of PImages representing the animations of the coin. + * @param speed the speed at which the coin moves. + */ + public Coin(PVector position, PVector direction, PImage[] animations, float speed) { + super(position, direction); + this.speed = speed; + this.window = Window.getInstance(); + this.index = (int) window.random(0, 6); + this.animations = animations; + this.height = animations[0].width / 50f; + this.width = animations[0].width / 50f; + } + + /** + * Constructor for the coin class without animations array. + * + * @param position the position of the coin in the game world. + * @param direction the direction in which the coin moves. + * @param speed the speed at which the coin moves. + */ + public Coin(PVector position, PVector direction, float speed) { + super(position, direction); + this.speed = speed; + } + + /** + * Set the position of the object. + * + * @param position the position of the coin. + */ + @Override + public void setPosition(PVector position) { + this.position = position; + } + + /** + * Returns the position of the object. + * + * @return the position of the object + */ + public PVector getPosition() { + return position; + } + + /** + * Moves the object by updating its position along the x-axis. + */ + @Override + public void move() { + position.add(-1 * speed, 0); + } + + /** + * Returns the speed of the object. + * + * @return the speed of the object + */ + @Override + public float getSpeed() { + return speed; + } + + /** + * Returns the direction of the coin object. + * + * @return direction + */ + public PVector getDirection() { + return direction; + } + + /** + * Set the speed of the object. + * + * @param speed a float + */ + @Override + public void setSpeed(float speed) { + this.speed = speed; + } + + /** + * Draws the animated object on the screen with its current position and animation frame. + */ + public void draw() { + window.image(animations[index % animations.length], + position.x, position.y, + animations[0].width / 50f, + animations[0].height / 50f); + animate(); + window.stroke(0); // set the stroke color to black + window.fill(255, 0, 0); // set the fill color to red + window.ellipse(position.x, position.y, 4, 4); + } + + /** + * Animation function for the coin object. + */ + private void animate() { + if (window.frameCount % 6 == 0) { + this.index += this.speed; + } + } + + /** + * Determines whether the player collides with the current object. + * + * @param player The player object to check for collision with. + * @return True if the player collides with the object, false otherwise. + */ + @Override + public boolean collided(Player player) { + float xDistance = this.position.x - player.position.x; + float yDistance = this.position.y - player.position.y; + float xDistanceOffset = player.getWidth() * .3f; + float yDistanceOffset = player.getHeight() * .3f; + return abs(yDistance) + < player.getHeight() + - xDistanceOffset && abs(xDistance) + < player.getWidth() - yDistanceOffset; + } + + /** + * Returns the height of the object. + * + * @return the height of the object + */ + public float getHeight() { + return height; + } + + /** + * Returns the width of the object. + * + * @return the width of the object + */ + public float getWidth() { + return width; + } + +} \ No newline at end of file diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Collideable.java b/src/main/java/org/comp2522/ProjectRocketMan/Collideable.java new file mode 100644 index 0000000..a80ad0a --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Collideable.java @@ -0,0 +1,17 @@ +package org.comp2522.ProjectRocketMan; + + +/** + * Defines all the methods a colliable object will implement. + */ +public interface Collideable { + + /** + * Checks if the class that implements collidable has collided with Player. + * + * @param player The Player that object might collide with. + * @return true if collided. + */ + boolean collided(Player player); + +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Database.java b/src/main/java/org/comp2522/ProjectRocketMan/Database.java new file mode 100644 index 0000000..674ca7e --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Database.java @@ -0,0 +1,122 @@ +package org.comp2522.ProjectRocketMan; + +import com.mongodb.ConnectionString; +import com.mongodb.MongoClientSettings; +import com.mongodb.ServerApi; +import com.mongodb.ServerApiVersion; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.mongodb.client.result.InsertOneResult; +import org.bson.BsonObjectId; +import org.bson.Document; +import org.bson.conversions.Bson; + +import java.util.ArrayList; +import java.util.Objects; + +import static com.mongodb.client.model.Sorts.descending; + +/** + * Database class. + * + * @author Aditya Agrawal + * @version 1.0.0 + * */ +public class Database { + + /** + * Password for mongodb. + */ + private final static String mongoPassword = "aditya123"; + + /** + * Database instance. + */ + private static Database database; + + /** + * MongoDB connection instance. + */ + MongoDatabase mongoDB; + + /** + * Private constructor for Database class for singleton purposes. + */ + private Database() { + ConnectionString connectionString = new ConnectionString("mongodb+srv://aditya:" + + mongoPassword + + "@comp-2522.jyuxhsa.mongodb.net/?retryWrites=true&w=majority"); + MongoClientSettings settings = MongoClientSettings.builder() + .applyConnectionString(connectionString) + .serverApi(ServerApi.builder() + .version(ServerApiVersion.V1) + .build()) + .build(); + MongoClient mongoClient = MongoClients.create(settings); + mongoDB = mongoClient.getDatabase("test"); + } + + /** + * Get instance of the Database object, for singleton purposes. + * + * @return the Database instance + */ + public static Database getInstance() { + if (database == null) { + database = new Database(); + } + return database; + } + + /** + * Initial score document function. Called when a game first starts running. + * + * @return BsonObjectId + */ + public BsonObjectId createInitScoreDocument() { + Document document = new Document(); + document.append("hearts", 0); + document.append("coins", 0); + document.append("score", 0); + InsertOneResult res = mongoDB.getCollection("scores").insertOne(document); + return Objects.requireNonNull(res.getInsertedId()).asObjectId(); + } + + /** + * Update the game score in the same MongoDB document as the game proceeds. + * + * @param id a BsonObjectId + * @param hearts an int + * @param coins an int + * @param score an int + */ + public void updateGameScore(BsonObjectId id, int hearts, int coins, int score) { + Bson updates = Updates.combine( + Updates.set("hearts", hearts), + Updates.set("coins", coins), + Updates.set("score", score)); + new Thread(() -> mongoDB.getCollection("scores") + .findOneAndUpdate(Filters.eq("_id", id), updates)).start(); + } + + /** + * Get the global leaderboard for the rocket-man game. + * + * @return an ArrayList of Documents + */ + public ArrayList getLeaderBoard() { + ArrayList scores = new ArrayList<>(); + try (MongoCursor cursor = mongoDB.getCollection("scores") + .find() + .sort(descending("score")).limit(5).iterator()) { + while (cursor.hasNext()) { + scores.add(cursor.next()); + } + } + return scores; + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/DeadGameUI.java b/src/main/java/org/comp2522/ProjectRocketMan/DeadGameUI.java new file mode 100644 index 0000000..bda82b3 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/DeadGameUI.java @@ -0,0 +1,53 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.event.KeyEvent; + +/** + * DeadGameUI displayed when the player dies in the game. + * + * @author Mangat Toor + * @version 1.0.0 + */ +public class DeadGameUI extends GameUI { + + /** + * Constructor for DeadGameUI class. + * + * @param buttons an array of the buttons used in this UI + * @param manager GameManager instance + * @param background the background image + */ + public DeadGameUI(Button[] buttons, GameManager manager, PImage background) { + super(buttons, manager, background); + } + + /** + * Buttons clicked handler. + * + * @param label a String + */ + @Override + void buttonClicked(String label) { + switch (label) { + case "Retry" -> { + manager.resetToStart(); + manager.setGameState(1); + } + case "Main Menu" -> manager.setGameState(0); + default -> { + } + } + + } + + /** + * Handles any key events happening in the game. + * + * @param keyEvent key events + */ + @Override + void keyEvent(KeyEvent keyEvent) { + //no specific key events + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/GameDBManager.java b/src/main/java/org/comp2522/ProjectRocketMan/GameDBManager.java new file mode 100644 index 0000000..cd1e8c4 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/GameDBManager.java @@ -0,0 +1,42 @@ +package org.comp2522.ProjectRocketMan; + +import org.bson.BsonObjectId; + +/** + * GameDBManager class manages the current score in the db and keeps track of the document id to + * update the current score in the database. + * + * @author Aditya Agrawal + * @version 1.0.0 + */ +public class GameDBManager { + + /** + * Database instance. + */ + private final Database database; + + /** + * Document id. + */ + private final BsonObjectId id; + + /** + * Constructor for the GameDBManager class. + */ + public GameDBManager() { + database = Database.getInstance(); + id = database.createInitScoreDocument(); + } + + /** + * Updates the game score in the database as the game proceeds. + * + * @param hearts the number of hearts currently + * @param coins the number of coins currently + * @param score the current score + */ + public void updateGameScore(int hearts, int coins, int score) { + database.updateGameScore(id, hearts, coins, score); + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/GameManager.java b/src/main/java/org/comp2522/ProjectRocketMan/GameManager.java new file mode 100644 index 0000000..973d80f --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/GameManager.java @@ -0,0 +1,702 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.core.PVector; +import processing.event.KeyEvent; +import processing.sound.SoundFile; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * The GameManager class represents a game manager object that manages the game state and elements. + * It is responsible for managing the game logic and data, + * updating the game world, + * managing the player input, + * handling collision detection, and + * keeping track of the game score. + * + * @author Lisa Zhu + * @version 1.0 2023-03-30 + */ +public class GameManager { + + /** + * Window instance. + */ + private final Window window; + /** + * Half window width, used multiple times in the GameManager class, hence an instance + * variable. + */ + private final float halfWindowWidth; + /** + * LeaderboardUI instance. + */ + LeaderboardUI leaderboard; + /** + * Background instance. + */ + private Background background; + /** + * Sound played when player hits a coin. + */ + private SoundFile coinSound; + /** + * Sound played when player hits a heart. + */ + private SoundFile heartSound; + /** + * Sound played when player hits a rocket. + */ + private SoundFile rocketSound; + /** + * Player instance. + */ + private Player player; + /** + * List to keep track of the rockets. + */ + private List rockets; + /** + * List to keep track of the sprites. + */ + private List sprites; + /** + * List to keep track of the movables. + */ + private List moveables; + /** + * List to keep track of to collide ables. + */ + private List collideables; + /** + * List to keep track of the coins. + */ + private List coins; + /** + * Heart instance. + */ + private Heart heart; + /** + * Image of the rocket. + */ + private PImage rocket_image; + /** + * Image of the menu background. + */ + private PImage menu_background; + /** + * Images to show a coin animating. + */ + private PImage[] coinAnimation; + /** + * Images to show a heart animating. + */ + private PImage[] heartAnimation; + /** + * GameUIs displayed in different states of the game. + */ + private GameUI[] gameUIS; + /** + * Stores the game state of the Game.x + * State: + * 0: Start State + * 1: Game running state + * 2: Game Pause + * 3: Player Dead + */ + private int gameState; + /** + * GameDBManager instance, updates the current score in the database. + */ + private GameDBManager dbManager; + + /** + * This method starts the program by initializing a new instance of the Window class + * and starting the window. It also sets the isRunning flag to true. + */ + public GameManager() { + window = Window.getInstance(600, 1000); + window.startWindow(this); + halfWindowWidth = (float) window.width / 2; + } + + /** + * The main entry point for the application. + * Creates an instance of GameManager and calls its start method. + * + * @param args command line arguments (not used in this implementation) + */ + public static void main(String[] args) { + new GameManager(); + } + + /** + * Initializes the game with necessary objects and images. + */ + public void init() { + this.coins = new ArrayList<>(); + this.sprites = new ArrayList<>(); + this.moveables = new ArrayList<>(); + this.collideables = new ArrayList<>(); + rockets = new ArrayList<>(); + rocket_image = window.loadImage("images/rocket_images/rocket_3.png"); + menu_background = window.loadImage("images/rocket_man_backgrounds/Start.png"); + coinSound = new SoundFile(window, "music/coin.wav"); + heartSound = new SoundFile(window, "music/heart.wav"); + rocketSound = new SoundFile(window, "music/rocket.wav"); + gameState = 0; + + PImage background_images = window.loadImage("images/rocket_man_backgrounds/main.png"); + background = new Background( + background_images, + 1, + new PVector(0, 0), + new PVector(1, 1) + ); + + PImage rocket_man_image = window.loadImage("images/rocket_man_images/My project2.png"); + player = Player.getInstance( + new PVector(window.width * 0.10f, (float) window.height / 2), + new PVector(1, 1), + rocket_man_image, + 0 + ); + + setUpGameUIs(); + setupCoinAnimations(); + setupHeartAnimations(); + setupBackgroundPlayer(); + } + + /** + * Sets up the coin animations for the RocketMan game. + * This method loads a series of images of a rotating star coin and stores them in an array. + */ + private void setupCoinAnimations() { + coinAnimation = new PImage[6]; + for (int i = 1; i <= 6; i++) { + coinAnimation[i - 1] = window + .loadImage("images/rocket_man_coins/star coin rotate " + i + ".png"); + } + } + + /** + * Initializes an array of PImages for heart animations. + * Load images from the "images/Hearts/" directory, with filenames + * "heart01.png" to "heart05.png". + */ + private void setupHeartAnimations() { + heartAnimation = new PImage[5]; + for (int i = 1; i <= 5; i++) { + heartAnimation[i - 1] = window.loadImage("images/Hearts/heart0" + i + ".png"); + } + } + + /** + * Sets up the game user interfaces. + * This method initializes three game user interfaces: StartGameUI, PauseGameUI, and DeadGameUI. + * It also creates buttons for each user interface and assigns them to their respective UI. + */ + private void setUpGameUIs() { + //Setup Start UI + gameUIS = new GameUI[4]; + + //set up buttons for start + Button[] startButtons = new Button[3]; + startButtons[0] = new Button(new PVector(halfWindowWidth, 200), + new PVector(100, 50), + "Start"); + startButtons[1] = new Button(new PVector(halfWindowWidth, 300), + new PVector(100, 50), + "Leaderboard"); + startButtons[2] = new Button(new PVector(halfWindowWidth, 400), + new PVector(100, 50), + "Quit"); + + //set up buttons for pause + Button[] pauseButtons = new Button[1]; + pauseButtons[0] = new Button(new PVector(halfWindowWidth, 400), + new PVector(100, 50), "Quit"); + + gameUIS[0] = new StartGameUI(startButtons, this, menu_background); + gameUIS[1] = new PauseGameUI(pauseButtons, this, menu_background); + + Button[] deadButtons = new Button[2]; + deadButtons[0] = new Button(new PVector(halfWindowWidth, 300), + new PVector(100, 50), + "Retry"); + deadButtons[1] = new Button(new PVector(halfWindowWidth, 450), + new PVector(100, 50), + "Main Menu"); + + gameUIS[2] = new DeadGameUI(deadButtons, this, menu_background); + + Button[] leaderboardButtons = new Button[1]; + leaderboardButtons[0] = new Button(new PVector(halfWindowWidth, 400), + new PVector(100, 50), + "Main Menu"); + leaderboard = new LeaderboardUI(leaderboardButtons, this, menu_background); + gameUIS[3] = leaderboard; + } + + /** + * This method manages the game based on the current game state. + * It performs different actions depending on the value of the gameState variable. + */ + public void manageTheGame() { + switch (gameState) { + case 0: + //main menu + gameUIS[0].draw(); + break; + case 1: + //State when the game is running + draw(); + move(); + checkForCollisions(); + manageRockets(); + manageCoins(); + manageHeart(); + manageBackground(); + updatePlayerScore(); + break; + case 2: + gameUIS[1].draw(); + break; + //State when the game is paused; + case 3: + // state when player has died + gameUIS[2].draw(); + break; + case 4: + //leaderboard + gameUIS[3].draw(); + default: + //Game has ended. + } + } + + /** + * Resets the game to the beginning to re-run the game. + */ + public void resetToStart() { + clearAll(); + setupBackgroundPlayer(); + background.setSpeed(0.5f); + player.setScore(0); + player.setNumberOfCoinsCollected(0); + player.setHearts(0); + heart.setPosition(new PVector(window.random(window.width, + window.width * 2), + window.random(0, window.height))); + sprites.add(heart); + moveables.add(heart); + collideables.add(heart); + } + + /** + * Clear all the lists. + */ + private void clearAll() { + sprites.clear(); + moveables.clear(); + collideables.clear(); + rockets.clear(); + coins.clear(); + collideables.clear(); + } + + /** + * Add background and player + */ + private void setupBackgroundPlayer() { + this.sprites.add(background); + this.sprites.add(player); + this.moveables.add(background); + this.moveables.add(player); + } + + /** + * This method draws sprites and information. + */ + private void draw() { + drawSprites(); + drawInformation(); + } + + /** + * Drawn information. + * Draws the player's current score, + * number of coins collected, + * and remaining hearts on the screen. + */ + private void drawInformation() { + window.textSize(20); + window.textAlign(window.CENTER, window.CENTER); + window.text("Current Score: " + player.getScore(), window.width - 100, 20); + + window.text("Coins: " + player.getNumberOfCoinsCollected(), window.width - 250, 20); + window.text("Hearts: " + player.getHearts(), window.width - 350, 20); + + + window.text("Current Score: " + player.getScore(), window.width - 100, 20); + + window.text("Coins: " + player.getNumberOfCoinsCollected(), window.width - 250, 20); + window.text("Hearts: " + player.getHearts(), window.width - 350, 20); + } + + /** + * Draws all sprites in the list of sprites. + */ + private void drawSprites() { + for (Sprite sprite : sprites) { + sprite.draw(); + } + } + + /** + * This method moves each object in the list of moveables. + */ + private void move() { + for (Sprite moveable : moveables) { + moveable.move(); + } + } + + /** + * Checks for collisions between player and collidables, and updates the game state accordingly. + * If the player collides with a Rocket, the player loses a heart and + * if the player runs out of hearts, the game state is set to 3. + * If the player collides with a Coin, the player's number of collected coins is incremented. + * If the player collides with a Heart, the player gains a heart. + */ + public void checkForCollisions() { + ArrayList toRemove = new ArrayList<>(); + for (Collideable temp : collideables) { + if (temp.collided(player)) { + if (temp instanceof Rocket) { + rocketSound.play(); + dbManager.updateGameScore(player.getHearts(), + player.getNumberOfCoinsCollected(), + player.getScore()); + sprites.remove((Sprite) temp); + toRemove.add(temp); + player.setHearts(player.getHearts() - 1); + if (player.getHearts() < 0) { + gameState = 3; + } + } else { + if (temp instanceof Coin) { + coinSound.play(); + player.setNumberOfCoinsCollected(player.getNumberOfCoinsCollected() + 1); + } else { + heartSound.play(); + player.setHearts(player.getHearts() + 1); + } + sprites.remove((Sprite) temp); + toRemove.add(temp); + } + } + } + collideables.removeAll(toRemove); + } + + /** + * This method handles mouse events based on the current state of the game. + */ + public void mouseEvents() { + switch (gameState) { + case 0: + gameUIS[0].checkForClicks(); + break; + case 1: + //State when the game is running + break; + case 2: + gameUIS[1].checkForClicks(); + //State when the game is paused; + break; + case 4: + gameUIS[3].checkForClicks(); + default: + //Player is dead . + gameUIS[2].checkForClicks(); + } + } + + /** + * Handles key events for the game. + * + * @param event the KeyEvent object representing the key event that occurred + */ + public void keyEvents(KeyEvent event) { + switch (gameState) { + case 0 -> gameUIS[0].checkForClicks(); + case 1 -> { + player.keyPressed(event); + if (event.getKey() == 'p' || event.getKey() == 'P') { + gameState = 2; + } + } + case 2 -> { + player.keyPressed(event); + gameUIS[1].keyEvent(event); + } + default -> { + } + } + } + + /** + * Updates the player's score based on the current speed of the background. + */ + private void updatePlayerScore() { + player.setScore((int) (player.getScore() + background.getSpeed())); + } + + /** + * This method manages the behavior of the Heart sprite in the game. + * If the Heart sprite does not exist, it is created and added to the relevant lists. + * If the Heart sprite moves out of bounds, it is repositioned and added back to the relevant + * lists. + * The Heart sprite's speed is set to match the game's background speed. + */ + private void manageHeart() { + if (heart == null) { + heart = new Heart(new PVector(1000, 440), + new PVector(0, 0), + heartAnimation, + background.getSpeed()); + sprites.add(heart); + moveables.add(heart); + collideables.add(heart); + } + + if (heart.getPosition().x < -1000) { + heart.setPosition(new PVector(window.random(window.width, + window.width * 2), + window.random(0, window.height))); + if (!sprites.contains(heart)) { + sprites.add(heart); + moveables.add(heart); + collideables.add(heart); + } + } + heart.setSpeed(background.getSpeed()); + } + + /** + * This method manages the speed of the background based on the frame count. + */ + private void manageBackground() { + if (window.frameCount % 200 == 0) { + background.setSpeed(Window.min(10f, background.getSpeed() + 0.2f)); + } + } + + /** + * This method manages the rockets in the game, by updating their speed and + * removing any rockets that have gone out of bounds. + * If there are no rockets left on the screen, this method will add a new + * set of rockets to the game. + */ + private void manageRockets() { + ArrayList rocketsOutOfBound = new ArrayList<>(); + int numRocketsOffScreen = 0; + + for (Rocket temp : rockets) { + temp.setSpeed((background.getSpeed() * -1) - (0.4f * background.getSpeed())); + if (temp.getPosition().x < -50) { + rocketsOutOfBound.add(temp); + numRocketsOffScreen++; + } + } + rockets.removeAll(rocketsOutOfBound); + sprites.removeAll(rocketsOutOfBound); + moveables.removeAll(rocketsOutOfBound); + collideables.removeAll(rocketsOutOfBound); + + if (rockets.size() == 0) { + numRocketsOffScreen = 2; + addRockets(numRocketsOffScreen); + } + + } + + /** + * Adds a specified number of rockets to the game. + * + * @param rocketsToAdd the number of rockets to add to the game + */ + private void addRockets(int rocketsToAdd) { + for (int i = 0; i < rocketsToAdd; i++) { + Rocket tobeAdded = new Rocket( + new PVector(window.random(window.width, window.width * 2), + window.random(0, window.height)), + new PVector(window.random(-1, 1), + window.random(-1, 1)), rocket_image, + (background.getSpeed() * -1) - (0.2f * background.getSpeed())); + rockets.add(tobeAdded); + sprites.add(tobeAdded); + moveables.add(tobeAdded); + collideables.add(tobeAdded); + } + } + + /** + * Manages the coins that are currently in play, removing any that are offscreen + * and adding new ones if necessary. + */ + private void manageCoins() { + ArrayList coinsOutOfBound = new ArrayList<>(); + for (Coin temp : coins) { + if (temp.getPosition().x < -10) { + coinsOutOfBound.add(temp); + } + } + coins.removeAll(coinsOutOfBound); + sprites.removeAll(coinsOutOfBound); + moveables.removeAll(coinsOutOfBound); + collideables.removeAll(coinsOutOfBound); + if (coins.size() == 0) { + addCoins(); + } + + } + + /** + * Adds a random number of coins to the game board in different patterns. + * The pattern in which the coins will be placed is chosen randomly from 4 options: + * placing coins in a line, in a zigzag, in a rectangle or scattered. + */ + private void addCoins() { + Random random = new Random(); + int numberOfCoinsTobeAdded = random.nextInt(10); + int typeOfPatternToPutCoinsIn = random.nextInt(3); + switch (typeOfPatternToPutCoinsIn) { + case 0 -> makeCoinsInALine(numberOfCoinsTobeAdded); + case 1 -> makeCoinsInZigZag(numberOfCoinsTobeAdded); + default -> makeCoinsInARectangle(); + } + } + + /** + * Generates a rectangular pattern of coins within the game board. + */ + private void makeCoinsInARectangle() { + Random random = new Random(); + int numberOfCoinsTobeAdded = (int) window.random(0, 20); + int widthOfRectangle = (int) window.random(1, numberOfCoinsTobeAdded); + int heightOfRectangle = numberOfCoinsTobeAdded / widthOfRectangle; + int y = random.nextInt(5) + 1; + int x = random.nextInt(5) + 1; + float startPositionOfLine = window.random(window.width + window.width / 10f, + 2 * window.width * 2); + float yPositionOfTheLine = window.random(10f, + window.height - heightOfRectangle * (coinAnimation[0].height / 50f)); + float speedForAllCoinsInThePattern = window.random(10, 1); + + for (int i = 0; i < y; i++) { + for (int j = 0; j < x; j++) { + getCoinInstance(startPositionOfLine + j * (coinAnimation[0].height / 50f), + yPositionOfTheLine + i * (coinAnimation[0].height / 50f), + speedForAllCoinsInThePattern); + } + } + + } + + /** + * Adds the specified number of coins to the current game board in a zigzag pattern. + * + * @param numberOfCoinsTobeAdded the number of coins to add to the board + */ + private void makeCoinsInZigZag(int numberOfCoinsTobeAdded) { + Random random = new Random(); + float startPositionOfLine = window.random(window.width + window.width / 10f, + 2 * window.width * 2); + float yPositionOfTheLine = window.random(10f, window.height - 50); + float speedForAllCoinsInThePattern = window.random(10, 1); + int zigZagStartUpDown = random.nextBoolean() ? 1 : -1; + + for (int i = 0; i < numberOfCoinsTobeAdded; i++) { + Coin toBeAdded = getCoinInstance(startPositionOfLine, + yPositionOfTheLine, + speedForAllCoinsInThePattern); + startPositionOfLine = startPositionOfLine + toBeAdded.getWidth(); + yPositionOfTheLine = yPositionOfTheLine + (toBeAdded.getHeight() * zigZagStartUpDown); + } + + } + + /** + * Adds a specified number of coins in a line on the game board. + * + * @param numberOfCoinsTobeAdded the number of coins to be added in a line + */ + private void makeCoinsInALine(int numberOfCoinsTobeAdded) { + float startPositionOfLine = window.random(window.width + window.width / 10f, + 2 * window.width * 2); + float yPositionOfTheLine = window.random(10f, window.height - 50); + float speedForAllCoinsInThePattern = window.random(10, 1); + + for (int i = 0; i < numberOfCoinsTobeAdded; i++) { + Coin toBeAdded = getCoinInstance(startPositionOfLine, + yPositionOfTheLine, + speedForAllCoinsInThePattern); + startPositionOfLine = startPositionOfLine + toBeAdded.getWidth(); + } + + } + + /** + * Creates a new instance of the Coin class with the specified parameters. + * + * @param xPosition the x position of the coin on the screen + * @param yPosition the y position of the coin on the screen + * @param speedOfCoins the speed at which the coin moves + * @return a new instance of the Coin class with the specified parameters + */ + private Coin getCoinInstance(float xPosition, float yPosition, float speedOfCoins) { + Coin temp = new Coin(new PVector(xPosition, yPosition), + new PVector(0, 0), coinAnimation, speedOfCoins); + coins.add(temp); + sprites.add(temp); + moveables.add(temp); + collideables.add(temp); + return temp; + } + + /** + * Returns the current state of the game. + * + * @return an integer representing the current state of the game. + * Possible values include + * 0: Start State + * 1: Game running state + * 2: Game Pause + * 3: Player Dead + */ + public int getGameState() { + return gameState; + } + + /** + * Sets the current game state to the specified value. + * + * @param gameState the new game state to be set + */ + public void setGameState(int gameState) { + boolean fromPause = this.gameState == 2; + this.gameState = gameState; + if (!fromPause) { + dbManager = new GameDBManager(); + } + if (gameState == 4) { + leaderboard.updateLeaderboard(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/comp2522/ProjectRocketMan/GameUI.java b/src/main/java/org/comp2522/ProjectRocketMan/GameUI.java new file mode 100644 index 0000000..7e42757 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/GameUI.java @@ -0,0 +1,78 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.event.KeyEvent; + +/** + * GameUI abstract class. + * + * @author Mangat Toor + * @version 1.0.0 + */ +public abstract class GameUI { + + /** + * Buttons array. + */ + protected Button[] buttons; + + /** + * Window instance. + */ + protected Window window; + + /** + * GameManager instance. + */ + protected GameManager manager; + + /** + * Background image. + */ + protected PImage background; + + /** + * Constructor for the GameUI abstract class. + * + * @param buttons buttons array + * @param manager GameManager instance + * @param background background image + */ + public GameUI(Button[] buttons, GameManager manager, PImage background) { + window = Window.getInstance(); + this.manager = manager; + this.buttons = buttons; + this.background = background; + } + + /** + * Draw method. + */ + protected void draw() { + window.image(background, 0, 0); + for (Button button : buttons) { + button.draw(); + } + } + + /** + * Check for clicks function. + */ + protected void checkForClicks() { + for (Button button : buttons) { + if (button.isClicked()) { + buttonClicked(button.getLabel()); + } + } + } + + /** + * Buttons clicked method. + */ + abstract void buttonClicked(String label); + + /** + * Key event method. + */ + abstract void keyEvent(KeyEvent keyEvent); +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Heart.java b/src/main/java/org/comp2522/ProjectRocketMan/Heart.java new file mode 100644 index 0000000..87637b1 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Heart.java @@ -0,0 +1,182 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.core.PVector; + +import static processing.core.PApplet.abs; + +/** + * Heart class represents one heart on the screen during the game. + * + * @author Jeevanjot Virk + * @version 1.0.0 + */ +public class Heart extends Sprite implements Collideable { + + /** + * Images to form animations of the heart. + */ + private PImage[] animations; + + /** + * Index of the heart. + */ + private int index; + + /** + * Current speed of the heart. + */ + private float speed; + + /** + * Window instance. + */ + private Window window; + + /** + * Height of the heart. + */ + private float height; + + /** + * Width of the heart. + */ + private float width; + + /** + * Constructor for the heart class. + * + * @param position position of the heart on the screen + * @param direction direction of the heart + * @param animations heart animations + * @param speed speed of the heart + */ + public Heart(PVector position, PVector direction, PImage[] animations, float speed) { + super(position, direction); + this.speed = speed; + this.window = Window.getInstance(); + this.index = (int) window.random(0, 6); + this.animations = animations; + this.height = animations[0].width * 10; + this.width = animations[0].width * 10; + + } + + /** + * Constructor for the heart class without animations. + * + * @param position position of the heart on the screen + * @param direction direction of the heart + * @param speed speed of the heart + */ + public Heart(PVector position, PVector direction, float speed) { + super(position, direction); + this.speed = speed; + } + + /** + * Getter for position. + */ + public PVector getPosition() { + return position; + } + + /** + * Setter for the position. + * + * @param position a PVector + */ + @Override + public void setPosition(PVector position) { + this.position = position; + + } + + /** + * Method to move the heart. + */ + @Override + public void move() { + position.add(-1 * speed, 0); + } + + /** + * Getter for speed. + * + * @return speed + */ + @Override + public float getSpeed() { + return 0; + } + + /** + * Setter for speed. + * + * @param speed speed of the heart + */ + @Override + public void setSpeed(float speed) { + this.speed = speed; + + } + + /** + * Draw method to display heart on the screen. + */ + public void draw() { + window.image(animations[index % animations.length], + position.x, position.y, + animations[0].width / 20f, + animations[0].height / 20f); + animate(); + window.stroke(0); // set the stroke color to black + window.fill(255, 0, 0); // set the fill color to red + window.ellipse(position.x, position.y, 4, 4); + } + + /** + * Animate method to animate the heart. + */ + private void animate() { + if (window.frameCount % 6 == 0) { + this.index += this.speed; + } + } + + /** + * Method that is called when the player collides with the heart. + * + * @param player Player instance + * @return whether collided or not + */ + @Override + public boolean collided(Player player) { + float xDistance = this.position.x - player.position.x; + float yDistance = this.position.y - player.position.y; + float xDistanceOffset = player.getWidth() * .3f; + float yDistanceOffset = player.getHeight() * .3f; + return abs(yDistance) + < player.getHeight() - xDistanceOffset && abs(xDistance) + < player.getWidth() - yDistanceOffset; + } + + /** + * Getter for height. + * + * @return height of the heart + */ + public float getHeight() { + return height; + } + + /** + * Getter for width. + * + * @return width of the heart + */ + public float getWidth() { + return width; + } + +} \ No newline at end of file diff --git a/src/main/java/org/comp2522/ProjectRocketMan/LeaderboardScore.java b/src/main/java/org/comp2522/ProjectRocketMan/LeaderboardScore.java new file mode 100644 index 0000000..7ce6251 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/LeaderboardScore.java @@ -0,0 +1,54 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PVector; + +/** + * Leaderboard score class. + * + * @author Aditya Agrawal + * @version 1.0.0 + */ +public class LeaderboardScore { + + /** + * Text displayed on screen. + */ + private final String text; + + /** + * Position of the LeaderboardScore. + */ + private final PVector position; + + /** + * Window instance. + */ + private final Window window; + + /** + * Constructor for the LeaderboardScore class. + * + * @param position position of the LeaderboardScore + * @param score score of the leaderboard + * @param coins number of coins to display + */ + public LeaderboardScore(PVector position, String score, String coins) { + this.text = "Score: " + score + " Coins: " + coins; + this.position = position; + window = Window.getInstance(); + } + + /** + * Draw method to display the leaderboard on screen. + */ + public void draw() { + float x = window.textWidth(text) + 20; + float y = 50; + window.stroke(0); + window.fill(255); + window.rect(position.x, position.y, window.textWidth(text) + 20, 50); + window.fill(0); + window.textAlign(window.CENTER, window.CENTER); + window.text(text, position.x + x / 2, position.y + y / 2); + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/LeaderboardUI.java b/src/main/java/org/comp2522/ProjectRocketMan/LeaderboardUI.java new file mode 100644 index 0000000..31ec0b7 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/LeaderboardUI.java @@ -0,0 +1,104 @@ +package org.comp2522.ProjectRocketMan; + +import org.bson.Document; +import processing.core.PImage; +import processing.core.PVector; +import processing.event.KeyEvent; +import java.util.ArrayList; + +/** + * LeaderboardUI class to show the leaderboards of the global rocket-man game. + * + * @author Aditya Agrawal + * @version 1.0.0 + */ +public class LeaderboardUI extends GameUI { + + /** + * Database instance. + */ + private final Database database; + /** + * Window instance. + */ + protected Window window; + /** + * GameManager instance. + */ + protected GameManager manager; + /** + * Background image. + */ + protected PImage background; + /** + * ArrayList of the leaderboard scores. + */ + private ArrayList scores; + + /** + * Constructor for the LeaderboardUI class. + * + * @param buttons buttons in the leaderboard ui + * @param manager GameManager instance + * @param background background image + */ + public LeaderboardUI(Button[] buttons, GameManager manager, PImage background) { + super(buttons, manager, background); + window = Window.getInstance(); + this.manager = manager; + this.background = background; + this.database = Database.getInstance(); + this.scores = new ArrayList<>(); + } + + /** + * Update leader board method, called whenever the leaderboard ui is displayed. + */ + public void updateLeaderboard() { + ArrayList documents = database.getLeaderBoard(); + scores = new ArrayList<>(); + int y = 50; + int lineHeight = 60; + for (int i = 0; i < documents.size(); i++) { + Document document = documents.get(i); + scores.add(new LeaderboardScore(new PVector(400, y + i * lineHeight), + document.get("score").toString(), + document.get("coins").toString())); + } + } + + /** + * Draw method to display the ui on screen. + */ + @Override + protected void draw() { + window.image(background, 0, 0); + window.textSize(20); + for (LeaderboardScore score : scores) { + score.draw(); + } + for (Button button : buttons) { + button.draw(); + } + } + + /** + * Button clicked method. + * + * @param label a String + */ + @Override + void buttonClicked(String label) { + manager.setGameState(0); + } + + /** + * Key event method. + * + * @param keyEvent key events + */ + @Override + void keyEvent(KeyEvent keyEvent) { + //no key events + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/PauseGameUI.java b/src/main/java/org/comp2522/ProjectRocketMan/PauseGameUI.java new file mode 100644 index 0000000..4c894b6 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/PauseGameUI.java @@ -0,0 +1,72 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.event.KeyEvent; + +/** + * PauseGameUI displayed when the game is paused. + * + * @author Mangat Toor + * @version 1.0.0 + */ +public class PauseGameUI extends GameUI { + + /** + * Constructor for the PauseGameUI class. + * + * @param buttons buttons in the ui + * @param manager GameManager instance + * @param background background image + */ + public PauseGameUI(Button[] buttons, GameManager manager, PImage background) { + super(buttons, manager, background); + } + + /** + * Draw method to display the ui on the screen. + */ + @Override + public void draw() { + window.image(background, 0, 0); + drawMessage(); + for (Button button : buttons) { + button.draw(); + } + } + + /** + * Draws the message on the screen. + */ + private void drawMessage() { + // Set the font size and color + window.textSize(24); + window.fill(255, 0, 0); + + // Draw the message + window.text("Press P to unpause game", + (float) window.width / 2 - 150, + ((float) window.height / 2) + 50); + } + + /** + * Key event method. + * + * @param event key events + */ + @Override + void keyEvent(KeyEvent event) { + if (event.getKey() == 'p' || event.getKey() == 'P') { + manager.setGameState(1); + } + } + + /** + * Buttons clicked method. + * + * @param label a String + */ + @Override + void buttonClicked(String label) { + window.exit(); + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Player.java b/src/main/java/org/comp2522/ProjectRocketMan/Player.java new file mode 100644 index 0000000..78c9b66 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Player.java @@ -0,0 +1,377 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.core.PVector; +import processing.event.KeyEvent; + +import static processing.core.PConstants.UP; + +/** + * A class representing the player. + * + * @author Manget Toor + * @version 1.0.0 + */ +public class Player extends Sprite { + /** + * The only instance of the player object. + */ + private static Player player; + /** + * Stores the instance of the Window. + */ + private final Window window; + /** + * The width of the player. + */ + private final float width; + /** + * The height of the player. + */ + private final float height; + /** + * Stores the image of the player. + */ + private PImage image; + /** + * The speed of the player. The Player starts with the 0 speed. + */ + private float speed = 0; + /** + * The acceleration of the player. + */ + private float acceleration = 1.5f; + /** + * The maximum speed of the player. + */ + private float maxSpeed = -7f; + /** + * The gravity applied to the player. + */ + private float gravity = 0.2f; + /** + * The score of the player. + */ + private int score; + /** + * The number of coins the player has collected. + */ + private int numberOfCoinsCollected; + + /** + * The number of hearts the player has. + */ + private int hearts; + + /** + * Private constructor to create the player object. + * + * @param position The position of the player. + * @param direction The direction of the player. + * @param image The image of the player. + * @param speed The speed of the player. + */ + Player(PVector position, PVector direction, PImage image, float speed) { + super(position, direction); + this.image = image; + this.window = Window.getInstance(); + this.width = image.height / 10f; + this.height = image.height / 10f; + } + + /** + * Private constructor to create the player object. + * + * @param position The position of the player. + * @param direction The direction of the player. + * @param speed The speed of the player. + */ + Player(PVector position, PVector direction, float speed) { + super(position, direction); + this.window = Window.getInstance(); + this.width = 1; + this.height = 1; + } + + /** + * Creates a new instance of the player object if it doesn't exist or returns the existing one. + * + * @param position The position of the player. + * @param direction The direction of the player. + * @param image The image of the player. + * @param speed The speed of the player. + * @return The only instance of the player object. + */ + public static Player getInstance(PVector position, PVector direction, PImage image, float speed) { + if (player == null) { + player = new Player(position, direction, image, speed); + } + return player; + } + + /** + * Get instance of a Player object if it doesn't exist returns the new one. + * + * @param position The position of the player. + * @param direction The direction of the player. + * @param speed The speed of the player. + */ + public static Player getInstance(PVector position, PVector direction, float speed) { + if (player == null) { + player = new Player(position, direction, speed); + } + return player; + } + + /** + * Returns the instance of the player. + * + * @return the player instance + */ + public static Player getInstance() { + return player; + } + + /** + * Returns the gravity applied to the player. + * + * @return The gravity applied to the player. + */ + public float getGravity() { + return gravity; + } + + /** + * Sets the gravity applied to the player. + * + * @param gravity The gravity applied to the player. + */ + public void setGravity(float gravity) { + this.gravity = gravity; + } + + /** + * Returns the number of coins the player has collected. + * + * @return The number of coins the player has collected. + */ + public int getNumberOfCoinsCollected() { + return numberOfCoinsCollected; + } + + /** + * Sets the number of coins the player has collected. + * + * @param numberOfCoinsCollected The number of coins the player has collected. + */ + public void setNumberOfCoinsCollected(int numberOfCoinsCollected) { + this.numberOfCoinsCollected = numberOfCoinsCollected; + } + + /** + * Returns the acceleration of the player. + * + * @return The acceleration of the player. + */ + public float getAcceleration() { + return acceleration; + } + + /** + * Sets the acceleration of the player. + * + * @param acceleration the acceleration to set + */ + public void setAcceleration(float acceleration) { + this.acceleration = acceleration; + } + + /** + * Returns the maximum speed of the player. + * + * @return the maximum speed + */ + public float getMaxSpeed() { + return maxSpeed; + } + + /** + * Sets the maximum speed of the player. + * + * @param maxSpeed the maximum speed to set + */ + public void setMaxSpeed(float maxSpeed) { + this.maxSpeed = maxSpeed; + } + + /** + * Returns the player image. + * + * @return the player image + */ + public PImage getImage() { + return image; + } + + /** + * Sets the player image. + * + * @param image the player image to set + */ + public void setImage(PImage image) { + this.image = image; + } + + /** + * Handles the key pressed event for the player. + * + * @param event the key event + */ + public void keyPressed(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (keyCode == UP && position.y > image.height / 10f) { + setSpeed(getSpeed() * 0.8f - getAcceleration()); + if (getSpeed() < maxSpeed) { + setSpeed(maxSpeed); + } + } else { + //put the logic of movement in move + if (keyCode == UP && position.y < image.height / 20f) { + if (speed != 0) { + speed = 0; + } + } + } + } + + /** + * Returns the number of hearts the player has. + * + * @return the number of hearts + */ + public int getHearts() { + return hearts; + } + + /** + * Sets the number of hearts the player has. + * + * @param hearts the number of hearts to set + */ + public void setHearts(int hearts) { + this.hearts = hearts; + } + + /** + * Returns the position of the object as a PVector. + * + * @return the position of the object as a PVector + */ + @Override + public PVector getPosition() { + return position; + } + + /** + * Sets the position of the player. + * + * @param position the position to set + */ + @Override + public void setPosition(PVector position) { + this.position = position; + } + + /** + * Moves the object vertically according to its speed, gravity, and limits. + * Sets the speed to 0 if the object hits the bottom limit. + */ + @Override + public void move() { + PVector temp = new PVector(getPosition().x, getPosition().y); + float bottomLimit = window.height - height - 90; // set bottom limit to 50 pixels + if (temp.y + getSpeed() >= image.height / 10f && temp.y + getSpeed() <= bottomLimit) { + temp.add(0, getSpeed()); + setPosition(temp); + } + if (position.y + getSpeed() >= bottomLimit && speed != 0) { + this.speed = 0; + } else { + if (position.y + getSpeed() < bottomLimit) { + if (getSpeed() > -(maxSpeed + 2)) { + setSpeed(-maxSpeed); + } else { + setSpeed(getSpeed() + getGravity()); + } + } + } + } + + /** + * Returns the score of the object. + * + * @return the score of the object + */ + public int getScore() { + return score; + } + + /** + * Sets the score of the object. + * + * @param score the new score of the object + */ + public void setScore(int score) { + this.score = score; + } + + /** + * Draws the object on the screen. + */ + @Override + void draw() { + float angle = PVector.angleBetween(position, new PVector(0, 1)); + window.image(this.image, position.x, position.y, image.height / 10f, image.width / 10f); + window.stroke(0); // set the stroke color to black + window.fill(255, 0, 0); // set the fill color to red + window.ellipse(position.x, position.y, 4, 4); + } + + /** + * Returns the width of the object. + * + * @return the width of the object + */ + public float getWidth() { + return width; + } + + /** + * Returns the height of the object. + * + * @return the height of the object + */ + public float getHeight() { + return height; + } + + /** + * Returns the speed of the object. + * + * @return the speed of the object + */ + @Override + public float getSpeed() { + return speed; + } + + /** + * Sets the speed of the object. + * + * @param speed the new speed of the object + */ + @Override + public void setSpeed(float speed) { + this.speed = speed; + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Rocket.java b/src/main/java/org/comp2522/ProjectRocketMan/Rocket.java new file mode 100644 index 0000000..878e175 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Rocket.java @@ -0,0 +1,131 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.core.PVector; + +import static processing.core.PApplet.abs; + +/** + * The Rocket class represents a rocket object that can move and collide with other objects. + * + * @author Mangat Toor + * @version 1.0.0 + */ +public class Rocket extends Sprite implements Collideable { + + /** + * Stores the rocket image. + */ + PImage image; + + /** + * Stores the instance of the Window. + */ + Window window; + + /** + * the speed of the rocket. + */ + float speed; + + /** + * Constructs a Rocket object with the specified position, direction, and speed. + * + * @param position the initial position of the rocket + * @param direction the initial direction of the rocket + * @param speed the speed at which the rocket moves + */ + public Rocket(PVector position, PVector direction, float speed) { + super(position, direction); + this.window = Window.getInstance(); + this.speed = speed; + } + + /** + * Constructs a Rocket object with the specified position, direction, image, and speed. + * + * @param position the initial position of the rocket + * @param direction the initial direction of the rocket + * @param image the image to use for the rocket + * @param speed the speed at which the rocket moves + */ + public Rocket(PVector position, PVector direction, PImage image, float speed) { + super(position, direction); + this.image = image; + this.window = Window.getInstance(); + this.speed = speed; + } + + /** + * Draws the rocket on the window. + */ + public void draw() { + window.image(image, position.x, position.y, image.height / 2f, image.width / 6f); + } + + /** + * Returns the current position of the rocket. + * + * @return the current position of the rocket + */ + @Override + public PVector getPosition() { + return position; + } + + /** + * Sets the position of the rocket to the specified position. + * + * @param position the new position of the rocket + */ + @Override + public void setPosition(PVector position) { + this.position = position; + } + + /** + * Moves the rocket in its current direction at its current speed. + */ + @Override + public void move() { + PVector temp = getPosition(); + temp.add(getSpeed(), 0); + setPosition(temp); + } + + /** + * Returns the current speed of the rocket. + * + * @return the current speed of the rocket + */ + @Override + public float getSpeed() { + return speed; + } + + /** + * Sets the speed of the rocket to the specified speed. + * + * @param speed the new speed of the rocket + */ + @Override + public void setSpeed(float speed) { + this.speed = speed; + } + + /** + * Checks if the rocket has collided with the specified player. + * + * @param player the player to check for a collision with + * @return true if the rocket has collided with the player, false otherwise + */ + @Override + public boolean collided(Player player) { + float yDistance = this.position.y - player.position.y; + float xDistance = this.position.x - player.position.x; + float xDistanceOffset = player.getWidth() * .3f; + float yDistanceOffset = player.getHeight() * .3f; + return abs(yDistance) < player.getHeight() - xDistanceOffset + && abs(xDistance) < player.getWidth() - yDistanceOffset; + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Sprite.java b/src/main/java/org/comp2522/ProjectRocketMan/Sprite.java new file mode 100644 index 0000000..0e74b2f --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Sprite.java @@ -0,0 +1,74 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PVector; + +/** + * The abstract class Sprite represents a game sprite with a position and direction in 2D space. + * it also contains abstract methods related to Sprite like drawable. + * + * @author Aditya Agrawal + * @version 1.0.0 + */ +public abstract class Sprite { + + /** + * Position of the Sprite. + */ + protected PVector position; + + /** + * Direction of the Sprite. + */ + protected PVector direction; + + /** + * Constructor for the Sprite class. + * + * @param position Position of the Sprite. + * @param direction Direction of the Sprite. + */ + public Sprite(PVector position, PVector direction) { + this.direction = direction; + this.position = position; + } + + /** + * Draw method called to display the Sprite on screen. + */ + abstract void draw(); + + /** + * Returns the current position of the movable object + * as an object containing x and y coordinates. + * + * @return An object containing the x and y coordinates of the movable object's position. + */ + abstract PVector getPosition(); + + /** + * Sets the position of the movable object to the specified x and y coordinates. + * + * @param position - The position to set. + */ + abstract void setPosition(PVector position); + + /** + * Moves the movable object by its own velocity amount in the x and y directions. + */ + abstract void move(); + + /** + * Returns the speed of the movable object. + * + * @return A double value containing the x and y velocities of the movable object. + */ + abstract float getSpeed(); + + /** + * Sets the velocity of the movable object to the specified values in the x and y directions. + * + * @param speed The velocity to set in both direction. + */ + abstract void setSpeed(float speed); + +} \ No newline at end of file diff --git a/src/main/java/org/comp2522/ProjectRocketMan/StartGameUI.java b/src/main/java/org/comp2522/ProjectRocketMan/StartGameUI.java new file mode 100644 index 0000000..d06ffb2 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/StartGameUI.java @@ -0,0 +1,50 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PImage; +import processing.event.KeyEvent; + +/** + * StartGameUI ui for when the game starts. + * + * @author Jeevanjot Virk + * @version 1.0.0 + */ +public class StartGameUI extends GameUI { + + /** + * Constructor for the StartGameUI class. + * + * @param buttons buttons of the ui + * @param manager GameManager instance + * @param background background image + */ + public StartGameUI(Button[] buttons, GameManager manager, PImage background) { + super(buttons, manager, background); + } + + /** + * Buttons clicked method. + * + * @param label a String + */ + @Override + void buttonClicked(String label) { + switch (label) { + case "Start" -> manager.setGameState(1); + case "Leaderboard" -> manager.setGameState(4); + case "Quit" -> window.exit(); + default -> { + } + } + } + + /** + * Key event handler. + * + * @param keyEvent key events + */ + @Override + void keyEvent(KeyEvent keyEvent) { + //no specific key events + } +} diff --git a/src/main/java/org/comp2522/ProjectRocketMan/Window.java b/src/main/java/org/comp2522/ProjectRocketMan/Window.java new file mode 100644 index 0000000..c773237 --- /dev/null +++ b/src/main/java/org/comp2522/ProjectRocketMan/Window.java @@ -0,0 +1,173 @@ +package org.comp2522.ProjectRocketMan; + +import processing.core.PApplet; +import processing.event.KeyEvent; +import processing.sound.SoundFile; + +/** + * Runs the applet. + * + * @author Jeevanjot Virk + * @version 1.0.0 + */ +public class Window extends PApplet { + + /** + * Static variable to store the instance of the Window. + */ + static Window window; + + /** + * Height of the Window. + */ + private final int heightOfWindow; + + /** + * Width of the Window. + */ + private final int widthOfWindow; + + /** + * Background music. + */ + SoundFile gameBackground; + + /** + * Background music for menu. + */ + SoundFile menuBackground; + + /** + * Holds the manager class instance. + */ + GameManager manager; + + /** + * This stores the instance of the player. + */ + Player player; + + /** + * Constructor for the Window class. + * + * @param height Height of the Window. + * @param width Width of the Window. + */ + private Window(int height, int width) { + this.heightOfWindow = height; + this.widthOfWindow = width; + } + + /** + * Get a window instance. + * + * @param height Height of the Window. + * @param width Width of the Window. + * @return Window instance + */ + public static Window getInstance(int height, int width) { + if (window == null) { + return window = new Window(height, width); + } + return window; + } + + /** + * Get a window instance. + * + * @return Window instance. + */ + public static Window getInstance() { + return window; + } + + /** + * Called once at the beginning of the program. + */ + public void settings() { + size(widthOfWindow, heightOfWindow); + } + + /** + * Called once at the beginning of the program. + * Initializes all objects. + */ + public void setup() { + manager.init(); + player = Player.getInstance(); + gameBackground = new SoundFile(this, "music/background_babbu.mp3"); + menuBackground = new SoundFile(this, "music/background.mp3"); + } + + /** + * Called on every frame. Updates scene object + * state and redraws the scene. Drawings appear + * in order of function calls. + */ + public void draw() { + manager.manageTheGame(); + int gameState = manager.getGameState(); + switch (gameState) { + case 0, 3, 4 -> { + if (gameBackground.isPlaying()) { + gameBackground.stop(); + } + if (!menuBackground.isPlaying()) { + menuBackground.play(); + } + } + case 1 -> { + if (menuBackground.isPlaying()) { + menuBackground.stop(); + } + if (!gameBackground.isPlaying()) { + gameBackground.play(); + } + } + case 2 -> { + if (gameBackground.isPlaying()) { + gameBackground.pause(); + } + if (!menuBackground.isPlaying()) { + menuBackground.play(); + } + } + default -> { + if (gameBackground.isPlaying()) { + gameBackground.stop(); + } + if (menuBackground.isPlaying()) { + menuBackground.stop(); + } + } + } + } + + /** + * Mouse clicked event handler. + */ + @Override + public void mouseClicked() { + manager.mouseEvents(); + } + + /** + * Key pressed event handler. + * + * @param event key events + */ + public void keyPressed(KeyEvent event) { + manager.keyEvents(event); + } + + /** + * Start window method. + * + * @param manager GameManager + */ + public void startWindow(GameManager manager) { + this.manager = manager; + String[] appletArgs = new String[]{"eatBubbles"}; + PApplet.runSketch(appletArgs, this); + } +} \ No newline at end of file diff --git a/src/test/java/org/comp2522/ProjectRocketMan/BackgroundTest.java b/src/test/java/org/comp2522/ProjectRocketMan/BackgroundTest.java new file mode 100644 index 0000000..7f31a33 --- /dev/null +++ b/src/test/java/org/comp2522/ProjectRocketMan/BackgroundTest.java @@ -0,0 +1,81 @@ +package org.comp2522.ProjectRocketMan; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import processing.core.PApplet; +import processing.core.PImage; +import processing.core.PVector; + +import static org.junit.jupiter.api.Assertions.*; + +/** + + The BackgroundTest class tests the methods of the Background class. + It tests the functionality of the setSpeed() and move() methods. + */ +public class BackgroundTest { + + /** + * an instance of the Background class + */ + private Background background; + + /** + * a PImage for the background image + */ + private PImage image; + /** + * the speed of the background + */ + private float speed = 2.0f; + + /** + * the position of the background + */ + private PVector position; + + /** + * the direction of the background + */ + private PVector direction; + + /** + + Sets up the test fixture. + This method is called before each test. + */ + @BeforeEach + public void setUp() { + PApplet applet = new PApplet(); + //image = applet.loadImage("background.png"); + position = new PVector(0, 0); + direction = new PVector(0, 0); + background = new Background(speed, position, direction); + } + + /** + + Tests the setSpeed() method of the Background class. + It sets a new speed value for the background and checks if the new value is returned correctly. + */ + @Test + public void testSetSpeed() { + float newSpeed = 3.0f; + background.setSpeed(newSpeed); + assertEquals(newSpeed, background.getSpeed()); + } + + /** + + Tests the move() method of the Background class. + It moves the background and checks if the position of the background has changed correctly. + */ + @Test + public void testMove() { + float initialX = background.getPosition().x; + background.move(); + assertEquals(initialX - speed, background.getPosition().x); + } + +} + diff --git a/src/test/java/org/comp2522/ProjectRocketMan/ButtonTest.java b/src/test/java/org/comp2522/ProjectRocketMan/ButtonTest.java new file mode 100644 index 0000000..c4941ec --- /dev/null +++ b/src/test/java/org/comp2522/ProjectRocketMan/ButtonTest.java @@ -0,0 +1,77 @@ +package org.comp2522.ProjectRocketMan; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import processing.core.PVector; + +import static org.junit.jupiter.api.Assertions.*; +/** + + This test class tests the Button class which represents a clickable button in a Processing sketch. + It tests the functionality of the isClicked() method, getLabel() method, and setLabel() method. + */ +public class ButtonTest { + + /** + + The window object on which the tests will be performed. + */ + private Window window; + + /** + + Initializes the window object before each test. + */ + @BeforeEach + public void setUp() { + window = Window.getInstance(800, 1000); + } + + /** + + Tests the functionality of the isClicked() method. + It creates a button object and checks if it is initially not clicked, then simulates a click on the button + and checks if the button is clicked, and then simulates a click outside the button and checks if the button is + not clicked. + */ + @Test + void testIsClicked() { + Button button = new Button(new PVector(100, 100), new PVector(50, 30), "Click me!"); + assertFalse(button.isClicked(), "Button should not be clicked initially"); + + // Simulate a click on the button + Window.getInstance().mouseX = 125; + Window.getInstance().mouseY = 115; + assertTrue(button.isClicked(), "Button should be clicked"); + + // Simulate a click outside the button + Window.getInstance().mouseX = 75; + Window.getInstance().mouseY = 85; + assertFalse(button.isClicked(), "Button should not be clicked"); + } + + /** + + Tests the functionality of the getLabel() method. + It creates a button object and checks if the label text is correct initially. + */ + @Test + void testGetLabel() { + Button button = new Button(new PVector(100, 100), new PVector(50, 30), "Click me!"); + assertEquals("Click me!", button.getLabel(), "Label should be 'Click me!' initially"); + } + + /** + + Tests the functionality of the setLabel() method. + It creates a button object, sets a new label text using the setLabel() method and then checks if the label text + is updated correctly. + */ + @Test + void testSetLabel() { + Button button = new Button(new PVector(100, 100), new PVector(50, 30), "Click me!"); + button.setLabel("Press me!"); + assertEquals("Press me!", button.getLabel(), "Label should be 'Press me!' after calling setLabel()"); + } +} + diff --git a/src/test/java/org/comp2522/ProjectRocketMan/CoinTest.java b/src/test/java/org/comp2522/ProjectRocketMan/CoinTest.java new file mode 100644 index 0000000..7e5cf79 --- /dev/null +++ b/src/test/java/org/comp2522/ProjectRocketMan/CoinTest.java @@ -0,0 +1,92 @@ +package org.comp2522.ProjectRocketMan; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import processing.core.PVector; + +import java.util.ArrayList; +/** + + The CoinTest class contains unit tests for the Coin class. + It tests the functionality of the Coin class's constructor, move method, and collided method. + @author Jeevan Virk + @version 1.0 +*/ +public class CoinTest { + /** + * the coin object + */ + private Coin coin; + /** + + Sets up the initial state for each test method by creating a new Coin object with a position, + direction, and speed. + */ + @BeforeEach + public void setUp() { + PVector position = new PVector(100, 200); + PVector direction = new PVector(1, 0); + float speed = 2; + coin = new Coin(position, direction, speed); + } + /** + + Tests the constructor of the Coin class by checking that the position, direction, and speed are + set correctly. + */ + @Test + public void testConstructor() { + PVector position = coin.getPosition(); + PVector direction = coin.getDirection(); + float speed = coin.getSpeed(); + Assertions.assertEquals(100, position.x); + Assertions.assertEquals(200, position.y); + Assertions.assertEquals(1, direction.x); + Assertions.assertEquals(0, direction.y); + Assertions.assertEquals(2, speed); + //Assertions.assertNotNull(coin.getAnimations()); + } + /** + + Tests the move method of the Coin class by checking that the coin's position changes correctly. + */ + @Test + public void testMove() { + coin.move(); + PVector position = coin.getPosition(); + Assertions.assertEquals(98, position.x); + Assertions.assertEquals(200, position.y); + } + /** + + Tests the collided method of the Coin class by checking that it returns false when a player is + not colliding with the coin, and false when a player is colliding with the coin. + */ + @Test + public void testCollided() { + Player player = new Player(new PVector(90, 200), new PVector(1, 0), 1 ); + boolean collided = coin.collided(player); + Assertions.assertFalse(collided); + + player = new Player(new PVector(90, 200), new PVector(1, 0), 1); + collided = coin.collided(player); + Assertions.assertFalse(collided); + + player = new Player(new PVector(50, 200), new PVector(1, 0), 1); + collided = coin.collided(player); + Assertions.assertFalse(collided); + + player = new Player(new PVector(110, 200), new PVector(1, 0), 1); + collided = coin.collided(player); + Assertions.assertFalse(collided); + + player = new Player(new PVector(100, 150), new PVector(1, 0), 1); + collided = coin.collided(player); + Assertions.assertFalse(collided); + + player = new Player(new PVector(100, 250), new PVector(1, 0), 1); + collided = coin.collided(player); + Assertions.assertFalse(collided); + } +} diff --git a/src/test/java/org/comp2522/ProjectRocketMan/GameManagerTest.java b/src/test/java/org/comp2522/ProjectRocketMan/GameManagerTest.java new file mode 100644 index 0000000..4dc9e5e --- /dev/null +++ b/src/test/java/org/comp2522/ProjectRocketMan/GameManagerTest.java @@ -0,0 +1,79 @@ +package org.comp2522.ProjectRocketMan; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import processing.core.PVector; + + +public class GameManagerTest { + GameManager manager; + public Player player; + @BeforeEach + public void setUp() { + manager = new GameManager(); + player = Player.getInstance( + new PVector(5,5), + new PVector(1,1), + 0 + ); + } + + @Test + public void testCheckForCollisionsWithRocket() { + Rocket rocket = new Rocket( + new PVector(5, 5), + new PVector(1, 1), + 0); + + Assertions.assertTrue(rocket.collided(player)); + } + + @Test + public void testNotCheckForCollisionsWithRocket() { + Rocket rocket = new Rocket( + new PVector(10, 10), + new PVector(1, 1), + 0); + + Assertions.assertFalse(rocket.collided(player)); + } + @Test + public void testCheckForCollisionsWithCoin() { + Coin coin = new Coin( + new PVector(5, 5), + new PVector(1, 1), + 0 + ); + Assertions.assertTrue(coin.collided(player)); + } + @Test + public void testNotCheckForCollisionsWithCoin() { + Coin coin = new Coin( + new PVector(15, 15), + new PVector(1, 1), + 0 + ); + Assertions.assertFalse(coin.collided(player)); + } + @Test + public void testCheckForCollisionsWithHeart() { + Heart heart = new Heart( + new PVector(5, 5), + new PVector(1, 1), + 0); + + Assertions.assertTrue(heart.collided(player)); + + } + + @Test + public void testNotCheckForCollisionsWithHeart() { + Heart heart = new Heart( + new PVector(55, 55), + new PVector(1, 1), + 0); + + Assertions.assertFalse(heart.collided(player)); + } +} diff --git a/src/test/java/org/comp2522/ProjectRocketMan/HeartTest.java b/src/test/java/org/comp2522/ProjectRocketMan/HeartTest.java new file mode 100644 index 0000000..ac29ebb --- /dev/null +++ b/src/test/java/org/comp2522/ProjectRocketMan/HeartTest.java @@ -0,0 +1,104 @@ +package org.comp2522.ProjectRocketMan; + +import org.comp2522.ProjectRocketMan.Heart; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import processing.core.PImage; +import processing.core.PVector; +import static org.junit.jupiter.api.Assertions.*; +/** + + The HeartTest class tests the methods of the Heart class using JUnit 5. + It contains test cases for the move, update, collided, and getWidthAndHeight methods. + @author Jeevan Virk + @version 1.0 + */ +public class HeartTest { + /** + * an array of PImage objects to store the animation frames + */ + private PImage[] animations; + + /** + * the speed of the heart + */ + private final float speed = 0.0f; + + /** + * the heart object to be tested + */ + private Heart heart; + + /** + * the window object + */ + private Window window; + + /** + * Player object + */ + private Player player; + + /** + Sets up the test environment before each test. + */ + @BeforeEach + public void setUp() { + window = Window.getInstance(800, 1000); + heart = new Heart(new PVector(0, 0), new PVector(0, 0), speed); + player = Player.getInstance(new PVector(0, 0), new PVector(0, 0), speed); + animations = new PImage[1]; + animations[0] = new PImage(); + } + + /** + + Tests the move method of the Heart class. + It checks if the heart moves to the left at the correct speed. + */ + @Test + public void testMove() { + final float delta = 0.01f; + final float initialX = heart.getPosition().x; + heart.move(); + assertEquals(initialX - speed, heart.getPosition().x, delta); + } + + /** + Tests the collided method of the Heart class. + It checks if the heart collides with the player when they are within the offset distance. + It also checks if the heart does not collide with the player when they are beyond the offset distance. + */ + @Test + public void testCollided() { + // Test if heart collides with player within the offset distance + final PVector playerPosition = new PVector(10, 10); + final PVector heartPosition = new PVector(20, 20); + final float playerWidth = 30; + final float playerHeight = 30; + final float xDistanceOffset = playerWidth * .3f; + final float yDistanceOffset = playerHeight * .3f; + + heart.setPosition(heartPosition); + player.setPosition(playerPosition); + final boolean isCollided = heart.collided(player); + assertFalse(isCollided); + + // Test if heart does not collide with player beyond the offset distance + final PVector playerPosition2 = new PVector(100, 100); + player.setPosition(playerPosition2); + final boolean isNotCollided = heart.collided(player); + assertFalse(isNotCollided); + } + + /** + * Getting width and height + */ + @Test + public void testGetWidthAndHeight() { + final float delta = 0.01f; + assertEquals(animations[0].width * 10, heart.getWidth(), delta); + assertEquals(animations[0].height * 10, heart.getHeight(), delta); + } +} + diff --git a/src/test/java/org/comp2522/ProjectRocketMan/PlayerTest.java b/src/test/java/org/comp2522/ProjectRocketMan/PlayerTest.java new file mode 100644 index 0000000..c78c4d6 --- /dev/null +++ b/src/test/java/org/comp2522/ProjectRocketMan/PlayerTest.java @@ -0,0 +1,92 @@ +package org.comp2522.ProjectRocketMan; + +import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import processing.core.PImage; +import processing.core.PVector; +import processing.event.KeyEvent; + +import static org.junit.jupiter.api.Assertions.*; +import static processing.core.PConstants.UP; + +class PlayerTest { + + private Player player; + private PImage image; + private PVector position; + private PVector direction; + private float speed; + + @BeforeEach + void setUp() { + image = new PImage(); + position = new PVector(0, 0); + direction = new PVector(1, 0); + speed = 0; + player = Player.getInstance(position, direction, image, speed); + } + + @Test + void getInstanceReturnsSameObject() { + Player player2 = Player.getInstance(position, direction, image, speed); + assertSame(player, player2); + } +/** + @Test + void moveOnKeyEventMovesPlayer() { + float initialY = player.getPosition().y; + player.setSpeed(1); + player.moveOnKeyEvent(); + float newY = player.getPosition().y; + assertTrue(newY < initialY); + } +**/ + /**@Test + void keyPressedIncreasesSpeed() { + float initialSpeed = player.getSpeed(); + player.keyPressed(new KeyEvent(null, ' ', '\0', KeyEvent.VK_UP, 0)); + float newSpeed = player.getSpeed(); + assertTrue(newSpeed > initialSpeed); + }**/ + + @Test + void getGravityReturnsGravity() { + float gravity = 1.0f; + player.setGravity(gravity); + assertEquals(gravity, player.getGravity()); + } + + @Test + void getNumberOfCoinsCollectedReturnsCoins() { + int coins = 5; + player.setNumberOfCoinsCollected(coins); + assertEquals(coins, player.getNumberOfCoinsCollected()); + } + + @Test + void getAccelerationReturnsAcceleration() { + float acceleration = 1.5f; + player.setAcceleration(acceleration); + assertEquals(acceleration, player.getAcceleration()); + } + + @Test + void getMaxSpeedReturnsMaxSpeed() { + float maxSpeed = -7f; + player.setMaxSpeed(maxSpeed); + assertEquals(maxSpeed, player.getMaxSpeed()); + } +/** + @Test + void getImageReturnsImage() { + assertEquals(image, player.getImage()); + } +**/ + @Test + void setImageSetsImage() { + PImage newImage = new PImage(); + player.setImage(newImage); + assertEquals(newImage, player.getImage()); + } +} diff --git a/src/test/java/org/comp2522/ProjectRocketMan/RocketTest.java b/src/test/java/org/comp2522/ProjectRocketMan/RocketTest.java new file mode 100644 index 0000000..3c143c3 --- /dev/null +++ b/src/test/java/org/comp2522/ProjectRocketMan/RocketTest.java @@ -0,0 +1,103 @@ +package org.comp2522.ProjectRocketMan; + +import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; + +import processing.core.PImage; +import processing.core.PVector; +import java.util.ArrayList; + +public class RocketTest { + + @Test + public void testConstructor() { + PVector position = new PVector(0, 0); + PVector direction = new PVector(1, 0); + float speed = 10; + Rocket rocket = new Rocket(position, direction, speed); + + assertEquals(position, rocket.getPosition()); + //assertEquals(direction, rocket.getDirection()); + assertEquals(speed, rocket.getSpeed(), 0.1); + } + + @Test + public void testDraw() { + PVector position = new PVector(0, 0); + PVector direction = new PVector(1, 0); + float speed = 10; + PImage image = new PImage(); + Rocket rocket = new Rocket(position, direction, image, speed); + + // TODO: How to test the draw method? + } + + @Test + public void testSetPosition() { + PVector position = new PVector(0, 0); + PVector newPosition = new PVector(10, 20); + PVector direction = new PVector(1, 0); + float speed = 10; + Rocket rocket = new Rocket(position, direction, speed); + + rocket.setPosition(newPosition); + + assertEquals(newPosition, rocket.getPosition()); + } + + @Test + public void testMove() { + PVector position = new PVector(0, 0); + PVector direction = new PVector(1, 0); + float speed = 10; + Rocket rocket = new Rocket(position, direction, speed); + + rocket.move(); + + assertEquals(new PVector(speed, 0), rocket.getPosition()); + } + + @Test + public void testGetSpeed() { + PVector position = new PVector(0, 0); + PVector direction = new PVector(1, 0); + float speed = 10; + Rocket rocket = new Rocket(position, direction, speed); + + assertEquals(speed, rocket.getSpeed(), 0.1); + } + + @Test + public void testSetSpeed() { + PVector position = new PVector(0, 0); + PVector direction = new PVector(1, 0); + float speed = 10; + Rocket rocket = new Rocket(position, direction, speed); + + rocket.setSpeed(20); + + assertEquals(20, rocket.getSpeed(), 0.1); + } + + @Test + public void testCollided() { + PVector position1 = new PVector(0, 0); + PVector position2 = new PVector(10, 10); + PVector direction = new PVector(1, 0); + float speed = 10; + PImage image = new PImage(); + Player player = Player.getInstance(position1, direction, speed); + Rocket rocket = new Rocket(position2, direction, image, speed); + + // assertTrue(rocket.collided(player)); + } + + @Test + public void testManageItself() { + ArrayList rockets = new ArrayList(); + rockets.add(new Rocket(new PVector(0, 0), new PVector(1, 0), 10)); + rockets.add(new Rocket(new PVector(10, 10), new PVector(-1, 0), 20)); + + + } +} \ No newline at end of file