diff --git a/.gitignore b/.gitignore index 2873e189e1..7c0eb941cd 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ bin/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT + +#custom +/data diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..13eae68f38 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: main.CalculaChroniclesOfTheAlgorithmicKingdom + diff --git a/README.md b/README.md index f82e2494b7..ffd2d4313d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# Game This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. diff --git a/build.gradle b/build.gradle index ea82051fab..c44faf18ad 100644 --- a/build.gradle +++ b/build.gradle @@ -29,11 +29,11 @@ test { } application { - mainClass.set("seedu.duke.Duke") + mainClass.set("main.CalculaChroniclesOfTheAlgorithmicKingdom") } shadowJar { - archiveBaseName.set("duke") + archiveBaseName.set("Release v2.1") archiveClassifier.set("") } @@ -42,5 +42,7 @@ checkstyle { } run{ + enableAssertions = true standardInput = System.in + enableAssertions = true } diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..b180aa580f 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,10 @@ # About us -Display | Name | Github Profile | Portfolio ---------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) + Display | Name | Github Profile | Portfolio +-----------------------------------------------------|:-----------:|:--------------------------------------:|:---------------------------------: + ![](https://via.placeholder.com/100.png?text=Photo) | Aarav Rawal | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) + ![](https://via.placeholder.com/100.png?text=Photo) | Tanner Lie | [Github](https://github.com/tannerlie) | [Portfolio](team/tannerlie.md) + ![](https://via.placeholder.com/100.png?text=Photo) | Fang Sihan | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) + ![](https://via.placeholder.com/100.png?text=Photo) | Samuel | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) + + diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..c3ca0d2e15 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,38 +1,263 @@ # Developer Guide -## Acknowledgements +## Design -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +### Architecture -## Design & implementation +Given below is a higher-level overview of the main components for the app to work. -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +```Main``` has methods which are responsible for: +1. App launch: Initialises the various classes needed and starting up the game +2. App running: Calls the various methods in other Classes to run the game +3. App shutdown: Saves the game state + +listed below are a collection of classes used by multiple components which will be generalised as ```Commons```. +1. ```TextBox``` which is used to set all the messages and narrations for the user. +2. ```FileReader``` to read our design.txt files in order to print certain screens. +3. ```PlayerStatus``` which stores the status and inventory of the player. + +```Ui``` responsible for displaying the game's UI, interactions and narrations to the user.
+```Storage``` responsible for saving the current state of the game when quitting the app.
+```Parser``` is a collection of classes that converts the user's commands and starts the command execution process.
+```Map``` is a collection of classes that handles the data that is being printed to the main portion of the screen.
+
+Below is how some of the architecture components would interact with each other when the user inputs the command to move. +
+![architecture_sequence_diagram](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/ArchitectureSequenceDiagram.png) +
+The section below gives more details of each component. +### UI Component (Ui class) +
+![Ui_diagram](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/Ui.png) +
+The Ui class is responsible for managing the user interface aspects of the application. It handles the display +of various elements such as player status, text boxes, maps, inventory, help menu, and messages. + +The `Ui` Component, +- contains only one class `Ui.java` for Ui related job +- depends on attributes such as `mapData` or `shopItems` in other classes to print +### Parser Component (Parser class) +
+![Parser_diagram](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/Parser.png) +
+The Parser class package is designed to analyze and parse user commands in a text-based adventure game scenario. +It processes user input and determines the appropriate action to take based on predefined command types. + +The `parser` component, +- provides method to read text-based user command +- analyzes and returns the corresponding command type based on the text-based user command + +### User Command Component + +User can type command to do things on the Map. + +If the user type the command, `parseCommand` function will be called. After that, the original input will be +analyzed by `analyseCommand` function to see if it matches a kind of `CommandType`. Then, we will return new +Command back to the main based on the command type. The Final step is to call the `execute` function. If the command +is the type of `fightCommand`, we will call the execute function with one parameter `Scanner`. For all other +conditions, we will call the execute function with no parameter. + +### Map Component: Overview + +The API of this component is defined in BaseMap.java. + +Each map instance is associated with a 2-dimensional array of characters which represents the +printed map for the player, all the printable data is stored in the `MapData` for each instance of a map. +All maps will come with a given `height` and `width`, all of these attributes are inherited +from the AMap abstract class. Currently, the `FirstMap` and `BattleInterface` classes +extend AMap. `FirstMap` is the first map displayed upon entering the game and it displays the position of the player. +The `BattleInterface` is the map displayed when the player interacts with an `interactable`. + +The `MapGenerator` is a class that handles the random generation of the enemies and the location of the shop, and is +only used in `FirstMap` only. + +The following image shows the architecture of the Map component +
+![Map UML](https://github.com/AY2324S2-CS2113-W12-3/tp/blob/master/picture/Map.png?raw=true) +
+ +The reason why the player's map(FirstMap), the shop's interface and the battle interface all extend off of the `BaseMap` +class is because during the game loop, these maps are being cycled through as the main screen the user will view. When +an `Enemy` is interacted with and the [FIGHT] command is used, the `enableFight` is executed for either the Enemy, this +also applies for the Shop. `enableFight` is another game loop that handles all the user - Enemy or user - ShopKeeper +interactions. + +### Map Component: ShopMap Class + +The API of this class is defined in ShopMap.java. + +There exists only 1 shop at any given time during gameplay. During an interaction with the shop. A new separate gameloop + will execute. This comes from the execution of `enableFight`. Below is the diagram that displays how the `enableFight` +method works. +
+![ShopMap](https://github.com/AY2324S2-CS2113-W12-3/tp/blob/master/picture/ShopMap.png?raw=true) +
+ +This is the general flow of `enableFight`. + +1. Enter game loop. +2. Print player status, the shopkeeper and the text box. +3. Get a command from the user. +4. if a valid purchase is detected the purchase is processed. +5. push dialogue to the text box. +6. repeat until the command given is "exit". + +This general flow is also similar to that of the battleInterface's enableFight. However, in the battleInterface, the +fight loop only ends when either the player or the enemy dies. + +### Interacting with Environment Component + +The API of this component is defined in InteractingCommand.java +
+![Interaction UML](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/Interaction.png) +
+ +This component happens when the user chooses to key the interact command ```e```.
+Here is how it works: +1. When the user chooses to fight, the command is parsed. +2. The ```CalculaChroniclesOfTheAlgorithmicKingdom``` component then calls the execute() method in ```InteractingCommand```. +3. It executes the method and creates other objects like ```Enemy``` and ```ShopKeaper``` components which are responsible for the +entity classes in the game and also ```BattleInterface``` and ```ShopMap``` which are responsible for displaying these entities among other things. +4. The ```BattleInterface``` or ```ShopKeeper``` will then read from a .txt file to store their displays by creating the ```FileReader``` object and +running their respective methods. +5. These displays, textboxes and player status will then be subsequently printed by the ```CalculaChroniclesOfTheAlgorithmicKingdom``` object. + +### Battling Component + +The API of this component is defined in FightCommand.java. +
+![BattleInterface UML](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/BattleInterface.png) +
+ +This component occurs when the user chooses to fight an enemy after interacting with it using the command +```f``` or ```fight```.
+Here is how it works: +1. When the user chooses to fight, the command is parsed. +2. The ```CalculaChroniclesOfTheAlgorithmicKingdom``` object then calls the execute() method in ```FightCommand``` and enables the fighting. +3. The ```MathPool``` object is created, which is responsible for the math questions to answer and another ```Ui``` object is created to interact with the user. +4. In the enableFight() method, it has a loop which asks the user math questions to answer until the player or enemy dies. The player takes damage for +every wrong answer and deals damage to the enemy for every correct answer. +5. In this loop, there is another loop to parse the answer given by the user, and displays an error message and the same math question +until the user gives an answer which is a valid integer. +6. Once either the player or enemy dies, it then exits and runs the relevant checks to eventually print the output to be shown to +the user after battle, handled by ```CalculaChroniclesOfTheAlgorithmicKingdom```. + +### Item Usage Component + +The API of the following component is defined in OpenInventoryCommand.java. +
+![OpenInventory_UML](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/OpenInventory.png) +
+This component occurs when the user decides to open up the inventory. The user opens up the inventory using the command ```i``` +or ```inventory```.
+Here is how it works: +1. When the user chooses to open the inventory, the command is parsed. +2. The ```CalculaChroniclesOfTheAlgorithmicKingdom``` object then calls the execute() method in ```OpenInventoryCommand```to get +the inventory from the stored maps in ```BaseMap```. +3. The inventory would then be printed on the Ui for display. + +The API of the following component is defined in UseCommand.java. +
+![UseItem_UML](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/ItemUsage.png) +
+This component occurs when the user decides to use an item after navigating to the inventory page containing consumable items. +Here is how it works: +1. When the user chooses to use an item after navigating to the consumable items page, the command is parsed. +2. The ```CalculaChroniclesOfTheAlgorithmicKingdom``` object then calls the execute() method +3. The method goes through the necessary checks to check if the item intended to use has been indicated as stated in the UserGuide. +4. If any of the checks fail, an error message would be displayed to flag out what went wrong. +5. If all the checks passes, the inventory is obtained from the ```PlayerStatus``` object. The inventory is then searched to check if it +contains the item. +6. The method useItem(item) in the ```PlayerInventory``` object is called if the item is found. Subsequently, +an error message is printed outlining the error. + +## Implementation +### Saving feature +In the main class, the saving is done in every loop through a method called `saveAllGameFile`. +The following sequence diagram shows how methods are called during saving mechanism. +
+![](https://raw.githubusercontent.com/AY2324S2-CS2113-W12-3/tp/master/picture/SavingFeature.png) +
## Product scope ### Target user profile -{Describe the target user profile} +Our target users are young students who are hoping the revise their mathematical skills. ### Value proposition -{Describe the value proposition: what problem does it solve?} +It allows the target user to supplement their existing revision with a more fun and interacting way to revise their mathematics knowledge. ## User Stories -|Version| As a ... | I want to ... | So that I can ...| -|--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| +| Priority | As a ... | I want to ... | So that I can ... | +|----------|----------------|----------------------------------------------------------|---------------------------------------------------------| +| *** | new player | get access to a help menu | refer to them when I don't know the commands to proceed | +| *** | player | see a map of the play area | see the map and location in real-time | +| *** | player | move around at will | explore the world as I want to | +| *** | player | have an ending to the game | win the game | +| *** | player | be able to track stats/items | gauge how my characters progress | +| *** | player | save my game | come back and finish it when I have time | +| *** | player | have a death and restart mechanic | add challenge to the game | +| ** | player | be able to fight entities | battle in and interactive way | +| ** | player | collect items | enhance my character | +| ** | player | interact with things in the environment | be more immersed into the game | +| ** | player | see an actual image of the characters | know what I am fighting against | +| ** | student player | have variations in the questions asked | revise more stuff rather than the same questions | +| ** | player | have clear distinctions between entities I interact with | have a clearer picture of what I'm doing | +| ** | student player | refresh my knowledge of math | revise as I play at the same time | +| * | player | know the background of this game | follow the storyline | +| * | player | see funny and engaging dialogue | enjoy the story | +| * | player | have access to hints to the questions | make calculations easier | -## Non-Functional Requirements -{Give non-functional requirements} +## Non-Functional Requirements -## Glossary +1. Should work on any Windows, Linux, Unix as long as it has Java 11 or above installed. +2. Users should be able to use the app without problems with basic CLI knowledge. +3. A student user should be able to complete the game by answering all the questions. -* *glossary item* - Definition ## Instructions for manual testing -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +### Launch and Shutdown +1. Initial launch + 1. Download the jar file and copy it into an empty folder. + 2. In your command line interface (CLI), navigate to the directory the jar file is in + and enter this command ```java -jar Release.v2.1.jar```. +2. Saving the game + 1. When you feel like closing the game, enter the command ```quit``` or ```q```. + 2. This will close and save the game for when you're ready to come back. + +### Game Movement +1. The game controls utilises w, a, s and d keys to move about as we are all familiar with. ```w``` is for moving upwards, +```a``` is for moving leftwards, ```s``` is for moving downwards and ```d``` is for moving rightwards. +2. Enter ```s``` in the command line and the character represented by the symbol P will move 1 space downwards. A space is denoted by a ```.``` on the map. +3. To move multiple spaces at a time, you may enter a movement direction followed by an integer with a space between them.
+e.g. ```s 3``` + +### Interacting with entities +1. An interactable entity is denoted by a character on the map. +2. The ```#``` character denotes the shop to buy items and the rest is for you to explore and find out. +3. Navigate next to a character and enter ```e``` to interact with it. +4. There will be prompts in the game to help you navigate the interactions. + +### Battling +1. Interacting with an enemy will trigger a battle prompt. +2. ```r``` or ```run``` will allow u to eun away if you're not ready to face the enemy. +3. ```f``` or ```fight``` will trigger the battling sequence where the aim is to get the math questions correct +in order to chip away at the enemies health to defeat it. +4. All answers to the questions are integers. +5. Upon successfully defeating the enemy, exp and money is obtained. + +### Shop +1. Once enough money is accumulated, items can be bought from the shop in order to defeat persistent enemies. +2. Interacting with the shop will bring up a page to enter or exit the shop. +3. Key in the command to enter the shop to be greeted with items to purchase. +4. In order to purchase an item enter the index of the item, e.g. ```1```. + +### Item usage +1. In order to use the items, open up the inventory using the ```i``` or ```inventory``` command. +2. To use an item of index item 1, enter the command ```use 1``` . +3. This only works if there are items in your inventory. \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..9c68c3ef98 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,14 @@ -# Duke +# CalculaChroniclesOfTheAlgorithmicKingdom -{Give product intro here} - -Useful links: +CalculaChroniclesOfTheAlgorithmicKingdom is an open-world game played on the CLI. It aims to provide a fun and interactive +way for younger students to learn/revise their mathematics knowledge. It is equipped with full player autonomy to explore, +interact with shops and their inventory as well as slaying monsters with their wits and intellect by solving mathematical problems.
+
+Below is a list of links for our project: * [User Guide](UserGuide.md) * [Developer Guide](DeveloperGuide.md) * [About Us](AboutUs.md) +* [B1G-SAM PPP](team/b1g-sam.md) +* [bestdownloader365 PPP](team/bestdownloader365.md) +* [tannerlie PPP](team/tannerlie.md) +* [aaravrawal_PPP](team/aaravrawal.md) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..68a909779d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,41 +2,147 @@ ## Introduction -{Give a product intro} +Welcome to CalculaChroniclesOfTheAlgorithmicKingdom. This is a simple text based +game. Your goal in this game is to defeat the enemies within and claim victory for yourself as a +math wizard. ## Quick Start -{Give steps to get started quickly} 1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +2. Download the latest version of `CalculaChroniclesOfTheAlgorithmicKingdom` from [here](https://github.com/AY2324S2-CS2113-W12-3/tp/releases). ## Features -{Give detailed description of each feature} +### The Map +This game displays the character's position on a 2D grid, the player is denoted by a `P` +and the other interactable entities are denoted by other symbols. The entities on the map are randomly generated in each +play through of the game. -### Adding a todo: `todo` -Adds a new item to the list of todo items. +### The Player +At all times, the player's health, money and exp is displayed at the top of the screen. Dropping the player's health +to 0 will cause the player to die and the game is over. + +### The Text Box +At all times, on the bottom part of the screen, a text box will be visible and it will be visible. The text box informs +the player of narrations from the narrator, dialogue from the entities, general instructions from the user interface and +also error messages when they occur. + +### Movement +Traversing the map is similar to many common computer games. We use the "WASD" system such that +entering `w` to the program shifts the player up by 1 space, entering `a` shifts the player to the left, +entering `s` shifts the player down 1 space and lastly, entering `d` shifts the player to the right by 1 space. +This movement can only take place when the 2D grid map is visible. + +Additionally, should the player wish to move more than 1 space at a time, the player can modify the directional commands +by adding a number in this manner. + +E.g `d 10` + +The above command moves the player rightwards by 10 spaces. + + +### Interacting + +To interact with any entity in the game the player first has to move towards the symbol and be within close proximity +to the symbol. We define close as the player needing to be directly above, below, to the left or to the right of an +entity symbol, in other words, there can be no gap in between the player and the entity and interactions cannot occur +diagonally. + +To interact the player enters `e`. + +This action will bring up a new interface called the battle interface which can be for either battling enemies or accessing the shop. +This interface will display an image that corresponds to the symbol on the map and gives you 2 options, to `run` or to `fight`. + +### Run + +Should the player, in the battle interface, determine that the enemy is too strong, the player may choose to type `run` +to back out of the fight and return to the main map to continue moving around. + +### Fight + +Should the player, in the battle interface, wish to fight the enemy. Entering `fight` in the battle interface will +start a fight. A series of math questions will appear based on the strength of the enemy. Generally, stronger enemies +will come with harder math questions. + +The player will then enter the correct answer. The answer is guaranteed to be an integer. Getting the question correct +will cause the player to attack the enemy and getting it wrong will cause the enemy to attack the player. Getting the +questions wrong repeatedly will deplete the player's health and once the player's health reaches 0, the player dies and +the game is over. However, depleting the enemy's health to 0 will result in a victory and the player is rewarded with +some gold and exp before returning the player to the main map. + + +### Items + +There are various consumables the player can acquire throughout the game. The items are consumable items, meaning the +item can be only used once before it is destroyed. There are 2 general types of items, the healing item and the damage +item. Healing items will recover the player's health, if the player's health is already maxed out at +100hp(health points), the healing item will OVER-HEAl and increase the player's health past the initial 100hp. Damage +items will empower the player's next attack to do increased damage on a successful hit. + +### Shop + +Items are purchasable at the shop denoted by a '#' on the map. Interacting with the shop causes the player to enter the +shop. Just like the enemies, the player must enter `fight` to browse the shop's items. After entering the shop, buy an +item by entering the index of the item and the item will automatically be purchased and transferred to the Inventory. +The purchase will only go through if the player has enough money. To exit the shop, the player enters `exit`. + +### Inventory + +The inventory of the player can only be accessed when the player is not currently interacting with an entity. To bring +up the inventory the player enters `i`. To use an item the player enters the keyword `use` followed by the index of the +item. + +E.g `use 1` + +To close the inventory, the player enters `close`. + +### Hints + +Sometimes, hints are scattered throughout the map such that some text will display just below the map to help the +player, hints are entirely passive and no user input is required for the hints to trigger. + +### Quit + +Enter `q` to quit the game. The game is automatically saved. + + +### Help + +If you need a refresher on the controls, entering either `h` or `help` will bring up a set of instructions. + +### Saving + +Our program can save map when you are playing the game. +Also, it will save the player status and your inventory items +Please Note: After buying things in shop or fighting with a monster, you have to move on the map to save your file. + +### Reset + +Should a reset be necessary, the player can completely wipe the current progress of the game and regenerate a new game +at ANY POINT IN THE GAME by entering `reset`. -Format: `todo n/TODO_NAME d/DEADLINE` -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. -Example of usage: -`todo n/Write the rest of the User Guide d/next week` -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` ## FAQ -**Q**: How do I transfer my data to another computer? +**Q**: Is there any prerequisite to run the application. -**A**: {your answer here} +**A**: Java 11 needs to be installed and the jar file must be in an empty folder before running. ## Command Summary -{Give a 'cheat sheet' of commands here} - -* Add todo `todo n/TODO_NAME d/DEADLINE` +`w` `a` `s` `d` to move around +`e` to interact +`q` to quit +`h` to print help menu +`run` to escape the battle interface +`fight` to commence a fight +`i` to bring up inventory +`close` to leave inventory +`exit` to leave the shop +`reset` to reset the entire game and start a new game. + All math questions have integer answers. diff --git a/docs/diagrams/Architecture.puml b/docs/diagrams/Architecture.puml new file mode 100644 index 0000000000..89985d3b41 --- /dev/null +++ b/docs/diagrams/Architecture.puml @@ -0,0 +1,26 @@ +@startuml +box Architecture +participant "User" as user +participant ":CalculaChroniclesOfTheAlgorithmicKingdom" as main +participant ":UI" as ui +participant ":Parser" as parser +participant ":MoveRightCommand" as command + +user -> main : "w" +activate main +main -> parser : parseCommand() +activate parser +parser --> main : command +deactivate parser +main -> command : execute() +activate command +command --> main +deactivate command +main -> ui : print() +activate ui +ui --> user +deactivate ui +user -> main : "d" + +end box +@enduml \ No newline at end of file diff --git a/docs/diagrams/BattleInterface.puml b/docs/diagrams/BattleInterface.puml new file mode 100644 index 0000000000..04b8cf37d3 --- /dev/null +++ b/docs/diagrams/BattleInterface.puml @@ -0,0 +1,123 @@ +@startuml + +box Battle Component +participant ":CalculaChroniclesOfTheAlgorithmicKingdom" as main +participant ":UI" as ui +participant ":Parser" as parser +participant ":TextBox" as text +participant ":FightCommand" as f +participant ":PlayerStatus" as player +participant ":Enemy" as e +participant ":BattleInterface" as bi +participant ":FileReader" as fileReader +participant ":Map" as map +participant ":MathPool" as mathpool + +'activate main +'main -> parser : parseCommand(userCommandText) +'activate parser +'create f +'parser -> f +'activate f +'f --> parser +'deactivate f +'parser --> main : command class +'deactivate parser +'main -> main : executeCommand() +activate main +main -> f : execute() +activate f +f -> bi : enableFight() +activate bi +create mathpool +bi -> mathpool +activate mathpool +mathpool --> bi +deactivate mathpool +bi -> mathpool : init() +activate mathpool +mathpool --> bi +deactivate +create ":UI" as UI +bi -> UI +activate UI +UI --> bi +deactivate UI +loop player and enemy is alive + bi -> player : getPlayerHealth() + activate player + player --> bi : player health + deactivate player + bi -> e : getHealth() + activate e + e --> bi : enemy health + deactivate e + bi -> UI : printPlayerStatus(currentPlayer) + activate UI + UI --> bi + deactivate UI + bi -> UI : printMap(mapData, currentEntity) + activate UI + UI --> bi + deactivate UI + bi -> mathpool : getQuestionByDifficulty(difficulty); + activate mathpool + mathpool --> bi : question + deactivate mathpool + bi -> UI : printQuestion(question) + activate UI + UI --> bi + deactivate UI + loop command is not an integer + bi -> text : setNextError(error) + activate text + text --> bi + deactivate text + bi -> text : setNextInstruction(question) + activate text + text --> bi + deactivate text + bi -> UI : printTextBox() + activate UI + UI --> bi + deactivate UI + end + alt correct answer + bi -> bi : playerHitEnemy() + else wrong answer + bi -> bi : enemyHitPlayer() + end +end +bi --> f +deactivate bi +alt enemy died + f -> map : getInteractX() + activate map + map --> f : x coordinate + deactivate map + f -> map : getInteractY() + activate map + map --> f : y coordinate + deactivate map + f -> map : clearSpot(x coordinate, y coordinate) + activate map + map --> f + deactivate map + f -> map : handleLootingByPlayer() + activate map + map --> f + deactivate map +else player died + f -> map : handleDeath(); + activate map + map --> f + deactivate map +end +f --> main +deactivate f + + + +end box + +@enduml diff --git a/docs/diagrams/Command.puml b/docs/diagrams/Command.puml new file mode 100644 index 0000000000..3fcf96b717 --- /dev/null +++ b/docs/diagrams/Command.puml @@ -0,0 +1,9 @@ +@startuml +'https://plantuml.com/sequence-diagram + +Alice -> Bob: Authentication Request +Bob --> Alice: Authentication Response + +Alice -> Bob: Another authentication Request +Alice <-- Bob: another authentication Response +@enduml \ No newline at end of file diff --git a/docs/diagrams/Interaction.puml b/docs/diagrams/Interaction.puml new file mode 100644 index 0000000000..69898d2d18 --- /dev/null +++ b/docs/diagrams/Interaction.puml @@ -0,0 +1,118 @@ +@startuml + +box Battle Component +participant ":CalculaChroniclesOfTheAlgorithmicKingdom" as main +participant ":UI" as ui +participant ":Parser" as parser +participant ":TextBox" as text +participant ":InteractingCommand" as iCommand +participant ":Enemy" as e +participant ":BattleInterface" as bi +participant ":ShopKeeper" as staff +participant ":ShopMap" as shopMap +participant ":FileReader" as fileReader +participant ":Map" as map + +activate main +'main -> parser : parseCommand(userCommandText) +'activate parser +'create iCommand +'parser -> iCommand +'activate iCommand +'iCommand --> parser +'deactivate iCommand +'parser --> main : command class +'deactivate parser +'main -> main : executeCommand() +'activate main +main -> iCommand : execute() +activate iCommand +iCommand -> map : handleInteract() +activate map +map --> iCommand : entity +deactivate map +alt noEntityToInteract + iCommand -> text : setNextNarration(narration) + activate text + text --> iCommand + deactivate text +else hasEntityToInteract + iCommand -> text : setNextNarration(narration) + activate text + text --> iCommand + deactivate text + iCommand -> text : setNextInstruction(instruction) + activate text + text --> iCommand + deactivate text +end +iCommand -> map : getInteractX() +activate map +map --> iCommand : x coordinate +deactivate map +iCommand -> map : getInteractY() +activate map +map --> iCommand : y coordinate +deactivate map +alt is enemy + create e + iCommand -> e + activate e + e --> iCommand + deactivate e + iCommand -> text : setNextDialogue(dialogue) + activate text + text -> iCommand + deactivate text + create bi + iCommand -> bi + activate bi + bi --> iCommand + deactivate bi + iCommand -> bi : initMap(width, height) + activate bi + create fileReader + bi -> fileReader + activate fileReader + fileReader --> bi + deactivate fileReader + bi -> fileReader : readEnemyDesign() + activate fileReader + fileReader --> bi : enemy display map + deactivate fileReader + bi --> iCommand + deactivate bi +else is a shop + create staff + iCommand -> staff + activate staff + staff --> iCommand + deactivate staff + create shopMap + iCommand -> shopMap + activate shopMap + shopMap --> iCommand + deactivate shopMap +end +iCommand --> main +deactivate iCommand +'deactivate main +'main -> main : printMessageUnderMap(userCommand, ui, playerStatus, textBox) +'activate main +'alt not calling help menu or quitting game +' alt show battle interface +' main -> ui : printEnemy(currentMap) +' activate ui +' ui --> main +' deactivate ui +' else +' main -> ui : printMap(currentMap) +' activate ui +' ui --> main +' deactivate ui +' end +'end + +end box + +@enduml \ No newline at end of file diff --git a/docs/diagrams/ItemUsage.puml b/docs/diagrams/ItemUsage.puml new file mode 100644 index 0000000000..3672e3dc4d --- /dev/null +++ b/docs/diagrams/ItemUsage.puml @@ -0,0 +1,62 @@ +@startuml +box +participant ":CalculaChroniclesOfTheAlgorithmicKingdom" as main +participant ":UseCommand" as use +participant ":TextBox" as text +participant ":PlayerStatus" as status +participant ":BaseMap" as map +participant ":PlayerInventory" as inventory + +activate main +main -> use : execute() +activate use +opt current screen displayed is consumable inventory screen +use -> text : setNextInstruction(instruction) +activate text +text --> use +deactivate text +use -> text : setNextError(error) +activate text +text --> use +deactivate text +use --> main +end +opt consumable list is empty +use -> text : setNextError(error) +activate text +text --> use +deactivate text +use --> main +end +opt item not specified +use -> text : setNextError(error) +activate text +text --> use +deactivate text +use --> main +end +opt item index was not a valid integer +use -> text : setNextError(error) +activate text +text --> use +deactivate text +end +use -> status : getPlayerInventory() +activate status +status --> use +deactivate status +opt item index doesn't exist +use -> text : setNextError(error) +activate text +text --> use +deactivate text +use --> main +end +use -> inventory : useItem(item) +activate inventory +inventory --> use +deactivate inventory +use --> main +deactivate use +end box +@enduml \ No newline at end of file diff --git a/docs/diagrams/Map.puml b/docs/diagrams/Map.puml new file mode 100644 index 0000000000..68a8b59a09 --- /dev/null +++ b/docs/diagrams/Map.puml @@ -0,0 +1,58 @@ +## AMap Class Diagram +@startuml +hide circle + + + class BaseMap { +- currentMap : int (static) +- storedMaps : ArrayList (static) ++ movePlayerUpOne() : void ++ movePlayerDownOne() : void ++ movePlayerLeftOne() : void ++ movePlayerRightOne() : void ++ handleInteract() : String + } + + class FirstMap { ++ isWon() : boolean + } + + class MapGenerator { + + generateMap() : void + } + + class ShopMap { + - currentPlayer : PlayerStatus + - currentTextBox : TextBox + - currentEntity : ShopKeeper + - inventory : PLayerInventory + + enableFight(Scanner in) : void + } + + class BattleInterface { + - currentPlayer : PlayerStatus + - currentTextBox : TextBox + - currentEntity : InteractableEntity + + enableFight() : void + } + + class PLayerInventory { + - currentTextBox : TextBox + - playerstatus : PlayerStatus + + useItem(Consumable item) + } + + + + BaseMap <|-- FirstMap + BaseMap <|-- ShopMap + BaseMap <|-- BattleInterface + BaseMap <|-- PLayerInventory + + +@enduml + + + + +## AMap Sequence Diagram \ No newline at end of file diff --git a/docs/diagrams/OpenInventory.puml b/docs/diagrams/OpenInventory.puml new file mode 100644 index 0000000000..d950fb4397 --- /dev/null +++ b/docs/diagrams/OpenInventory.puml @@ -0,0 +1,17 @@ +@startuml +box +participant ":CalculaChroniclesOfTheAlgorithmicKingdom" as main +participant ":OpenInventoryCommand" as open +participant ":BaseMap" as map + +activate main +main -> open : "execute()" +activate open +open -> map : "get(INVENTORY_IDENTITY)" +activate map +map --> open : inventory +deactivate map +open --> main +deactivate open +end box +@enduml \ No newline at end of file diff --git a/docs/diagrams/Parser.puml b/docs/diagrams/Parser.puml new file mode 100644 index 0000000000..b2ada50c98 --- /dev/null +++ b/docs/diagrams/Parser.puml @@ -0,0 +1,29 @@ +@startuml +hide circle +skinparam classAttributeIconSize 0 + +class Parser{ +{method} +parseCommand(userCommand:String) : Command +} +enum "<>\nCommandType" as CommandType{ +FIGHT +RUN +MOVE_FORWARD +MOVE_DOWNWARD +MOVE_LEFT +MOVE_RIGHT +INTERACT +QUIT +HELP +EXIT +ERROR +INVENTORY +USE_ITEM +CLOSE_INV +RESET +} + +Parser ..> CommandType + +hide CommandType method +@enduml \ No newline at end of file diff --git a/docs/diagrams/SavingFeature.puml b/docs/diagrams/SavingFeature.puml new file mode 100644 index 0000000000..4b3446ef1e --- /dev/null +++ b/docs/diagrams/SavingFeature.puml @@ -0,0 +1,45 @@ +@startuml +hide footbox + +actor User + + +participant ":CalculaChroniclesOfTheAlgorithmicKingdom" as mainClass +participant ":MapStorage" as MapStorage +participant ":PlayerStatusStorage" as PlayerStatusStorage +participant ":InventoryItemsStorage" as InventoryItemsStorage + + +loop until the command type is QuitCommand + User -> mainClass:type a random command + activate mainClass + mainClass -> mainClass: setUserCommand(...) + activate mainClass + deactivate mainClass + mainClass -> mainClass: executeCommand(...) + activate mainClass + deactivate mainClass + mainClass -> mainClass: printMessageUnderMap(...) + activate mainClass + deactivate mainClass + mainClass -> mainClass: saveAllGameFile(...) + activate mainClass + mainClass -> MapStorage:saveMap(map) + activate MapStorage + mainClass -> PlayerStatusStorage:savePlayerStatus(playerStatus) + activate PlayerStatusStorage + mainClass -> InventoryItemsStorage:saveFile(PLAYER_INVENTORY) + activate InventoryItemsStorage + deactivate mainClass + deactivate MapStorage + deactivate PlayerStatusStorage + deactivate InventoryItemsStorage +end + +deactivate mainClass + +destroy mainClass +destroy MapStorage +destroy PlayerStatusStorage +destroy InventoryItemsStorage +@enduml \ No newline at end of file diff --git a/docs/diagrams/ShopMap.puml b/docs/diagrams/ShopMap.puml new file mode 100644 index 0000000000..2ac09fdadf --- /dev/null +++ b/docs/diagrams/ShopMap.puml @@ -0,0 +1,42 @@ +@startuml +actor Player +participant ":ShopMap" as Shop +participant ":Ui" as UI +participant ":TextBox" as TextBox +participant ":Scanner" as Scanner + +Player -> Shop : enableFight(Scanner) +activate Shop + +Shop -> TextBox : queueTextBox() +activate TextBox +TextBox -> TextBox : setNextNarration("You are greeted...") +TextBox -> TextBox : setNextDialogue(currentEntity.getDefaultMessage() + formatShop()) +TextBox -> TextBox : setNextInstruction("Give the shopkeeper...") +deactivate TextBox + +loop while answerCommand != "exit" + Shop -> UI : printPlayerStatus(currentPlayer) + Shop -> UI : printShopKeeper(currentEntity) + Shop -> UI : printTextBox(currentTextBox) + UI --> Player : await command + Player -> Scanner : nextLine() + Scanner --> Shop : user command + + alt answerCommand matches "\\d+" + Shop -> Shop : processPurchase(answerCommand) + else answerCommand == "run" + TextBox -> TextBox : setNextError("Invalid command...") + else else + TextBox -> TextBox : setNextError("Invalid command...") + end if + + Shop -> TextBox : prepareNextDialogue() + TextBox --> Player : Display updated dialogue/instruction +end + +Shop -> TextBox : clearAll() +TextBox -> TextBox : setNextNarration("You exited the shop!!") +deactivate Shop +@enduml + diff --git a/docs/diagrams/Ui.puml b/docs/diagrams/Ui.puml new file mode 100644 index 0000000000..79102a3a6f --- /dev/null +++ b/docs/diagrams/Ui.puml @@ -0,0 +1,32 @@ +@startuml +hide circle +skinparam classAttributeIconSize 0 + +class Ui +class PlayerStatus +class TextBox +class "{abstract}\nBaseMap" as BaseMap{ +#mapData:ArrayList> +} +class Enemy +class ShopKeeper{ +#shopItems:ArrayList +} +class InteractableEntity +class BattleInterface +class ShopMap +class PlayerInventory +class FirstMap + +Ui ..> PlayerStatus +Ui ..> TextBox +Ui ..> BaseMap +Ui ..> Enemy +Ui ..> ShopKeeper +InteractableEntity <|-- ShopKeeper +InteractableEntity <|-- Enemy +BaseMap <|-- BattleInterface +BaseMap <|-- ShopMap +BaseMap <|-- PlayerInventory +BaseMap <|-- FirstMap +@enduml \ No newline at end of file diff --git a/docs/team/aaravrawal.md b/docs/team/aaravrawal.md new file mode 100644 index 0000000000..35303cb0dc --- /dev/null +++ b/docs/team/aaravrawal.md @@ -0,0 +1,28 @@ +# Aarav Rawal - Project Portfolio Page + +## Project: CalculaChroniclesOfTheAlgorithmicKingdom +CalculaChroniclesOfTheAlgorithmicKingdom - Chronicles of the Algorithmic Kingdom. This is a simple text based +game played entirely on the terminal. It is written in Java. + +### Summary of Contributions +- Features: + - Implemented the interacting command class that allows user to interact with various entities in the map + - implemented the inventory class and shop class that allows user to purchase various items from the shop + - Implemente the Shop map class that displays the shop in the CLI + - Implemented the Math pool class and designed all questions in the game for increasing difficulty +- ###### Additions: + - Added the functionality to exit the shop + - Added the functionality to progressivly increase math question difficulty + + +- Code contributed + [RepoSense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=aaravrawal52&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other). + + +- Project management + - Documented the Architecture in the Developer Guide. + - Documented the InteractingCommand, usage and Item Usage component in the Developer Guide with the use of UML diagrams. + +- Community + - Reported an above-average number of bugs in the PE-D (9 bugs to be exact) + diff --git a/docs/team/b1g-sam.md b/docs/team/b1g-sam.md new file mode 100644 index 0000000000..b15f92e5cb --- /dev/null +++ b/docs/team/b1g-sam.md @@ -0,0 +1,39 @@ +# Samuel Chung - Project Portfolio Page + +## Project: CalculaChroniclesOfTheAlgorithmicKingdom +CalculaChroniclesOfTheAlgorithmicKingdom - Chronicles of the Algorithmic Kingdom. This is a simple text based +game played entirely on the terminal. It is written in Java. + +### Summary of Contributions +- Features: + - Implemented BaseMap as a template class to allow seamless transitions between what is being displayed on the main + screen of the User Interface. + - Implemented BattleInterface to handle player-enemy interactions as well as to display relevant ASCII art for + aesthetics. + - Implemented ShopMap to handle player-ShopKeeper interactions as well as to display the relevant ASCII art. + - Implemented the HintHandler class which creates invisible triggers around the map which gives the player hints, + or messages, similar to other, currently existing, games. + - Implemented TextBox. TextBox is a class that supplements the main UI display towards the bottom of the screen. + It handles the error messages, narration messages, dialogue from entities, player instructions in a nicely + formatted manner. + - Implemented PlayerStatus. PlayerStatus is another supplementary class in addition to the main UI display. It + handles the tracking of the health, money and exp statistic of the player. + - Implemented the Math Package. This package allows the team to quickly create math questions and answers and sort + them by difficulty to properly determine the questions being asked by the various enemies on the map. + - Implemented the skeleton of the Consumable item class. + + +- Code contributed + [RepoSense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=B1G-SAM&tabRepo=AY2324S2-CS2113-W12-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + + +- Project management + Created the first release of the User Guide. + Documented the Map component in the Developer Guide with the use of puml diagrams. + + +- Community + - Created overall framework of the project with the help of bestdownloader365 at the start of the tp + - Reported "an above-average number of bugs in the PE-D (8 bugs to be exact)"(CS2113 Teaching Team, April 2024) + during the PE-D. + diff --git a/docs/team/bestdownloader365.md b/docs/team/bestdownloader365.md new file mode 100644 index 0000000000..f0a595292f --- /dev/null +++ b/docs/team/bestdownloader365.md @@ -0,0 +1,48 @@ +# Fang Sihan - Project Portfolio Page + +## Project: CalculaChroniclesOfTheAlgorithmicKingdom + +This is a simple text based game. Your goal in this game is to defeat the enemies within and claim victory +for yourself as a math wizard. The user interacts with it using a CLI. It is written in Java 11. + +### Summary of Contributions + +- New Feature: + - Added the ability to use `w`, `a`, `s`, `d` command on the map. + - Added the ability to use `e` command to interact with shop or monsters. + - Added the ability to use `fight` or `run` command when the user is in battle interface. + - What it does: Created an inner loop framework in `FightingCommand` to interact with various + monsters' interfaces + or shop interface. + - Added the ability to save `map`, `player status` and `inventory` data in the local disk. + - What it does: It can save the player's playing status to ensure that they can read the archive + and + return to the place where they last played the next time they enter the game. + - Added a singleton pattern class `MapGenerator` for generating random map at the beginning of the + game. + +- Code contributed: + [RepoSense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=fang&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23) + + +- Project management: + - Create and manage the release `v1.0` - `v2.1` (3 releases) on Github + - The overall framework was written down by me and another teammate at the beginning of the project, and + the + framework was continuously optimized in the subsequent time. + +- Enhancements to existing features: + - Added exception handling in class `PlayerStatusStorage` + - Added new `execute(Scanner)` method to class `Command`, made fighting with monsters and entering shop + possible + +- Documentation: + - User Guide: + - Added documentation for the features `Saving` and `Inventory` + - Developer Guide: + - Added `Parser` and `Ui` component + - Added the implementation details of `Saving` feature + +- Community: + - Reported bugs and suggestions for other teams in the class (PE-D) + diff --git a/docs/team/tannerlie.md b/docs/team/tannerlie.md new file mode 100644 index 0000000000..36cd5297d7 --- /dev/null +++ b/docs/team/tannerlie.md @@ -0,0 +1,30 @@ +# Tanner Lie - Project Portfolio Page + +## Project: CalculaChroniclesOfTheAlgorithmicKingdom +CalculaChroniclesOfTheAlgorithmicKingdom - Chronicles of the Algorithmic Kingdom. This is a simple text based +game played entirely on the terminal. It is written in Java. + +### Summary of Contributions +- Features: + - Implemented a FileReader class and framework to read from our resources in order to display relevant ASCII art for the game. + - Created Enemy classes for different enemies and their art, as well as their printing methods. + - Implemented PlayerInventory class which stores the players' inventory equipped with logic to use items, add items, and to display the + items on the CLI. + - Implemented the commands in order to open up the inventory as well as the various methods mentioned above. + - Implemented many of the exceptions handling throughout the app. + - Cleaned up checkstyle violations. + + +- Code contributed + [RepoSense link](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=tannerlie&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-02-23&tabOpen=true&tabType=authorship&tabAuthor=tannerlie&tabRepo=AY2324S2-CS2113-W12-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false). + + +- Project management + - Documented the Architecture in the Developer Guide. + - Documented the BattleInterface, Interacting, Inventory usage and Item Usage component in the Developer Guide with the use of puml diagrams. + - Documented the User Stories, nonfunctional requirements and manual testing guide in the Developer Guide. + + +- Community + - Reported "an above-average number of bugs in the PE-D (8 bugs to be exact)"(CS2113 Teaching Team, April 2024) + during the PE-D. \ No newline at end of file diff --git a/picture/ArchitectureSequenceDiagram.png b/picture/ArchitectureSequenceDiagram.png new file mode 100644 index 0000000000..a227542d64 Binary files /dev/null and b/picture/ArchitectureSequenceDiagram.png differ diff --git a/picture/BattleInterface.png b/picture/BattleInterface.png new file mode 100644 index 0000000000..3f71b5ce1b Binary files /dev/null and b/picture/BattleInterface.png differ diff --git a/picture/Command.svg b/picture/Command.svg new file mode 100644 index 0000000000..3625e977e0 --- /dev/null +++ b/picture/Command.svg @@ -0,0 +1 @@ +AliceAliceBobBobAuthentication RequestAuthentication ResponseAnother authentication Requestanother authentication Response \ No newline at end of file diff --git a/picture/Interaction.png b/picture/Interaction.png new file mode 100644 index 0000000000..6e2c2f0123 Binary files /dev/null and b/picture/Interaction.png differ diff --git a/picture/ItemUsage.png b/picture/ItemUsage.png new file mode 100644 index 0000000000..6c53663630 Binary files /dev/null and b/picture/ItemUsage.png differ diff --git a/picture/Map.png b/picture/Map.png new file mode 100644 index 0000000000..4723146448 Binary files /dev/null and b/picture/Map.png differ diff --git a/picture/OpenInventory.png b/picture/OpenInventory.png new file mode 100644 index 0000000000..81c53b8702 Binary files /dev/null and b/picture/OpenInventory.png differ diff --git a/picture/Parser.png b/picture/Parser.png new file mode 100644 index 0000000000..34c64226d1 Binary files /dev/null and b/picture/Parser.png differ diff --git a/picture/SavingFeature.png b/picture/SavingFeature.png new file mode 100644 index 0000000000..c2c242c7ee Binary files /dev/null and b/picture/SavingFeature.png differ diff --git a/picture/ShopMap.png b/picture/ShopMap.png new file mode 100644 index 0000000000..d83ad3bca2 Binary files /dev/null and b/picture/ShopMap.png differ diff --git a/picture/Ui.png b/picture/Ui.png new file mode 100644 index 0000000000..376a714269 Binary files /dev/null and b/picture/Ui.png differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..05526476fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include 'graph' + diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java new file mode 100644 index 0000000000..075038423e --- /dev/null +++ b/src/main/java/command/Command.java @@ -0,0 +1,49 @@ +package command; + +import textbox.PlayerStatus; +import textbox.TextBox; +import map.BaseMap; + +import java.io.FileNotFoundException; +import java.util.Scanner; + + +public abstract class Command { + protected TextBox textBox; + protected PlayerStatus playerStatus; + protected String commandDescription; + protected BaseMap currentMapForCommand; + + public Command() { + commandDescription = "Impossible"; + currentMapForCommand = null; + } + + public abstract void execute(); + + public void execute(Scanner in) throws FileNotFoundException { + } + + public void execute(PlayerStatus playerStatus) { + } + + public String getCommandDescription() { + return commandDescription; + } + + public BaseMap getCurrentMapForCommand() { + return currentMapForCommand; + } + + public void setCurrentMapForCommand(BaseMap givenMap) { + currentMapForCommand = givenMap; + } + + public void setPlayerStatus(PlayerStatus playerStatus) { + this.playerStatus = playerStatus; + } + + public void setTextBox(TextBox textBox) { + this.textBox = textBox; + } +} diff --git a/src/main/java/command/CommandType.java b/src/main/java/command/CommandType.java new file mode 100644 index 0000000000..ce4256f64d --- /dev/null +++ b/src/main/java/command/CommandType.java @@ -0,0 +1,28 @@ +package command; + +public enum CommandType { + FIGHT("(?i)\\h*(f|fight)\\h*"), + RUN("(?i)\\h*(r|run)\\h*"), + MOVE_FORWARD("(?i)\\h*(w)(\\h+\\d{1,5})?\\h*"), + MOVE_DOWNWARD("(?i)\\h*(s)(\\h+\\d{1,5})?\\h*"), + MOVE_LEFT("(?i)\\h*(a)(\\h+\\d{1,5})?\\h*"), + MOVE_RIGHT("(?i)\\h*(d)(\\h+\\d{1,5})?\\h*"), + INTERACT("(?i)\\h*(e)\\h*"), + QUIT("(?i)\\h*(q|quit)\\h*"), + HELP("(?i)\\h*(h|help)\\h*"), + EXIT("(?i)\\h*(exit)\\h*"), + ERROR(""), + INVENTORY("(?i)\\h*(i|inventory)\\h*"), + USE_ITEM("(?i)\\h*(use)(\\h+\\d+|\\h+\\w+)?\\h*"), + CLOSE_INV("(?i)\\h*(close)\\h*"), + RESET("(?i)\\h*(reset)\\h*"); + final String regExpression; + + CommandType(String regExpression) { + this.regExpression = regExpression; + } + + public String getRegExpression() { + return regExpression; + } +} diff --git a/src/main/java/command/ErrorCommand.java b/src/main/java/command/ErrorCommand.java new file mode 100644 index 0000000000..327bc01f5a --- /dev/null +++ b/src/main/java/command/ErrorCommand.java @@ -0,0 +1,9 @@ +package command; + +public class ErrorCommand extends Command { + + @Override + public void execute() { + textBox.setNextError("Invalid Command here"); + } +} diff --git a/src/main/java/command/HelpCommand.java b/src/main/java/command/HelpCommand.java new file mode 100644 index 0000000000..9a0db26e29 --- /dev/null +++ b/src/main/java/command/HelpCommand.java @@ -0,0 +1,14 @@ +package command; + +import ui.Ui; + +public class HelpCommand extends Command { + public HelpCommand(){ + commandDescription = "HelpMe!!"; + } + @Override + public void execute() { + Ui ui = new Ui(); + ui.printHelpMenu(); + } +} diff --git a/src/main/java/command/QuitCommand.java b/src/main/java/command/QuitCommand.java new file mode 100644 index 0000000000..03671db2e4 --- /dev/null +++ b/src/main/java/command/QuitCommand.java @@ -0,0 +1,11 @@ +package command; + +public class QuitCommand extends Command{ + public QuitCommand(){ + commandDescription = "TIRED"; + } + @Override + public void execute() { + + } +} diff --git a/src/main/java/command/ResetCommand.java b/src/main/java/command/ResetCommand.java new file mode 100644 index 0000000000..3a951fd31d --- /dev/null +++ b/src/main/java/command/ResetCommand.java @@ -0,0 +1,101 @@ +package command; + +import inventoryitems.Item; +import map.BaseMap; +import map.FirstMap; +import map.MapGenerator; +import textbox.PlayerStatus; + +import java.util.ArrayList; + +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.PLAYER_INVENTORY; +import static map.BaseMap.mapIndex; +import static map.BaseMap.currentMap; +import static map.BaseMap.storedMaps; +import static map.MapGenerator.FIRST_MAP_IDENTITY; +import static map.MapGenerator.INVENTORY_IDENTITY; + +public class ResetCommand extends Command { + public ResetCommand() { + commandDescription = "RESET!"; + } + + @Override + public void execute() { + + } + + @Override + public void execute(PlayerStatus playerStatus) { + System.out.print("\n\n\n\t\t\t\t\tResetting the game\n\n\n"); + for (int i = 0; i < 201; i++) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + System.out.println("Thread.sleep function failed!\n" + e.getMessage()); + } + System.out.print("Cleaning all maps |" + "#".repeat(i / 6) + + " ".repeat(33 - i / 6) + + "|" + "%" + i / 2 + "\r"); + } + System.out.println(); + + storedMaps.clear(); + mapIndex.clear(); + for (int i = 0; i < 201; i++) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + System.out.println("Thread.sleep function failed!\n" + e.getMessage()); + } + System.out.print("Cleaning player inventory |" + "#".repeat(i / 6) + + " ".repeat(33 - i / 6) + + "|" + "%" + i / 2 + "\r"); + } + System.out.println(); + + ArrayList generalItem = PLAYER_INVENTORY.getGeneralItems(); + generalItem.clear(); + + for (int i = 0; i < 201; i++) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + System.out.println("Thread.sleep function failed!\n" + e.getMessage()); + } + System.out.print("Resetting player status |" + "#".repeat(i / 6) + + " ".repeat(33 - i / 6) + + "|" + "%" + i / 2 + "\r"); + } + System.out.println(); + + playerStatus.setPlayerHealth(100); + playerStatus.setPlayerMoney(0); + playerStatus.setPlayerExp(0); + playerStatus.setPlayerDamage(5); + + for (int i = 0; i < 201; i++) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + System.out.println("Thread.sleep function failed!\n" + e.getMessage()); + } + System.out.print("Generating new map |" + "#".repeat(i / 6) + + " ".repeat(33 - i / 6) + + "|" + "%" + i / 2 + "\r"); + } + System.out.println(); + + BaseMap map = new FirstMap(); + MapGenerator.getInstance().generateMap(map); + map.setTextBox(textBox); + + storedMaps.add(PLAYER_INVENTORY); + mapIndex.put(INVENTORY_IDENTITY, storedMaps.size() - 1); + PLAYER_INVENTORY.setPlayerStatus(playerStatus); + PLAYER_INVENTORY.setCurrentTextBox(textBox); + storedMaps.add(map); + mapIndex.put(FIRST_MAP_IDENTITY, storedMaps.size() - 1); + currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + } +} diff --git a/src/main/java/command/fight/FightingCommand.java b/src/main/java/command/fight/FightingCommand.java new file mode 100644 index 0000000000..f25862eb4c --- /dev/null +++ b/src/main/java/command/fight/FightingCommand.java @@ -0,0 +1,46 @@ +package command.fight; +import command.Command; +import command.ResetCommand; +import map.BaseMap; +import map.ShopMap; + +import java.io.FileNotFoundException; +import java.util.Scanner; + +import static map.BaseMap.mapIndex; +import static map.BaseMap.storedMaps; +import static map.MapGenerator.FIRST_MAP_IDENTITY; + + +public class FightingCommand extends Command { + public FightingCommand() { + commandDescription = "FIGHT!"; + } + + @Override + public void execute() { + } + + @Override + public void execute(Scanner in) throws FileNotFoundException { + if (currentMapForCommand instanceof map.battleinterface.BattleInterface) { + currentMapForCommand.enableFight(in); + BaseMap.currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + if (currentMapForCommand.getEntityDeath()) { + int xPos = storedMaps.get(BaseMap.currentMap).getInteractX(); + int yPos = storedMaps.get(BaseMap.currentMap).getInteractY(); + storedMaps.get(BaseMap.currentMap).clearSpot(xPos, yPos); + currentMapForCommand.handleLootingByPlayer(); + } else if (currentMapForCommand.getPlayerDeath()) { + currentMapForCommand.handleDeath(); + Command deathReset = new ResetCommand(); + deathReset.execute(playerStatus); + } + } + + if (currentMapForCommand instanceof ShopMap) { + currentMapForCommand.enableFight(in); + BaseMap.currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + } + } +} diff --git a/src/main/java/command/fight/RunningCommand.java b/src/main/java/command/fight/RunningCommand.java new file mode 100644 index 0000000000..49442f3f88 --- /dev/null +++ b/src/main/java/command/fight/RunningCommand.java @@ -0,0 +1,28 @@ +package command.fight; + +import command.Command; +import map.BaseMap; +import map.battleinterface.BattleInterface; +import map.ShopMap; + +import static map.BaseMap.storedMaps; +import static map.MapGenerator.FIRST_MAP_IDENTITY; +import static map.BaseMap.mapIndex; + +public class RunningCommand extends Command { + public RunningCommand() { + commandDescription = "RUN!"; + } + @Override + public void execute(){ + if(currentMapForCommand instanceof BattleInterface) { + textBox.setNextNarration("You decide to run and successfully got away"); + BaseMap.currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + currentMapForCommand = storedMaps.get(BaseMap.currentMap); + } else if(currentMapForCommand instanceof ShopMap){ + textBox.setNextNarration("You exited the shop!!"); + BaseMap.currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + currentMapForCommand = storedMaps.get(BaseMap.currentMap); + } + } +} diff --git a/src/main/java/command/inventory/CloseInventoryCommand.java b/src/main/java/command/inventory/CloseInventoryCommand.java new file mode 100644 index 0000000000..18a3a68247 --- /dev/null +++ b/src/main/java/command/inventory/CloseInventoryCommand.java @@ -0,0 +1,18 @@ +package command.inventory; + +import command.Command; +import map.BaseMap; + +import static map.BaseMap.mapIndex; +import static map.MapGenerator.FIRST_MAP_IDENTITY; + +public class CloseInventoryCommand extends Command { + + public CloseInventoryCommand() { + + } + @Override + public void execute() { + BaseMap.currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + } +} diff --git a/src/main/java/command/inventory/OpenInventoryCommand.java b/src/main/java/command/inventory/OpenInventoryCommand.java new file mode 100644 index 0000000000..96f2891a51 --- /dev/null +++ b/src/main/java/command/inventory/OpenInventoryCommand.java @@ -0,0 +1,20 @@ +package command.inventory; + +import command.Command; +import map.BaseMap; + +import static map.BaseMap.mapIndex; +import static map.MapGenerator.INVENTORY_IDENTITY; + +public class OpenInventoryCommand extends Command { + public OpenInventoryCommand() { + commandDescription = "Inventory"; + } + + @Override + public void execute() { + BaseMap.currentMap = mapIndex.get(INVENTORY_IDENTITY); + textBox.setNextNarration("Here's your inventory! You can use an item by keying in [use] followed by it's \n" + + "index with a space between them. Alternatively, enter [close] to close your inventory."); + } +} diff --git a/src/main/java/command/inventory/SellCommand.java b/src/main/java/command/inventory/SellCommand.java new file mode 100644 index 0000000000..3fde6c4e05 --- /dev/null +++ b/src/main/java/command/inventory/SellCommand.java @@ -0,0 +1,64 @@ +package command.inventory; + +import command.Command; +import inventoryitems.Item; +import map.PlayerInventory; + +import java.util.ArrayList; +import java.util.stream.Collectors; + +import static java.lang.Integer.parseInt; + +public class SellCommand extends Command { + String userCommand; + + public SellCommand(String userCommand) { + commandDescription = "SELL!"; + this.userCommand = userCommand; + } + + public void findItem(String item, ArrayList itemList) { + PlayerInventory inventory = playerStatus.getPlayerInventory(); + Item itemToSell; + try { + itemToSell = itemList.stream().filter(x -> x.getName().equalsIgnoreCase(item)) + .collect(Collectors.toList()).get(0); + inventory.sellItem(itemToSell); + } catch (Exception e) { + textBox.setNextError("Please enter a valid item number or item name"); + } + } + + @Override + public void execute() { + PlayerInventory inventory = playerStatus.getPlayerInventory(); + ArrayList list = playerStatus.getPlayerInventory().getGeneralItems(); + if (list.isEmpty()) { + textBox.setNextError("The item does not exist"); + return; + } + String itemString = ""; + int itemIndex; + try { + itemString = userCommand.split(" ", 2)[1]; + itemIndex = parseInt(itemString); + } catch (ArrayIndexOutOfBoundsException e) { + textBox.setNextError("Please enter an item number or item name"); + return; + } catch (NumberFormatException e) { + findItem(itemString, list); + return; + } catch (IndexOutOfBoundsException e) { + textBox.setNextError("The item does not exist"); + return; + } + Item item; + try { + item = list.get(itemIndex - 1); + } catch (Exception e) { + textBox.setNextError("The item does not exist"); + return; + } + inventory.sellItem(item); + } +} diff --git a/src/main/java/command/inventory/UseCommand.java b/src/main/java/command/inventory/UseCommand.java new file mode 100644 index 0000000000..f9e16def67 --- /dev/null +++ b/src/main/java/command/inventory/UseCommand.java @@ -0,0 +1,46 @@ +package command.inventory; + +import command.Command; +import inventoryitems.Consumable; +import inventoryitems.Item; +import map.PlayerInventory; + +import static java.lang.Integer.parseInt; + +public class UseCommand extends Command { + String userCommand; + + public UseCommand(String userCommand) { + commandDescription = "USE!"; + this.userCommand = userCommand; + } + + @Override + public void execute() { + if (playerStatus.getPlayerInventory().getGeneralItems().isEmpty()) { + textBox.setNextError("The item does not exist"); + return; + } + String itemString = ""; + int itemIndex; + try { + itemString = userCommand.split(" ", 2)[1]; + itemIndex = parseInt(itemString); + } catch (ArrayIndexOutOfBoundsException e) { + textBox.setNextError("Please enter an item index"); + return; + } catch (NumberFormatException e) { + textBox.setNextError("Please enter a valid integer for the item index"); + return; + } + Item item; + PlayerInventory inventory = playerStatus.getPlayerInventory(); + try { + item = inventory.getGeneralItems().get(itemIndex - 1); + } catch (Exception e) { + textBox.setNextError("The item does not exist"); + return; + } + inventory.useItem((Consumable) item); + } +} diff --git a/src/main/java/command/mapmove/ExitShop.java b/src/main/java/command/mapmove/ExitShop.java new file mode 100644 index 0000000000..4e5d2f2586 --- /dev/null +++ b/src/main/java/command/mapmove/ExitShop.java @@ -0,0 +1,23 @@ +package command.mapmove; + +import command.Command; +import map.BaseMap; +import map.ShopMap; + +import static map.BaseMap.storedMaps; +import static map.MapGenerator.FIRST_MAP_IDENTITY; +import static map.BaseMap.mapIndex; + +public class ExitShop extends Command { + public ExitShop() { + commandDescription = "RUN!"; + } + @Override + public void execute(){ + if(currentMapForCommand instanceof ShopMap) { + textBox.setNextNarration("You exited the shop!!"); + BaseMap.currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + currentMapForCommand = storedMaps.get(BaseMap.currentMap); + } + } +} diff --git a/src/main/java/command/mapmove/InteractingCommand.java b/src/main/java/command/mapmove/InteractingCommand.java new file mode 100644 index 0000000000..000e4686e5 --- /dev/null +++ b/src/main/java/command/mapmove/InteractingCommand.java @@ -0,0 +1,124 @@ +package command.mapmove; + +import interactable.InteractableEntity; +import interactable.ShopKeeper; +import interactable.enemies.Centaur; +import interactable.enemies.Dragon; +import interactable.enemies.Demon; +import interactable.enemies.Gryphon; +import interactable.enemies.Goblin; +import map.BaseMap; +import map.ShopMap; +import map.battleinterface.BattleInterface; + +import java.util.Objects; + +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.PLAYER_INVENTORY; +import static map.BaseMap.storedMaps; +import static map.BaseMap.mapIndex; +import static map.MapGenerator.CENTAUR; +import static map.MapGenerator.DEMON; +import static map.MapGenerator.DRAGON; +import static map.MapGenerator.GOBLIN; +import static map.MapGenerator.GRYPHON; +import static map.MapGenerator.SHOP; + +public class InteractingCommand extends MapMoveCommand { + + public InteractingCommand() { + commandDescription = "interact"; + } + + @Override + public void execute() { + String entityInteractedWith = currentMapForCommand.handleInteract(); + if (Objects.equals(entityInteractedWith, "no interaction")) { + textBox.setNextNarration("Nothing to interact with here"); + } else if (Objects.equals(entityInteractedWith, "@") || + Objects.equals(entityInteractedWith, "$") || + Objects.equals(entityInteractedWith, "%") || + Objects.equals(entityInteractedWith, "^") || + Objects.equals(entityInteractedWith, "&")) { + textBox.setNextNarration(entityInteractedWith + " appears in your path. What will you do?"); + textBox.setNextInstruction("Will you [fight] or will you [run]?"); + } + + + char entity = entityInteractedWith.charAt(0); + BaseMap battleMap; + InteractableEntity monster; + int xPos = currentMapForCommand.getInteractX(); + int yPos = currentMapForCommand.getInteractY(); + + switch (entity) { + case CENTAUR: + monster = new Centaur(10, 10, 10, xPos, yPos, 10, 10); + textBox.setNextDialogue("*the " + monster.getName() + " stares at you menacingly*"); + battleMap = new BattleInterface(playerStatus, textBox, monster); + battleMap.initMap(30, monster.getHeight()); + storedMaps.add(battleMap); + mapIndex.put(CENTAUR, storedMaps.size() - 1); + BaseMap.currentMap = mapIndex.get(CENTAUR); + break; + case DEMON: + monster = new Demon(15, 15, 15, xPos, yPos, 15, 15); + textBox.setNextDialogue("*the " + monster.getName() + " growls at you menacingly*"); + battleMap = new BattleInterface(playerStatus, textBox, monster); + battleMap.initMap(30, monster.getHeight()); + storedMaps.add(battleMap); + mapIndex.put(DEMON, storedMaps.size() - 1); + BaseMap.currentMap = mapIndex.get(DEMON); + break; + case DRAGON: + monster = new Dragon(20, 20, 20, xPos, yPos, 20, 20); + textBox.setNextDialogue("*the " + monster.getName() + " breathes a ball of flame menacingly*"); + battleMap = new BattleInterface(playerStatus, textBox, monster); + battleMap.initMap(30, monster.getHeight()); + storedMaps.add(battleMap); + mapIndex.put(DRAGON, storedMaps.size() - 1); + BaseMap.currentMap = mapIndex.get(DRAGON); + break; + case GOBLIN: + monster = new Goblin(25, 25, 25, xPos, yPos, 25, 25); + textBox.setNextDialogue("*the " + monster.getName() + " laughs maniacally*"); + battleMap = new BattleInterface(playerStatus, textBox, monster); + battleMap.initMap(30, monster.getHeight()); + storedMaps.add(battleMap); + mapIndex.put(GOBLIN, storedMaps.size() - 1); + BaseMap.currentMap = mapIndex.get(GOBLIN); + break; + case GRYPHON: + monster = new Gryphon(30, 30, 30, xPos, yPos, 30, 30); + textBox.setNextDialogue("*the " + monster.getName() + " screams at you loudly*"); + battleMap = new BattleInterface(playerStatus, textBox, monster); + battleMap.initMap(30, monster.getHeight()); + storedMaps.add(battleMap); + mapIndex.put(GRYPHON, storedMaps.size() - 1); + BaseMap.currentMap = mapIndex.get(GRYPHON); + break; + case SHOP: //some shopkeeper + ShopMap shopMap; + ShopKeeper shopkeeper = new ShopKeeper("ShopKeeper/ShopKeeper.txt", + "*meow* Hi welcome to my shop! *meow*"); + shopMap = new ShopMap(playerStatus, textBox, shopkeeper, PLAYER_INVENTORY); + shopMap.initMap(30, 0); // Set appropriate width and height + shopkeeper.addConsumable(20, 0, "The caffeine is so strong, it heals wounds", + "Cup of Coffee", 10); + shopkeeper.addConsumable(0, 100, "Gun with a single round. Why does a cat have a " + + "gun anyway?", "Desert Eagle", 50); + + storedMaps.add(shopMap); + + mapIndex.put(SHOP, storedMaps.size() - 1); + BaseMap.currentMap = mapIndex.get(SHOP); + + textBox.setNextNarration("You are greeted by a cat with oddly small eyes."); + textBox.setNextInstruction("To enter the shop enter [fight]. To leave now, enter [exit]."); + break; + + default: + battleMap = new BattleInterface(null, null, null); + break; + } + } +} diff --git a/src/main/java/command/mapmove/MapMoveCommand.java b/src/main/java/command/mapmove/MapMoveCommand.java new file mode 100644 index 0000000000..99d80e77f2 --- /dev/null +++ b/src/main/java/command/mapmove/MapMoveCommand.java @@ -0,0 +1,24 @@ +package command.mapmove; + +import command.Command; + +public abstract class MapMoveCommand extends Command { + protected int commandModifier; + + public MapMoveCommand() { + + } + + public MapMoveCommand(String userInput) { + commandDescription = "MapMove"; + userInput = userInput.trim(); + String[] splitUserInput = userInput.split("\\h+"); + if (splitUserInput.length == 1) { + commandModifier = 1; + } else { + commandModifier = Integer.parseInt(splitUserInput[1]); + } + } + + public abstract void execute(); +} diff --git a/src/main/java/command/mapmove/MovingDownwardCommand.java b/src/main/java/command/mapmove/MovingDownwardCommand.java new file mode 100644 index 0000000000..85359f37c3 --- /dev/null +++ b/src/main/java/command/mapmove/MovingDownwardCommand.java @@ -0,0 +1,14 @@ +package command.mapmove; + +public class MovingDownwardCommand extends MapMoveCommand { + public MovingDownwardCommand(String userInput) { + super(userInput); + } + + @Override + public void execute() { + for (int i = 0; i < commandModifier; i++) { + currentMapForCommand.movePlayerDownOne(); + } + } +} diff --git a/src/main/java/command/mapmove/MovingForwardCommand.java b/src/main/java/command/mapmove/MovingForwardCommand.java new file mode 100644 index 0000000000..7abd04a6f0 --- /dev/null +++ b/src/main/java/command/mapmove/MovingForwardCommand.java @@ -0,0 +1,14 @@ +package command.mapmove; + +public class MovingForwardCommand extends MapMoveCommand { + + public MovingForwardCommand(String userInput) { + super(userInput); + } + @Override + public void execute() { + for (int i = 0; i < commandModifier; i++) { + currentMapForCommand.movePlayerUpOne(); + } + } +} diff --git a/src/main/java/command/mapmove/MovingLeftCommand.java b/src/main/java/command/mapmove/MovingLeftCommand.java new file mode 100644 index 0000000000..0ec1d39c26 --- /dev/null +++ b/src/main/java/command/mapmove/MovingLeftCommand.java @@ -0,0 +1,15 @@ +package command.mapmove; + +public class MovingLeftCommand extends MapMoveCommand { + + public MovingLeftCommand(String userInput) { + super(userInput); + } + + @Override + public void execute() { + for (int i = 0; i < commandModifier; i++) { + currentMapForCommand.movePlayerLeftOne(); + } + } +} diff --git a/src/main/java/command/mapmove/MovingRightCommand.java b/src/main/java/command/mapmove/MovingRightCommand.java new file mode 100644 index 0000000000..0f4986dc3d --- /dev/null +++ b/src/main/java/command/mapmove/MovingRightCommand.java @@ -0,0 +1,16 @@ +package command.mapmove; + + +public class MovingRightCommand extends MapMoveCommand { + public MovingRightCommand(String userInput) { + super(userInput); + commandDescription = "Right"; + } + + @Override + public void execute() { + for (int i = 0; i < commandModifier; i++) { + currentMapForCommand.movePlayerRightOne(); + } + } +} diff --git a/src/main/java/filereader/FileReader.java b/src/main/java/filereader/FileReader.java new file mode 100644 index 0000000000..b8f436cb98 --- /dev/null +++ b/src/main/java/filereader/FileReader.java @@ -0,0 +1,37 @@ +package filereader; + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; + +public class FileReader { + private final String filePath; + + + public FileReader(String filePath) { + this.filePath = filePath; + } + + public ArrayList> readDesign() throws IOException { + ArrayList> map = new ArrayList<>(); + InputStream inputStream = getClass().getClassLoader().getResourceAsStream(filePath); + if (inputStream == null) { + throw new IllegalArgumentException("file not found at: " + filePath); + } + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = reader.readLine()) != null) { + ArrayList row = new ArrayList<>(); + for (int i = 0; i < line.length(); i += 1) { + row.add(line.charAt(i)); + } + map.add(row); + } + reader.close(); + inputStream.close(); + return map; + } +} diff --git a/src/main/java/filereader/InventoryItemsStorage.java b/src/main/java/filereader/InventoryItemsStorage.java new file mode 100644 index 0000000000..8e0b9395f9 --- /dev/null +++ b/src/main/java/filereader/InventoryItemsStorage.java @@ -0,0 +1,70 @@ +package filereader; + +import inventoryitems.Consumable; +import inventoryitems.Item; +import map.PlayerInventory; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Scanner; + +import static filereader.filepath.InventoryItemsPath.INVENTORY_ITEMS_PATH; +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.PLAYER_INVENTORY; + +public class InventoryItemsStorage { + + public void readFile() throws IOException { + try { + Files.createDirectories(Paths.get("./data")); + } catch (IOException e) { + System.out.println("Fail to create data directory!\n" + e.getMessage()); + } + + File inventoryItems = new File(INVENTORY_ITEMS_PATH); + + if (!inventoryItems.exists() || inventoryItems.length() == 0) { + try { + Files.createFile(Paths.get(INVENTORY_ITEMS_PATH)); + } catch (IOException e) { + System.out.println("Fail to create inventory text!\n" + e.getMessage()); + } + return; + } + + Scanner fileContent = new Scanner(inventoryItems); + try { + while (fileContent.hasNext()) { + String originalData = fileContent.nextLine(); + String[] data = originalData.split("\\|"); + Consumable item = new Consumable(Integer.parseInt(data[0]), Integer.parseInt(data[1]), data[2], + data[3], Integer.parseInt(data[4])); + item.setQuantity(Integer.parseInt(data[5])); + PLAYER_INVENTORY.loadInventory(item); + } + } catch (ArrayIndexOutOfBoundsException | NumberFormatException | StringIndexOutOfBoundsException e){ + System.out.println("Inventory data corrupted, cleaning your items : ("); + new FileWriter(INVENTORY_ITEMS_PATH).close(); + } + fileContent.close(); + } + + public void saveFile(PlayerInventory playerInventory) throws IOException { + ArrayList generalItems = playerInventory.getGeneralItems(); + new FileWriter(INVENTORY_ITEMS_PATH).close(); + FileWriter fileWriter = new FileWriter(INVENTORY_ITEMS_PATH); + for (Item item : generalItems) { + String currentItem = ""; + currentItem += item.getHealAmt() + "|"; + currentItem += item.getDamageAmpAmt() + "|"; + currentItem += item.getDescription() + "|"; + currentItem += item.getName() + "|"; + currentItem += item.getSellPrice() + "|"; + currentItem += item.getQuantity(); + fileWriter.write(currentItem + System.lineSeparator()); + } + fileWriter.close(); + } +} diff --git a/src/main/java/filereader/MapStorage.java b/src/main/java/filereader/MapStorage.java new file mode 100644 index 0000000000..542cf67dee --- /dev/null +++ b/src/main/java/filereader/MapStorage.java @@ -0,0 +1,135 @@ +package filereader; + +import map.BaseMap; +import map.FirstMap; +import map.MapGenerator; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Scanner; + +import static filereader.filepath.MapFilePath.BASE_MAP_PATH; +import static filereader.filepath.MapFilePath.MAP_PROPERTY_PATH; + +public class MapStorage { + + public BaseMap readFile() throws FileNotFoundException, InterruptedException { + try { + Files.createDirectories(Paths.get("./data")); + } catch (IOException e) { + System.out.println("Fail to create data directory!\n" + e.getMessage()); + } + + File mapPicture = new File(BASE_MAP_PATH); + File mapProperty = new File(MAP_PROPERTY_PATH); + + if (!mapPicture.exists() || !mapProperty.exists() || mapPicture.length() == 0 || + mapProperty.length() == 0) { + try { + Files.createFile(Paths.get(BASE_MAP_PATH)); + } catch (IOException e) { + System.out.println("Fail to create map text!\n" + e.getMessage()); + } + try { + Files.createFile(Paths.get(MAP_PROPERTY_PATH)); + } catch (IOException e) { + System.out.println("Fail to create map property text!\n" + e.getMessage()); + } + System.out.print("\n\n\nNo local archives found or the map data is corrupted\n\n\n"); + for (int i = 0; i < 201; i++) { + Thread.sleep(20); + System.out.print("Generating maps |" + "#".repeat(i / 6) + " ".repeat(33 - i / 6) + + "|" + "%" + i / 2 + "\r"); + } + System.out.println(); + BaseMap map = new FirstMap(); + MapGenerator.getInstance().generateMap(map); + return map; + } + System.out.print("\n\n\nFind local archive\n\n\n"); + for (int i = 0; i < 201; i++) { + Thread.sleep(20); + System.out.print("Loading maps |" + "#".repeat(i / 6) + " ".repeat(33 - i / 6) + + "|" + "%" + i / 2 + "\r"); + } + System.out.println(); + return getBaseMap(mapPicture, mapProperty); + } + + private BaseMap getBaseMap(File mapPicture, File mapProperty) throws FileNotFoundException { + Scanner fileContent1 = new Scanner(mapPicture); + + BaseMap map = new FirstMap(); + ArrayList> mapData = new ArrayList<>(); + while (fileContent1.hasNext()) { + ArrayList chars = new ArrayList<>(); + String content = fileContent1.nextLine(); + for (char ch : content.toCharArray()) { + chars.add(ch); + } + mapData.add(chars); + } + map.setMapData(mapData); + fileContent1.close(); + Scanner fileContent2 = new Scanner(mapProperty); + int round = 0; + while (fileContent2.hasNext()) { + int currentData = Integer.parseInt(fileContent2.nextLine()); + switch (round) { + case 0: + map.setWidth(currentData); + break; + case 1: + map.setHeight(currentData); + break; + case 2: + map.setPlayerX(currentData); + break; + case 3: + map.setPlayerY(currentData); + break; + default: + } + round++; + } + fileContent2.close(); + return map; + } + + public void saveMap(BaseMap map) throws IOException { + new FileWriter(MAP_PROPERTY_PATH).close(); + FileWriter fileWriter1 = getFileWriter(map); + + fileWriter1.close(); + FileWriter fileWriter2 = new FileWriter(BASE_MAP_PATH); + ArrayList> mapData = map.getMapData(); + for (ArrayList ch : mapData) { + StringBuilder temp = new StringBuilder(); + for (Character c : ch) { + temp.append(c); + } + fileWriter2.write(temp + System.lineSeparator()); + } + fileWriter2.close(); + } + + private FileWriter getFileWriter(BaseMap map) throws IOException { + FileWriter fileWriter1 = new FileWriter(MAP_PROPERTY_PATH); + + String width = String.valueOf(map.getWidth()); + String height = String.valueOf(map.getHeight()); + String playerX = String.valueOf(map.getPlayerX()); + String playerY = String.valueOf(map.getPlayerY()); + + fileWriter1.write(width + System.lineSeparator()); + fileWriter1.write(height + System.lineSeparator()); + fileWriter1.write(playerX + System.lineSeparator()); + fileWriter1.write(playerY + System.lineSeparator()); + return fileWriter1; + } +} diff --git a/src/main/java/filereader/PlayerStatusStorage.java b/src/main/java/filereader/PlayerStatusStorage.java new file mode 100644 index 0000000000..d7495c9640 --- /dev/null +++ b/src/main/java/filereader/PlayerStatusStorage.java @@ -0,0 +1,88 @@ +package filereader; + +import textbox.PlayerStatus; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Scanner; + +import static filereader.filepath.PlayerStatusPath.PLAYER_STATUS_PATH; +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.START_HEALTH; +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.START_MONEY; +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.START_EXP; +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.START_DAMAGE; +import static main.CalculaChroniclesOfTheAlgorithmicKingdom.PLAYER_INVENTORY; + +public class PlayerStatusStorage { + public PlayerStatus readPlayerStatus() throws FileNotFoundException { + try { + Files.createDirectories(Paths.get("./data")); + } catch (IOException e) { + System.out.println("Fail to create directory!\n" + e.getMessage()); + } + + File file = new File(PLAYER_STATUS_PATH); + if (!file.exists() || file.length() == 0) { + try { + Files.createFile(Paths.get(PLAYER_STATUS_PATH)); + } catch (IOException e) { + System.out.println("Fail to create playerStatus File!\n" + e.getMessage()); + } + return new PlayerStatus(START_HEALTH, START_MONEY, START_EXP, START_DAMAGE, + PLAYER_INVENTORY); + } + + Scanner fileContent = new Scanner(file); + int round = 0; + int startHealth = -1; + int startMoney = -1; + int startExp = -1; + int startDamage = -1; + try { + while (fileContent.hasNext()) { + int playerStatusData = Integer.parseInt(fileContent.nextLine()); + switch (round) { + case 0: + startHealth = playerStatusData; + break; + case 1: + startMoney = playerStatusData; + break; + case 2: + startExp = playerStatusData; + break; + case 3: + startDamage = playerStatusData; + break; + default: + } + round++; + } + } catch (ArrayIndexOutOfBoundsException | NumberFormatException | StringIndexOutOfBoundsException e) { + System.out.println("Player status corrupted, resetting it to default : ("); + return new PlayerStatus(START_HEALTH, START_MONEY, START_EXP, START_DAMAGE, + PLAYER_INVENTORY); + } + return new PlayerStatus(startHealth, startMoney, startExp, startDamage, PLAYER_INVENTORY); + } + + public void savePlayerStatus(PlayerStatus playerStatus) throws IOException { + new FileWriter(PLAYER_STATUS_PATH).close(); + FileWriter fileWriter = new FileWriter(PLAYER_STATUS_PATH); + + String startHealth = String.valueOf(playerStatus.getPlayerHealth()); + String startMoney = String.valueOf(playerStatus.getPlayerMoney()); + String startExp = String.valueOf(playerStatus.getPlayerExp()); + String startDamage = String.valueOf(playerStatus.getPlayerDamage()); + + fileWriter.write(startHealth + System.lineSeparator()); + fileWriter.write(startMoney + System.lineSeparator()); + fileWriter.write(startExp + System.lineSeparator()); + fileWriter.write(startDamage + System.lineSeparator()); + fileWriter.close(); + } +} diff --git a/src/main/java/filereader/filepath/EnemiesDesignFilePath.java b/src/main/java/filereader/filepath/EnemiesDesignFilePath.java new file mode 100644 index 0000000000..d324e385fd --- /dev/null +++ b/src/main/java/filereader/filepath/EnemiesDesignFilePath.java @@ -0,0 +1,14 @@ +package filereader.filepath; + +public class EnemiesDesignFilePath { + + public static final String GOBLIN_PATH = "enemiesDesign/goblin.txt"; + public static final String CENTAUR_PATH = "enemiesDesign/centaur.txt"; + public static final String DEMON_PATH = "enemiesDesign/demon.txt"; + public static final String GRYPHON_PATH = "enemiesDesign/gryphon.txt"; + public static final String DRAGON_PATH = "enemiesDesign/dragon.txt"; + + public EnemiesDesignFilePath() { + } + +} diff --git a/src/main/java/filereader/filepath/InventoryItemsPath.java b/src/main/java/filereader/filepath/InventoryItemsPath.java new file mode 100644 index 0000000000..a198338e1d --- /dev/null +++ b/src/main/java/filereader/filepath/InventoryItemsPath.java @@ -0,0 +1,5 @@ +package filereader.filepath; + +public class InventoryItemsPath { + public static final String INVENTORY_ITEMS_PATH = "./data/inventory.txt"; +} diff --git a/src/main/java/filereader/filepath/MapFilePath.java b/src/main/java/filereader/filepath/MapFilePath.java new file mode 100644 index 0000000000..cc0a81e619 --- /dev/null +++ b/src/main/java/filereader/filepath/MapFilePath.java @@ -0,0 +1,6 @@ +package filereader.filepath; + +public class MapFilePath { + public static final String BASE_MAP_PATH = "./data/mapPicture.txt"; + public static final String MAP_PROPERTY_PATH = "./data/mapData.txt"; +} diff --git a/src/main/java/filereader/filepath/PlayerStatusPath.java b/src/main/java/filereader/filepath/PlayerStatusPath.java new file mode 100644 index 0000000000..0544331b17 --- /dev/null +++ b/src/main/java/filereader/filepath/PlayerStatusPath.java @@ -0,0 +1,5 @@ +package filereader.filepath; + +public class PlayerStatusPath { + public static final String PLAYER_STATUS_PATH = "./data/playerStatus.txt"; +} diff --git a/src/main/java/hint/HintHandler.java b/src/main/java/hint/HintHandler.java new file mode 100644 index 0000000000..fa8d6f670d --- /dev/null +++ b/src/main/java/hint/HintHandler.java @@ -0,0 +1,53 @@ +package hint; + +import map.BaseMap; +import textbox.TextBox; + +import java.util.ArrayList; + +public class HintHandler { + BaseMap currentMap; + TextBox currentTextBox; + ArrayList invisibleTriggers; + + + public HintHandler(BaseMap map, TextBox text){ + currentMap = map; + currentTextBox = text; + invisibleTriggers = new ArrayList(); + + addTrigger(0, 1, 3, 2, "You should visit the shop sometime. The owner sells many" + + " useful items for your journey"); //add all triggers in the map here + } + + + /** + * Creates an invisible trigger with a defined area using 2 coordinates such that when a player walks over the + * defined area, a message is injected into the Text Box. + * + * @param xStart starting x coordinate + * @param yStart starting y coordinate + * @param xEnd ending x coordinate + * @param yEnd ending y coordinate + * @param message message to be printed + */ + public void addTrigger(int xStart, int yStart, int xEnd, int yEnd, String message){ + Trigger newTrigger = new Trigger(xStart, yStart, xEnd, yEnd, message); + invisibleTriggers.add(newTrigger); + } + + private boolean isWithinArea(Trigger trigger){ + return currentMap.getPlayerX() >= trigger.getXInitial() && currentMap.getPlayerX() <= trigger.getXEnding() && + currentMap.getPlayerY() >= trigger.getYInitial() && currentMap.getPlayerX() <= trigger.getYEnding(); + } + + + public void checkMapThenDisplayHint(){ //takes first hint in list within an area and pushes it to the text box + for (Trigger trigger : invisibleTriggers) { + if (isWithinArea(trigger)){ + currentTextBox.setNextInstruction(trigger.getMessage()); + return; + } + } + } +} diff --git a/src/main/java/hint/Trigger.java b/src/main/java/hint/Trigger.java new file mode 100644 index 0000000000..fb49916809 --- /dev/null +++ b/src/main/java/hint/Trigger.java @@ -0,0 +1,37 @@ +package hint; + +public class Trigger { + protected String message; + protected int xInitial; + protected int yInitial; + protected int xEnding; + protected int yEnding; + + public Trigger(int xStart, int yStart, int xEnd, int yEnd, String newMessage){ + message = newMessage; + xInitial = xStart; + yInitial = yStart; + xEnding = xEnd; + yEnding = yEnd; + } + + public int getXInitial() { + return xInitial; + } + + public int getYInitial() { + return yInitial; + } + + public int getXEnding() { + return xEnding; + } + + public int getYEnding() { + return yEnding; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/interactable/Enemy.java b/src/main/java/interactable/Enemy.java new file mode 100644 index 0000000000..e8aa406a99 --- /dev/null +++ b/src/main/java/interactable/Enemy.java @@ -0,0 +1,51 @@ +package interactable; + +public abstract class Enemy extends InteractableEntity{ + protected int damage; + protected int defence; + protected int health; + protected String filePath; + + + + public Enemy(int dmg, int def, int hp, int xCoordinate, int yCoordinate, int exp, int money){ + this.damage = dmg; + this.defence = def; + this.health = hp; + this.x = xCoordinate; + this.y = yCoordinate; + this.expDropped = exp; + this.moneyDropped = money; + } + + public int getHealth(){ + return health; + } + public void setDamage(int dmg){ + damage = dmg; + } + + public void harmHealth(int dmg){ + health -= dmg; + } + + public int getDefence(){ + return defence; + } + + public int getDamage() { + return damage; + } + + public int getXCoordinate(){ + return this.x; + } + + public int getYCoordinate(){ + return this.y; + } + + public String getFilePath() { + return filePath; + } +} diff --git a/src/main/java/interactable/InteractableEntity.java b/src/main/java/interactable/InteractableEntity.java new file mode 100644 index 0000000000..7db56cdc9c --- /dev/null +++ b/src/main/java/interactable/InteractableEntity.java @@ -0,0 +1,32 @@ +package interactable; + +public abstract class InteractableEntity { + protected int x; + protected int y; + protected int expDropped; + protected int moneyDropped; + protected String name; + + public abstract int getHealth(); + + public abstract int getDefence(); + + public abstract int getDamage(); + + public abstract String getName(); + + public abstract String getFilePath(); + + public int getExp_dropped(){ + return expDropped; + } + + public int getMoney_dropped(){ + return moneyDropped; + } + public int getHeight() { + return 0; + } + + public void setHeight() {} +} diff --git a/src/main/java/interactable/ShopKeeper.java b/src/main/java/interactable/ShopKeeper.java new file mode 100644 index 0000000000..7906a16739 --- /dev/null +++ b/src/main/java/interactable/ShopKeeper.java @@ -0,0 +1,80 @@ +package interactable; + +import inventoryitems.Consumable; +import inventoryitems.ShopItem; + +import java.util.ArrayList; + +public class ShopKeeper extends InteractableEntity{ + protected ArrayList shopItems; + protected String defaultMessage; //whatever the guy says to introduce his items + protected String filePath; + + + public ShopKeeper(String filePathway, String message){ + //addConsumable(); + this.filePath = filePathway; + this.defaultMessage = message; + this.shopItems = new ArrayList<>(); + } + + public ArrayList getShopItems() { + return shopItems; + } + + public void addConsumable(int heal, int damage, String itemDescription, String itemName, int price){ + Consumable newConsumable = new Consumable(heal, damage, itemDescription, itemName, price); + shopItems.add(newConsumable); + } + + public String getDefaultMessage() { + return defaultMessage; + } + + + public String formatShop() { + if (shopItems != null) { + StringBuilder formattedList = new StringBuilder(); + for (int i = 0; i < shopItems.size(); i += 1) { + ShopItem item = shopItems.get(i); + formattedList.append(i + 1).append(". ") + .append(item.getName()) + .append(" - ") + .append(item.getDescription()) + .append(" - ") + .append(" $") + .append(item.getSellPrice()) + .append("\n"); + } + return formattedList.toString(); + } + return "The shop is empty"; + } + + + @Override + public int getHealth() { + return 0; + } + + @Override + public int getDefence() { + return 0; + } + + @Override + public int getDamage() { + return 0; + } + + @Override + public String getName() { + return null; + } + + @Override + public String getFilePath() { + return filePath; + } + +} diff --git a/src/main/java/interactable/enemies/Centaur.java b/src/main/java/interactable/enemies/Centaur.java new file mode 100644 index 0000000000..df92db2b88 --- /dev/null +++ b/src/main/java/interactable/enemies/Centaur.java @@ -0,0 +1,22 @@ +package interactable.enemies; + +import interactable.Enemy; +import static filereader.filepath.EnemiesDesignFilePath.CENTAUR_PATH; + +public class Centaur extends Enemy { + + public Centaur(int dmg, int def, int hp, int xCoordinate, int yCoordinate, int exp, int money) { + super(dmg, def, hp, xCoordinate, yCoordinate, exp, money); + this.name = "Centaur"; + this.filePath = CENTAUR_PATH; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/interactable/enemies/Demon.java b/src/main/java/interactable/enemies/Demon.java new file mode 100644 index 0000000000..cd53917405 --- /dev/null +++ b/src/main/java/interactable/enemies/Demon.java @@ -0,0 +1,22 @@ +package interactable.enemies; + +import interactable.Enemy; +import static filereader.filepath.EnemiesDesignFilePath.DEMON_PATH; + +public class Demon extends Enemy { + + public Demon(int dmg, int def, int hp, int xCoordinate, int yCoordinate, int exp, int money) { + super(dmg, def, hp, xCoordinate, yCoordinate, exp, money); + this.name = "Demon"; + this.filePath = DEMON_PATH; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/interactable/enemies/Dragon.java b/src/main/java/interactable/enemies/Dragon.java new file mode 100644 index 0000000000..42acc0b34d --- /dev/null +++ b/src/main/java/interactable/enemies/Dragon.java @@ -0,0 +1,22 @@ +package interactable.enemies; + +import interactable.Enemy; +import static filereader.filepath.EnemiesDesignFilePath.DRAGON_PATH; + +public class Dragon extends Enemy { + + public Dragon(int dmg, int def, int hp, int xCoordinate, int yCoordinate, int exp, int money) { + super(dmg, def, hp, xCoordinate, yCoordinate, exp, money); + this.name = "Dragon"; + this.filePath = DRAGON_PATH; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/interactable/enemies/Goblin.java b/src/main/java/interactable/enemies/Goblin.java new file mode 100644 index 0000000000..14f3712d54 --- /dev/null +++ b/src/main/java/interactable/enemies/Goblin.java @@ -0,0 +1,22 @@ +package interactable.enemies; + +import interactable.Enemy; +import static filereader.filepath.EnemiesDesignFilePath.GOBLIN_PATH; + +public class Goblin extends Enemy { + + public Goblin(int dmg, int def, int hp, int xCoordinate, int yCoordinate, int exp, int money) { + super(dmg, def, hp, xCoordinate, yCoordinate, exp, money); + this.name = "Goblin"; + this.filePath = GOBLIN_PATH; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/interactable/enemies/Gryphon.java b/src/main/java/interactable/enemies/Gryphon.java new file mode 100644 index 0000000000..713dbc3740 --- /dev/null +++ b/src/main/java/interactable/enemies/Gryphon.java @@ -0,0 +1,22 @@ +package interactable.enemies; + +import interactable.Enemy; +import static filereader.filepath.EnemiesDesignFilePath.GRYPHON_PATH; + +public class Gryphon extends Enemy { + + public Gryphon(int dmg, int def, int hp, int xCoordinate, int yCoordinate, int exp, int money) { + super(dmg, def, hp, xCoordinate, yCoordinate, exp, money); + this.name = "Gryphon"; + this.filePath = GRYPHON_PATH; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/inventoryitems/Consumable.java b/src/main/java/inventoryitems/Consumable.java new file mode 100644 index 0000000000..03ce489a32 --- /dev/null +++ b/src/main/java/inventoryitems/Consumable.java @@ -0,0 +1,40 @@ +package inventoryitems; + +import textbox.PlayerStatus; + +import java.util.ArrayList; + +public class Consumable extends ShopItem { //we assume all consumables are for 1 turn only + protected int healAmt; + protected int damageAmpAmt; + + public Consumable(int heal, int damage, String itemDescription, String itemName, int cost) { + super.description = itemDescription; + healAmt = heal; + damageAmpAmt = damage; + super.name = itemName; + super.price = cost; + super.sellPrice = cost; + } + + + //use() assumes damage items heal for 0 and healing items do 0 damage + public void use(PlayerStatus currentPlayer, Consumable item, ArrayList generalItems) { + for (Item item1 : generalItems){ + if (item1.equals(item)){ + currentPlayer.setPlayerDamageAmp(item.getDamageAmpAmt()); + int currentHealth = currentPlayer.getPlayerHealth(); + currentPlayer.setPlayerHealth(currentHealth + item.getHealAmt()); + break; + } + } + } + + public int getDamageAmpAmt() { + return damageAmpAmt; + } + + public int getHealAmt() { + return healAmt; + } +} diff --git a/src/main/java/inventoryitems/Gear.java b/src/main/java/inventoryitems/Gear.java new file mode 100644 index 0000000000..486f64d499 --- /dev/null +++ b/src/main/java/inventoryitems/Gear.java @@ -0,0 +1,17 @@ +package inventoryitems; + +public abstract class Gear extends Item{ + protected boolean isEquipped; + + public Gear(boolean isEquipped) { + this.isEquipped = isEquipped; + } + + public boolean isEquipped() { + return isEquipped; + } + + public void setEquipped(boolean equipped) { + isEquipped = equipped; + } +} diff --git a/src/main/java/inventoryitems/Item.java b/src/main/java/inventoryitems/Item.java new file mode 100644 index 0000000000..c2094fb16f --- /dev/null +++ b/src/main/java/inventoryitems/Item.java @@ -0,0 +1,44 @@ +package inventoryitems; + +public abstract class Item { + protected String name; + protected String description; + protected int quantity; + protected int sellPrice; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public void addQuantity(int additional) { + this.quantity += additional; + } + + public int getSellPrice() { + return sellPrice; + } + + public int getDamageAmpAmt() { + return -1; + } + + public int getHealAmt() { + return -1; + } + + public void setSellPrice(int sellPrice) { + this.sellPrice = sellPrice; + } +} diff --git a/src/main/java/inventoryitems/ShopItem.java b/src/main/java/inventoryitems/ShopItem.java new file mode 100644 index 0000000000..853be69066 --- /dev/null +++ b/src/main/java/inventoryitems/ShopItem.java @@ -0,0 +1,13 @@ +package inventoryitems; + +public abstract class ShopItem extends Item{ + protected int price; + + public ShopItem() { + + } + + public int getPrice() { + return price; + } +} diff --git a/src/main/java/inventoryitems/wearableitems/Armor.java b/src/main/java/inventoryitems/wearableitems/Armor.java new file mode 100644 index 0000000000..2c74ad8fb9 --- /dev/null +++ b/src/main/java/inventoryitems/wearableitems/Armor.java @@ -0,0 +1,22 @@ +package inventoryitems.wearableitems; + +import inventoryitems.Gear; + +public class Armor extends Gear { + protected int additionalHealth; + + public Armor(String name, String description, boolean isEquipped, int additionalHealth) { + super(isEquipped); + this.name = name; + this.description = description; + this.additionalHealth = additionalHealth; + } + + public int getAdditionalHealth() { + return additionalHealth; + } + + public void setAdditionalHealth(int additionalHealth) { + this.additionalHealth = additionalHealth; + } +} diff --git a/src/main/java/inventoryitems/wearableitems/Helmet.java b/src/main/java/inventoryitems/wearableitems/Helmet.java new file mode 100644 index 0000000000..c08b293910 --- /dev/null +++ b/src/main/java/inventoryitems/wearableitems/Helmet.java @@ -0,0 +1,21 @@ +package inventoryitems.wearableitems; + +import inventoryitems.Gear; + +public class Helmet extends Gear { + protected int additionalHealth; + public Helmet(String name, String description, boolean isEquipped, int additionalHealth) { + super(isEquipped); + this.name = name; + this.description = description; + this.additionalHealth = additionalHealth; + } + + public int getAdditionalHealth() { + return additionalHealth; + } + + public void setAdditionalHealth(int additionalHealth) { + this.additionalHealth = additionalHealth; + } +} diff --git a/src/main/java/inventoryitems/wearableitems/Weapon.java b/src/main/java/inventoryitems/wearableitems/Weapon.java new file mode 100644 index 0000000000..c6979292b8 --- /dev/null +++ b/src/main/java/inventoryitems/wearableitems/Weapon.java @@ -0,0 +1,22 @@ +package inventoryitems.wearableitems; + +import inventoryitems.Gear; + +public class Weapon extends Gear { + protected int additionalDamage; + + public Weapon(String name, String description, boolean isEquipped, int additionalDamage) { + super(isEquipped); + this.name = name; + this.description = description; + this.additionalDamage = additionalDamage; + } + + public int getAdditionalDamage() { + return additionalDamage; + } + + public void setAdditionalDamage(int additionalDamage) { + this.additionalDamage = additionalDamage; + } +} diff --git a/src/main/java/main/CalculaChroniclesOfTheAlgorithmicKingdom.java b/src/main/java/main/CalculaChroniclesOfTheAlgorithmicKingdom.java new file mode 100644 index 0000000000..63938e79d9 --- /dev/null +++ b/src/main/java/main/CalculaChroniclesOfTheAlgorithmicKingdom.java @@ -0,0 +1,167 @@ +package main; + + +import filereader.InventoryItemsStorage; +import filereader.MapStorage; +import filereader.PlayerStatusStorage; +import hint.HintHandler; +import command.Command; +import map.BaseMap; +import map.ShopMap; +import map.PlayerInventory; +import map.battleinterface.BattleInterface; +import parser.Parser; +import textbox.PlayerStatus; +import textbox.TextBox; +import ui.Ui; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Scanner; + +import static map.BaseMap.mapIndex; +import static map.BaseMap.storedMaps; +import static map.BaseMap.currentMap; +import static map.MapGenerator.FIRST_MAP_IDENTITY; +import static map.MapGenerator.INVENTORY_IDENTITY; + + +public class CalculaChroniclesOfTheAlgorithmicKingdom { + + public static final int START_HEALTH = 100; + public static final int START_MONEY = 0; + public static final int START_EXP = 0; + public static final int START_DAMAGE = 5; + public static final PlayerInventory PLAYER_INVENTORY = new PlayerInventory(); + + public static void main(String[] args) throws InterruptedException, FileNotFoundException { + new CalculaChroniclesOfTheAlgorithmicKingdom().startGame(); + } + + public void startGame() throws InterruptedException, FileNotFoundException { + Scanner in = new Scanner(System.in); + + InventoryItemsStorage inventoryItemsStorage = new InventoryItemsStorage(); + MapStorage mapStorage = new MapStorage(); + PlayerStatusStorage playerStatusStorage = new PlayerStatusStorage(); + + PlayerStatus playerStatus = null; + try { + playerStatus = playerStatusStorage.readPlayerStatus(); + } catch (IOException e) { + System.out.println("Can not read playerStatus !!\n" + e.getMessage()); + } + + try { + inventoryItemsStorage.readFile(); + } catch (IOException e) { + System.out.println("Can not read inventory file!\n" + e.getMessage()); + } + storedMaps.add(PLAYER_INVENTORY); + mapIndex.put(INVENTORY_IDENTITY, storedMaps.size() - 1); + TextBox textBox = new TextBox(); + Parser parser = new Parser(); + Ui ui = new Ui(); + + + textBox.initTextBox(); + + + PLAYER_INVENTORY.setPlayerStatus(playerStatus); + PLAYER_INVENTORY.setCurrentTextBox(textBox); + + BaseMap map = null; + try { + map = mapStorage.readFile(); + } catch (FileNotFoundException e) { + System.out.println("\tCan not find your file!!!\n" + e.getMessage()); + } catch (InterruptedException e) { + System.out.println("Timer error !!\n" + e.getMessage()); + } + assert map != null; + map.setTextBox(textBox); // so that first map can use the text box + HintHandler hints = new HintHandler(map, textBox); + storedMaps.add(map); + mapIndex.put(FIRST_MAP_IDENTITY, storedMaps.size() - 1); + currentMap = mapIndex.get(FIRST_MAP_IDENTITY); + + + assert playerStatus != null; + ui.printPlayerStatus(playerStatus); + ui.printMap(storedMaps.get(currentMap)); + ui.printTextBox(textBox); + + Command userCommand; + do { + + String userCommandText = in.nextLine(); + hints.checkMapThenDisplayHint(); //handles invisible map triggers for hints + userCommand = parser.parseCommand(userCommandText); + setUserCommand(userCommand, storedMaps.get(currentMap), playerStatus, textBox); + + executeCommand(userCommand, in, playerStatus); + + printMessageUnderMap(userCommand, ui, playerStatus, textBox); + saveAllGameFile(mapStorage, playerStatusStorage, playerStatus, userCommand, inventoryItemsStorage); + if (storedMaps.get(mapIndex.get(FIRST_MAP_IDENTITY)).isWon()) { + ui.printWinMessage(playerStatus); + break; + } + } while (!userCommand.getCommandDescription().equals("TIRED")); + } + + private static void saveAllGameFile(MapStorage mapStorage, PlayerStatusStorage playerStatusStorage, + PlayerStatus playerStatus, Command userCommand, + InventoryItemsStorage inventoryItemsStorage) { + try { + mapStorage.saveMap(storedMaps.get(mapIndex.get(FIRST_MAP_IDENTITY))); + } catch (IOException e) { + System.out.println("Can not save the map!\n" + e.getMessage()); + } + try { + playerStatusStorage.savePlayerStatus(playerStatus); + } catch (IOException e) { + System.out.println("Can not save Player Status" + e.getMessage()); + } + try { + inventoryItemsStorage.saveFile(PLAYER_INVENTORY); + } catch (IOException e) { + System.out.println("Can not save inventory items!\n" + e.getMessage()); + } + } + + private static void printMessageUnderMap(Command userCommand, Ui ui, PlayerStatus playerStatus, TextBox textBox) { + if (!userCommand.getCommandDescription().equals("HelpMe!!") && + !userCommand.getCommandDescription().equals("TIRED")) { + ui.printPlayerStatus(playerStatus); + if (storedMaps.get(currentMap) instanceof BattleInterface || + storedMaps.get(currentMap) instanceof ShopMap) { + ui.printEnemy(storedMaps.get(currentMap)); + } else if (storedMaps.get(currentMap) instanceof PlayerInventory) { + ui.printInventory(playerStatus.getPlayerInventory().getGeneralItems(), + playerStatus.getPlayerInventory().getInventoryNames().get(0), + storedMaps.get(currentMap).getWidth(), storedMaps.get(currentMap).getHeight()); + } else { + ui.printMap(storedMaps.get(currentMap)); + } + ui.printTextBox(textBox); + } + } + + private static void executeCommand(Command userCommand, Scanner in, PlayerStatus playerStatus) + throws FileNotFoundException { + if (userCommand.getCommandDescription().equals("FIGHT!")) { + userCommand.execute(in); + } else if (userCommand.getCommandDescription().equals("RESET!")) { + userCommand.execute(playerStatus); + } else { + userCommand.execute(); + } + } + + private static void setUserCommand(Command userCommand, BaseMap map, PlayerStatus playerStatus, TextBox textBox) { + userCommand.setCurrentMapForCommand(map); + userCommand.setPlayerStatus(playerStatus); + userCommand.setTextBox(textBox); + } +} diff --git a/src/main/java/map/BaseMap.java b/src/main/java/map/BaseMap.java new file mode 100644 index 0000000000..cf88974d55 --- /dev/null +++ b/src/main/java/map/BaseMap.java @@ -0,0 +1,246 @@ +package map; + +import textbox.TextBox; +import ui.Ui; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Scanner; + +public abstract class BaseMap { + + public static int currentMap; + public static ArrayList storedMaps = new ArrayList<>(); + public static HashMap mapIndex = new HashMap<>(); + protected int width; + protected int height; + protected ArrayList> mapData; + protected int playerX; + protected int playerY; + protected String mapName; + protected TextBox textBox; + + public BaseMap() { + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setPlayerX(int playerX) { + this.playerX = playerX; + } + + public void setPlayerY(int playerY) { + this.playerY = playerY; + } + + public abstract void enableFight(); + + public void enableFight(Scanner in) throws FileNotFoundException { + assert in != null; + } + + public void initMap(int givenWidth, int givenHeight) { + assert givenHeight != 0; + assert givenWidth != 0; + } + + public void setTextBox(TextBox box){ + textBox = box; + } + + public ArrayList> getMapData() { + return mapData; + } + public void setMapData(ArrayList> mapData) { + this.mapData = mapData; + } + + public void initPlayerLocation(int x, int y) { + + if (x >= 0 && x < width && y >= 0 && y < height) { + mapData.get(y).set(x, 'P'); + this.playerX = x; + this.playerY = y; + } + + } + + public void initShopLocation(int x, int y) { + if (x >= 0 && x < width && y >= 0 && y < height) { + mapData.get(y).set(x, '#'); + } + } + + public void movePlayerUpOne() { + Ui ui = new Ui(); + if (this.playerY - 1 >= 0) { + if (mapData.get(playerY - 1).get(playerX) != '.') { + ui.insertObjectObstructionMessage(textBox); + } else { + mapData.get(playerY).set(playerX, '.'); + mapData.get(playerY - 1).set(playerX, 'P'); + this.playerY -= 1; + } + } else { + ui.insertOutOfBoundsMessage(textBox); + } + } + + public void movePlayerDownOne() { + Ui ui = new Ui(); + if (this.playerY + 1 < height) { + if (mapData.get(playerY + 1).get(playerX) != '.') { + ui.insertObjectObstructionMessage(textBox); + } else { + mapData.get(playerY).set(playerX, '.'); + mapData.get(playerY + 1).set(playerX, 'P'); + this.playerY += 1; + } + } else { + ui.insertOutOfBoundsMessage(textBox); + } + } + + public void movePlayerLeftOne() { + Ui ui = new Ui(); + if (this.playerX - 1 >= 0) { + if (mapData.get(playerY).get(playerX - 1) != '.') { + ui.insertObjectObstructionMessage(textBox); + } else { + mapData.get(playerY).set(playerX, '.'); + mapData.get(playerY).set(playerX - 1, 'P'); + this.playerX -= 1; + } + } else { + ui.insertOutOfBoundsMessage(textBox); + } + } + + public void movePlayerRightOne() { + Ui ui = new Ui(); + if (this.playerX + 1 < width) { + if (mapData.get(playerY).get(playerX + 1) != '.') { + ui.insertObjectObstructionMessage(textBox); + } else { + mapData.get(playerY).set(playerX, '.'); + mapData.get(playerY).set(playerX + 1, 'P'); + this.playerX += 1; + } + } else { + ui.insertOutOfBoundsMessage(textBox); + } + } + + + public ArrayList> getMap() { + return mapData; + } + + public String handleInteract() { + if (playerY > 0 && mapData.get(playerY - 1).get(playerX) != '.') { + return String.valueOf(mapData.get(playerY - 1).get(playerX)); + } + + if (playerX < mapData.get(0).size() - 1 && mapData.get(playerY).get(playerX + 1) != '.') { + return String.valueOf(mapData.get(playerY).get(playerX + 1)); + } + + if (playerY < mapData.size() - 1 && mapData.get(playerY + 1).get(playerX) != '.') { + return String.valueOf(mapData.get(playerY + 1).get(playerX)); + } + + if (playerX > 0 && mapData.get(playerY).get(playerX - 1) != '.') { + return String.valueOf(mapData.get(playerY).get(playerX - 1)); + } + return "no interaction"; + } + + public int getInteractX() { + if (playerY > 0 && mapData.get(playerY - 1).get(playerX) != '.') { + return playerX; + } + if (playerX < mapData.get(0).size() - 1 && mapData.get(playerY).get(playerX + 1) != '.') { + return playerX + 1; + } + if (playerY < mapData.size() - 1 && mapData.get(playerY + 1).get(playerX) != '.') { + return playerX; + } + if (playerX > 0 && mapData.get(playerY).get(playerX - 1) != '.') { + return playerX - 1; + } + return -1; + } + + public int getInteractY() { + if (playerY > 0 && mapData.get(playerY - 1).get(playerX) != '.') { + return playerY - 1; + } + if (playerX < mapData.get(0).size() - 1 && mapData.get(playerY).get(playerX + 1) != '.') { + return playerY; + } + if (playerY < mapData.size() - 1 && mapData.get(playerY + 1).get(playerX) != '.') { + return playerY + 1; + } + if (playerX > 0 && mapData.get(playerY).get(playerX - 1) != '.') { + return playerY; + } + return -1; + } + + public void clearSpot(int x, int y) { + if (x >= 0 && x < width && y >= 0 && y < height) { + mapData.get(y).set(x, '.'); + } + } + + public abstract boolean getEntityDeath(); + + public abstract boolean getPlayerDeath(); + + public abstract void handleDeath(); + + public abstract void handleLootingByPlayer(); + + public void placeMonsterInTheMap(int x, int y, char monsterType) { + mapData.get(y).set(x, monsterType); + } + + public Character getCurrentMapInfo(int x, int y) { + return mapData.get(y).get(x); + } + + public int getPlayerX() { + return playerX; + } + + public int getPlayerY() { + return playerY; + } + public void printMap(){ + for(int i = 0; i < height; i++){ + for (int j = 0; j < width; j++){ + System.out.print(mapData.get(i).get(j) + " "); + } + System.out.println(); + } + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public boolean isWon() { + return false; + } +} diff --git a/src/main/java/map/FirstMap.java b/src/main/java/map/FirstMap.java new file mode 100644 index 0000000000..ea45450478 --- /dev/null +++ b/src/main/java/map/FirstMap.java @@ -0,0 +1,52 @@ +package map; + +import java.util.ArrayList; + +public class FirstMap extends BaseMap { + protected String difficultyModifier = "easy"; //can use to determine question difficulty + + @Override + public void enableFight() { + System.out.println("lol"); + } + public boolean getEntityDeath(){ + return false; + } + @Override + public boolean getPlayerDeath(){ + return false; + } + @Override + public void handleDeath() { + } + @Override + public void handleLootingByPlayer(){ + + } + public void initMap(int givenWidth, int givenHeight) { //creates a box of "." of a given width and height and width + this.width = givenWidth; + this.height = givenHeight; + this.mapData = new ArrayList<>(height); + + for (int i = 0; i < height; i += 1) { + ArrayList row = new ArrayList<>(width); + for (int j = 0; j < width; j += 1) { + row.add('.'); + } + mapData.add(row); + } + } + + + public boolean isWon() { + for (ArrayList row : mapData) { + for (char element : row) { + if (element != '.' && element != 'P' && element != '#') { + + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/map/MapGenerator.java b/src/main/java/map/MapGenerator.java new file mode 100644 index 0000000000..0641a0152d --- /dev/null +++ b/src/main/java/map/MapGenerator.java @@ -0,0 +1,67 @@ +package map; + +import java.util.concurrent.ThreadLocalRandom; + +public class MapGenerator { + public static final char FIRST_MAP_IDENTITY = '!'; + public static final char SHOP = '#'; + public static final char CENTAUR = '@'; + public static final char DEMON = '$'; + public static final char DRAGON= '%'; + public static final char GOBLIN = '^'; + public static final char GRYPHON = '&'; + public static final char INVENTORY_IDENTITY = 'i'; + + private static final int MIN = 0; + private static final int MAX = 999; + private static MapGenerator mapGen = null; + + private MapGenerator() { + + } + + public static MapGenerator getInstance() { + if (mapGen == null) { + mapGen = new MapGenerator(); + } + return mapGen; + } + + public void generateMap(BaseMap map) { + int mapWidth = 30; + int mapHeight = 10; + int initPlayerX = 0; + int initPlayerY = 0; + int shopX = ThreadLocalRandom.current().nextInt(0, mapWidth); + int shopY = ThreadLocalRandom.current().nextInt(0, mapHeight); + + map.initMap(mapWidth, mapHeight); + map.initPlayerLocation(initPlayerX, initPlayerY); + map.initShopLocation(shopX, shopY); + + int generateFactor; + for (int i = 0; i < mapWidth; i++) { + for (int j = 0; j < mapHeight; j++) { + generateFactor = ThreadLocalRandom.current().nextInt(MIN, MAX + 1); + if(map.getCurrentMapInfo(i, j) == '.'){ + if(generateFactor == 0){ + map.placeMonsterInTheMap(i, j, GRYPHON); + } else if (generateFactor >= 0 && generateFactor <= 4){ + map.placeMonsterInTheMap(i, j, GOBLIN); + } else if (generateFactor >= 0 && generateFactor <= 9){ + map.placeMonsterInTheMap(i, j, DRAGON); + } else if (generateFactor >= 0 && generateFactor <= 29){ + map.placeMonsterInTheMap(i, j, DEMON); + } else if (generateFactor >= 0 && generateFactor <= 49){ + map.placeMonsterInTheMap(i, j, CENTAUR); + } + } + } + } + + } + + public void setMap(BaseMap mapToGenerate) { + getInstance().generateMap(mapToGenerate); + } +} diff --git a/src/main/java/map/PlayerInventory.java b/src/main/java/map/PlayerInventory.java new file mode 100644 index 0000000000..0aabd88a93 --- /dev/null +++ b/src/main/java/map/PlayerInventory.java @@ -0,0 +1,126 @@ +package map; + +import inventoryitems.Consumable; +import inventoryitems.Item; +import textbox.PlayerStatus; +import textbox.TextBox; +import ui.Ui; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class PlayerInventory extends BaseMap { + protected ArrayList inventoryNames; + protected ArrayList generalItems; + protected Ui ui; + protected TextBox currentTextBox; + protected PlayerStatus playerStatus; + + public PlayerInventory() { + this.generalItems = new ArrayList<>(); + this.inventoryNames = new ArrayList<>(); + inventoryNames.add("General"); + this.ui = new Ui(); + width = 59; + height = 8; + } + + public void loadInventory(Consumable item){ + generalItems.add(item); + } + + public void addItems(Consumable item) { + if (generalItems.isEmpty()) { + item.setQuantity(1); + generalItems.add(item); + return; + } + List filteredList = generalItems.stream().filter(x -> x.getName().equalsIgnoreCase( + item.getName())).collect(Collectors.toList()); + if (filteredList.isEmpty()) { + item.setQuantity(1); + generalItems.add(item); + return; + } + filteredList.get(0).addQuantity(1); + } + + public void useItem(Consumable item) { + item.use(playerStatus, item, generalItems); + int leftover = item.getQuantity() - 1; + if (leftover <= 0) { + generalItems.remove(item); + } else { + item.setQuantity(item.getQuantity() - 1); + } + currentTextBox.setNextDialogue(item.getName() + " has been used."); + } + + public void sellItem(Item item) { + try { + if (item.getSellPrice() == 0) { + currentTextBox.setNextError("This item cannot be sold"); + return; + } + } catch (Exception e) { + currentTextBox.setNextError("This item cannot be sold"); + } + int leftover = item.getQuantity() - 1; + if (leftover <= 0) { + generalItems.remove(item); + } else { + item.setQuantity(item.getQuantity() - 1); + } + playerStatus.addMoney(item.getSellPrice()); + currentTextBox.setNextDialogue("Congrats, you just sold a " + item.getName() + " for $" + item.getSellPrice()); + } + + public ArrayList getGeneralItems() { + return generalItems; + } + + public void setGeneralItems(ArrayList items) { + this.generalItems = items; + } + + public ArrayList getInventoryNames() { + return inventoryNames; + } + + public void setInventoryNames(ArrayList inventoryNames) { + this.inventoryNames = inventoryNames; + } + + public void setPlayerStatus(PlayerStatus playerStatus) { + this.playerStatus = playerStatus; + } + + public void setCurrentTextBox(TextBox currentTextBox) { + this.currentTextBox = currentTextBox; + } + @Override + public void enableFight() { + + } + + @Override + public boolean getEntityDeath() { + return false; + } + + @Override + public boolean getPlayerDeath() { + return false; + } + + @Override + public void handleDeath() { + + } + + @Override + public void handleLootingByPlayer() { + + } +} diff --git a/src/main/java/map/ShopMap.java b/src/main/java/map/ShopMap.java new file mode 100644 index 0000000000..15a99d02ad --- /dev/null +++ b/src/main/java/map/ShopMap.java @@ -0,0 +1,133 @@ +package map; + +import interactable.ShopKeeper; +import filereader.FileReader; +import inventoryitems.Consumable; +import inventoryitems.ShopItem; +import textbox.PlayerStatus; +import textbox.TextBox; +import ui.Ui; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Scanner; + + +public class ShopMap extends BaseMap{ + protected PlayerStatus currentPlayer; + protected TextBox currentTextBox; + protected ShopKeeper currentEntity; + protected PlayerInventory inventory; + + public ShopMap(PlayerStatus player, TextBox text, ShopKeeper shopKeeper, PlayerInventory bag){ + this.currentPlayer = player; + this.currentTextBox = text; + this.currentEntity = shopKeeper; + this.inventory = bag; + //this.currentMap = new ArrayList<>(height); + //loadShopMap(); + } + @Override + public void initMap(int givenWidth, int givenHeight) { + this.width = givenWidth; + this.height = givenHeight; + + this.mapData = new ArrayList<>(height); + + FileReader fileReader = new FileReader(currentEntity.getFilePath()); + try { + mapData = fileReader.readDesign(); + } catch (Exception e) { + // display exception, see how sihan wants to do. + } + } + + + public void queueTextBox(){ + currentTextBox.setNextNarration("You are greeted by a cat with oddly small eyes.\n"); + currentTextBox.setNextDialogue(currentEntity.getDefaultMessage() + "\n" + currentEntity.formatShop()); + currentTextBox.setNextInstruction("Give the shop keeper an [INDEX] to view the item and purchase" + + " or enter [exit]" + + " to leave the shop."); + } + + + + public void enableFight(){ + + } + + + @Override + public void enableFight(Scanner in) throws FileNotFoundException { + String answerCommand = ""; + Ui ui = new Ui(); + queueTextBox(); + while (true) { + + ui.printPlayerStatus(currentPlayer); + ui.printShopKeeper(currentEntity); + ui.printTextBox(currentTextBox); + + answerCommand = in.nextLine().trim(); + answerCommand = (answerCommand.length() > 10) ? answerCommand.substring(0, 10) : answerCommand; + + // Check if the command is "exit" to break the loop + if (answerCommand.equalsIgnoreCase("exit")) { + break; // Exit the loop if the command is "exit" + } + + // Check if the input is numeric and not 'run' + if (answerCommand.matches("\\d+")) { + int index = Integer.parseInt(answerCommand) - 1; + ArrayList shopItems = currentEntity.getShopItems(); + + if (index >= 0 && index < shopItems.size()) { + ShopItem item = shopItems.get(index); + if (currentPlayer.getPlayerMoney() >= item.getPrice()) { + int currentMoney = currentPlayer.getPlayerMoney(); + currentPlayer.setPlayerMoney(currentMoney - item.getPrice()); + currentTextBox.setNextNarration("NEW ITEM ADDED TO INVENTORY"); + inventory.addItems((Consumable) item); + } else { + currentTextBox.setNextNarration("The cat silently judged your broke ass.\n"); + } + } else { + currentTextBox.setNextError("Invalid index. Please enter a valid item index or 'exit'."); + } + } else if (answerCommand.equalsIgnoreCase("run")) { + // Handle "run" as an invalid input, maybe logging or just ignoring + currentTextBox.setNextError("Invalid command. The word 'run' is not recognized in this context."); + } else { + currentTextBox.setNextError("Invalid command. Please enter a valid item index or 'exit'."); + } + + currentTextBox.setNextDialogue(currentEntity.getDefaultMessage() + "\n" + currentEntity.formatShop()); + currentTextBox.setNextInstruction("Give the shopkeeper an [INDEX] to view the item and purchase or " + + "enter [exit]" + + " to leave the shop."); + } + currentTextBox.clearAll(); + currentTextBox.setNextNarration("You exited the shop!!"); + } + + + + @Override + public boolean getEntityDeath() { + return false; + } + + @Override + public boolean getPlayerDeath() { + return false; + } + + @Override + public void handleDeath() { + } + + @Override + public void handleLootingByPlayer() { + } +} diff --git a/src/main/java/map/battleinterface/BattleInterface.java b/src/main/java/map/battleinterface/BattleInterface.java new file mode 100644 index 0000000000..b460f25f54 --- /dev/null +++ b/src/main/java/map/battleinterface/BattleInterface.java @@ -0,0 +1,142 @@ +package map.battleinterface; + +import interactable.Enemy; +import interactable.InteractableEntity; +import filereader.FileReader; +import map.BaseMap; +import textbox.PlayerStatus; +import textbox.TextBox; +import ui.Ui; +import math.MathQuestion; +import math.MathPool; + +import java.util.ArrayList; +import java.util.Scanner; +import java.util.regex.Pattern; + +public class BattleInterface extends BaseMap { + protected PlayerStatus currentPlayer; + protected TextBox currentTextBox; + protected InteractableEntity currentEntity; + + + public BattleInterface(PlayerStatus player, TextBox text, InteractableEntity entity) { + this.currentPlayer = player; + this.currentTextBox = text; + this.currentEntity = entity; + } + + @Override + public void enableFight() { + + } + + @Override + public void enableFight(Scanner in) { + MathPool mathPool = new MathPool(); + mathPool.init(); + Ui ui = new Ui(); + int difficulty = 0; + while (currentPlayer.getPlayerHealth() > 0 && currentEntity.getHealth() > 0) { + int answer; + Pattern pattern = Pattern.compile("^[--]?[0-9]+$"); // Pattern to check if the input is numeric + ui.printPlayerStatus(currentPlayer); + ui.printMap(mapData, (Enemy) currentEntity); + MathQuestion mathQuestion = mathPool.getQuestionByDifficulty(difficulty); + currentTextBox.setNextNarration(mathQuestion.getQuestion()); + ui.printTextBox(currentTextBox); + String answerCommand = in.nextLine().trim(); + if (answerCommand.length() > 5) { // Check if input length exceeds 5 characters + answerCommand = answerCommand.substring(0, 5); // Take only the first 5 characters + } + while (!pattern.matcher(answerCommand).matches()) { // Validate the trimmed input + currentTextBox.setNextError("Answer must be an integer."); + currentTextBox.setNextInstruction(mathQuestion.getQuestion()); + ui.printTextBox(currentTextBox); + answerCommand = in.nextLine().trim(); + if (answerCommand.length() > 5) { + answerCommand = answerCommand.substring(0, 5); // Again trim input if needed + } + } + answer = Integer.parseInt(answerCommand); // Parse the possibly truncated input + if (mathQuestion.checkAns(answer)) { + currentTextBox.setNextDialogue("You got the question CORRECT. You then proceed to swing as " + + "hard as you can"); + playerHitEnemy(); + difficulty += 1; + } else { + currentTextBox.setNextDialogue("You got the question WRONG. The enemy proceeds to attack you."); + enemyHitPlayer(); + } + } + } + + + public void initMap(int givenWidth, int givenHeight) { + this.width = givenWidth; + this.height = givenHeight; + this.mapData = new ArrayList<>(height); + + FileReader fileReader = new FileReader(currentEntity.getFilePath()); + try { + mapData = fileReader.readDesign(); + } catch (Exception e) { + currentTextBox.setNextError("Unable to read file from local"); + } + } + + + public void playerHitEnemy() { + if (currentEntity instanceof Enemy) { + int dmgDone = currentPlayer.getPlayerDamage() + currentPlayer.getPlayerDamageAmp(); + ((Enemy) currentEntity).harmHealth(dmgDone); + if (currentPlayer.getPlayerDamageAmp() != 0){ + currentPlayer.setPlayerDamageAmp(0); + } + } + } + + public void enemyHitPlayer() { + if (currentEntity instanceof Enemy) { + int dmgDone = ((Enemy) currentEntity).getDamage(); + currentPlayer.harmHealth(dmgDone); + } + } + + + public InteractableEntity getCurrentEntity() { + return currentEntity; + } + + @Override + public boolean getEntityDeath() { + return currentEntity.getHealth() <= 0; + } + + @Override + public boolean getPlayerDeath() { + return currentPlayer.getPlayerHealth() <= 0; + } + + @Override + public void handleDeath(){ + Ui ui = new Ui(); + ui.printDeathMessage(); + } + + public PlayerStatus getCurrentPlayer() { + return currentPlayer; + } + + public void handleLootingByPlayer(){ + int exp = this.currentEntity.getExp_dropped(); + int money = this.currentEntity.getMoney_dropped(); + this.currentPlayer.addExp(exp); + this.currentPlayer.addMoney(money); + this.currentTextBox.setNextNarration("The beast was slain. You looted its cold dead corpse and found $" + money + + " and gained " + exp + " exp."); + } + + + +} diff --git a/src/main/java/math/MathPool.java b/src/main/java/math/MathPool.java new file mode 100644 index 0000000000..035b5fc53e --- /dev/null +++ b/src/main/java/math/MathPool.java @@ -0,0 +1,105 @@ +package math; + +import java.util.Random; +import java.util.ArrayList; +import java.util.Collections; + + +public class MathPool { + private ArrayList poolOfQuestions; + private final Random random; + + + public MathPool() { + poolOfQuestions = new ArrayList(); + random = new Random(); + } + + public void addMathQuestion(String wordProblem, int solution, int difficulty) { + MathQuestion problem = new MathQuestion(wordProblem, solution, difficulty); + poolOfQuestions.add(problem); + } + + /*public MathQuestion getQuestionByDifficulty(int targetDifficulty) { + ArrayList filteredQuestions = new ArrayList<>(); + for (MathQuestion question : poolOfQuestions) { + if (question.getDifficulty() == targetDifficulty) { + filteredQuestions.add(question); + } + } + if (!filteredQuestions.isEmpty()) { + int index = random.nextInt(filteredQuestions.size()); + return filteredQuestions.get(index); + } else { + return null; + } + }*/ + + public MathQuestion getQuestionByDifficulty(int targetDifficulty) { + ArrayList filteredQuestions = new ArrayList<>(); + for (MathQuestion question : poolOfQuestions) { + if (question.getDifficulty() == targetDifficulty) { + filteredQuestions.add(question); + } + } + if (!filteredQuestions.isEmpty()) { + // Shuffle the list of questions + Collections.shuffle(filteredQuestions); + // Iterate through shuffled questions + for (MathQuestion question : filteredQuestions) { + // Return the first question found (after shuffling) + return question; + } + } + return null; // Return null if no questions of the specified difficulty are found + } + + + public void init() { + + + // Difficulty 2 + addMathQuestion("6 * 4 = ", 24, 0); + addMathQuestion("12 / 3 = ", 4, 0); + addMathQuestion("9 * 5 = ", 45, 0); + addMathQuestion("20 / 4 = ", 5, 0); + addMathQuestion("8 * 7 = ", 56, 0); + + // Difficulty 3 + addMathQuestion("5^2 = ", 25, 1); + addMathQuestion("square root of 144 = ", 12, 1); + addMathQuestion("3^3 = ", 27, 1); + addMathQuestion("square root of 81 = ", 9, 1); + addMathQuestion("7^2 = ", 49, 1); + + // Difficulty 4 + addMathQuestion("What is the sum of all angles in a triangle?", 180, 2); + addMathQuestion("How many sides does a hexagon have?", 6, 2); + addMathQuestion("What is the area of a square with side length 5?", 25, 2); + addMathQuestion("What is the perimeter of a rectangle with sides 4 and 6?", 20, 2); + // Difficulty 3 + addMathQuestion("What is 2 times the square root of 64?", 16, 3); + addMathQuestion("How many degrees are in a right angle?", 90, 3); + addMathQuestion("If a square has an area of 25 square units, what is the length of one side?", 5, 3); + addMathQuestion("What is the sum of the first 10 positive integers?", 55, 3); + addMathQuestion("How many edges does a cube have?", 12, 3); + + // Difficulty 4 + addMathQuestion("What is the value of 5 factorial (5!)?", 120, 4); + addMathQuestion("What is the next prime number after 31?", 37, 4); + addMathQuestion("How many vertices does a tetrahedron have?", 4, 4); + addMathQuestion("How many diagonals does a hexagon have?", 9, 4); + addMathQuestion("How many millimeters are in a meter?", 1000, 4); + + // Difficulty 5 + addMathQuestion("What is the value of 7 choose 3 (7C3)?", 35, 5); + addMathQuestion("How many faces does a dodecahedron have?", 12, 5); + addMathQuestion("How many sides does a regular polygon have if each exterior angle measures 30 " + + "degrees?", 12, 5); + addMathQuestion("What is the 20th Fibonacci number?", 6765, 5); + addMathQuestion("What is the value of 2^10?", 1024, 5); + + + } + +} diff --git a/src/main/java/math/MathQuestion.java b/src/main/java/math/MathQuestion.java new file mode 100644 index 0000000000..6a0edde19e --- /dev/null +++ b/src/main/java/math/MathQuestion.java @@ -0,0 +1,25 @@ +package math; + +public class MathQuestion { + private final String question; + private final int answer; + private final int difficulty; + + public MathQuestion(String qn, int ans, int diff){ + this.question = qn; + this.answer = ans; + this.difficulty = diff; + } + + public String getQuestion(){ + return question; + } + + public int getDifficulty() { + return difficulty; + } + + public boolean checkAns(int userAns){ + return answer == userAns; + } +} diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java new file mode 100644 index 0000000000..ad7241d374 --- /dev/null +++ b/src/main/java/parser/Parser.java @@ -0,0 +1,115 @@ +package parser; + +import command.Command; +import command.HelpCommand; +import command.CommandType; +import command.ResetCommand; +import command.ErrorCommand; +import command.QuitCommand; +import command.fight.FightingCommand; +import command.fight.RunningCommand; +import command.inventory.OpenInventoryCommand; +import command.inventory.CloseInventoryCommand; +import command.inventory.UseCommand; +import command.mapmove.InteractingCommand; +import command.mapmove.MovingDownwardCommand; +import command.mapmove.MovingForwardCommand; +import command.mapmove.MovingLeftCommand; +import command.mapmove.MovingRightCommand; +import command.mapmove.ExitShop; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +import static map.BaseMap.currentMap; +import static map.BaseMap.mapIndex; +import static map.MapGenerator.FIRST_MAP_IDENTITY; +import static map.MapGenerator.SHOP; +import static map.MapGenerator.INVENTORY_IDENTITY; + +public class Parser { + + private static final int FIRST_MAP = 0; + + public CommandType analyseCommand(String userCommand) { + Pattern pattern; + Matcher matcher; + for (CommandType commandType : CommandType.values()) { + pattern = Pattern.compile(commandType.getRegExpression()); + matcher = pattern.matcher(userCommand); + if (matcher.matches()) { + return commandType; + } + } + return CommandType.ERROR; + } + + public Command parseCommand(String userCommand) { + Command command; + CommandType commandType = analyseCommand(userCommand); + + switch (commandType) { + case FIGHT: + command = (currentMap != mapIndex.get(FIRST_MAP_IDENTITY)) ? new FightingCommand() : new ErrorCommand(); + break; + case EXIT: + try { + command = (currentMap == mapIndex.get(SHOP)) ? new ExitShop() : new ErrorCommand(); + } catch (NullPointerException e){ + command = new ErrorCommand(); + } + break; + case RUN: + command = (currentMap != mapIndex.get(FIRST_MAP_IDENTITY)) ? new RunningCommand() : new ErrorCommand(); + break; + case MOVE_FORWARD: + command = (currentMap == mapIndex.get(FIRST_MAP_IDENTITY)) ? + new MovingForwardCommand(userCommand) : new ErrorCommand(); + break; + case MOVE_DOWNWARD: + command = (currentMap == mapIndex.get(FIRST_MAP_IDENTITY)) ? + new MovingDownwardCommand(userCommand) : new ErrorCommand(); + break; + case MOVE_LEFT: + command = (currentMap == mapIndex.get(FIRST_MAP_IDENTITY)) ? + new MovingLeftCommand(userCommand) : new ErrorCommand(); + break; + case MOVE_RIGHT: + command = (currentMap == mapIndex.get(FIRST_MAP_IDENTITY)) ? + new MovingRightCommand(userCommand) : new ErrorCommand(); + break; + case QUIT: + command = new QuitCommand(); + break; + case INTERACT: + command = (currentMap == mapIndex.get(FIRST_MAP_IDENTITY)) ? new InteractingCommand() : new ErrorCommand(); + break; + case HELP: + command = new HelpCommand(); + break; + case ERROR: + command = new ErrorCommand(); + break; + case INVENTORY: + command = (currentMap == mapIndex.get(FIRST_MAP_IDENTITY)) ? + new OpenInventoryCommand() : new ErrorCommand(); + break; + case USE_ITEM: + command = (currentMap == mapIndex.get(INVENTORY_IDENTITY)) ? + new UseCommand(userCommand) : new ErrorCommand(); + break; + case CLOSE_INV: + command = (currentMap == mapIndex.get(INVENTORY_IDENTITY)) ? + new CloseInventoryCommand() : new ErrorCommand(); + break; + case RESET: + command = new ResetCommand(); + break; + default: + command = new ErrorCommand(); + } + return command; + } + +} diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 5c74e68d59..0000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} diff --git a/src/main/java/textbox/PlayerStatus.java b/src/main/java/textbox/PlayerStatus.java new file mode 100644 index 0000000000..8cc78ce1c4 --- /dev/null +++ b/src/main/java/textbox/PlayerStatus.java @@ -0,0 +1,109 @@ +package textbox; + +import map.PlayerInventory; + +public class PlayerStatus { + private static final int MAX_HEALTH = 100000; + private static final int MIN_VALUE = 0; + private static final int DEFAULT_HEALTH = 100; + + private int playerHealth; + private int playerMoney; + private int playerExp; + private int playerDamage; + private PlayerInventory playerInventory; + private int playerDamageAmp; + + public PlayerStatus(int startHealth, int startMoney, int startExp, int startDamage, + PlayerInventory playerInventory) { + this.playerHealth = setValidHealth(startHealth); + this.playerMoney = Math.max(startMoney, MIN_VALUE); + this.playerExp = Math.max(startExp, MIN_VALUE); + this.playerDamage = Math.max(startDamage, MIN_VALUE); + this.playerInventory = playerInventory; + this.playerDamageAmp = 0; + } + + private int setValidHealth(int health) { + if (health <= 1 || health > MAX_HEALTH) { + System.out.println("INVALID HEALTH DETECTED. RESETTING TO DEFAULT"); + return DEFAULT_HEALTH; + + } + return health; + } + + public int getPlayerHealth() { + return this.playerHealth; + } + + public int getPlayerMoney() { + return this.playerMoney; + } + + public int getPlayerExp() { + return this.playerExp; + } + + public void setPlayerExp(int playerExp) { + this.playerExp = playerExp; + } + + public void setPlayerHealth(int playerHealth) { + this.playerHealth = playerHealth; + } + + public void setPlayerMoney(int playerMoney) { + this.playerMoney = playerMoney; + } + + public int getPlayerDamage() { + return playerDamage; + } + + public void setPlayerDamage(int playerDamage) { + this.playerDamage = playerDamage; + } + + public void harmHealth(int dmg) { + int newHealth = this.playerHealth - dmg; + setPlayerHealth(newHealth); + } + + public void addMoney(int money) { + long newMoney = (long) this.playerMoney + money; + if (newMoney > Integer.MAX_VALUE) { + this.playerMoney = Integer.MAX_VALUE; + } else { + this.playerMoney = (int) newMoney; + } + } + + public void addExp(int exp) { + long newExp = (long) this.playerExp + exp; + if (newExp > Integer.MAX_VALUE) { + this.playerExp = Integer.MAX_VALUE; + } else { + this.playerExp = (int) newExp; + } + } + + public PlayerInventory getPlayerInventory() { + return playerInventory; + } + + public void setPlayerInventory(PlayerInventory playerInventory) { + if (playerInventory == null) { + throw new NullPointerException("Player inventory cannot be null."); + } + this.playerInventory = playerInventory; + } + + public int getPlayerDamageAmp() { + return playerDamageAmp; + } + + public void setPlayerDamageAmp(int dmg) { + this.playerDamageAmp = Math.max(dmg, MIN_VALUE); + } +} diff --git a/src/main/java/textbox/TextBox.java b/src/main/java/textbox/TextBox.java new file mode 100644 index 0000000000..afd469a0cb --- /dev/null +++ b/src/main/java/textbox/TextBox.java @@ -0,0 +1,58 @@ +package textbox; + +import command.Command; +import map.BaseMap; + +public class TextBox { + protected static String nextInstruction; + protected static String nextNarration; + protected static String nextDialogue; + protected static String nextError; + public void initTextBox(){ + nextInstruction = "Type 'h' to get the help menu."; + nextDialogue = " "; + nextNarration = "Welcome to Calcula: Chronicles of the Algorithmic Kingdom"; + nextError = ""; + } + public void nextTextBoxBasedOnMapAndCommand(Command userCommand, BaseMap map){ + + } + + public void setNextInstruction(String message){ + nextInstruction = message; + } + public void setNextNarration(String message){ + nextNarration = message; + } + + public void setNextDialogue(String message) { + nextDialogue = message; + } + + public void setNextError(String message){ + nextError = message; + } + + public String getNextDialogue() { + return nextDialogue; + } + + public String getNextInstruction() { + return nextInstruction; + } + + public String getNextNarration() { + return nextNarration; + } + + public String getNextError() { + return nextError; + } + + public void clearAll(){ + nextNarration = ""; + nextInstruction = ""; + nextDialogue = ""; + nextError = ""; + } +} diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java new file mode 100644 index 0000000000..d4e0915fa7 --- /dev/null +++ b/src/main/java/ui/Ui.java @@ -0,0 +1,256 @@ +package ui; + +import interactable.Enemy; +import interactable.ShopKeeper; +import inventoryitems.Item; +import map.BaseMap; +import textbox.PlayerStatus; +import textbox.TextBox; +import math.MathQuestion; +import filereader.FileReader; + +import java.io.FileNotFoundException; +import java.util.ArrayList; + + +public class Ui { + private static final int DEFAULT_WIDTH_OF_BATTLE_INTERFACE = 50; + private static final int DEFAULT_HEIGHT_OF_BATTLE_INTERFACE = 50; + + public void printDividingLine() { + System.out.println("==========================================================="); + } + + public void printPlayerStatus(PlayerStatus statusBar) { + printDividingLine(); + System.out.print("HEALTH: " + statusBar.getPlayerHealth() + " "); + System.out.print("MONEY: $" + statusBar.getPlayerMoney() + " "); + System.out.println("EXP: " + statusBar.getPlayerExp() + " "); + printDividingLine(); + } + + + public void printTextBox(TextBox box) { + assert box.getNextDialogue() != null : "next dialogue is null"; + assert box.getNextError() != null : "next error is null"; + assert box.getNextInstruction() != null : "next instruction is null"; + assert box.getNextNarration() != null : "next narration is null"; + + + printDividingLine(); + if (!box.getNextError().isEmpty()) { + System.out.println(box.getNextError()); + } + if (!box.getNextNarration().isEmpty()) { + System.out.println(box.getNextNarration()); + System.out.println("\n"); + } + if (!box.getNextDialogue().isEmpty()) { + System.out.println(box.getNextDialogue()); + } + if (!box.getNextInstruction().isEmpty()) { + System.out.println(box.getNextInstruction()); + } + printDividingLine(); + box.clearAll(); + } + + public void printTextbox(String message){ //for custom messages + printDividingLine(); + System.out.println(message); + printDividingLine(); + } + + private static StringBuilder getStringBuilder(ArrayList row, String healthInfo) { + StringBuilder firstRowWithHealth = new StringBuilder(); + for (int cellIndex = 0; cellIndex < row.size(); cellIndex++) { + // Append the art character until reaching the position to overlay health info + if (cellIndex < row.size() - healthInfo.length()) { + firstRowWithHealth.append(row.get(cellIndex)); + } else { + // Start overlaying health info onto the map + int healthInfoIndex = cellIndex - (row.size() - healthInfo.length()); + firstRowWithHealth.append(healthInfo.charAt(healthInfoIndex)); + } + } + return firstRowWithHealth; + } + + + public void printMap(BaseMap map) { + printDividingLine(); + for (ArrayList row : map.getMapData()) { + for (char cell : row) { + System.out.print(cell + " "); + } + System.out.println(); + } + printDividingLine(); + } + + public void printMap(ArrayList> map, Enemy monster) { + printDividingLine(); + String healthInfo = " Health: " + monster.getHealth(); // Health information as a string + + for (int rowIndex = 0; rowIndex < map.size(); rowIndex++) { + ArrayList row = map.get(rowIndex); + + // Overlay the health information on the first row directly within the ASCII art + if (rowIndex == 0) { + StringBuilder firstRowWithHealth = getStringBuilder(row, healthInfo); + System.out.println(firstRowWithHealth.toString()); + } else { + for (char cell : row) { + System.out.print(cell); + } + System.out.println(); + } + } + printDividingLine(); + } + + + public void printEnemy(BaseMap map) { + printDividingLine(); + for (ArrayList row : map.getMapData()) { + for (char cell : row) { + System.out.print(cell); + } + System.out.println(); + } + printDividingLine(); + } + + public void printShopKeeper(ShopKeeper cat) throws FileNotFoundException { + FileReader fileReader = new FileReader(cat.getFilePath()); + ArrayList> mapData = new ArrayList<>(); + try { + mapData = fileReader.readDesign(); + } catch (Exception e) { + System.out.println("Unable to read file from local"); + } + for (ArrayList row : mapData) { + for (Character ch : row) { + System.out.print(ch); // Print each character without a newline + } + System.out.println(); // Print a newline after each row + } + } + + public void printInventoryLine(String text, int quantity, int width) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("|"); + stringBuilder.append(text); + int itemQuantityCharacters = quantity <= 0 ? 0 : (String.valueOf(quantity).length() + 3); + int length = width - text.length() - itemQuantityCharacters - 2; + stringBuilder.append(" ".repeat(Math.max(0, length))); + if (quantity > 0) { + stringBuilder.append("x"); + stringBuilder.append(" "); + stringBuilder.append(quantity); + stringBuilder.append(" "); + } + + stringBuilder.append("|"); + System.out.println(stringBuilder.toString()); + } + + public void printInventory(ArrayList inventory, String name, int width, int height) { + printDividingLine(); + StringBuilder header = buildHeader(name, width); + System.out.println(header); + printInventoryLine("", 0, width); + if (inventory.isEmpty()) { + for (int i = 0; i < height - 1; i += 1) { + System.out.print("|"); + for (int j = 0; j < width - 2; j += 1) { + System.out.print(" "); + } + System.out.println("|"); + } + return; + } + for (Item item : inventory) { + int itemIndex = inventory.indexOf(item) + 1; + printInventoryLine(" " + itemIndex + ". " + item.getName(), item.getQuantity(), width); + } + if (inventory.size() < height - 1) { + for (int i = 0; i < height - inventory.size() - 1; i += 1) { + printInventoryLine(" ", 0, width); + } + } + printDividingLine(); + } + + private static StringBuilder buildHeader(String inventoryName, int width) { + StringBuilder header = new StringBuilder(); + int length = width - inventoryName.length() - 2; + header.append("|"); + for (int i = 0; i <= length; i += 1) { + if (i == 1) { + header.append("<"); + } else if (i == length - 1) { + header.append(">"); + } else if (i == length / 2 - 1) { + header.append(inventoryName); + } else { + header.append(" "); + } + } + header.append("|"); + return header; + } + + public void printHelpMenu() { + printDividingLine(); + System.out.println("'w' 'a' 's' 'd' to move around"); + System.out.println("'e' to interact"); + System.out.println("'i' to open inventory"); + System.out.println("'q' to quit"); + System.out.println("'h' to print help menu"); + System.out.println("'run' to escape the battle interface"); + printDividingLine(); + } + public void printQuestion(MathQuestion mathQuestion){ + System.out.println(mathQuestion.getQuestion()); + } + + + public void printDeathMessage(){ + System.out.println(" _______ ______ ________ ______ _____ "); + System.out.println("|\\ /|( ___ )|\\ /| ( __ \\ \\__ __/( ____ \\( __ \\ "); + System.out.println("( \\ / )| ( ) || ) ( | | ( \\ ) ) ( | ( \\/| ( \\ )"); + System.out.println(" \\ (_) / | | | || | | | | | ) | | | | (__ | | ) |"); + System.out.println(" \\ / | | | || | | | | | | | | | | __) | | | |"); + System.out.println(" ) ( | | | || | | | | | ) | | | | ( | | ) |"); + System.out.println(" | | | (___) || (___) | | (__/ )___) (___| (____/\\| (__/ )"); + System.out.println(" \\_/ (_______)(_______) (______/ \\_______/(_______/(______/ "); + } + + + public void printWinMessage(PlayerStatus player) throws InterruptedException { + System.out.println(" __ __ ______ __ __ __ __ ______ __ __ \n" + + "| \\ / \\ / \\ | \\ | \\ | \\ _ | \\| \\| \\ | \\\n" + + " \\$$\\ / $$| $$$$$$\\| $$ | $$ | $$ / \\ | $$ \\$$$$$$| $$\\ | $$\n" + + " \\$$\\/ $$ | $$ | $$| $$ | $$ | $$/ $\\| $$ | $$ | $$$\\| $$\n" + + " \\$$ $$ | $$ | $$| $$ | $$ | $$ $$$\\ $$ | $$ | $$$$\\ $$\n" + + " \\$$$$ | $$ | $$| $$ | $$ | $$ $$\\$$\\$$ | $$ | $$\\$$ $$\n" + + " | $$ | $$__/ $$| $$__/ $$ | $$$$ \\$$$$ _| $$_ | $$ \\$$$$\n" + + " | $$ \\$$ $$ \\$$ $$ | $$$ \\$$$| $$ \\| $$ \\$$$\n" + + " \\$$ \\$$$$$$ \\$$$$$$ \\$$ \\$$ \\$$$$$$ \\$$ \\$$\n" + + " "); + Thread.sleep(3000); + System.out.println("You Completed the game with $" + player.getPlayerMoney() + " remaining and a total" + + " of " + player.getPlayerExp() + " exp!!"); + Thread.sleep(3000); + System.out.println("Thank you for playing!!!"); + } + + public void insertOutOfBoundsMessage(TextBox box){ + box.setNextNarration("You ran straight into a wall"); + } + + public void insertObjectObstructionMessage(TextBox box){ + box.setNextNarration("Something appears to be blocking your way"); + } +} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..13eae68f38 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: main.CalculaChroniclesOfTheAlgorithmicKingdom + diff --git a/src/main/resources/ShopKeeper/ShopKeeper.txt b/src/main/resources/ShopKeeper/ShopKeeper.txt new file mode 100644 index 0000000000..15c47bd731 --- /dev/null +++ b/src/main/resources/ShopKeeper/ShopKeeper.txt @@ -0,0 +1,12 @@ += = = = = = = = = = = = = = = = = = = = = = = +| | +| /\ /\ | +| ( o o ) | +| \ >-< / | +| / \ | +| / \ ^ | +| | | // | +| \ / // | +| /// /// -- | +| | += = = = = = = = = = = = = = = = = = = = = = = \ No newline at end of file diff --git a/src/main/resources/enemiesDesign/centaur.txt b/src/main/resources/enemiesDesign/centaur.txt new file mode 100644 index 0000000000..07e917ddb9 --- /dev/null +++ b/src/main/resources/enemiesDesign/centaur.txt @@ -0,0 +1,12 @@ += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +| <=======]}====== | +| --. /| | +| _\"/_.'/ | +| .'._._,.' | +| :/ \{}/ | +| (L /--',----._ | +| | \\ | +| : /-\ .'-'\ / | | +| \\, || \| | +| \/ || || | += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = \ No newline at end of file diff --git a/src/main/resources/enemiesDesign/demon.txt b/src/main/resources/enemiesDesign/demon.txt new file mode 100644 index 0000000000..9414fb7fbb --- /dev/null +++ b/src/main/resources/enemiesDesign/demon.txt @@ -0,0 +1,12 @@ += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +| | +| , , /\ /\ | +| /( /\ )\ _\ \_/ /_ | +| |\_||_/| < \_ _/ > | +| \______/ \|0 0|/ | +| _\/_ _(_ ^ _)_ | +| ( () ) /`\|V"""V|/`\ | +| {} \ \_____/ / | +| () /\ )=( /\ | +| {} / \_/\=/\_/ \ | += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = \ No newline at end of file diff --git a/src/main/resources/enemiesDesign/dragon.txt b/src/main/resources/enemiesDesign/dragon.txt new file mode 100644 index 0000000000..bd8581104f --- /dev/null +++ b/src/main/resources/enemiesDesign/dragon.txt @@ -0,0 +1,39 @@ += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +| / ) | +| ( |\ | +| /| \\ | +| // \\ | +| /// \| | +| /( \ )\ | +| \\ \_ //) | +| \\ :\__ /// | +| \\ ) // \ | +| \\: / // |/ | +| \\ / \ // \ | +| /) \ ___..-' (| \_| | +| // / _.' \ \ \ | +| /| \ \________ \ | / | +| (| _ _ __/ '-. ) /.' | +| \\ . '-.__ \_ / / \ | +| \\_'. > --._ '. \ / / / | +| \ \ \ \ \ .' /.' | +| \ \ '._ / \ ) / .' | | +| \ \_ \_ | .'_/ __/ | +| \ \ \_ | / / _/ \_ | +| \ \ / _.' / / \ | +| \ | /.' / .' '-,_ | +| \ \ .' _.'_/ \ | +| /\ /\ ) ___( /_.' \ | | +| | _\__// \ (.' _/ | | | +| \/_ __ /--'` , __/ / | +| (o ) /o) \ '. : \___.-'_/ \__/ | +| /:/: , ) : ( /_.'__/-'|_ _ / | +| /:/: __/\ > __,_.----.__\ / (/(/(/ | +| (_(,_/V .'/--' _/ __/ | / | +| VvvV //` _.-' _.' \ \ | +| n_n// (((/->/ | / | +| '--' ~=' \ | | +| | |_,,, | +| \ \ / | +| '.__) | += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = \ No newline at end of file diff --git a/src/main/resources/enemiesDesign/goblin.txt b/src/main/resources/enemiesDesign/goblin.txt new file mode 100644 index 0000000000..b97ffaf887 --- /dev/null +++ b/src/main/resources/enemiesDesign/goblin.txt @@ -0,0 +1,12 @@ += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +| _____ | +| .-,;='';_),-. | +| \_\(),()/_/ | +| (,___,) | +| ,-/`~`\-,___ | +| / /).:.('--._) | +| {_[ (_,_) | +| | Y | | +| / | \ | +| """ """ | += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = \ No newline at end of file diff --git a/src/main/resources/enemiesDesign/gryphon.txt b/src/main/resources/enemiesDesign/gryphon.txt new file mode 100644 index 0000000000..6d9924c53d --- /dev/null +++ b/src/main/resources/enemiesDesign/gryphon.txt @@ -0,0 +1,15 @@ += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +| ______ | +| ______,---'__,---' | +| _,-'---_---__,---' | +| /_ (, ---____', | +| / ',, `, ,-' | +| ;/) ,',,_/,' | +| | /\ ,.'//\ | +| `-` \ ,,' `. | +| `', ,-- `. | +| '/ / | `, _ | +| //'',.\_ .\\ ,{==>- | +| __// __;_`- \ `;.__,;' | +| ((,--,) (((,------; `--' | += = = = = = = = = = = = = = = = = = = = = = = = = = = = = = \ No newline at end of file diff --git a/src/test/java/command/CommandTest.java b/src/test/java/command/CommandTest.java new file mode 100644 index 0000000000..fc88973629 --- /dev/null +++ b/src/test/java/command/CommandTest.java @@ -0,0 +1,11 @@ +package command; + +import org.junit.jupiter.api.BeforeEach; + +public class CommandTest { + Command a; + @BeforeEach + void setup(){ + + } +} diff --git a/src/test/java/command/fight/FightCommandTest.java b/src/test/java/command/fight/FightCommandTest.java new file mode 100644 index 0000000000..66292c1299 --- /dev/null +++ b/src/test/java/command/fight/FightCommandTest.java @@ -0,0 +1,20 @@ +package command.fight; + +import command.Command; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FightCommandTest { + Command a; + @BeforeEach + void setup() { + a = new FightingCommand(); + } + + @Test + void fightExecuteCorrectly() { + assertEquals("FIGHT!", a.getCommandDescription()); + } +} diff --git a/src/test/java/command/mapmove/MapMoveCommandTest.java b/src/test/java/command/mapmove/MapMoveCommandTest.java new file mode 100644 index 0000000000..35186ec533 --- /dev/null +++ b/src/test/java/command/mapmove/MapMoveCommandTest.java @@ -0,0 +1,4 @@ +package command.mapmove; + +public class MapMoveCommandTest { +} diff --git a/src/test/java/main/CalculaChroniclesOfTheAlgorithmicKingdom.java b/src/test/java/main/CalculaChroniclesOfTheAlgorithmicKingdom.java new file mode 100644 index 0000000000..ab72d54c16 --- /dev/null +++ b/src/test/java/main/CalculaChroniclesOfTheAlgorithmicKingdom.java @@ -0,0 +1,4 @@ +package main; + +public class CalculaChroniclesOfTheAlgorithmicKingdom { +} diff --git a/src/test/java/map/MapTest.java b/src/test/java/map/MapTest.java new file mode 100644 index 0000000000..fa0b8a65e1 --- /dev/null +++ b/src/test/java/map/MapTest.java @@ -0,0 +1,69 @@ +package map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MapTest { + + private FirstMap map; + + @BeforeEach + void setUp() { + map = new FirstMap(); + map.initMap(5, 5); + map.initPlayerLocation(2, 2); + } + + @Test + void mapShouldBeCorrectlyInitialized() { + ArrayList> expectedMap = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + ArrayList row = new ArrayList<>(); + for (int j = 0; j < 5; j++) { + if (i == 2 && j == 2) { + row.add('P'); + } else { + row.add('.'); + } + } + expectedMap.add(row); + } + + assertEquals(expectedMap, map.getMap(), "Map should be initialized correctly with player at (2,2)"); + } + + @Test + void playerShouldMoveUpCorrectly() { + map.movePlayerUpOne(); + assertEquals('P', map.getMapData().get(1).get(2), "Player should move up 1 place"); + assertEquals('.', map.getMapData().get(2).get(2), + "Original player position should be empty after moving up"); + } + + @Test + void playerShouldMoveDownCorrectly() { + map.movePlayerDownOne(); + assertEquals('P', map.getMapData().get(3).get(2), "Player should move down 1 place"); + assertEquals('.', map.getMapData().get(2).get(2), + "Original player position should be empty after moving down"); + } + + @Test + void playerShouldMoveLeftCorrectly() { + map.movePlayerLeftOne(); + assertEquals('P', map.getMapData().get(2).get(1), "Player should move left 1 place"); + assertEquals('.', map.getMapData().get(2).get(2), + "Original player position should be empty after moving left"); + } + + @Test + void playerShouldMoveRightCorrectly() { + map.movePlayerRightOne(); + assertEquals('P', map.getMapData().get(2).get(3), "Player moves right 1 place"); + assertEquals('.', map.getMapData().get(2).get(2), + "Original player position should be empty after moving right"); + } +} + diff --git a/src/test/java/parser/ParserTest.java b/src/test/java/parser/ParserTest.java new file mode 100644 index 0000000000..3d734295c5 --- /dev/null +++ b/src/test/java/parser/ParserTest.java @@ -0,0 +1,29 @@ +package parser; + + +import command.Command; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ParserTest { + + private Parser parser; + + @BeforeEach + public void setup() { + this.parser = new Parser(); + } + + @Test + public void parse_emptyInput_returnsIncorrect() { + final String[] emptyInputs = { "", " ", "\n \n" }; + parseAndAssertEmpty(emptyInputs); + } + + public void parseAndAssertEmpty(String[] inputs) { + for (String input : inputs) { + final Command result = parser.parseCommand(input); + //assertEquals(result, new ErrorCommand()); + } + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..e69de29bb2 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +0,0 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - -What is your name? -Hello James Gosling diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f95..c502eafb86 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1,3 @@ -James Gosling \ No newline at end of file +s 3 +d +quit \ No newline at end of file