Difference From release-1 To trunk
2022-12-29
| ||
21:40 | add feature matching exercise Leaf check-in: 0418693d59 user: theo tags: main, trunk | |
21:05 | update supporting materials check-in: ed0e82e855 user: theo tags: main, trunk | |
2022-12-22
| ||
04:42 | Remove unnecessary image check-in: 05807086e1 user: theo tags: main, trunk | |
2022-12-09
| ||
04:34 | Merge pull request #108 from CSC207-2022F-UofT/readme added m chip issues check-in: aa3fd4bfd4 user: siddharth tags: main, prerelease-41, release-1, trunk | |
04:19 | Update README.md check-in: 05eeb9e756 user: siddharth tags: main, trunk | |
02:14 | Merge pull request #101 from CSC207-2022F-UofT/meta/readme-round-2 Final README Updates Leaf check-in: 127699655f user: theo tags: main, prerelease-40, trunk | |
Added EXT_LICENSE.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | ============================================================ License for src/main/resources/fonts/Strait-Regular.ttf ============================================================ Copyright (c) 2012, Eduardo Tunni (http://www.tipo.net.ar), with Reserved Font Name 'Strait' This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. ============================================================ License for src/main/resources/fonts/HomemadeApple-Regular.ttf ============================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ============================================================ License for src/main/resources/fonts/IBMPlexMono-Medium.ttf ============================================================ Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. |
Changes to README.md.
1 2 | # Mountain Group 105 | > > > > | | | | | | | | | | | < | < < | < < | < < | < < < < < < | | > > > | > | > > > > > > | < | | > | > > > | > | > | > | > | > | > | > > | > | | | < | < < < < | | | < | | > | > | | | | > > > | | > > | > | | > > | > > > > | < | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | # Mountain Group 105 ![Screenshots of the game](screenshots.webp) Mountain Group 105 is a rogue-like video game originally created as part of a [software design course](https://artsci.calendar.utoronto.ca/course/csc207h1), with the following premise: > You are a group of four weary travellers. > You have been walking for so long that you no longer have any memory of where you came from or who you are. > You cannot recall anything but the endless action of putting one foot in front of the other, traversing this empty landscape. > Suddenly, you look up, startled out of your reverie. > An imposing mountain looms before you. > Your party steps forward, drawn to it for some inexplicable reason. > You crane your neck to see if you can make out the peak, but the morning mist impedes your view. > You all know you cannot go back to wherever you came from. > You have to keep going. > You have to climb the mountain. > Maybe whatever waits for you up there will remind you of who you are… and what you’re searching for. ## Running Instructions ### IntelliJ IDEA (IDE) Instructions First download the latest source code from [here](https://vcs.pta.gg/mg105/download) and extract it. Next, open the project in IntelliJ by going to to `File > Open`, and navigating to the folder created in the last step. Next, if IntelliJ does not immediately recognize the fact this is a Gradle project (you can tell by the lack of a `Gradle` tab on any of the edges), navigate to the `build.gradle` file in the IDE. An icon with an elephant should appear, click it. There should now be a `Gradle` tab on one of the edges. Finally, open the `Gradle` tab and navigate to `course-project-group-105 > Tasks > application` and double-click `run`. ### Command-Line (CLI) Instructions First, this project requires [Java 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html), so make sure that is installed. Next download the source code and change into that directory which can either be done by ```sh fossil clone https://vcs.pta.gg/mg105 cd mg105 ``` or by ```sh wget https://vcs.pta.gg/mg105/zip/trunk/download.zip unzip download.zip cd download ``` if you do not have [Fossil](https://fossil-scm.org) installed. Finally, this is a standard Gradle project, so to start the application simply run ```sh gradlew.bat run ``` in the Windows CMD, or ```sh ./gradlew run ``` on any Unix-like operating system (or the built-in IntelliJ IDEA terminal). ## Features As a complete game, Mountain Group 105 features - a randomly generated room-based map, - the ability to change the character on the map, - a progressively updating minimap that only shows currently discovered rooms, - a turn-based battle sequence, - an inventory system (with health potions and upgrade tokens), - an item-based character upgrade system, - an interactive controls display, and it has been balanced so that it plays decently well! *Exercise to the reader: match a feature to each of the four screenshots at the top of this README.* ## Program Architecture Being part of a specific course, this project strictly adheres to [Robert Martin's Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html). The code is packaged by layers where - `com.mg105.entities` contain the enterprise business rules, - `com.mg105.use_cases` contain the application business rules, - `com.mg105.interface_adapters` contain the interface adapters, and - `com.mg105.user_interface` and `com.mg105.data_control` both contain frameworks and drivers. Additionally the code makes use of a plethora of design patterns including - facade pattern in `com.mg105.use_cases.save.Saver` and `com.mg105.use_cases.set_up.data_system_creator.DataStorageSystemCreator`, - simple factory pattern in `com.mg105.use_cases.inventory.ItemFactory` and `com.mg105.user_interface.StandardComponentFactory`, - builder pattern in `com.mg105.use_cases.set_up.data_system_creator.DataStorageSystemCreator`, - chain of responsibility pattern in `com.mg105.user_interface.InputListener`, `com.mg105.interface_adapters.InputInterpreter`, and `com.mg105.interface_adapters.InputControllable`, - observer pattern in `com.mg105.use_cases.map.RoomUpdater`, - mediator pattern in `com.mg105.entities.GameState`, and - dependency injection in essentially all the classes. ## Changes Since Initial Release As previously mentioned this was originally part of a course project. The original state of the source code as of the submission date can be found [here](https://vcs.pta.gg/mg105/dir?ci=release-1) or at its original home on GitHub, [here](https://github.com/CSC207-2022F-UofT/course-project-group-105). Since the original release, the only changes have been a complete UI overhaul (the core functionality and overall architecture is essentially unchanged). The marking scheme allocated no marks for how pretty it looked, and so no time was spent on that. In particular the overhaul aims to improve 1. lack of consistent game resolution, 1. unintuitive and inconsistent controls (some buttons, some keyboard), and 1. lack of any real graphics or use of colour. A full diff of these changes can be found [here](https://vcs.pta.gg/mg105/vdiff?from=release-1&to=trunk). ## Note for Apple Silicon Users Mountain Group 105 has a hard dependency on JavaFX, which has been flaky on Apple Silicon chips. The game may not be playable on your hardware. ## Note for Tiling Window Manager Users Put your window manager into its floating mode before starting the game. The game expects a constant, self-set resolution, which tiling window managers tend to violate. ## Copyright Unless mentioned in the [EXT_LICENSE](/file?name=EXT_LICENSE&ci=trunk) file, code is licensed under the GNU Affero General Public License, Version 3.0. See the [LICENSE](/file?name=LICENSE&ci=trunk) and [EXT_LICENSE](/file?name=EXT_LICENSE&ci=trunk) files for more details. |
Deleted images/closed_issue.png.
cannot compute difference between binary files
Deleted images/closed_project.png.
cannot compute difference between binary files
Deleted images/create_branch.png.
cannot compute difference between binary files
Deleted images/create_pr.png.
cannot compute difference between binary files
Deleted images/create_project.png.
cannot compute difference between binary files
Deleted images/link_branch.png.
cannot compute difference between binary files
Deleted images/link_project.png.
cannot compute difference between binary files
Deleted images/new_issue.png.
cannot compute difference between binary files
Deleted images/new_pr.png.
cannot compute difference between binary files
Deleted images/rename.png.
cannot compute difference between binary files
Deleted images/set_tags.png.
cannot compute difference between binary files
Deleted imgs/background2.jpg.
cannot compute difference between binary files
Deleted project_plan_dev.md.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added screenshots.webp.
cannot compute difference between binary files
Changes to src/main/java/com/mg105/Application.java.
︙ | ︙ | |||
11 12 13 14 15 16 17 | import com.mg105.interface_adapters.battle.BattlePresenter; import com.mg105.interface_adapters.inventory.InventoryController; import com.mg105.interface_adapters.inventory.InventoryPresenter; import com.mg105.interface_adapters.map.MapGeneratorInterpreter; import com.mg105.interface_adapters.map.MinimapInterpreter; import com.mg105.interface_adapters.map.RoomInterpreter; import com.mg105.interface_adapters.map.RoomInterpreterInterface; | < < | < < > < < < < | < < < | < > > > | < < | | > > > < < < < < < < < | < < | < < < < < < | < < | | > > > > > > > > > > > > > | | > > < > > > | < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import com.mg105.interface_adapters.battle.BattlePresenter; import com.mg105.interface_adapters.inventory.InventoryController; import com.mg105.interface_adapters.inventory.InventoryPresenter; import com.mg105.interface_adapters.map.MapGeneratorInterpreter; import com.mg105.interface_adapters.map.MinimapInterpreter; import com.mg105.interface_adapters.map.RoomInterpreter; import com.mg105.interface_adapters.map.RoomInterpreterInterface; import com.mg105.use_cases.*; import com.mg105.use_cases.battle.BattleInteractor; import com.mg105.use_cases.inventory.InventoryInteractor; import com.mg105.use_cases.map.*; import com.mg105.use_cases.save.PartySaver; import com.mg105.use_cases.save.Save; import com.mg105.use_cases.save.Saver; import com.mg105.use_cases.set_up.data_system_creator.CreateDataStorage; import com.mg105.use_cases.set_up.data_system_creator.DataStorageSystemCreator; import com.mg105.use_cases.set_up.state_setter.GameStateSetter; import com.mg105.use_cases.set_up.state_setter.PartyCreator; import com.mg105.user_interface.*; import com.mg105.utils.UIConstants; import javafx.scene.input.KeyEvent; import javafx.stage.Stage; import java.awt.*; import java.util.HashMap; import java.util.Map; /** * Effectively, the main class that sets up the clean architecture mountain group 105 game! */ public class Application extends javafx.application.Application { /** * Note that while this isn't our main method explicitly, we (probably) need this to effectively be our main method * for scoping rules. * * @param primaryStage the primary stage for this application. */ @Override public void start(Stage primaryStage) { GameState state = new GameState(new Inventory(), new WalkingCharacter(new Point(1, 1))); CreateDataStorage[] databaseCreators = {new MoveDataCreator(), new PartyDataCreator()}; DataStorageSystemCreator databaseCreator = new DataStorageSystemCreator(databaseCreators); databaseCreator.create(); PartyCreator[] partyCreator = {new PartyCreator(new PartyDataAccess(new MoveDataAccess()))}; GameStateSetter setter = new GameStateSetter(partyCreator); setter.setState(state); Map<Toggler.ToggleableComponent, Toggleable> drawableComponents = new HashMap<>(); // We fill this map in later because of the ordering of parameters SceneController sceneController = new SceneController( primaryStage, drawableComponents, Toggler.ToggleableComponent.MAP ); StandardComponentFactory componentFactory = new StandardComponentFactory(sceneController); MapGenerator mapGenerator = new MapGenerator(state); MapGeneratorInterpreter mapGeneratorInterpreter = new MapGeneratorInterpreter(mapGenerator); MapGeneratorButton generateMapButton = new MapGeneratorButton(mapGeneratorInterpreter, sceneController); MainMenu mainMenu = new MainMenu(sceneController, generateMapButton, componentFactory); RoomGetterInterface roomGetter = new RoomGetter(state); RoomInterpreterInterface roomInterpreter = new RoomInterpreter(roomGetter); MapDrawer mapDrawer = new MapDrawer(roomInterpreter); drawableComponents.put(Toggler.ToggleableComponent.MAIN_MENU, mainMenu); drawableComponents.put(Toggler.ToggleableComponent.MAP, mapDrawer); MinimapInterpreter minimapInterpreter = new MinimapInterpreter(roomGetter); MinimapDrawer minimapDrawer = new MinimapDrawer(minimapInterpreter); drawableComponents.put(Toggler.ToggleableComponent.MINIMAP, minimapDrawer); InventoryPresenter inventoryPresenter = new InventoryPresenter(); InventoryInteractor inventoryInteractor = new InventoryInteractor(state, inventoryPresenter); InventoryDisplay inventoryDisplay = new InventoryDisplay( new InventoryController(inventoryInteractor), componentFactory, new PartyGetter(new PartyStatGetter(state)) ); inventoryPresenter.setView(inventoryDisplay); drawableComponents.put(Toggler.ToggleableComponent.INVENTORY, inventoryDisplay); WalkVisInteractor walkVisInteractor = new WalkVisInteractor(state); WalkVisController walkVisController = new WalkVisController(walkVisInteractor); WalkingMenu walkingMenu = new WalkingMenu(walkVisController, sceneController, componentFactory); drawableComponents.put(Toggler.ToggleableComponent.WALK_MENU, walkingMenu); ReplayGenerator replayGenerator = new ReplayGenerator(state, minimapInterpreter); replayGenerator.replay(); ReplayGeneratorInterpreter replayGeneratorInterpreter = new ReplayGeneratorInterpreter(replayGenerator); ReplayGeneratorButton loseButton = new ReplayGeneratorButton(replayGeneratorInterpreter, sceneController, Toggler.ToggleableComponent.LOSE_MENU); LoseMenu loseMenu = new LoseMenu(loseButton, componentFactory, mainMenu); drawableComponents.put(Toggler.ToggleableComponent.LOSE_MENU, loseMenu); OpponentSetInteractor opponentInteractor = new OpponentSetInteractor(state); Save[] savers = {new PartySaver(state, new PartyDataAccess(new MoveDataAccess()))}; Saver saver = new Saver(savers); BattleInteractor battleInteractor = new BattleInteractor(state, inventoryInteractor, saver); BattlePresenter battlePresenter = new BattlePresenter(battleInteractor, sceneController); BattleMenu battleMenu = new BattleMenu(battlePresenter, componentFactory); drawableComponents.put(Toggler.ToggleableComponent.BATTLE, battleMenu); RoomUpdater roomUpdater = new RoomUpdater(); roomUpdater.addObserver(mapDrawer); roomUpdater.addObserver(minimapInterpreter); CharacterMoverInterface characterMover = new CharacterMover(state, roomUpdater); ReplayGeneratorButton winButton = new ReplayGeneratorButton(replayGeneratorInterpreter, sceneController, Toggler.ToggleableComponent.WIN_MENU); WinMenu winMenu = new WinMenu(winButton, componentFactory, mainMenu); WinDisplay winDisplay = new WinDisplay(sceneController, roomGetter, replayGenerator); roomUpdater.addObserver(winDisplay); drawableComponents.put(Toggler.ToggleableComponent.WIN_MENU, winMenu); KeymapDisplay keymapDisplay = new KeymapDisplay(componentFactory); drawableComponents.put(Toggler.ToggleableComponent.CONTROLS, keymapDisplay); IntroDisplay introDisplay = new IntroDisplay(sceneController, keymapDisplay, componentFactory); drawableComponents.put(Toggler.ToggleableComponent.INTRO, introDisplay); ChestInteractor chestInteractor = new ChestInteractor(state, inventoryInteractor, roomUpdater); Map<Toggler.ToggleableComponent, InputControllable> inputPassthroughs = new HashMap<>(); inputPassthroughs.put(Toggler.ToggleableComponent.MAIN_MENU, mainMenu); inputPassthroughs.put(Toggler.ToggleableComponent.WIN_MENU, winMenu); inputPassthroughs.put(Toggler.ToggleableComponent.LOSE_MENU, loseMenu); inputPassthroughs.put(Toggler.ToggleableComponent.INTRO, introDisplay); inputPassthroughs.put(Toggler.ToggleableComponent.BATTLE, battleMenu); InputInterpreter inputInterpreter = new InputInterpreter(characterMover, sceneController, chestInteractor, opponentInteractor, inventoryDisplay, walkingMenu, sceneController, inputPassthroughs); InputListener inputListener = new InputListener(inputInterpreter); sceneController.toggle(Toggler.ToggleableComponent.INTRO); sceneController.toggle(Toggler.ToggleableComponent.MAIN_MENU); primaryStage.addEventFilter(KeyEvent.KEY_TYPED, inputListener); primaryStage.addEventFilter(KeyEvent.ANY, keymapDisplay); primaryStage.setResizable(false); primaryStage.setMinWidth(UIConstants.CANVAS_SIZE); primaryStage.setMinHeight(UIConstants.CANVAS_SIZE); primaryStage.show(); } } |
Deleted src/main/java/com/mg105/entities/GiveTutorial.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/main/java/com/mg105/interface_adapters/InputControllable.java.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.mg105.interface_adapters; /** * InputControllable represents something that can be directly controlled by a keyboard input. */ public interface InputControllable { /** * Send key to this component. * * @param key the key this component should process. */ void interpret(String key); } |
Changes to src/main/java/com/mg105/interface_adapters/InputInterpreter.java.
1 2 | package com.mg105.interface_adapters; | < < > > | | | > < > > > > | > | | > > > > < > > > > > > > > > > | > > < | < < < | < < < | < < < | < < < < < < < < < < < < < | < < < < < < | < < | | < | | > | < | > > > | | | | | > > < | | > > > > | > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | package com.mg105.interface_adapters; import com.mg105.use_cases.ChestInteractor; import com.mg105.use_cases.OpponentSetInteractor; import com.mg105.use_cases.map.CharacterMoverInterface; import org.jetbrains.annotations.NotNull; import java.awt.*; import java.util.Map; /** * InputInterpreter takes in keyboard inputs and distributes them to their appropriate use cases. */ public class InputInterpreter { private final @NotNull CharacterMoverInterface mover; private final @NotNull Toggler toggler; private final @NotNull ChestInteractor chestInteractor; private final @NotNull OpponentSetInteractor opponentInteractor; private final @NotNull InputControllable inventory; private final @NotNull InputControllable walkMenu; private final @NotNull InputControllable alerter; private final @NotNull Map<Toggler.ToggleableComponent, InputControllable> passthroughs; /** * Create a new InputInterpreter that translates keyboard inputs to appropriate function invocations. * * @param mover the character mover. * @param toggler the toggler used to change the displayed interface. * @param chestInteractor the ChestInteractor used to interact with chests. * @param opponentInteractor the interactor used to interact with opponents. * @param inventory the inventory. * @param walkMenu the walking menu. * @param alerter the alerter. * @param passthroughs components that just get input directly pushed to them. */ public InputInterpreter(@NotNull CharacterMoverInterface mover, @NotNull Toggler toggler, @NotNull ChestInteractor chestInteractor, @NotNull OpponentSetInteractor opponentInteractor, @NotNull InputControllable inventory, @NotNull InputControllable walkMenu, @NotNull InputControllable alerter, @NotNull Map<Toggler.ToggleableComponent, InputControllable> passthroughs) { this.mover = mover; this.toggler = toggler; this.opponentInteractor = opponentInteractor; this.chestInteractor = chestInteractor; this.inventory = inventory; this.walkMenu = walkMenu; this.alerter = alerter; this.passthroughs = passthroughs; } /** * Interpret key being pressed as an action. * * @param key the key being pressed as a string. */ public void interpret(String key) { // There is one ALWAYS GLOBAL keyboard mapping... 'k' to show the keyboard mappings. if (key.equals("k")) { toggler.toggle(Toggler.ToggleableComponent.CONTROLS); return; } Toggler.ToggleableComponent currentComponent = toggler.getCurrentComponent(); switch (currentComponent) { case MAP -> { switch (key) { case "w" -> mover.generateMapMoveBy(new Point(0, -1)); case "a" -> mover.generateMapMoveBy(new Point(-1, 0)); case "s" -> mover.generateMapMoveBy(new Point(0, 1)); case "d" -> mover.generateMapMoveBy(new Point(1, 0)); case "e" -> chestInteractor.getChestItem(); case "i" -> toggler.toggle(Toggler.ToggleableComponent.INVENTORY); case "c" -> toggler.toggle(Toggler.ToggleableComponent.WALK_MENU); case "f" -> { if (opponentInteractor.setOpponentSet()) { toggler.toggle(Toggler.ToggleableComponent.BATTLE); } } case "m" -> toggler.toggle(Toggler.ToggleableComponent.MINIMAP); } } case WALK_MENU -> { if (key.equals("c")) { toggler.toggle(Toggler.ToggleableComponent.WALK_MENU); } else { walkMenu.interpret(key); } } case MINIMAP -> { if (key.equals("m")) { toggler.toggle(Toggler.ToggleableComponent.MINIMAP); } } case INVENTORY -> { if (key.equals("i")) { toggler.toggle(Toggler.ToggleableComponent.INVENTORY); } else { inventory.interpret(key); } } case ALERT -> { if (key.equals(" ")) { alerter.interpret(key); } } default -> { if (passthroughs.containsKey(currentComponent)) { passthroughs.get(currentComponent).interpret(key); } } } } } |
Added src/main/java/com/mg105/interface_adapters/PartyGetter.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package com.mg105.interface_adapters; import com.mg105.use_cases.PartyStatGetter; import org.jetbrains.annotations.NotNull; /** * Get information about a party member. */ public class PartyGetter { private final @NotNull PartyStatGetter getter; /** * Create a new PartyGetter. * * @param getter the getter used to access information. */ public PartyGetter(@NotNull PartyStatGetter getter) { this.getter = getter; } /** * Get if name is fainted. * * @param name the party member's name. * * @return if name is fainted. */ public boolean isFainted(@NotNull String name) { return getter.isFainted(name); } /** * Get the HP of a party member. * * @param name the party member's name. * * @return the HP of a party member. */ public int getHp(@NotNull String name) { return getter.getHp(name); } /** * Get the maximum HP of a party member. * * @param name the party member's name. * * @return the maximum HP of a party member. */ public int getMaxHp(@NotNull String name) { return getter.getMaxHp(name); } /** * Get the Dmg of a party member. * * @param name the party member's name. * * @return the Dmg of a party member. */ public int getDmg(@NotNull String name) { return getter.getDmg(name); } } |
Changes to src/main/java/com/mg105/interface_adapters/Toggler.java.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | */ @NotNull ToggleableComponent getCurrentComponent(); /** * All the possible components that could theoretically be toggled. */ enum ToggleableComponent { /** * The main menu */ MAIN_MENU, /** * The main map */ | > > > > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | */ @NotNull ToggleableComponent getCurrentComponent(); /** * All the possible components that could theoretically be toggled. */ enum ToggleableComponent { /** * An alert is visible. * <p> * Note: in practice this one should not be directly toggled. */ ALERT, /** * The main menu */ MAIN_MENU, /** * The main map */ |
︙ | ︙ | |||
41 42 43 44 45 46 47 | */ INVENTORY, /** * The battle menu */ BATTLE, /** | < < < < | | > > > > > > > | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | */ INVENTORY, /** * The battle menu */ BATTLE, /** * The character selection screen. */ WALK_MENU, /** * The game Lose menu */ LOSE_MENU, /** * The game Win menu */ WIN_MENU, /** * The intro message */ INTRO, /** * The keyboard layout */ CONTROLS } } |
Changes to src/main/java/com/mg105/interface_adapters/battle/BattlePresenter.java.
︙ | ︙ | |||
110 111 112 113 114 115 116 117 118 119 120 121 122 123 | * returns null iff the battle has ended * * @return a String of the name of the moving character */ public String roundStart() { return interactor.roundStart(); } /** * Returns the name of every BattleCharacter which can be targeted by the given move. * Note: Function should only be called from view when caster is friendly, so method does not accommodate for case * where caster is an opponent. * * @param moveNum integer representing which of the two Moves is being used. | > > > > > > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | * returns null iff the battle has ended * * @return a String of the name of the moving character */ public String roundStart() { return interactor.roundStart(); } /** * Have the opponents apply an attack, if necessary. */ public void applyOpponentAttack() { interactor.applyOpponentAttack(); } /** * Returns the name of every BattleCharacter which can be targeted by the given move. * Note: Function should only be called from view when caster is friendly, so method does not accommodate for case * where caster is an opponent. * * @param moveNum integer representing which of the two Moves is being used. |
︙ | ︙ |
Changes to src/main/java/com/mg105/interface_adapters/inventory/InventoryPresenter.java.
︙ | ︙ | |||
48 49 50 51 52 53 54 | * @param itemDetails the state of the item that was potentially used * @see ItemDetails * @see InventoryViewInterface */ @Override public void addItem(boolean isSuccessful, ItemDetails itemDetails) { if (isSuccessful) { | | | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | * @param itemDetails the state of the item that was potentially used * @see ItemDetails * @see InventoryViewInterface */ @Override public void addItem(boolean isSuccessful, ItemDetails itemDetails) { if (isSuccessful) { this.display.alert("Successfully added a(n) " + itemDetails.getName() + "."); return; } this.display.alert(itemDetails.getName() + " could not be added. The inventory might be full, try removing an item."); } /** * Sends an alert the view about the status of the potentially removed item. * Also updates the view to display the appropriate amount of items left fo that type * * @param isSuccessful if an item of itemName was removed from the inventory * @param itemDetails the state of the item that was potentially used * @see ItemDetails * @see InventoryViewInterface */ @Override public void removeItem(boolean isSuccessful, ItemDetails itemDetails) { if (!isSuccessful) { this.display.alert("Could not remove a(n) " + itemDetails.getDescription() + "."); return; } this.display.alert("Successfully removed a(n) " + itemDetails.getName() + "."); if (itemDetails.getCount() == 0) { this.display.removeItemView(itemDetails.getName()); return; } |
︙ | ︙ |
Changes to src/main/java/com/mg105/interface_adapters/map/RoomInterpreter.java.
︙ | ︙ | |||
26 27 28 29 30 31 32 | * Get the current room as represented in an easier to draw grid. * * @return the room as a 2 dimension array of TileType representing the current state of the room. Note that (0, 0) * represents the top-left corner and (MapConstants.ROOM_SIZE, MapConstants.ROOM_SIZE) represents the bottom * right corner. */ @Override | | | | < < < | < < | | > > | > > < | < < < | > > > | | | < < | < < < < < < < < < < < < < < < < < < | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | * Get the current room as represented in an easier to draw grid. * * @return the room as a 2 dimension array of TileType representing the current state of the room. Note that (0, 0) * represents the top-left corner and (MapConstants.ROOM_SIZE, MapConstants.ROOM_SIZE) represents the bottom * right corner. */ @Override public @NotNull RoomState getCurrentRoom() { RoomTileType[][] baseLayer = new RoomTileType[MapConstants.ROOM_SIZE + 1][MapConstants.ROOM_SIZE]; RoomTileType[][] offsetLayer = new RoomTileType[MapConstants.ROOM_SIZE + 1][MapConstants.ROOM_SIZE]; for (int i = 0; i < MapConstants.ROOM_SIZE; i++) { offsetLayer[i][MapConstants.ROOM_SIZE - 1] = RoomTileType.WALL; offsetLayer[i][0] = RoomTileType.WALL; offsetLayer[0][i] = RoomTileType.WALL; offsetLayer[MapConstants.ROOM_SIZE - 1][i] = RoomTileType.WALL; offsetLayer[MapConstants.ROOM_SIZE][i] = RoomTileType.WALL_FACE; } for (int i = 1; i < MapConstants.ROOM_SIZE - 1; i++) { offsetLayer[1][i] = RoomTileType.WALL_FACE; } RoomLayout room = getter.getCurrentRoomLayout(); for (Point doorway : room.getDoorways()) { if (doorway.x == 0 || doorway.x == MapConstants.ROOM_SIZE - 1) { offsetLayer[doorway.y][doorway.x] = RoomTileType.WALL_FACE; } else { offsetLayer[doorway.y][doorway.x] = null; offsetLayer[doorway.y + 1][doorway.x] = null; } } for (Point chest : room.getClosedChests()) { baseLayer[chest.y][chest.x] = RoomTileType.CHEST; } for (Point chest : room.getOpenChests()) { baseLayer[chest.y][chest.x] = RoomTileType.CHEST_OPEN; } for (Point opponents : room.getOpponents()) { baseLayer[opponents.y][opponents.x] = RoomTileType.OPPONENT_SET; } return new RoomState(baseLayer, offsetLayer, room.getPlayer(), getter.getWalkingSprite()); } } |
Changes to src/main/java/com/mg105/interface_adapters/map/RoomInterpreterInterface.java.
1 2 3 4 | package com.mg105.interface_adapters.map; import org.jetbrains.annotations.NotNull; | < < | | < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package com.mg105.interface_adapters.map; import org.jetbrains.annotations.NotNull; /** * Layout the current room. */ public interface RoomInterpreterInterface { /** * Get the current room state. * * @return the state of the current room. */ @NotNull RoomState getCurrentRoom(); } |
Added src/main/java/com/mg105/interface_adapters/map/RoomState.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | package com.mg105.interface_adapters.map; import org.jetbrains.annotations.NotNull; import java.awt.*; /** * RoomState is a easy-to-draw representation of the state of the current room. */ public class RoomState { private final @NotNull RoomTileType[][] baseLayer; private final @NotNull RoomTileType[][] offsetLayer; private final @NotNull Point playerPosition; private final @NotNull String playerSprite; /** * Create a new RoomState. * * @param baseLayer the base layer (chests, battles) * @param offsetLayer the wall and decoration layer (walls, wall faces) * @param playerPosition the position of the player in the room. * @param playerSprite the player sprite. */ public RoomState(@NotNull RoomTileType[][] baseLayer, @NotNull RoomTileType[][] offsetLayer, @NotNull Point playerPosition, @NotNull String playerSprite) { this.baseLayer = baseLayer; this.offsetLayer = offsetLayer; this.playerPosition = playerPosition; this.playerSprite = playerSprite; } /** * Get the representation of the contents of the room. * <p> * Note the return value is of size MapConstants.MAP_SIZE x MapConstants.MAP_SIZE+1. * * @return a 2D representation of the contents of the room. */ public @NotNull RoomTileType[][] getBaseLayer() { return baseLayer; } /** * Get a representation of the border of the room * <p> * Note the return value is of size MapConstants.MAP_SIZE x MapConstants.MAP_SIZE+1. * * @return a representation of the border of the room. */ public @NotNull RoomTileType[][] getOffsetLayer() { return offsetLayer; } /** * Get the player position in the room. * * @return the player position in the room. */ public @NotNull Point getPlayerPosition() { return playerPosition; } /** * Get the player sprite. * * @return the player sprite. */ public @NotNull String getPlayerSprite() { return playerSprite; } } |
Changes to src/main/java/com/mg105/interface_adapters/map/RoomTileType.java.
1 2 3 4 5 6 7 | package com.mg105.interface_adapters.map; /** * Types of tiles that can appear within a room. */ public enum RoomTileType { /** | < < < < | | < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.mg105.interface_adapters.map; /** * Types of tiles that can appear within a room. */ public enum RoomTileType { /** * Wall that cannot be walked on. Mainly used for the border */ WALL, /** * The isometric wall face. */ WALL_FACE, /** * A treasure chest */ CHEST, /** * A treasure chest that has been opened */ |
︙ | ︙ |
Deleted src/main/java/com/mg105/interface_adapters/tutorial/TutorialTextController.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/main/java/com/mg105/use_cases/PartyStatGetter.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package com.mg105.use_cases; import com.mg105.entities.GameState; import org.jetbrains.annotations.NotNull; /** * Get ad hoc information about party members. */ public class PartyStatGetter { private final @NotNull GameState state; /** * Create a new PartyStatGetter. * * @param state the game state that stores the data. */ public PartyStatGetter(@NotNull GameState state) { this.state = state; } /** * Get if name is fainted. * * @param name the party member's name. * * @return if name is fainted. */ public boolean isFainted(@NotNull String name) { return state.getFaintedPartyMember(name) != null; } /** * Get the HP of a party member. * * @param name the party member's name. * * @return the HP of a party member. */ public int getHp(@NotNull String name) { return state.getPartyMember(name).getHp(); } /** * Get the maximum HP of a party member. * * @param name the party member's name. * * @return the maximum HP of a party member. */ public int getMaxHp(@NotNull String name) { return state.getPartyMember(name).getMaxHp(); } /** * Get the Dmg of a party member. * * @param name the party member's name. * * @return the Dmg of a party member. */ public int getDmg(@NotNull String name) { return state.getPartyMember(name).getDmg(); } } |
Deleted src/main/java/com/mg105/use_cases/PlayerGetsTutorial.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to src/main/java/com/mg105/use_cases/ReplayGenerator.java.
1 2 3 4 5 | package com.mg105.use_cases; import com.mg105.entities.BattleCharacter; import com.mg105.entities.GameState; import com.mg105.entities.items.MegaPotion; | < | 1 2 3 4 5 6 7 8 9 10 11 12 | package com.mg105.use_cases; import com.mg105.entities.BattleCharacter; import com.mg105.entities.GameState; import com.mg105.entities.items.MegaPotion; import com.mg105.utils.PartyConstants; import org.jetbrains.annotations.NotNull; /** * A class that implement the game restart and replay function. * this class have players' attribute inheritance method and players' inventory clean method. |
︙ | ︙ | |||
78 79 80 81 82 83 84 | //Party is empty, fainted is full. state.getParty().addAll(state.getFainted()); state.getFainted().removeAll(state.getParty()); this.inventoryClean(); this.attributeInheritance(); | < < < < | 77 78 79 80 81 82 83 84 85 86 87 88 | //Party is empty, fainted is full. state.getParty().addAll(state.getFainted()); state.getFainted().removeAll(state.getParty()); this.inventoryClean(); this.attributeInheritance(); for (Resetable resetable : resetables) { resetable.reset(); } } } |
Changes to src/main/java/com/mg105/use_cases/battle/BattleInteractor.java.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | private final static int MAX_UPGRADE_TOKEN_REWARDED = 3; private final GameState state; private final InventoryInteractor inventoryInteractor; private final Saver saver; private BattlePresenterInterface presenter; /** * Creates a new BattleInteractor with a reference to the GameState. * * @param state the GameState to be referred to. * @param inventoryInteractor the inventoryInteractor to be referred to. * @param saver an instance of Saver used to save data. | > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | private final static int MAX_UPGRADE_TOKEN_REWARDED = 3; private final GameState state; private final InventoryInteractor inventoryInteractor; private final Saver saver; private BattlePresenterInterface presenter; private BattleCharacter moving; /** * Creates a new BattleInteractor with a reference to the GameState. * * @param state the GameState to be referred to. * @param inventoryInteractor the inventoryInteractor to be referred to. * @param saver an instance of Saver used to save data. |
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 | for (int i = 0; i < party.size(); ++i) { partyNames[i] = party.get(i).getName(); } for (int i = 0; i < opponents.size(); ++i) { opponentNames[i] = opponents.get(i).getName(); } presenter.setViewNames(partyNames, opponentNames); } /** * Returns whether the given name is associated with a fainted character. * Assumes that the inputted name corresponds to a character who was in the encounter at some point. | > > | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | for (int i = 0; i < party.size(); ++i) { partyNames[i] = party.get(i).getName(); } for (int i = 0; i < opponents.size(); ++i) { opponentNames[i] = opponents.get(i).getName(); } moving = null; presenter.setViewNames(partyNames, opponentNames); } /** * Returns whether the given name is associated with a fainted character. * Assumes that the inputted name corresponds to a character who was in the encounter at some point. |
︙ | ︙ | |||
146 147 148 149 150 151 152 153 154 155 156 157 | * If encounter is still in progress, get the next moving character. * If opponent is moving, choose a random move and random target and use it. * returns null iff the battle has ended * * @return a String of the name of the moving character */ public String roundStart() { int status = getBattleStatus(); if (Math.abs(status) == 1) { //Player either lost or won the battle return null; } else { //Battle is ongoing | > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | * If encounter is still in progress, get the next moving character. * If opponent is moving, choose a random move and random target and use it. * returns null iff the battle has ended * * @return a String of the name of the moving character */ public String roundStart() { moving = null; int status = getBattleStatus(); if (Math.abs(status) == 1) { //Player either lost or won the battle return null; } else { //Battle is ongoing moving = state.getCurrEncounter().getMovingCharacter(); //No matter what, the moving BattleCharacter's name is returned return moving.getName(); } } /** * Have the opponents apply an attack. */ public void applyOpponentAttack() { assert moving != null; assert moving.isOpponent(); Random rand = new Random(); int moveNumber = rand.nextInt(2); Move chosenMove; if (moveNumber == 0) { chosenMove = moving.getMoveOne(); } else { chosenMove = moving.getMoveTwo(); } if (!chosenMove.isFriendly()) { ArrayList<BattleCharacter> players = state.getCurrEncounter().getPlayerCharacters(); //Choose a random player character to attack int target = rand.nextInt(players.size()); this._useMove(chosenMove, moving, players.get(target)); } else { ArrayList<BattleCharacter> opponents = state.getCurrEncounter().getOpponents(); //Choose a random opponent to use the friendly move on int target = rand.nextInt(opponents.size()); this._useMove(chosenMove, moving, opponents.get(target)); } } /** * Returns the name of every BattleCharacter which can be targeted by the given move. * Note: Function should only be called from view when caster is friendly, so method does not accommodate for case * where caster is an opponent. * * @param moveNum integer representing which of the two Moves is being used. |
︙ | ︙ |
Deleted src/main/java/com/mg105/user_interface/AlertBox.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/main/java/com/mg105/user_interface/Alerter.java.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package com.mg105.user_interface; import javafx.scene.Parent; import org.jetbrains.annotations.NotNull; /** * An Alerter can display and handle alerts (modal dialogues). */ public interface Alerter { /** * Display an alert. * * @param body the actual alert. */ void displayAlert(@NotNull Parent body); } |
Changes to src/main/java/com/mg105/user_interface/BattleMenu.java.
1 2 3 4 5 | package com.mg105.user_interface; import com.mg105.interface_adapters.battle.BattleMenuInterface; import com.mg105.interface_adapters.battle.BattlePresenter; import javafx.event.ActionEvent; | > > > > > | | | | | > | | < | < | < < < < | | | < | | < < < | | | | | | | < < < < < < | | | > | > | | | | | > | < | | | | | > | > > > | | < < < > > < < | | | | | | | | < < | > | > | | > > > > > > | < | < | > | < | > | | < < < > > > | | | < < | > > > | < | | > > | < < > < | < | > | | < < < < < < < < < < | | < < > | > > > | | > > > > > | > > > > > > > > > > > > > > > > > | > > | | > > > | > > > > | > > | > > | > > > > > | > > > > > | > > > > > > > > > > | > | > > > > | > > > > > > > > > > | | | | < < < < | < < < < | < | | > | < < < < < < < < < < < < < < < | < < | < | < | | < | | | | < < < | < < < | < | | > > | | | | < < < < > < > | | < < | < < < < < < > > > > | < < | < < < < < < | > | < < < < < < > < < < < < | | < < < | < < < < > | < < < < < < < < > > > > > < < | < < | < < | | < < < < < < | | < < < < < < < > | | < | | < < < < | < < | < < < < < > | > | < < | > > | > | < < | < < < < < < < < < > | < < < > > | | | < | < < < < < < < < | < < < < < < < | < < | < | < < < < < | | < < < < < < < < < < | | > > > | | < < > > > > > | < < < < < | < > > | | > | > | > > | < < > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.interface_adapters.battle.BattleMenuInterface; import com.mg105.interface_adapters.battle.BattlePresenter; import com.mg105.utils.PartyConstants; import com.mg105.utils.UIConstants; import javafx.animation.FadeTransition; import javafx.animation.TranslateTransition; import javafx.event.ActionEvent; import javafx.geometry.Bounds; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.control.Label; import javafx.scene.layout.*; import javafx.util.Duration; import org.jetbrains.annotations.NotNull; import java.util.Arrays; /** * This class uses JavaFX and is displayed during an active battle. */ public class BattleMenu implements InputControllable, BattleMenuInterface, Toggleable { private final @NotNull BattlePresenter presenter; private final @NotNull StandardComponentFactory componentFactory; private final @NotNull Label[] opponentNames; private final @NotNull Label[] opponentHps; private final @NotNull Label[] opponentDmgs; private final @NotNull Label[] playerNames; private final @NotNull Label[] playerHps; private final @NotNull Label[] playerDmgs; private final @NotNull Label turnHint; private final @NotNull Label actionHint; private final @NotNull MultiselectGrid options; private final @NotNull StackPane layoutWrapper; private final @NotNull Group effectLayer; private boolean needsSelection; private String mover; private int selectedMove; private String selectedTarget; /** * Creates a new BattleMenu. * Sets every UI element, except for labels which are set when the view is toggled on. * * @param battlePres the BattlePresenter to refer to (following MVP pattern) * @param componentFactory the component factory to use. */ public BattleMenu(@NotNull BattlePresenter battlePres, @NotNull StandardComponentFactory componentFactory) { this.presenter = battlePres; presenter.setView(this); this.componentFactory = componentFactory; opponentNames = new Label[4]; opponentHps = new Label[4]; opponentDmgs = new Label[4]; playerNames = new Label[4]; playerHps = new Label[4]; playerDmgs = new Label[4]; for (int i = 0; i < 4; i++) { opponentNames[i] = componentFactory.createSubtitleLabel("UNSET", true); opponentHps[i] = componentFactory.createLabel("UNSET", true); opponentDmgs[i] = componentFactory.createLabel("UNSET", true); playerNames[i] = componentFactory.createSubtitleLabel("UNSET", true); playerHps[i] = componentFactory.createLabel("UNSET", true); playerDmgs[i] = componentFactory.createLabel("UNSET", true); } turnHint = componentFactory.createSubtitleLabel("UNSET", true); actionHint = componentFactory.createSubtitleLabel("UNSET", true); HBox hintSlot = new HBox(); hintSlot.getChildren().addAll(turnHint, actionHint); options = componentFactory.createMultiselectGrid(); Region optionsSpacer = componentFactory.createFixedHalfSpacer(); optionsSpacer.setPrefSize(0, 11 * UIConstants.SINGLE_GAP); HBox optionsSlot = new HBox(); optionsSlot.getChildren().addAll(optionsSpacer, options.getGrid()); VBox layout = new VBox(); layout.setMinHeight(UIConstants.CANVAS_SIZE - 4 * UIConstants.SINGLE_GAP); // ???? layout.getChildren().addAll( makeBlock(opponentNames, opponentHps, opponentDmgs), componentFactory.createSpacer(), makeBlock(playerNames, playerHps, playerDmgs), componentFactory.createSpacer(), componentFactory.createSpacer(), hintSlot, componentFactory.createFixedHalfSpacer(), optionsSlot ); effectLayer = new Group(); effectLayer.setManaged(false); layoutWrapper = new StackPane(); layoutWrapper.setBackground(componentFactory.createBackground(true)); layoutWrapper.setPadding(componentFactory.createInsets()); layoutWrapper.setAlignment(Pos.CENTER); layoutWrapper.getChildren().addAll(layout, effectLayer); } /** * Make a row of characters. * * @param names the names of the characters. * @param hps the health values of the characters * @param dmgs the damage values of the characters. * * @return a layout of a row of characters. */ private @NotNull HBox makeBlock(@NotNull Label[] names, @NotNull Label[] hps, @NotNull Label[] dmgs) { HBox block = new HBox(); for (int i = 0; i < names.length; i++) { GridPane info = new GridPane(); info.addRow(0, componentFactory.createLabel("HP: ", true), hps[i]); info.addRow(1, componentFactory.createLabel("Dmg: ", true), dmgs[i]); VBox stack = new VBox(); stack.getChildren().addAll(names[i], info); block.getChildren().addAll(stack, componentFactory.createSpacer()); } block.getChildren().remove(block.getChildren().size() - 1); return block; } /** * Sets the names of the player and opponent characters participating in the active battle. * * @param playerNames array of name Strings representing player characters. * @param opponentNames array of name Strings representing opponents. */ @Override public void setNames(String[] playerNames, String[] opponentNames) { for (int i = 0; i < this.playerNames.length; i++) { this.playerNames[i].setText(PartyConstants.ALL_PARTY_MEMBER_NAMES[i]); // HACK: we shouldn't just ignore the playerNames parameter, but any discrepancy that this will cause will get instantly fixed somewhere else. this.opponentNames[i].setText(opponentNames[i]); } } /** * Updates the display corresponding to the given affected character. * * @param character the character who needs to be updated on the screen. */ @Override public void updateCharacter(String character) { for (int i = 0; i < playerNames.length; i++) { if (playerNames[i].getText().equals(character)) { updateCharacter(character, playerNames[i], playerHps[i], playerDmgs[i], true); return; } if (opponentNames[i].getText().equals(character)) { updateCharacter(character, opponentNames[i], opponentHps[i], opponentDmgs[i], true); return; } } } /** * Updates the information once the slot of the update is known. * * @param character the character to update. * @param name the name location. * @param hp the hp location. * @param dmg the damage location. * @param effect if an effect should be drawn to visually represent the change. */ private void updateCharacter(@NotNull String character, @NotNull Label name, @NotNull Label hp, @NotNull Label dmg, boolean effect) { int startHp = Integer.MIN_VALUE; int startDmg = Integer.MIN_VALUE; if (effect) { startHp = Integer.parseInt(hp.getText()); startDmg = Integer.parseInt(dmg.getText()); } int deltaHp = Integer.MIN_VALUE; int deltaDmg = Integer.MIN_VALUE; if (presenter.givenCharacterFainted(character)) { name.setText("FAINTED"); hp.setText("N/A"); dmg.setText("N/A"); if (effect) { deltaHp = -startHp; deltaDmg = 0; } } else { final int endHp = presenter.givenCharacterHealth(character); final int endDmg = presenter.givenCharacterDamage(character); hp.setText(Integer.toString(endHp)); dmg.setText(Integer.toString(endDmg)); if (effect) { deltaHp = endHp - startHp; deltaDmg = endDmg - startDmg; } } if (effect) { Bounds anchorLocation = name.localToScene(name.getBoundsInLocal()); if (anchorLocation == null) { return; } HBox effectBox = new HBox(); effectBox.getChildren().addAll( componentFactory.createNumberedLabel(deltaHp, true), componentFactory.createFixedHalfSpacer(), componentFactory.createNumberedLabel(deltaDmg, true) ); effectBox.setLayoutX(anchorLocation.getCenterX()); effectBox.setLayoutY(anchorLocation.getMinY() - (int)(UIConstants.SINGLE_GAP / 2)); effectBox.setTranslateX(0); effectBox.setTranslateY(0); Duration duration = Duration.millis(1000); TranslateTransition translate = new TranslateTransition(); translate.setDuration(duration); translate.setByY(-UIConstants.SINGLE_GAP); translate.setNode(effectBox); FadeTransition fade = new FadeTransition(); fade.setDuration(duration); fade.setFromValue(1); fade.setToValue(0); fade.setNode(effectBox); fade.setOnFinished((ActionEvent event) -> effectLayer.getChildren().remove(effectBox)); translate.play(); fade.play(); effectLayer.getChildren().add(effectBox); } } /** * Get the scene of this toggleable object. It is this scene that will be displayed. * * @return the scene to be displayed. */ @Override public @NotNull Pane getScene() { return layoutWrapper; } /** * Set the visibility of this component. * * @param isVisible true if the Toggleable is now visible, false otherwise. If false the Toggleable is expected * to do nothing on ANY user inputs. */ @Override public void toggle(boolean isVisible) { if (isVisible) { presenter.startBattle(); // Will call setNames and update character data. for (int i = 0; i < playerNames.length; i++) { updateCharacter(playerNames[i].getText(), playerNames[i], playerHps[i], playerDmgs[i], false); updateCharacter(opponentNames[i].getText(), opponentNames[i], opponentHps[i], opponentDmgs[i], false); } startTurn(); } } /** * Create a move description. * * @param name the name of the move. * @param hp the hp damage of the move. * @param dmg the dmg damage of the move. * * @return the move description. */ private @NotNull VBox makeMoveStack(@NotNull String name, int hp, int dmg) { GridPane info = new GridPane(); info.addRow(0, componentFactory.createLabel("HP: ", true), componentFactory.createLabel(Integer.toString(hp), true) ); info.addRow(1, componentFactory.createLabel("Dmg: ", true), componentFactory.createLabel(Integer.toString(dmg), true) ); VBox stack = new VBox(); stack.getChildren().addAll(componentFactory.createLabel(name, true), info); return stack; } /** * Start a new turn. */ private void startTurn() { options.reset(); options.addColumn(componentFactory.createLabel("Forfeit", true)); mover = presenter.roundStart(); if (mover == null) { presenter.endBattle(); } needsSelection = Arrays.stream(playerNames).anyMatch(x -> x.getText().equals(mover)); selectedMove = -1; selectedTarget = null; advanceOptions(); } /** * Advance the possible options to the correct state. */ private void advanceOptions() { turnHint.setText("It's " + mover + "'s turn... "); if (needsSelection) { actionHint.setVisible(true); if (selectedMove < 0) { actionHint.setText("Select a move:"); String[] moveNames = presenter.givenCharacterMoveNames(mover); int[] moveStats = presenter.givenCharacterMoveStats(mover); options.addColumn( makeMoveStack(moveNames[0], moveStats[0], moveStats[1]), makeMoveStack(moveNames[1], moveStats[2], moveStats[3]) ); } else if (selectedTarget == null) { actionHint.setText("Select a target:"); options.selectActive(); options.addColumn(); for (String target : presenter.retrieveTargets(selectedMove, mover)) { options.addToColumn(componentFactory.createLabel(target, true)); } } } else { actionHint.setVisible(false); options.addColumn(componentFactory.createLabel("Next Move", true)); } while (options.activeRight()); // Make sure we are in the right-most slot, for convenience } @Override public void interpret(String key) { switch (key) { case "w" -> options.activeUp(); case "s" -> options.activeDown(); case "a" -> { if (options.getActive().x == 2) { actionHint.setText("Select a move:"); selectedMove = -1; options.popColumn(); } else { options.activeLeft(); } } case "d" -> { if (options.getActive().x == 0) { options.activeRight(); } } case " " -> { if (options.getActive().x == 0) { // Forfeit presenter.endBattle(); return; } if (needsSelection) { if (options.getActive().x == 1) { selectedMove = options.getActive().y + 1; // options are 1 or 2 not 0 or 1 advanceOptions(); } else if (options.getActive().x == 2) { selectedTarget = presenter.retrieveTargets(selectedMove, mover).get(options.getActive().y); presenter.executeTurn(selectedMove, mover, selectedTarget); startTurn(); } } else { presenter.applyOpponentAttack(); startTurn(); } } } } } |
Added src/main/java/com/mg105/user_interface/IntroDisplay.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.interface_adapters.Toggler; import com.mg105.utils.UIConstants; import javafx.animation.AnimationTimer; import javafx.animation.FadeTransition; import javafx.event.ActionEvent; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.layout.Pane; import javafx.scene.layout.TilePane; import javafx.util.Duration; import org.jetbrains.annotations.NotNull; import java.util.LinkedList; import java.util.Queue; /** * IntroDisplay displays the intro text when you start a new game. */ public class IntroDisplay implements Toggleable, InputControllable { private final @NotNull Toggler toggler; private final @NotNull KeymapDisplay keymap; private final @NotNull StandardComponentFactory componentFactory; private final @NotNull TilePane pane; private final @NotNull Label[] messages; private BodyAnimation animations; /** * Create a new intro display. * * @param toggler the toggler used to toggle components. * @param keymap the keymap display. * @param componentFactory the component factory. */ public IntroDisplay(@NotNull Toggler toggler, @NotNull KeymapDisplay keymap, @NotNull StandardComponentFactory componentFactory) { this.toggler = toggler; this.keymap = keymap; this.componentFactory = componentFactory; pane = new TilePane(Orientation.VERTICAL); pane.setBackground(componentFactory.createBackground(false)); pane.setPadding(componentFactory.createInsets()); pane.setVgap(UIConstants.SINGLE_GAP); pane.setAlignment(Pos.CENTER); messages = new Label[]{ componentFactory.createLabel("You cannot recall anything but the endless action of putting one foot in front of the other, traversing this empty landscape.", false), componentFactory.createLabel("Suddenly, you look up, startled out of your reverie. An imposing mountain looms before you. Your party steps forward, drawn to it for some inexplicable reason.", false), componentFactory.createLabel("You crane your neck to see if you can make out the peak, but the morning mist impedes your view.", false), componentFactory.createLabel("You all know you cannot go back to wherever you came from. You have to keep going. You have to climb the mountain.", false), componentFactory.createLabel("Maybe whatever waits for you up there will remind you of who you are... and what you’re searching for.", false), componentFactory.createLabel("Press any key to continue...", false) }; for (Label l : messages) { l.setWrapText(true); l.setAlignment(Pos.CENTER); l.setMaxWidth(UIConstants.CANVAS_SIZE - 2 * UIConstants.SINGLE_GAP); } pane.getChildren().addAll(messages); } @Override public void interpret(String key) { if (animations != null && animations.advance()) { keymap.addExtraHints(componentFactory.createLabel("Pressing 'k' starts the game.", true)); toggler.toggle(Toggler.ToggleableComponent.CONTROLS); toggler.toggle(Toggler.ToggleableComponent.INTRO); } } @Override public @NotNull Pane getScene() { return pane; } @Override public void toggle(boolean isVisible) { if (isVisible) { animations = new BodyAnimation(); animations.start(); } else { animations.stop(); animations = null; } } /** * BodyAnimation slowly fades in the messages one by one. */ private class BodyAnimation extends AnimationTimer { private long last; private final @NotNull Queue<FadeTransition> transitions; /** * Create a new BodyAnimation. */ public BodyAnimation() { last = 0; transitions = new LinkedList<>(); for (Label l : messages) { l.setOpacity(0); FadeTransition fade = new FadeTransition(); fade.setDuration(Duration.millis(2500)); fade.setFromValue(0); fade.setToValue(1); fade.setOnFinished((ActionEvent value) -> l.setOpacity(1)); fade.setNode(l); transitions.add(fade); } advance(); // Don't want to wait 2 seconds for the first one to start } /** * Trigger the next animation if more than 2 seconds have passed. * * @param now * The timestamp of the current frame given in nanoseconds. This * value will be the same for all {@code AnimationTimers} called * during one frame. */ @Override public void handle(long now) { if (last == 0) { last = now; } else if (now - last > 1750e6 && advance()) { stop(); } } /** * Advance the animations by one. * * @return true if this did nothing. */ public boolean advance() { if (transitions.isEmpty()) { return true; } last = 0; transitions.remove().play(); return false; } } } |
Changes to src/main/java/com/mg105/user_interface/InventoryDisplay.java.
1 2 3 4 5 | package com.mg105.user_interface; import com.mg105.interface_adapters.inventory.InventoryController; import com.mg105.interface_adapters.inventory.InventoryViewInterface; import com.mg105.utils.PartyConstants; | > > > | | < | | | > > > < | > > > > > > > | | | | | | > > < | > > > > | | < < > > > | < < > > > > > | < | | < < > | < < < < | | < < | | < < > | < < < > | < < > | < | | | | < > | | < | < < > | | > > | < < | < < < < < < < < < < < | < < < < < | < < > > > | | > > > > > > | > > | > | | > > > > > | > > > > > > | | > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > | < | > > > > > | > | > > > > | > > > > > > | > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.interface_adapters.PartyGetter; import com.mg105.interface_adapters.inventory.InventoryController; import com.mg105.interface_adapters.inventory.InventoryViewInterface; import com.mg105.utils.PartyConstants; import com.mg105.utils.UIConstants; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.layout.Pane; import javafx.scene.layout.TilePane; import javafx.scene.layout.VBox; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A user interface class that displays the state of the inventory to the user and allows them to * perform actions that relate to the inventory and its items. */ public class InventoryDisplay implements InventoryViewInterface, Toggleable, InputControllable { private final @NotNull InventoryController controller; private final @NotNull PartyGetter partyGetter; private boolean isVisible = false; private final @NotNull StandardComponentFactory componentFactory; private final @NotNull MultiselectGrid options; private final @NotNull TilePane layoutWrapper; private final @NotNull Label hintLabel; private final @NotNull Map<String, String> descriptionCache; private final @NotNull Map<String, Boolean> usableCache; private final @NotNull Map<String, String> quantityCache; private final @NotNull List<String> nameCache; /** * Creates a new instance of inventory display * * @param controller an object that interprets user inputs to make changes about the inventory * @param componentFactory the component factory used to create UI components * @param partyGetter the party getter used to get party information. */ public InventoryDisplay(@NotNull InventoryController controller, @NotNull StandardComponentFactory componentFactory, @NotNull PartyGetter partyGetter) { this.controller = controller; this.componentFactory = componentFactory; this.partyGetter = partyGetter; layoutWrapper = new TilePane(); layoutWrapper.setPadding(componentFactory.createInsets()); layoutWrapper.setAlignment(Pos.CENTER); layoutWrapper.setBackground(componentFactory.createBackground(true)); VBox layout = new VBox(); layout.setPrefSize( UIConstants.CANVAS_SIZE - 2 * UIConstants.SINGLE_GAP, UIConstants.CANVAS_SIZE - 2 * UIConstants.SINGLE_GAP ); layoutWrapper.getChildren().add(layout); options = componentFactory.createMultiselectGrid(); layout.getChildren().add(options.getGrid()); layout.getChildren().add(componentFactory.createSpacer()); layout.getChildren().add(componentFactory.createSubtitleLabel("Hint(s):", true)); hintLabel = componentFactory.createLabel("UNSET TEXT", true); layout.getChildren().add(hintLabel); descriptionCache = new HashMap<>(); usableCache = new HashMap<>(); quantityCache = new HashMap<>(); nameCache = new ArrayList<>(); } /** * Returns the scene that displays the information for the Inventory * * @return the scene that displays the information for the Inventory. */ @Override public @NotNull Pane getScene() { return layoutWrapper; } /** * Changes the state of the InventoryDisplay based on if the inventory display is shown in the ui * * @param isVisible true if the Toggleable is now visible, false otherwise. If false the Toggleable is expected * to do nothing on ANY user inputs. */ @Override public void toggle(boolean isVisible) { this.isVisible = isVisible; if (isVisible) { nameCache.clear(); controller.getInventoryDetails(); options.reset(); if (nameCache.size() > 0) { options.addColumn(); for (String name : nameCache) { options.addToColumn(componentFactory.createLabel(name + " (" + quantityCache.get(name) + ")", true)); } } updateInfo(); } } /** * Opens a window displaying a message to the user * * @param msg the message to alert the user with */ @Override public void alert(String msg) { componentFactory.createAlert(msg, true); } /** * Adds the details of the item to the display * * @param name the name of the item to add * @param description the description of the item to add * @param isUsable true iff the item can be used by a user * @param quantity the number of items of this type in the Inventory */ @Override public void addItemView(String name, String description, boolean isUsable, String quantity) { changeItemView(name, description, isUsable, quantity); nameCache.add(name); } /** * Changes the details of an already displayed item and displays the changes * * @param name the name of the item to change details about * @param description the description of the item * @param isUsable true iff the item is usable * @param quantity the number of items of this type in the Inventory */ @Override public void changeItemView(String name, String description, boolean isUsable, String quantity) { descriptionCache.put(name, description); usableCache.put(name, isUsable); quantityCache.put(name, quantity); } /** * Removes an item from the inventory ui. * Precondition there must not be an items of the type name in the inventory * * @param name the name of the item to be removed */ @Override public void removeItemView(String name) { descriptionCache.remove(name); usableCache.remove(name); quantityCache.remove(name); nameCache.remove(name); } @Override public void interpret(String key) { if (!isVisible) { return; } switch (key) { case "w" -> options.activeUp(); case "s" -> options.activeDown(); case "a" -> { if (options.getActive().x > 0) { options.popColumn(); } } case " " -> { if (options.isEmpty()) { return; } if (options.getActive().x == 0) { options.selectActive(); options.addColumn(componentFactory.createLabel("Remove Item", true)); if (usableCache.get(nameCache.get(options.getActive().y))) { options.addToColumn(componentFactory.createLabel("Use Item", true)); } options.activeRight(); } else if (options.getActive().x == 1) { if (options.getActive().y == 0) { controller.removeItem(nameCache.get(options.getSelected().get(0))); toggle(true); } else { options.selectActive(); options.addColumn(); for (String name : PartyConstants.ALL_PARTY_MEMBER_NAMES) { options.addToColumn(componentFactory.createLabel(name, true)); } options.activeRight(); } } else { controller.useItem( nameCache.get(options.getSelected().get(0)), PartyConstants.ALL_PARTY_MEMBER_NAMES[options.getActive().y] ); toggle(true); } } } updateInfo(); } /** * Update the hint bottom text. */ private void updateInfo() { if (options.isEmpty()) { options.getGrid().setVisible(false); hintLabel.setText("Inventory is empty"); return; } options.getGrid().setVisible(true); if (options.getActive().x == 0) { hintLabel.setText(descriptionCache.get(nameCache.get(options.getActive().y))); return; } if (options.getActive().x == 1) { if (options.getActive().y == 0) { hintLabel.setText("Drop this item from your inventory."); } else { hintLabel.setText("Use this item on a character."); } return; } String name = PartyConstants.ALL_PARTY_MEMBER_NAMES[options.getActive().y]; if (partyGetter.isFainted(name)) { hintLabel.setText(name + " is fainted."); } else { hintLabel.setText(name + " has " + partyGetter.getHp(name) + "/" + partyGetter.getMaxHp(name) + " HP and " + partyGetter.getDmg(name) + " Dmg."); } } } |
Added src/main/java/com/mg105/user_interface/KeymapDisplay.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | package com.mg105.user_interface; import com.mg105.utils.ColorConstants; import com.mg105.utils.UIConstants; import javafx.event.EventHandler; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.input.KeyEvent; import javafx.scene.layout.*; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; /** * Display the keyboard mappings. */ public class KeymapDisplay implements Toggleable, EventHandler<KeyEvent> { private static final Border KEY_BORDER_UNSELECTED = new Border(new BorderStroke( ColorConstants.WALL, BorderStrokeStyle.SOLID, new CornerRadii((int)(UIConstants.SINGLE_GAP / 4)), new BorderWidths((int)(UIConstants.SINGLE_GAP / 8)) )); private static final Border KEY_BORDER_SELECTED = new Border(new BorderStroke( ColorConstants.ACTIVE, BorderStrokeStyle.SOLID, new CornerRadii((int)(UIConstants.SINGLE_GAP / 4)), new BorderWidths((int)(UIConstants.SINGLE_GAP / 8)) )); private final @NotNull TilePane pane; private final @NotNull VBox layout; private final int layoutSize; private boolean isVisible = false; private final @NotNull Map<String, Label> buttons; /** * Create a new keymap display. * * @param componentFactory the component factory used to create components. */ public KeymapDisplay(StandardComponentFactory componentFactory) { pane = new TilePane(Orientation.VERTICAL); pane.setBackground(componentFactory.createBackground(true)); pane.setPadding(componentFactory.createInsets()); pane.setAlignment(Pos.CENTER); layout = new VBox(); layout.setPrefHeight(UIConstants.CANVAS_SIZE - 2 * UIConstants.SINGLE_GAP); pane.getChildren().add(layout); GridPane grid = new GridPane(); grid.setHgap(UIConstants.SINGLE_GAP); grid.setVgap(UIConstants.SINGLE_GAP); grid.setPrefWidth(UIConstants.CANVAS_SIZE - 2 * UIConstants.SINGLE_GAP); ColumnConstraints keyConstraints = new ColumnConstraints(); keyConstraints.setPercentWidth(25); ColumnConstraints textConstraints = new ColumnConstraints(); textConstraints.setPercentWidth(75); grid.getColumnConstraints().addAll(keyConstraints, textConstraints); layout.getChildren().add(grid); layout.getChildren().add(componentFactory.createSpacer()); layout.getChildren().add(componentFactory.createSubtitleLabel("Hint(s):", true)); layout.getChildren().add(componentFactory.createLabel("Try pressing the keys!", true)); layoutSize = layout.getChildren().size(); Label wKey = componentFactory.createLabel("W", true); Label aKey = componentFactory.createLabel("A", true); Label sKey = componentFactory.createLabel("S", true); Label dKey = componentFactory.createLabel("D", true); GridPane wasdGrid = new GridPane(); wasdGrid.add(wKey, 1, 0); wasdGrid.addRow(1, aKey, sKey, dKey); Label wasdLabel = componentFactory.createLabel("Cursor keys (WASD for up, left, down, right respectively).", true); grid.addRow(0, wasdGrid, wasdLabel); Label spaceKey = componentFactory.createLabel("<space>", true); Label spaceLabel = componentFactory.createLabel("Make selection.", true); grid.addRow(1, spaceKey, spaceLabel); Label eKey = componentFactory.createLabel("E", true); Label fKey = componentFactory.createLabel("F", true); HBox efGrid = new HBox(); efGrid.getChildren().addAll(eKey, componentFactory.createFixedDoubleSpacer(), fKey); Label efLabel = componentFactory.createLabel("Open chest/start battle respectively (map only).", true); grid.addRow(2, efGrid, efLabel); Label iKey = componentFactory.createLabel("I", true); Label mKey = componentFactory.createLabel("M", true); Label cKey = componentFactory.createLabel("C", true); HBox imcGrid = new HBox(); imcGrid.getChildren().addAll(iKey, mKey, cKey); Label imcLabel = componentFactory.createLabel("Open/close inventory/minimap/character selector respectively (map only).", true); grid.addRow(3, imcGrid, imcLabel); Label kKey = componentFactory.createLabel("K", true); HBox kGrid = new HBox(); kGrid.getChildren().addAll(componentFactory.createFixedDoubleSpacer(), kKey, componentFactory.createFixedDoubleSpacer()); Label kLabel = componentFactory.createLabel("Open/close this menu (anywhere, even on the main menu!).", true); grid.addRow(4, kGrid, kLabel); buttons = new HashMap<>(); buttons.put("w", wKey); buttons.put("a", aKey); buttons.put("s", sKey); buttons.put("d", dKey); buttons.put(" ", spaceKey); buttons.put("e", eKey); buttons.put("f", fKey); buttons.put("i", iKey); buttons.put("m", mKey); buttons.put("c", cKey); buttons.put("k", kKey); for (Label l : new Label[]{wasdLabel, spaceLabel, efLabel, imcLabel, kLabel}) { l.setWrapText(true); l.setMaxWidth(0.7 * (UIConstants.CANVAS_SIZE - 2 * UIConstants.SINGLE_GAP)); } for (Map.Entry<String, Label> key : buttons.entrySet()) { key.getValue().setPrefSize(2 * UIConstants.SINGLE_GAP, 2 * UIConstants.SINGLE_GAP); key.getValue().setAlignment(Pos.CENTER); key.getValue().setFont(UIConstants.FONT_MONO); releaseKey(key.getKey()); } spaceKey.setMaxWidth(6 * UIConstants.SINGLE_GAP); // space edge case } @Override public @NotNull Pane getScene() { return pane; } @Override public void toggle(boolean isVisible) { this.isVisible = isVisible; if (isVisible) { for (Map.Entry<String, Label> key : buttons.entrySet()) { releaseKey(key.getKey()); } } else { while (layout.getChildren().size() > layoutSize) { layout.getChildren().remove(layout.getChildren().size() - 1); } } } @Override public void handle(KeyEvent event) { if (!isVisible) { return; } if (event.getEventType().equals(KeyEvent.KEY_PRESSED)) { pressKey(event.getText()); } else if (event.getEventType().equals(KeyEvent.KEY_RELEASED)) { releaseKey(event.getText()); } } /** * Add extra hints at the bottom that disappear when the menu is closed. * * @param hints the hints to add. */ public void addExtraHints(@NotNull Label... hints) { for (Label l : hints) { l.setMaxWidth(UIConstants.CANVAS_SIZE - 2 * UIConstants.SINGLE_GAP); layout.getChildren().add(l); } } /** * Set the style of the key to pressed. * * @param keyKey the key to be pressed. */ private void pressKey(@NotNull String keyKey) { if (buttons.containsKey(keyKey)) { buttons.get(keyKey).setTextFill(ColorConstants.ACTIVE); buttons.get(keyKey).setBorder(KEY_BORDER_SELECTED); } } /** * Set the style of the key to be released. * * @param keyKey the key to be released. */ private void releaseKey(@NotNull String keyKey) { if (buttons.containsKey(keyKey)) { buttons.get(keyKey).setTextFill(ColorConstants.WALL); buttons.get(keyKey).setBorder(KEY_BORDER_UNSELECTED); } } } |
Changes to src/main/java/com/mg105/user_interface/LoseMenu.java.
1 2 | package com.mg105.user_interface; | > > | | | | > > > | | > > | > | | | > > > > | | > > > | > > > > | | > > | > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.utils.UIConstants; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.layout.Pane; import javafx.scene.layout.TilePane; import org.jetbrains.annotations.NotNull; /** * LoseMenu is displayed when the player loses the game. */ public class LoseMenu implements Toggleable, InputControllable { private boolean isVisible = false; private final @NotNull Runnable replayButton; private final @NotNull MainMenu mainMenu; private final @NotNull TilePane layout; /** * Create a new LoseMenu * * @param replayButton the button that performs the replay action * @param componentFactory the component factory used to create UI components * @param mainMenu the main menu component. */ public LoseMenu(@NotNull Runnable replayButton, @NotNull StandardComponentFactory componentFactory, @NotNull MainMenu mainMenu) { this.replayButton = replayButton; this.mainMenu = mainMenu; layout = new TilePane(Orientation.VERTICAL); layout.setBackground(componentFactory.createBackground(false)); layout.setVgap(UIConstants.SINGLE_GAP); layout.setAlignment(Pos.CENTER); layout.getChildren().addAll( componentFactory.createTitleLabel("Battle Lost", false), componentFactory.createLabel("Well that's unfortunate.", false) ); MultiselectGrid options = componentFactory.createMultiselectGrid(); options.getGrid().setAlignment(Pos.CENTER); options.addColumn(componentFactory.createLabel("Now what...", false)); layout.getChildren().add(options.getGrid()); } @Override public @NotNull Pane getScene() { return layout; } @Override public void toggle(boolean isVisible) { this.isVisible = isVisible; } @Override public void interpret(String key) { if (isVisible && key.equals(" ")) { mainMenu.lost(); replayButton.run(); } } } |
Changes to src/main/java/com/mg105/user_interface/MainMenu.java.
1 2 | package com.mg105.user_interface; | > > > > > > | | | | | > > > > > > > > | > | > > > > | > > > > | > > | > > > > > > | | > > > > > > > > | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.interface_adapters.Toggler; import com.mg105.utils.ColorConstants; import com.mg105.utils.UIConstants; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.TilePane; import org.jetbrains.annotations.NotNull; /** * The main game menu. */ public class MainMenu implements Toggleable, InputControllable { private final @NotNull Toggler toggler; private final @NotNull Runnable startButton; private final @NotNull MultiselectGrid options; private boolean isVisible = false; private final @NotNull TilePane layout; private int playCount = 105; private final @NotNull Label playLabel; /** * Create a new MainMenu. * * @param toggler the toggler used to toggle displays. * @param startButton the button that starts the game. * @param componentFactory the component factory used to create UI components */ public MainMenu(@NotNull Toggler toggler, @NotNull Runnable startButton, @NotNull StandardComponentFactory componentFactory) { this.toggler = toggler; this.startButton = startButton; layout = new TilePane(Orientation.VERTICAL); layout.setVgap(UIConstants.SINGLE_GAP); layout.setAlignment(Pos.CENTER); layout.setBackground(componentFactory.createBackground(false)); playLabel = componentFactory.createTitleLabel(Integer.toString(playCount), false); playLabel.setFont(UIConstants.TITLE_FONT_ALT); HBox title = new HBox(); title.setAlignment(Pos.BASELINE_CENTER); title.getChildren().addAll( componentFactory.createTitleLabel("Mountain Group ", false), playLabel ); layout.getChildren().add(title); options = componentFactory.createMultiselectGrid(); options.addColumn( componentFactory.createLabel("Start Game", false), componentFactory.createLabel("Controls", false), componentFactory.createLabel("Quit Game", false) ); options.getGrid().setAlignment(Pos.CENTER); layout.getChildren().add(options.getGrid()); } @Override public @NotNull Pane getScene() { return layout; } @Override public void toggle(boolean isVisible) { this.isVisible = isVisible; } @Override public void interpret(String key) { if (isVisible) { switch (key) { case "w" -> options.activeUp(); case "s" -> options.activeDown(); case " " -> { switch (options.getActive().y) { case 0 -> startButton.run(); case 1 -> toggler.toggle(Toggler.ToggleableComponent.CONTROLS); default -> System.exit(0); } } } } } /** * Notify the title screen that the game has been lost. */ public void lost() { playLabel.setTextFill(ColorConstants.SNOW_ALT); playCount += 1; playLabel.setText(Integer.toString(playCount)); } /** * Gets the number displayed after 'mountain group' * * @return the number displayed after 'mountain group' */ public int getPlayCount() { return playCount; } } |
Changes to src/main/java/com/mg105/user_interface/MapDrawer.java.
1 2 3 4 5 | package com.mg105.user_interface; import com.mg105.interface_adapters.map.RoomInterpreterInterface; import com.mg105.interface_adapters.map.RoomTileType; import com.mg105.utils.MapConstants; | > > > | | | | < | < < < < < < < < | < | | < | | | | | | | < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | package com.mg105.user_interface; import com.mg105.interface_adapters.map.RoomInterpreterInterface; import com.mg105.interface_adapters.map.RoomState; import com.mg105.interface_adapters.map.RoomTileType; import com.mg105.utils.MapConstants; import com.mg105.utils.Textures; import com.mg105.utils.UIConstants; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.Pane; import javafx.scene.shape.Rectangle; import org.jetbrains.annotations.NotNull; import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.Map; /** * MapDrawer draws the map as a grid of tiles. */ public class MapDrawer implements PropertyChangeListener, Toggleable { private final @NotNull RoomInterpreterInterface interpreter; private final @NotNull Pane pane; private final @NotNull Map<RoomTileType, Image> tiles; private final @NotNull Map<String, Image> playerSprites; private boolean isVisible; /** * Create an instance of MapDrawer. * * @param interpreter the room interpreter used to format the data in an acceptable way for this class. */ public MapDrawer(@NotNull RoomInterpreterInterface interpreter) { this.interpreter = interpreter; isVisible = false; pane = new Pane(); tiles = new HashMap<>(6); tiles.put(RoomTileType.WALL, Textures.TILE_WALL); tiles.put(RoomTileType.WALL_FACE, Textures.TILE_WALL_FACE); tiles.put(RoomTileType.CHEST, Textures.TILE_CHEST); tiles.put(RoomTileType.CHEST_OPEN, Textures.TILE_CHEST_OPEN); tiles.put(RoomTileType.OPPONENT_SET, Textures.TILE_OPPONENT_SET); playerSprites = new HashMap<>(4); playerSprites.put("/sprites/A.png", Textures.PLAYER_A); playerSprites.put("/sprites/B.png", Textures.PLAYER_B); playerSprites.put("/sprites/C.png", Textures.PLAYER_C); playerSprites.put("/sprites/D.png", Textures.PLAYER_D); } /** * Get the scene that will be used to draw to. * * @return the scene that the MapDrawer will draw to. */ @Override public @NotNull Pane getScene() { return pane; } /** * Toggle the visibility of map drawer. In this case we need to make sure the map drawing is up-to-date. * * @param isVisible true if the map is now visible, false otherwise. */ |
︙ | ︙ | |||
90 91 92 93 94 95 96 | } /** * Redraw the current room. This method only needs to be called if something has changed in the underlying * current room. */ public void updateRoom() { | | | > > > > > > > > > | < | < | | | < > > | > > > > | | | < < | | | > > > | | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | } /** * Redraw the current room. This method only needs to be called if something has changed in the underlying * current room. */ public void updateRoom() { RoomState room = interpreter.getCurrentRoom(); pane.getChildren().clear(); Rectangle clip = new Rectangle(); clip.setX(0); clip.setY(0); clip.setWidth(UIConstants.CANVAS_SIZE); clip.setHeight(UIConstants.CANVAS_SIZE); pane.setClip(clip); final int offsetLayerOffset = -MapConstants.TILE_SIZE / 4; for (int y = 0; y < MapConstants.ROOM_SIZE + 1; y++) { for (int x = 0; x < MapConstants.ROOM_SIZE; x++) { pane.getChildren().add(makeTile( x * MapConstants.TILE_SIZE, y * MapConstants.TILE_SIZE, Textures.TILE_FLOOR )); if (room.getOffsetLayer()[y][x] != null) { pane.getChildren().add(makeTile( x * MapConstants.TILE_SIZE, y * MapConstants.TILE_SIZE + offsetLayerOffset, tiles.getOrDefault(room.getOffsetLayer()[y][x], Textures.MISSING) )); } if (room.getBaseLayer()[y][x] != null) { pane.getChildren().add(makeTile( x * MapConstants.TILE_SIZE, y * MapConstants.TILE_SIZE, tiles.getOrDefault(room.getBaseLayer()[y][x], Textures.MISSING) )); } if (room.getPlayerPosition().equals(new Point(x, y))) { pane.getChildren().add(makeTile( room.getPlayerPosition().x * MapConstants.TILE_SIZE, room.getPlayerPosition().y * MapConstants.TILE_SIZE, playerSprites.getOrDefault(room.getPlayerSprite(), Textures.MISSING) )); } } } } /** * Update the current room based on evt. * <p> * Note that none of the properties of evt are used. * * @param evt A PropertyChangeEvent object describing the event source * and the property that has changed. */ @Override public void propertyChange(PropertyChangeEvent evt) { if (!isVisible) { // As per the specification of Toggleable, we do nothing if we are not visible. return; } updateRoom(); } /** * Create a new tile appropriate given the size of the scene placed at a certain set of coordinates * * @param x the x position of the tile. * @param y the y position of the tile. * @param image the image used for the tile. * * @return the tile */ private @NotNull ImageView makeTile(int x, int y, @NotNull Image image) { ImageView tile = new ImageView(image); tile.setPreserveRatio(true); tile.setX(x); tile.setY(y); tile.setFitHeight(MapConstants.TILE_SIZE); tile.setFitWidth(MapConstants.TILE_SIZE); return tile; } } |
Changes to src/main/java/com/mg105/user_interface/MapGeneratorButton.java.
1 2 3 4 | package com.mg105.user_interface; import com.mg105.interface_adapters.Toggler; import com.mg105.interface_adapters.map.MapGeneratorInterpreterInterface; | < < | < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package com.mg105.user_interface; import com.mg105.interface_adapters.Toggler; import com.mg105.interface_adapters.map.MapGeneratorInterpreterInterface; import org.jetbrains.annotations.NotNull; /** * MapGeneratorButton acts as the event handler for the actual JavaFX button that will generate a map. */ public class MapGeneratorButton implements Runnable { private final @NotNull MapGeneratorInterpreterInterface interpreter; private final @NotNull Toggler toggler; /** * Create a new MapGeneratorButton. * * @param interpreter the interpreter for the map generator button * @param toggler the toggler used to close the user interface once pressed. */ public MapGeneratorButton(@NotNull MapGeneratorInterpreterInterface interpreter, @NotNull Toggler toggler) { this.interpreter = interpreter; this.toggler = toggler; } /** * This method is called when the button is pressed. It passes control to the appropriate interpreter. */ @Override public void run() { interpreter.generateMap(); toggler.toggle(Toggler.ToggleableComponent.MAIN_MENU); } } |
Changes to src/main/java/com/mg105/user_interface/MinimapDrawer.java.
1 2 3 4 | package com.mg105.user_interface; import com.mg105.interface_adapters.map.MinimapInterpreterInterface; import com.mg105.interface_adapters.map.MinimapRoomState; | | | | | < < < < | < | < < | | | | | | > > > > > > > | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | package com.mg105.user_interface; import com.mg105.interface_adapters.map.MinimapInterpreterInterface; import com.mg105.interface_adapters.map.MinimapRoomState; import com.mg105.utils.ColorConstants; import com.mg105.utils.UIConstants; import javafx.scene.layout.Pane; import javafx.scene.shape.Line; import javafx.scene.shape.Rectangle; import org.jetbrains.annotations.NotNull; import java.awt.*; /** * Draw the Minimap. */ public class MinimapDrawer implements Toggleable { private final @NotNull MinimapInterpreterInterface interpreter; private final @NotNull Pane pane; /** * Create a new MinimapDrawer. * * @param interpreter the MinimapInterpreter that will process room change data. */ public MinimapDrawer(@NotNull MinimapInterpreterInterface interpreter) { this.interpreter = interpreter; pane = new Pane(); } /** * Get the minimap scene. * * @return the minimap scene. */ @Override public @NotNull Pane getScene() { return pane; } /** * Toggle the minimap. * * @param isVisible true if the Toggleable is now visible, false otherwise. If false the Toggleable is expected * to do nothing on ANY user inputs. */ @Override public void toggle(boolean isVisible) { if (isVisible) { MinimapRoomState[][] map = interpreter.getMapSoFar(); Point currentPosition = interpreter.getCurrentPosition(); final int cellDimension = UIConstants.CANVAS_SIZE / Math.max(map.length, map[0].length); final int innerCellPadding = cellDimension / 6; final int topPadding = (UIConstants.CANVAS_SIZE - map.length * cellDimension) / 2; final int leftPadding = (UIConstants.CANVAS_SIZE - map[0].length * cellDimension) / 2; pane.getChildren().clear(); Rectangle background = new Rectangle(); background.setWidth(UIConstants.CANVAS_SIZE); background.setHeight(UIConstants.CANVAS_SIZE); background.setFill(ColorConstants.WALL); pane.getChildren().add(background); for (int y = 0; y < map.length; y++) { for (int x = 0; x < map[y].length; x++) { // Shading for the current room if (currentPosition.x == x && currentPosition.y == y) { Rectangle r = new Rectangle(); r.setX(leftPadding + x * cellDimension); r.setY(topPadding + y * cellDimension); r.setWidth(cellDimension); r.setHeight(cellDimension); r.setFill(ColorConstants.ACTIVE); pane.getChildren().add(r); } // Rectangle for each room if (map[y][x] == MinimapRoomState.EXPLORED) { // First we draw the middle square Rectangle r = new Rectangle(); r.setX(leftPadding + innerCellPadding + x * cellDimension); r.setY(topPadding + innerCellPadding + y * cellDimension); r.setWidth(cellDimension - 2 * innerCellPadding); r.setHeight(cellDimension - 2 * innerCellPadding); r.setFill(ColorConstants.SNOW); pane.getChildren().add(r); // Now we draw the lines that sick out final int xMidpoint = leftPadding + x * cellDimension + cellDimension / 2; final int yMidpoint = topPadding + y * cellDimension + cellDimension / 2; final int strokeWidth = cellDimension / 10; final int strokeWidthCorrection = strokeWidth / 2; // Line coming out the top if (y > 0 && map[y - 1][x] != null) { Line l = new Line(); l.setStartX(xMidpoint); l.setEndX(xMidpoint); l.setStartY(topPadding + y * cellDimension + innerCellPadding - strokeWidthCorrection); l.setEndY(topPadding + y * cellDimension + strokeWidthCorrection); l.setStroke(ColorConstants.SNOW_ALT); l.setStrokeWidth(strokeWidth); pane.getChildren().add(l); } // Line coming out the bottom if (y < map.length - 1 && map[y + 1][x] != null) { Line l = new Line(); l.setStartX(xMidpoint); l.setEndX(xMidpoint); l.setStartY(topPadding + y * cellDimension + cellDimension - innerCellPadding + strokeWidthCorrection); l.setEndY(topPadding + y * cellDimension + cellDimension - strokeWidthCorrection); l.setStroke(ColorConstants.SNOW_ALT); l.setStrokeWidth(strokeWidth); pane.getChildren().add(l); } // Line coming out the left if (x > 0 && map[y][x - 1] != null) { Line l = new Line(); l.setStartX(leftPadding + x * cellDimension + innerCellPadding - strokeWidthCorrection); l.setEndX(leftPadding + x * cellDimension + strokeWidthCorrection); l.setStartY(yMidpoint); l.setEndY(yMidpoint); l.setStroke(ColorConstants.SNOW_ALT); l.setStrokeWidth(strokeWidth); pane.getChildren().add(l); } // Line coming out the right if (x < map[0].length - 1 && map[y][x + 1] != null) { Line l = new Line(); l.setStartX(leftPadding + x * cellDimension + cellDimension - innerCellPadding + strokeWidthCorrection); l.setEndX(leftPadding + x * cellDimension + cellDimension - strokeWidthCorrection); l.setStartY(yMidpoint); l.setEndY(yMidpoint); l.setStroke(ColorConstants.SNOW_ALT); l.setStrokeWidth(strokeWidth); pane.getChildren().add(l); } } } } } } } |
Added src/main/java/com/mg105/user_interface/MultiselectGrid.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | package com.mg105.user_interface; import javafx.scene.Parent; import javafx.scene.layout.GridPane; import org.jetbrains.annotations.NotNull; import java.awt.*; import java.util.ArrayList; import java.util.List; /** * Multiselect grid represents a grid of keyboard controllable virtual buttons. * <p> * One option in each column can be selected, and there is only one active item. */ public class MultiselectGrid { private final @NotNull StandardComponentFactory componentFactory; private final @NotNull GridPane grid; private final @NotNull List<List<Parent>> columns; private final @NotNull List<Integer> selected; private @NotNull Point active; /** * Create a new MultiselectGrid. * * @param componentFactory the component factory used to create components */ public MultiselectGrid(@NotNull StandardComponentFactory componentFactory) { this.componentFactory = componentFactory; grid = new GridPane(); columns = new ArrayList<>(); selected = new ArrayList<>(); active = new Point(); } /** * Get the actual displayable grid. * * @return the grid. */ public @NotNull GridPane getGrid() { return grid; } /** * Update the visual representation of the grid. */ private void updateGrid() { grid.getChildren().clear(); for (int x = 0; x < columns.size(); x++) { for (int y = 0; y < columns.get(x).size(); y++) { grid.add(columns.get(x).get(y), x * 3 + 1, y); } } for (int x = 0; x < selected.size(); x++) { if (selected.get(x) != null) { grid.add(componentFactory.createActiveLabel(" [ "), x * 3, selected.get(x)); grid.add(componentFactory.createActiveLabel(" ] "), x * 3 + 2, selected.get(x)); } } grid.add(componentFactory.createActiveLabel(" > "), active.x * 3, active.y); grid.add(componentFactory.createActiveLabel(" < "), active.x * 3 + 2, active.y); } /** * Add a column to the end of the grid. * * @param items the contents of the column. */ public void addColumn(@NotNull Parent... items) { columns.add(new ArrayList<>(List.of(items))); updateGrid(); } /** * Add an item to the last column. * * @param item the item to add. */ public void addToColumn(@NotNull Parent item) { assert columns.size() > 0; columns.get(columns.size() - 1).add(item); updateGrid(); } /** * Remove the last column fromm the grid. */ public void popColumn() { assert columns.size() > 0; columns.remove(columns.size() - 1); if (active.x == columns.size()) { activeLeft(); } while (selected.size() >= columns.size()) { selected.remove(selected.size() - 1); } updateGrid(); } /** * Set next as the next active point. * * @param next the next active position. * * @return true if the active position changed, false otherwise. */ private boolean setActive(@NotNull Point next) { try { if (columns.get(next.x).get(next.y) != null && !active.equals(next)) { active = new Point(next); updateGrid(); return true; } else { return false; } } catch (IndexOutOfBoundsException e) { return false; } } /** * Get the active location. * * @return the active location. */ public @NotNull Point getActive() { return new Point(active); } /** * Move the active location up by 1 unit. * * @return true if the active position changed, false otherwise. */ public boolean activeUp() { Point next = new Point(active); next.y -= 1; return setActive(next); } /** * Move the active location down by 1 unit. * * @return true if the active position changed, false otherwise. */ public boolean activeDown() { Point next = new Point(active); next.y += 1; return setActive(next); } /** * Sets the active point to start or a point above it. * * @param start a starting point. * * @return true if the active position changed, false otherwise. */ private boolean setClosestActive(Point start) { if (setActive(start)) { return true; } else if (start.y >= 0) { start.y -= 1; return setClosestActive(start); } else { return false; } } /** * Move the active location left by 1 unit. * * @return true if the active position changed, false otherwise. */ public boolean activeLeft() { Point next = new Point(active); next.x -= 1; return setClosestActive(next); } /** * Move the active location right by 1 unit. * * @return true if the active position changed, false otherwise. */ public boolean activeRight() { Point next = new Point(active); next.x += 1; return setClosestActive(next); } /** * Set the current selected as active. */ public void selectActive() { while (selected.size() < columns.size()) { selected.add(null); } selected.set(active.x, active.y); } /** * Get the selected elements so far. * * @return the selected elements so far. */ public @NotNull List<Integer> getSelected() { return new ArrayList<>(selected); } /** * Reset the multiselect grid. */ public void reset() { active.x = 0; active.y = 0; selected.clear(); columns.clear(); } /** * Gets if this grid is empty. * * @return if the mutiselect grid contains no columns. */ public boolean isEmpty() { return columns.isEmpty(); } } |
Changes to src/main/java/com/mg105/user_interface/ReplayGeneratorButton.java.
1 2 3 4 | package com.mg105.user_interface; import com.mg105.interface_adapters.ReplayGeneratorInterpreter; import com.mg105.interface_adapters.Toggler; | < < | < < < < < | > | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package com.mg105.user_interface; import com.mg105.interface_adapters.ReplayGeneratorInterpreter; import com.mg105.interface_adapters.Toggler; import org.jetbrains.annotations.NotNull; /** * ReplayGeneratorButton acts as the event handler for the actual JavaFx button that will replay the game. */ public class ReplayGeneratorButton implements Runnable { private final @NotNull ReplayGeneratorInterpreter interpreter; private final @NotNull Toggler toggler; private final @NotNull Toggler.ToggleableComponent componentToToggle; /** * Create a new ReplayGeneratorButton. * * @param interpreter the interpreter for the replay generator button. * @param toggler the toggler used to close the user interface once pressed. * @param componentToToggle the component that needs to be toggled */ public ReplayGeneratorButton(@NotNull ReplayGeneratorInterpreter interpreter, @NotNull Toggler toggler, Toggler.@NotNull ToggleableComponent componentToToggle) { this.interpreter = interpreter; this.toggler = toggler; this.componentToToggle = componentToToggle; } @Override public void run() { interpreter.replayGenerateMap(); toggler.toggle(componentToToggle); toggler.toggle(Toggler.ToggleableComponent.MAIN_MENU); } } |
Changes to src/main/java/com/mg105/user_interface/SceneController.java.
1 2 3 4 5 6 7 8 9 10 11 12 | package com.mg105.user_interface; import com.mg105.interface_adapters.Toggler; import javafx.stage.Stage; import org.jetbrains.annotations.NotNull; import java.util.Map; import java.util.Stack; /** * SceneController acts as the central coordination mechanism for the user interface. */ | > > > > > > > > > | > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.interface_adapters.Toggler; import com.mg105.utils.UIConstants; import javafx.animation.AnimationTimer; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import org.jetbrains.annotations.NotNull; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import java.util.Stack; /** * SceneController acts as the central coordination mechanism for the user interface. */ public class SceneController implements Toggler, Alerter, InputControllable { private final @NotNull Stage primaryStage; private final @NotNull StackPane layout; private final @NotNull Map<ToggleableComponent, Toggleable> components; private final @NotNull ToggleableComponent defaultComponent; private final @NotNull Stack<ToggleableComponent> activeComponents; private final @NotNull Queue<Parent> alerts; /** * Create a new scene controller * * @param primaryStage the JavaFX stage to draw on. * @param components all the possible components in the system. Components should contain an entry for each * item in the ToggleableComponent enum. * @param defaultComponent the component that will be shown if there's nothing else to show. */ public SceneController(@NotNull Stage primaryStage, @NotNull Map<ToggleableComponent, Toggleable> components, @NotNull ToggleableComponent defaultComponent) { this.primaryStage = primaryStage; this.defaultComponent = defaultComponent; this.components = components; activeComponents = new Stack<>(); activeComponents.push(defaultComponent); layout = new StackPane(); layout.setPrefSize(UIConstants.CANVAS_SIZE, UIConstants.CANVAS_SIZE); layout.setMaxSize(UIConstants.CANVAS_SIZE, UIConstants.CANVAS_SIZE); layout.getChildren().add(new Pane()); primaryStage.setScene(new Scene( layout, UIConstants.CANVAS_SIZE, UIConstants.CANVAS_SIZE )); alerts = new LinkedList<>(); } /** * Toggle the component. i.e. switch it between invisible and visible. * * @param component the component to toggle. */ |
︙ | ︙ | |||
53 54 55 56 57 58 59 | } if (activeComponents.isEmpty()) { activeComponents.push(defaultComponent); } components.get(activeComponents.peek()).toggle(true); | > | > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | } if (activeComponents.isEmpty()) { activeComponents.push(defaultComponent); } components.get(activeComponents.peek()).toggle(true); Pane nextComponent = components.get(activeComponents.peek()).getScene(); layout.getChildren().set(0, nextComponent); // force alignment to top left and try to force appropriate sizing nextComponent.setTranslateX(-nextComponent.getLayoutX()); nextComponent.setTranslateY(-nextComponent.getLayoutY()); nextComponent.setPrefSize(UIConstants.CANVAS_SIZE, UIConstants.CANVAS_SIZE); nextComponent.setMaxSize(UIConstants.CANVAS_SIZE, UIConstants.CANVAS_SIZE); primaryStage.setTitle("Mountain Group 105: " + activeComponents.peek()); } /** * Get the current visible component. * * @return the current visible component. */ @Override public @NotNull ToggleableComponent getCurrentComponent() { if (layout.getChildren().size() > 1) { return ToggleableComponent.ALERT; } else { return activeComponents.peek(); } } /** * Display the alert on top of the current screen. * * @param body the alert. */ @Override public void displayAlert(@NotNull Parent body) { alerts.add(body); if (layout.getChildren().size() == 1) { layout.getChildren().add(alerts.remove()); } } /** * Interpret a key. Used for the sole purpose of dismissing alerts. * * @param key the key this component should process. */ @Override public void interpret(String key) { layout.getChildren().remove(1); if (!alerts.isEmpty()) { new AnimationTimer() { private long last = 0; @Override public void handle(long now) { if (last == 0) { last = now; } else if (now - last > 170 * 1e6) { layout.getChildren().add(alerts.remove()); stop(); } } }.start(); } } } |
Added src/main/java/com/mg105/user_interface/StandardComponentFactory.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | package com.mg105.user_interface; import com.mg105.utils.ColorConstants; import com.mg105.utils.UIConstants; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.effect.DropShadow; import javafx.scene.layout.*; import javafx.scene.paint.Color; import org.jetbrains.annotations.NotNull; /** * The StandardComponentFactory is used to delegate creation of JavaFX UI components so that they all look coherent. */ public class StandardComponentFactory { private final @NotNull Alerter alerter; /** * Create a StandardComponentFactory. * * @param alerter the interface used to alert. */ public StandardComponentFactory(@NotNull Alerter alerter) { this.alerter = alerter; } /** * Create a new background. * * @param isLight if the background should be light (otherwise it is dark). * * @return the new background. */ public @NotNull Background createBackground(boolean isLight) { Color backgroundColor = (isLight ? ColorConstants.SNOW : ColorConstants.WALL); return new Background(new BackgroundFill(backgroundColor, null, null)); } /** * Create a label. * * @param text the label text. * @param isLight if the background is light (otherwise it is presumed dark). * * @return a new label. */ public @NotNull Label createLabel(@NotNull String text, boolean isLight) { Label l = new Label(text); l.setTextFill((isLight ? ColorConstants.WALL : ColorConstants.SNOW)); l.setFont(UIConstants.FONT_NORMAL); return l; } /** * Create an active label. * * @param text the text of the label. * * @return a new label. */ public @NotNull Label createActiveLabel(@NotNull String text) { Label l = createLabel(text, true); l.setTextFill(ColorConstants.ACTIVE); return l; } /** * Create a title label. * * @param text the label text. * @param isLight if the background is light (otherwise it is presumed dark). * * @return a new label. */ public @NotNull Label createTitleLabel(@NotNull String text, boolean isLight) { Label l = createLabel(text, isLight); l.setFont(UIConstants.TITLE_FONT); return l; } /** * Create a label representing a number. * * @param num the label number. * @param isLight if the background is light (otherwise it is presumed dark). * * @return a new label. */ public @NotNull Label createNumberedLabel(int num, boolean isLight) { Label l = createLabel((num > 0 ? "+" : "") + Integer.toString(num), isLight); if (num > 0) { l.setTextFill(Color.GREEN); } else if (num < 0) { l.setTextFill(Color.RED); } return l; } /** * Create a subtitle label. * * @param text the label text. * @param isLight if the background is light (otherwise it is presumed dark). * * @return a new label. */ public @NotNull Label createSubtitleLabel(@NotNull String text, boolean isLight) { Label l = createLabel(text, isLight); l.setFont(UIConstants.SUBTITLE_FONT); return l; } /** * Create a new multiselect grid. * * @return a new multiselect grid. */ public @NotNull MultiselectGrid createMultiselectGrid() { return new MultiselectGrid(this); } /** * Create a growing spacer. * * @return a new spacer */ public @NotNull Region createSpacer() { Region r = new Region(); r.setPrefSize(UIConstants.SINGLE_GAP, UIConstants.SINGLE_GAP); VBox.setVgrow(r, Priority.ALWAYS); HBox.setHgrow(r, Priority.ALWAYS); return r; } /** * Create a larger spacer. * * @return a larger spacer. */ public @NotNull Region createFixedDoubleSpacer() { Region r = new Region(); r.setPrefSize(2 * UIConstants.SINGLE_GAP, 2 * UIConstants.SINGLE_GAP); return r; } /** * Create a small spacer. * * @return a smaller spacer. */ public @NotNull Region createFixedHalfSpacer() { Region r = new Region(); r.setPrefSize((int)(UIConstants.SINGLE_GAP / 2), (int)(UIConstants.SINGLE_GAP / 2)); return r; } /** * Create an alerting modal dialog. * * @param text the text of the alert. * @param isLight if the background should be light. */ public void createAlert(@NotNull String text, boolean isLight) { DropShadow shadow = new DropShadow(); shadow.setOffsetY((int)(UIConstants.SINGLE_GAP / 2)); shadow.setColor(Color.color(0.4, 0.4, 0.5)); TilePane pane = new TilePane(Orientation.VERTICAL); pane.setBackground(createBackground(isLight)); pane.setAlignment(Pos.CENTER); pane.setPadding(createInsets()); pane.setVgap(UIConstants.SINGLE_GAP); pane.setMaxWidth((int)(UIConstants.CANVAS_SIZE / 2)); pane.setMaxHeight((int)(UIConstants.CANVAS_SIZE / 2)); pane.setEffect(shadow); pane.setBorder(new Border(new BorderStroke( (isLight ? ColorConstants.WALL : ColorConstants.SNOW), BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths((int)(UIConstants.SINGLE_GAP / 4)) ))); Label message = createLabel(text, isLight); message.setWrapText(true); pane.getChildren().add(message); MultiselectGrid okButton = createMultiselectGrid(); okButton.getGrid().setAlignment(Pos.CENTER); okButton.addColumn(createLabel("Ok", isLight)); pane.getChildren().add(okButton.getGrid()); alerter.displayAlert(pane); } /** * Create standard insets. * * @return standard insets. */ public @NotNull Insets createInsets() { return new Insets(UIConstants.SINGLE_GAP); } } |
Changes to src/main/java/com/mg105/user_interface/Toggleable.java.
1 2 | package com.mg105.user_interface; | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package com.mg105.user_interface; import javafx.scene.layout.Pane; import org.jetbrains.annotations.NotNull; /** * A user interface component that can be toggled on or off depending on its visibility. */ public interface Toggleable { /** * Get the scene of this toggleable object. It is this scene that will be displayed. * * @return the scene to be displayed. */ @NotNull Pane getScene(); /** * Set the visibility of this component. * * @param isVisible true if the Toggleable is now visible, false otherwise. If false the Toggleable is expected * to do nothing on ANY user inputs. */ |
︙ | ︙ |
Deleted src/main/java/com/mg105/user_interface/TutorialTextDisplay.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/main/java/com/mg105/user_interface/TutorialTextWindow.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to src/main/java/com/mg105/user_interface/WalkingMenu.java.
1 2 3 4 | package com.mg105.user_interface; import com.mg105.interface_adapters.WalkVisController; import com.mg105.utils.PartyConstants; | > > | | | | | < < < | | | | | < < | | | > > | > | < < | < | < < < | < < < | < < < < < < < < < < < | < < | < < | < < < < < < < | < < < < < < < < < < > > | < > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.interface_adapters.Toggler; import com.mg105.interface_adapters.WalkVisController; import com.mg105.utils.PartyConstants; import com.mg105.utils.Textures; import com.mg105.utils.UIConstants; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.image.ImageView; import javafx.scene.layout.Pane; import javafx.scene.layout.TilePane; import org.jetbrains.annotations.NotNull; /** * This class uses JavaFX and is displayed when the user wants to change the walking character sprite. */ public class WalkingMenu implements InputControllable, Toggleable { private final WalkVisController controller; private final TilePane layout; private final @NotNull ImageView nextSelected; private final @NotNull MultiselectGrid options; private final @NotNull Toggler toggler; /** * Creates a new WalkingMenu with reference to a controller class. * Sets up UI elements. * * @param controller the WalkVisController to be referred to. * @param toggler the toggler used to toggle this menu. * @param componentFactory the component factory used to create UI components */ public WalkingMenu(@NotNull WalkVisController controller, @NotNull Toggler toggler, @NotNull StandardComponentFactory componentFactory) { this.controller = controller; this.toggler = toggler; layout = new TilePane(Orientation.VERTICAL); layout.setVgap(UIConstants.SINGLE_GAP); layout.setAlignment(Pos.CENTER); layout.setBackground(componentFactory.createBackground(true)); nextSelected = new ImageView(Textures.PLAYER_A); layout.getChildren().add(nextSelected); options = componentFactory.createMultiselectGrid(); options.addColumn(); for (String name : PartyConstants.ALL_PARTY_MEMBER_NAMES) { options.addToColumn(componentFactory.createLabel(name, true)); } layout.getChildren().add(options.getGrid()); } /** * Get the scene of this toggleable object. It is this scene that will be displayed. * * @return the scene to be displayed. */ @Override public @NotNull Pane getScene() { return layout; } /** * Set the visibility of this component. * * @param isVisible true if the Toggleable is now visible, false otherwise. If false the Toggleable is expected * to do nothing on ANY user inputs. */ @Override public void toggle(boolean isVisible) { //The menu should ideally remain the same. } @Override public void interpret(String key) { switch (key) { case "w" -> { options.activeUp(); updateImage(options.getActive().y); } case "s" -> { options.activeDown(); updateImage(options.getActive().y); } case " " -> { controller.changePlayerSprite(Character.toString('A' + options.getActive().y)); toggler.toggle(Toggler.ToggleableComponent.WALK_MENU); } } } /** * Update the visual image. * * @param id the id of the new image. */ private void updateImage(int id) { nextSelected.setImage(switch (id) { case 0 -> Textures.PLAYER_A; case 1 -> Textures.PLAYER_B; case 2 -> Textures.PLAYER_C; case 3 -> Textures.PLAYER_D; default -> Textures.MISSING; }); } } |
Changes to src/main/java/com/mg105/user_interface/WinMenu.java.
1 2 | package com.mg105.user_interface; | > | | | < | < | > > > > | | > > | > > | | < > > > | < < > < | > > > > > > > | < | | > | > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | package com.mg105.user_interface; import com.mg105.interface_adapters.InputControllable; import com.mg105.utils.UIConstants; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.layout.Pane; import javafx.scene.layout.TilePane; import org.jetbrains.annotations.NotNull; /** * A menu that is shown to the player after they have reached the final room */ public class WinMenu implements Toggleable, InputControllable { private boolean isVisible = false; private final @NotNull Runnable replayButton; private final @NotNull TilePane layout; private final @NotNull MainMenu mainMenu; private final @NotNull Label message; /** * Shows a button that congratulates you and lets you replay the game * * @param replayButton the button responsible for replaying the game * @param componentFactory the component factory used to create UI components * @param mainMenu the main menu component. */ public WinMenu(@NotNull Runnable replayButton, @NotNull StandardComponentFactory componentFactory, @NotNull MainMenu mainMenu) { this.replayButton = replayButton; this.mainMenu = mainMenu; layout = new TilePane(Orientation.VERTICAL); layout.setBackground(componentFactory.createBackground(true)); layout.setVgap(UIConstants.SINGLE_GAP); layout.setAlignment(Pos.CENTER); message = componentFactory.createLabel("UNSET", true); layout.getChildren().addAll( componentFactory.createTitleLabel("Summit Conquered", true), message ); MultiselectGrid options = componentFactory.createMultiselectGrid(); options.addColumn(componentFactory.createLabel("Now what...", true)); options.getGrid().setAlignment(Pos.CENTER); layout.getChildren().add(options.getGrid()); } /** * Get scene of the WinMenu * * @return the scene that WinMenu is in */ @Override public @NotNull Pane getScene() { return layout; } /** * Toggle the WinMenu * * @param isVisible whether the menu is visible */ @Override public void toggle(boolean isVisible) { this.isVisible = isVisible; if (isVisible) { message.setText("The venerated Mountain Group " + mainMenu.getPlayCount() + " does it again!"); } } @Override public void interpret(String key) { if (isVisible && key.equals(" ")) { replayButton.run(); } } } |
Added src/main/java/com/mg105/utils/ColorConstants.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.mg105.utils; import javafx.scene.paint.Color; import org.jetbrains.annotations.NotNull; /** * The colour palette of the game. */ public class ColorConstants { /** * The colour used for wall (out of bounds) areas. */ public static final @NotNull Color WALL = Color.rgb(38, 44, 68); /** * The colour used for snow. */ public static final @NotNull Color SNOW = Color.rgb(205, 220, 255); /** * The accent colour used for snow. */ public static final @NotNull Color SNOW_ALT = Color.rgb(135, 160, 227); /** * The colour used when something is active. */ public static final @NotNull Color ACTIVE = Color.rgb(200, 150, 61); } |
Added src/main/java/com/mg105/utils/Textures.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package com.mg105.utils; import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import java.util.Objects; /** * The textures (images) that are used in the game. */ public class Textures { /** * The texture if none is found. */ public static @NotNull Image MISSING = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/tiles/missing.png"))); /** * The floor image. */ public static @NotNull Image TILE_FLOOR = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/tiles/floor.png"))); /** * The wall image. */ public static @NotNull Image TILE_WALL = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/tiles/wall.png"))); /** * The wall's face image. */ public static @NotNull Image TILE_WALL_FACE = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/tiles/wall_face.png"))); /** * The tile used for a closed chest. */ public static @NotNull Image TILE_CHEST = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/tiles/chest.png"))); /** * The tile used for an open chest. */ public static @NotNull Image TILE_CHEST_OPEN = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/tiles/chest_open.png"))); /** * The tile used for an opponent set. */ public static @NotNull Image TILE_OPPONENT_SET = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/tiles/battle.png"))); /** * The tile for player A. */ public static @NotNull Image PLAYER_A = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/sprites/A.png"))); /** * The tile for player B. */ public static @NotNull Image PLAYER_B = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/sprites/B.png"))); /** * The tile for player C. */ public static @NotNull Image PLAYER_C = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/sprites/C.png"))); /** * The tile for player D. */ public static @NotNull Image PLAYER_D = new Image(Objects.requireNonNull(Textures.class.getResourceAsStream("/sprites/D.png"))); } |
Deleted src/main/java/com/mg105/utils/TutorialTexts.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/main/java/com/mg105/utils/UIConstants.java.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package com.mg105.utils; import javafx.scene.text.Font; /** * General user interface constants. */ public class UIConstants { /** * The width and height of the entire canvas. */ public static final int CANVAS_SIZE = MapConstants.TILE_SIZE * MapConstants.ROOM_SIZE; /** * The width of a line gap. */ public static final int SINGLE_GAP = CANVAS_SIZE / 32; /** * The standard font. */ public static final Font FONT_NORMAL = Font.loadFont(UIConstants.class.getResourceAsStream("/fonts/Strait-Regular.ttf"), 19); /** * The title font. */ public static final Font TITLE_FONT = Font.loadFont(UIConstants.class.getResourceAsStream("/fonts/Strait-Regular.ttf"), 30); /** * The title font used only for the number. */ public static final Font TITLE_FONT_ALT = Font.loadFont(UIConstants.class.getResourceAsStream("/fonts/HomemadeApple-Regular.ttf"), 35); /** * The subtitle font. */ public static final Font SUBTITLE_FONT = Font.loadFont(UIConstants.class.getResourceAsStream("/fonts/Strait-Regular.ttf"), 22); /** * The keyboard font. */ public static final Font FONT_MONO = Font.loadFont(UIConstants.class.getResourceAsStream("/fonts/IBMPlexMono-Medium.ttf"), 19); } |
Added src/main/resources/fonts/HomemadeApple-Regular.ttf.
cannot compute difference between binary files
Added src/main/resources/fonts/IBMPlexMono-Medium.ttf.
cannot compute difference between binary files
Added src/main/resources/fonts/Strait-Regular.ttf.
cannot compute difference between binary files
Changes to src/main/resources/tiles/battle.png.
cannot compute difference between binary files
Changes to src/main/resources/tiles/chest.png.
cannot compute difference between binary files
Changes to src/main/resources/tiles/chest_open.png.
cannot compute difference between binary files
Deleted src/main/resources/tiles/exit.png.
cannot compute difference between binary files
Changes to src/main/resources/tiles/wall_face.png.
cannot compute difference between binary files
Deleted src/test/java/com/mg105/entities/GiveTutorialTest.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/test/java/com/mg105/interface_adapters/map/RoomInterpreterTest.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/test/java/com/mg105/use_cases/tutorial/TutorialInteractorTest.java.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |