diff --git a/res/img/gameover/gameover.png b/res/img/gameover/gameover.png new file mode 100644 index 0000000..e3462b4 Binary files /dev/null and b/res/img/gameover/gameover.png differ diff --git a/res/img/heart/heart0.png b/res/img/heart/heart0.png new file mode 100755 index 0000000..2584c44 Binary files /dev/null and b/res/img/heart/heart0.png differ diff --git a/res/img/heart/heart1.png b/res/img/heart/heart1.png new file mode 100755 index 0000000..7155f3c Binary files /dev/null and b/res/img/heart/heart1.png differ diff --git a/res/img/heart/heart2.png b/res/img/heart/heart2.png new file mode 100755 index 0000000..c951146 Binary files /dev/null and b/res/img/heart/heart2.png differ diff --git a/res/img/heart/heart3.png b/res/img/heart/heart3.png new file mode 100755 index 0000000..7272547 Binary files /dev/null and b/res/img/heart/heart3.png differ diff --git a/res/img/heart/heart4.png b/res/img/heart/heart4.png new file mode 100755 index 0000000..5ff6020 Binary files /dev/null and b/res/img/heart/heart4.png differ diff --git a/res/img/key/key.png b/res/img/key/key.png new file mode 100644 index 0000000..29d1f7e Binary files /dev/null and b/res/img/key/key.png differ diff --git a/res/img/portal/red/000.png b/res/img/portal/red/000.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/000.png and /dev/null differ diff --git a/res/img/portal/red/001.png b/res/img/portal/red/001.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/001.png and /dev/null differ diff --git a/res/img/portal/red/002.png b/res/img/portal/red/002.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/002.png and /dev/null differ diff --git a/res/img/portal/red/003.png b/res/img/portal/red/003.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/003.png and /dev/null differ diff --git a/res/img/portal/red/004.png b/res/img/portal/red/004.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/004.png and /dev/null differ diff --git a/res/img/portal/red/005.png b/res/img/portal/red/005.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/005.png and /dev/null differ diff --git a/res/img/portal/red/006.png b/res/img/portal/red/006.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/006.png and /dev/null differ diff --git a/res/img/portal/red/007.png b/res/img/portal/red/007.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/007.png and /dev/null differ diff --git a/res/img/portal/red/008.png b/res/img/portal/red/008.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/008.png and /dev/null differ diff --git a/res/img/portal/red/009.png b/res/img/portal/red/009.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/009.png and /dev/null differ diff --git a/res/img/portal/red/010.png b/res/img/portal/red/010.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/010.png and /dev/null differ diff --git a/res/img/portal/red/011.png b/res/img/portal/red/011.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/011.png and /dev/null differ diff --git a/res/img/portal/red/012.png b/res/img/portal/red/012.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/012.png and /dev/null differ diff --git a/res/img/portal/red/013.png b/res/img/portal/red/013.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/013.png and /dev/null differ diff --git a/res/img/portal/red/014.png b/res/img/portal/red/014.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/014.png and /dev/null differ diff --git a/res/img/portal/red/015.png b/res/img/portal/red/015.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/015.png and /dev/null differ diff --git a/res/img/portal/red/016.png b/res/img/portal/red/016.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/016.png and /dev/null differ diff --git a/res/img/portal/red/017.png b/res/img/portal/red/017.png deleted file mode 100644 index bd78c1c..0000000 Binary files a/res/img/portal/red/017.png and /dev/null differ diff --git a/res/img/portal/red/018.png b/res/img/portal/red/018.png deleted file mode 100644 index 4836d4e..0000000 Binary files a/res/img/portal/red/018.png and /dev/null differ diff --git a/res/img/portal/red/019.png b/res/img/portal/red/019.png deleted file mode 100644 index 822665b..0000000 Binary files a/res/img/portal/red/019.png and /dev/null differ diff --git a/res/img/portal/red/020.png b/res/img/portal/red/020.png deleted file mode 100644 index eb89965..0000000 Binary files a/res/img/portal/red/020.png and /dev/null differ diff --git a/res/img/portal/red/021.png b/res/img/portal/red/021.png deleted file mode 100644 index 032ffab..0000000 Binary files a/res/img/portal/red/021.png and /dev/null differ diff --git a/res/img/portal/red/022.png b/res/img/portal/red/022.png deleted file mode 100644 index f4064bc..0000000 Binary files a/res/img/portal/red/022.png and /dev/null differ diff --git a/res/img/portal/red/023.png b/res/img/portal/red/023.png deleted file mode 100644 index 818818f..0000000 Binary files a/res/img/portal/red/023.png and /dev/null differ diff --git a/res/img/portal/red/024.png b/res/img/portal/red/024.png deleted file mode 100644 index a020c17..0000000 Binary files a/res/img/portal/red/024.png and /dev/null differ diff --git a/res/img/portal/red/025.png b/res/img/portal/red/025.png deleted file mode 100644 index 8217c87..0000000 Binary files a/res/img/portal/red/025.png and /dev/null differ diff --git a/res/img/portal/red/026.png b/res/img/portal/red/026.png deleted file mode 100644 index 4b2b53d..0000000 Binary files a/res/img/portal/red/026.png and /dev/null differ diff --git a/res/img/portal/red/027.png b/res/img/portal/red/027.png deleted file mode 100644 index ec1c516..0000000 Binary files a/res/img/portal/red/027.png and /dev/null differ diff --git a/res/img/portal/red/028.png b/res/img/portal/red/028.png deleted file mode 100644 index 7395d5f..0000000 Binary files a/res/img/portal/red/028.png and /dev/null differ diff --git a/res/img/portal/red/029.png b/res/img/portal/red/029.png deleted file mode 100644 index 45c3a91..0000000 Binary files a/res/img/portal/red/029.png and /dev/null differ diff --git a/res/img/portal/red/030.png b/res/img/portal/red/030.png deleted file mode 100644 index c5c8f87..0000000 Binary files a/res/img/portal/red/030.png and /dev/null differ diff --git a/res/img/portal/red/031.png b/res/img/portal/red/031.png deleted file mode 100644 index a044db7..0000000 Binary files a/res/img/portal/red/031.png and /dev/null differ diff --git a/res/img/portal/red/032.png b/res/img/portal/red/032.png deleted file mode 100644 index 16a803a..0000000 Binary files a/res/img/portal/red/032.png and /dev/null differ diff --git a/res/img/portal/red/033.png b/res/img/portal/red/033.png deleted file mode 100644 index 714d224..0000000 Binary files a/res/img/portal/red/033.png and /dev/null differ diff --git a/res/img/portal/red/034.png b/res/img/portal/red/034.png deleted file mode 100644 index 0527b75..0000000 Binary files a/res/img/portal/red/034.png and /dev/null differ diff --git a/res/img/portal/red/035.png b/res/img/portal/red/035.png deleted file mode 100644 index cb82f6f..0000000 Binary files a/res/img/portal/red/035.png and /dev/null differ diff --git a/res/img/portal/red/036.png b/res/img/portal/red/036.png deleted file mode 100644 index 6c5d48a..0000000 Binary files a/res/img/portal/red/036.png and /dev/null differ diff --git a/res/img/portal/red/037.png b/res/img/portal/red/037.png deleted file mode 100644 index 2106daf..0000000 Binary files a/res/img/portal/red/037.png and /dev/null differ diff --git a/res/img/portal/red/038.png b/res/img/portal/red/038.png deleted file mode 100644 index 909a3c1..0000000 Binary files a/res/img/portal/red/038.png and /dev/null differ diff --git a/res/img/portal/red/039.png b/res/img/portal/red/039.png deleted file mode 100644 index c6f3cf0..0000000 Binary files a/res/img/portal/red/039.png and /dev/null differ diff --git a/res/img/portal/red/040.png b/res/img/portal/red/040.png deleted file mode 100644 index 848f6e7..0000000 Binary files a/res/img/portal/red/040.png and /dev/null differ diff --git a/res/img/portal/red/041.png b/res/img/portal/red/041.png deleted file mode 100644 index d58d692..0000000 Binary files a/res/img/portal/red/041.png and /dev/null differ diff --git a/res/img/portal/red/042.png b/res/img/portal/red/042.png deleted file mode 100644 index 205d38d..0000000 Binary files a/res/img/portal/red/042.png and /dev/null differ diff --git a/res/img/portal/red/043.png b/res/img/portal/red/043.png deleted file mode 100644 index 82c6ab7..0000000 Binary files a/res/img/portal/red/043.png and /dev/null differ diff --git a/res/img/portal/red/044.png b/res/img/portal/red/044.png deleted file mode 100644 index ce39117..0000000 Binary files a/res/img/portal/red/044.png and /dev/null differ diff --git a/res/img/portal/red/045.png b/res/img/portal/red/045.png deleted file mode 100644 index 13562ac..0000000 Binary files a/res/img/portal/red/045.png and /dev/null differ diff --git a/res/img/portal/red/046.png b/res/img/portal/red/046.png deleted file mode 100644 index 8c253db..0000000 Binary files a/res/img/portal/red/046.png and /dev/null differ diff --git a/res/img/portal/red/047.png b/res/img/portal/red/047.png deleted file mode 100644 index 1fec220..0000000 Binary files a/res/img/portal/red/047.png and /dev/null differ diff --git a/res/img/portal/red/048.png b/res/img/portal/red/048.png deleted file mode 100644 index 6ee2ac0..0000000 Binary files a/res/img/portal/red/048.png and /dev/null differ diff --git a/res/img/portal/red/049.png b/res/img/portal/red/049.png deleted file mode 100644 index d6529ee..0000000 Binary files a/res/img/portal/red/049.png and /dev/null differ diff --git a/res/img/portal/red/050.png b/res/img/portal/red/050.png deleted file mode 100644 index da668f1..0000000 Binary files a/res/img/portal/red/050.png and /dev/null differ diff --git a/res/img/portal/red/051.png b/res/img/portal/red/051.png deleted file mode 100644 index dff620a..0000000 Binary files a/res/img/portal/red/051.png and /dev/null differ diff --git a/res/img/portal/red/052.png b/res/img/portal/red/052.png deleted file mode 100644 index d435864..0000000 Binary files a/res/img/portal/red/052.png and /dev/null differ diff --git a/res/img/portal/red/053.png b/res/img/portal/red/053.png deleted file mode 100644 index 0ba2555..0000000 Binary files a/res/img/portal/red/053.png and /dev/null differ diff --git a/res/img/portal/red/054.png b/res/img/portal/red/054.png deleted file mode 100644 index 8aa576a..0000000 Binary files a/res/img/portal/red/054.png and /dev/null differ diff --git a/res/img/portal/red/055.png b/res/img/portal/red/055.png deleted file mode 100644 index f10958a..0000000 Binary files a/res/img/portal/red/055.png and /dev/null differ diff --git a/res/img/portal/red/056.png b/res/img/portal/red/056.png deleted file mode 100644 index d254f81..0000000 Binary files a/res/img/portal/red/056.png and /dev/null differ diff --git a/res/img/portal/red/057.png b/res/img/portal/red/057.png deleted file mode 100644 index 7c71a8b..0000000 Binary files a/res/img/portal/red/057.png and /dev/null differ diff --git a/res/img/portal/red/058.png b/res/img/portal/red/058.png deleted file mode 100644 index 73961e7..0000000 Binary files a/res/img/portal/red/058.png and /dev/null differ diff --git a/res/img/portal/red/059.png b/res/img/portal/red/059.png deleted file mode 100644 index e7182a7..0000000 Binary files a/res/img/portal/red/059.png and /dev/null differ diff --git a/res/img/portal/red/060.png b/res/img/portal/red/060.png deleted file mode 100644 index 68a42cb..0000000 Binary files a/res/img/portal/red/060.png and /dev/null differ diff --git a/res/img/portal/red/061.png b/res/img/portal/red/061.png deleted file mode 100644 index 82d635f..0000000 Binary files a/res/img/portal/red/061.png and /dev/null differ diff --git a/res/img/portal/red/062.png b/res/img/portal/red/062.png deleted file mode 100644 index b876a06..0000000 Binary files a/res/img/portal/red/062.png and /dev/null differ diff --git a/res/img/portal/red/063.png b/res/img/portal/red/063.png deleted file mode 100644 index d879f86..0000000 Binary files a/res/img/portal/red/063.png and /dev/null differ diff --git a/res/img/portal/red/064.png b/res/img/portal/red/064.png deleted file mode 100644 index d2c63fe..0000000 Binary files a/res/img/portal/red/064.png and /dev/null differ diff --git a/res/img/portal/red/065.png b/res/img/portal/red/065.png deleted file mode 100644 index d48b027..0000000 Binary files a/res/img/portal/red/065.png and /dev/null differ diff --git a/res/img/portal/red/066.png b/res/img/portal/red/066.png deleted file mode 100644 index b8f9ef9..0000000 Binary files a/res/img/portal/red/066.png and /dev/null differ diff --git a/res/img/portal/red/067.png b/res/img/portal/red/067.png deleted file mode 100644 index f4eb004..0000000 Binary files a/res/img/portal/red/067.png and /dev/null differ diff --git a/res/img/portal/red/068.png b/res/img/portal/red/068.png deleted file mode 100644 index 960e39c..0000000 Binary files a/res/img/portal/red/068.png and /dev/null differ diff --git a/res/img/portal/red/069.png b/res/img/portal/red/069.png deleted file mode 100644 index ae77ab4..0000000 Binary files a/res/img/portal/red/069.png and /dev/null differ diff --git a/res/img/portal/red/070.png b/res/img/portal/red/070.png deleted file mode 100644 index 8e2bf33..0000000 Binary files a/res/img/portal/red/070.png and /dev/null differ diff --git a/res/img/portal/red/071.png b/res/img/portal/red/071.png deleted file mode 100644 index a8fa590..0000000 Binary files a/res/img/portal/red/071.png and /dev/null differ diff --git a/res/img/portal/red/072.png b/res/img/portal/red/072.png deleted file mode 100644 index 7189337..0000000 Binary files a/res/img/portal/red/072.png and /dev/null differ diff --git a/res/img/portal/red/073.png b/res/img/portal/red/073.png deleted file mode 100644 index dba5b3e..0000000 Binary files a/res/img/portal/red/073.png and /dev/null differ diff --git a/res/img/portal/red/074.png b/res/img/portal/red/074.png deleted file mode 100644 index e0c294b..0000000 Binary files a/res/img/portal/red/074.png and /dev/null differ diff --git a/res/img/portal/red/075.png b/res/img/portal/red/075.png deleted file mode 100644 index bde6f1c..0000000 Binary files a/res/img/portal/red/075.png and /dev/null differ diff --git a/res/img/portal/red/076.png b/res/img/portal/red/076.png deleted file mode 100644 index 56917dc..0000000 Binary files a/res/img/portal/red/076.png and /dev/null differ diff --git a/res/img/portal/red/077.png b/res/img/portal/red/077.png deleted file mode 100644 index cfbc0b1..0000000 Binary files a/res/img/portal/red/077.png and /dev/null differ diff --git a/res/img/portal/red/078.png b/res/img/portal/red/078.png deleted file mode 100644 index cc0711b..0000000 Binary files a/res/img/portal/red/078.png and /dev/null differ diff --git a/res/img/portal/red/079.png b/res/img/portal/red/079.png deleted file mode 100644 index eddd4d4..0000000 Binary files a/res/img/portal/red/079.png and /dev/null differ diff --git a/res/img/portal/red/080.png b/res/img/portal/red/080.png deleted file mode 100644 index 27349fa..0000000 Binary files a/res/img/portal/red/080.png and /dev/null differ diff --git a/res/img/portal/red/081.png b/res/img/portal/red/081.png deleted file mode 100644 index c9973cf..0000000 Binary files a/res/img/portal/red/081.png and /dev/null differ diff --git a/res/img/portal/red/082.png b/res/img/portal/red/082.png deleted file mode 100644 index 6929e42..0000000 Binary files a/res/img/portal/red/082.png and /dev/null differ diff --git a/res/img/portal/red/083.png b/res/img/portal/red/083.png deleted file mode 100644 index e7dbea4..0000000 Binary files a/res/img/portal/red/083.png and /dev/null differ diff --git a/res/img/portal/red/084.png b/res/img/portal/red/084.png deleted file mode 100644 index 8244366..0000000 Binary files a/res/img/portal/red/084.png and /dev/null differ diff --git a/res/img/portal/red/085.png b/res/img/portal/red/085.png deleted file mode 100644 index a4f4834..0000000 Binary files a/res/img/portal/red/085.png and /dev/null differ diff --git a/res/img/portal/red/086.png b/res/img/portal/red/086.png deleted file mode 100644 index 6db5af5..0000000 Binary files a/res/img/portal/red/086.png and /dev/null differ diff --git a/res/img/portal/red/087.png b/res/img/portal/red/087.png deleted file mode 100644 index 0078157..0000000 Binary files a/res/img/portal/red/087.png and /dev/null differ diff --git a/res/img/portal/red/088.png b/res/img/portal/red/088.png deleted file mode 100644 index 9478f64..0000000 Binary files a/res/img/portal/red/088.png and /dev/null differ diff --git a/res/img/portal/red/089.png b/res/img/portal/red/089.png deleted file mode 100644 index 7d8ad32..0000000 Binary files a/res/img/portal/red/089.png and /dev/null differ diff --git a/res/img/portal/red/090.png b/res/img/portal/red/090.png deleted file mode 100644 index fce6f83..0000000 Binary files a/res/img/portal/red/090.png and /dev/null differ diff --git a/res/img/portal/red/091.png b/res/img/portal/red/091.png deleted file mode 100644 index 7bba576..0000000 Binary files a/res/img/portal/red/091.png and /dev/null differ diff --git a/res/img/portal/red/092.png b/res/img/portal/red/092.png deleted file mode 100644 index f218e21..0000000 Binary files a/res/img/portal/red/092.png and /dev/null differ diff --git a/res/img/portal/red/093.png b/res/img/portal/red/093.png deleted file mode 100644 index 00eea0b..0000000 Binary files a/res/img/portal/red/093.png and /dev/null differ diff --git a/res/img/portal/red/094.png b/res/img/portal/red/094.png deleted file mode 100644 index 7d15a8d..0000000 Binary files a/res/img/portal/red/094.png and /dev/null differ diff --git a/res/img/portal/red/095.png b/res/img/portal/red/095.png deleted file mode 100644 index 643d29d..0000000 Binary files a/res/img/portal/red/095.png and /dev/null differ diff --git a/res/img/portal/red/096.png b/res/img/portal/red/096.png deleted file mode 100644 index 6e4ec4d..0000000 Binary files a/res/img/portal/red/096.png and /dev/null differ diff --git a/res/img/portal/red/097.png b/res/img/portal/red/097.png deleted file mode 100644 index 44ba367..0000000 Binary files a/res/img/portal/red/097.png and /dev/null differ diff --git a/res/img/portal/red/098.png b/res/img/portal/red/098.png deleted file mode 100644 index a9931f3..0000000 Binary files a/res/img/portal/red/098.png and /dev/null differ diff --git a/res/img/portal/red/099.png b/res/img/portal/red/099.png deleted file mode 100644 index 93e0265..0000000 Binary files a/res/img/portal/red/099.png and /dev/null differ diff --git a/res/img/portal/red/100.png b/res/img/portal/red/100.png deleted file mode 100644 index db7461a..0000000 Binary files a/res/img/portal/red/100.png and /dev/null differ diff --git a/res/img/portal/red/101.png b/res/img/portal/red/101.png deleted file mode 100644 index aa38c59..0000000 Binary files a/res/img/portal/red/101.png and /dev/null differ diff --git a/res/img/portal/red/102.png b/res/img/portal/red/102.png deleted file mode 100644 index b2e578d..0000000 Binary files a/res/img/portal/red/102.png and /dev/null differ diff --git a/res/img/portal/red/103.png b/res/img/portal/red/103.png deleted file mode 100644 index fdbc4ee..0000000 Binary files a/res/img/portal/red/103.png and /dev/null differ diff --git a/res/img/portal/red/104.png b/res/img/portal/red/104.png deleted file mode 100644 index 3405be4..0000000 Binary files a/res/img/portal/red/104.png and /dev/null differ diff --git a/res/img/portal/red/105.png b/res/img/portal/red/105.png deleted file mode 100644 index 30e4d28..0000000 Binary files a/res/img/portal/red/105.png and /dev/null differ diff --git a/res/img/portal/red/106.png b/res/img/portal/red/106.png deleted file mode 100644 index 8f35493..0000000 Binary files a/res/img/portal/red/106.png and /dev/null differ diff --git a/res/img/portal/red/107.png b/res/img/portal/red/107.png deleted file mode 100644 index 7cffb26..0000000 Binary files a/res/img/portal/red/107.png and /dev/null differ diff --git a/res/img/portal/red/108.png b/res/img/portal/red/108.png deleted file mode 100644 index d2ac284..0000000 Binary files a/res/img/portal/red/108.png and /dev/null differ diff --git a/res/img/portal/red/109.png b/res/img/portal/red/109.png deleted file mode 100644 index dbb92ec..0000000 Binary files a/res/img/portal/red/109.png and /dev/null differ diff --git a/res/img/portal/red/110.png b/res/img/portal/red/110.png deleted file mode 100644 index 163040c..0000000 Binary files a/res/img/portal/red/110.png and /dev/null differ diff --git a/res/img/portal/red/111.png b/res/img/portal/red/111.png deleted file mode 100644 index 6b3651b..0000000 Binary files a/res/img/portal/red/111.png and /dev/null differ diff --git a/res/img/portal/red/112.png b/res/img/portal/red/112.png deleted file mode 100644 index cbdb7e3..0000000 Binary files a/res/img/portal/red/112.png and /dev/null differ diff --git a/res/img/portal/red/113.png b/res/img/portal/red/113.png deleted file mode 100644 index c346062..0000000 Binary files a/res/img/portal/red/113.png and /dev/null differ diff --git a/res/img/portal/red/114.png b/res/img/portal/red/114.png deleted file mode 100644 index a7c43f3..0000000 Binary files a/res/img/portal/red/114.png and /dev/null differ diff --git a/res/img/portal/red/115.png b/res/img/portal/red/115.png deleted file mode 100644 index 50c7bf7..0000000 Binary files a/res/img/portal/red/115.png and /dev/null differ diff --git a/res/img/portal/red/116.png b/res/img/portal/red/116.png deleted file mode 100644 index f96fb8d..0000000 Binary files a/res/img/portal/red/116.png and /dev/null differ diff --git a/res/img/portal/red/117.png b/res/img/portal/red/117.png deleted file mode 100644 index 38bc27a..0000000 Binary files a/res/img/portal/red/117.png and /dev/null differ diff --git a/res/img/portal/red/118.png b/res/img/portal/red/118.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/118.png and /dev/null differ diff --git a/res/img/portal/red/119.png b/res/img/portal/red/119.png deleted file mode 100644 index 3e75d06..0000000 Binary files a/res/img/portal/red/119.png and /dev/null differ diff --git a/res/img/textures/1.png b/res/img/textures/1.png deleted file mode 100755 index 586ba8b..0000000 Binary files a/res/img/textures/1.png and /dev/null differ diff --git a/res/img/textures/2.png b/res/img/textures/2.png deleted file mode 100755 index ded9e78..0000000 Binary files a/res/img/textures/2.png and /dev/null differ diff --git a/res/img/textures/3.png b/res/img/textures/dungeon.png similarity index 100% rename from res/img/textures/3.png rename to res/img/textures/dungeon.png diff --git a/res/scene.json b/res/scene.json index edb8c59..17c1950 100644 --- a/res/scene.json +++ b/res/scene.json @@ -292,10 +292,9 @@ ] }, { - "type": "OBSTACLE", + "type": "SPACE", "textures": [ - 0, - 30 + 0 ] }, { @@ -443,8 +442,7 @@ { "type": "SPACE", "textures": [ - 0, - 12 + 0 ] }, { diff --git a/res/snd/EnemyAttack.wav b/res/snd/EnemyAttack.wav new file mode 100644 index 0000000..db2f2e3 Binary files /dev/null and b/res/snd/EnemyAttack.wav differ diff --git a/res/snd/GameOver.wav b/res/snd/GameOver.wav new file mode 100644 index 0000000..7a13314 Binary files /dev/null and b/res/snd/GameOver.wav differ diff --git a/res/snd/GetKey.wav b/res/snd/GetKey.wav new file mode 100644 index 0000000..f33c507 Binary files /dev/null and b/res/snd/GetKey.wav differ diff --git a/res/snd/OpenChest.wav b/res/snd/OpenChest.wav index c0a0827..10588e0 100644 Binary files a/res/snd/OpenChest.wav and b/res/snd/OpenChest.wav differ diff --git a/res/snd/TitleLoop.wav b/res/snd/TitleLoop.wav deleted file mode 100644 index 1d9584a..0000000 Binary files a/res/snd/TitleLoop.wav and /dev/null differ diff --git a/src/cl/cromer/game/Celda.java b/src/cl/cromer/game/Celda.java index 21e09ce..6d0a6e0 100644 --- a/src/cl/cromer/game/Celda.java +++ b/src/cl/cromer/game/Celda.java @@ -65,33 +65,19 @@ public class Celda extends JComponent implements Constantes { */ private Logger logger; - /** - * Initialize the cell with its coordinates - */ - public Celda() { - logger = getLogger(this.getClass(), CELDA_LOG_LEVEL); - } - /** * Initialize the cell with its coordinates * @param xPixels The x graphical coordinate * @param yPixels The y graphical coordinate + * @param x The x coordinate of the cell + * @param y The y coordinate of the cell */ - public Celda(int xPixels, int yPixels) { + public Celda(int xPixels, int yPixels, int x, int y) { this.xPixels = xPixels; this.yPixels = yPixels; - logger = getLogger(this.getClass(), CELDA_LOG_LEVEL); - } - - /** - * Set the x and y coordinates of the cell - * - * @param x The x coordinate - * @param y The y coordinate - */ - public void setCoords(int x, int y) { this.x = x; this.y = y; + logger = getLogger(this.getClass(), CELDA_LOG_LEVEL); } /** @@ -103,15 +89,6 @@ public class Celda extends JComponent implements Constantes { return x; } - /** - * Set the x coordinate for the cell - * - * @param x The new x coordinate - */ - public void setX(int x) { - this.x = x; - } - /** * Get the y coordinate for the cell * @@ -121,15 +98,6 @@ public class Celda extends JComponent implements Constantes { return y; } - /** - * Set the y coordinate for the cell - * - * @param y The new y coordinate - */ - public void setY(int y) { - this.y = y; - } - /** * Get the sprite for the cell * @@ -159,6 +127,11 @@ public class Celda extends JComponent implements Constantes { textureNumbers.add(textureNumber); } + public void removeTopTexture() { + textures.remove(textures.size() - 1); + textureNumbers.remove(textureNumbers.size() - 1); + } + public ArrayList getTextureNumbers() { return textureNumbers; } @@ -196,7 +169,7 @@ public class Celda extends JComponent implements Constantes { @Override public void update(Graphics g) { // Set the text font - g.setFont(new Font("monospaced", Font.BOLD, 10)); + //g.setFont(new Font("monospaced", Font.BOLD, 10)); for (BufferedImage tile : textures) { if (tile != null) { @@ -205,20 +178,15 @@ public class Celda extends JComponent implements Constantes { } // Draw a sprite in the cell if needed - switch (getType()) { - case PLAYER: - case ENEMY: - case CHEST: - case PORTAL: - try { - if (animation != null && animation.getFrame() != null) { - g.drawImage(animation.getFrame(), xPixels + animation.getXOffset(), yPixels + animation.getYOffset(), null); - } + if (getType() != Type.SPACE) { + try { + if (animation != null && animation.getFrame() != null) { + g.drawImage(animation.getFrame(), xPixels + animation.getXOffset(), yPixels + animation.getYOffset(), null); } - catch (AnimationException e) { - logger.warning(e.getMessage()); - } - break; + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } } } diff --git a/src/cl/cromer/game/Constantes.java b/src/cl/cromer/game/Constantes.java index 78a326d..92c63e8 100644 --- a/src/cl/cromer/game/Constantes.java +++ b/src/cl/cromer/game/Constantes.java @@ -42,11 +42,14 @@ public interface Constantes { Level VENTANA_PRINCIPAL_LOG_LEVEL = Level.WARNING; Level LIENZO_LOG_LEVEL = Level.WARNING; Level ESCENARIO_LOG_LEVEL = Level.WARNING; + Level PLAYER_LOG_LEVEL = Level.WARNING; Level ENEMY_LOG_LEVEL = Level.WARNING; + Level CHEST_LOG_LEVEL = Level.WARNING; Level CONFIG_LOG_LEVEL = Level.WARNING; Level SOUND_LOG_LEVEL = Level.WARNING; Level IMAGE_LOG_LEVEL = Level.WARNING; Level CELDA_LOG_LEVEL = Level.WARNING; + Level KEY_LOG_LEVEL = Level.WARNING; Level JSON_LOG_LEVEL = Level.WARNING; Level PORTAL_LOG_LEVEL = Level.WARNING; /** @@ -88,15 +91,7 @@ public interface Constantes { /** * The amount of enemies to draw */ - int ENEMIES = 2; - /** - * The player's start x position - */ - int PLAYER_START_X = 2; - /** - * The player's start y position - */ - int PLAYER_START_Y = 1; + int ENEMIES = 3; /** * The font size to use */ diff --git a/src/cl/cromer/game/Escenario.java b/src/cl/cromer/game/Escenario.java index 4f61b1b..7594247 100644 --- a/src/cl/cromer/game/Escenario.java +++ b/src/cl/cromer/game/Escenario.java @@ -19,18 +19,21 @@ import cl.cromer.game.json.Cell; import cl.cromer.game.json.Json; import cl.cromer.game.sound.Sound; import cl.cromer.game.sound.SoundException; -import cl.cromer.game.sprite.*; +import cl.cromer.game.sprite.Animation; +import cl.cromer.game.sprite.AnimationMap; +import cl.cromer.game.sprite.Sheet; +import cl.cromer.game.sprite.SheetException; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import javax.swing.*; import java.awt.*; -import java.awt.event.KeyEvent; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; @@ -59,9 +62,29 @@ public class Escenario extends JComponent implements Constantes { */ private Celda player; /** - * A hashmap that contains all the sprites for the game + * The magic portal + */ + private Celda portal; + /** + * The enemies + */ + private ArrayList enemies = new ArrayList<>(); + /** + * The chests + */ + private ArrayList chests = new ArrayList<>(); + /** + * The keys + */ + private ArrayList keys = new ArrayList<>(); + /** + * A hash map that contains all the sprites for the game */ private Map sprites = new AnimationMap(); + /** + * A hash map of the sounds + */ + private Map sounds = new HashMap<>(); /** * A collection of tiles that can be used in the scene */ @@ -74,18 +97,6 @@ public class Escenario extends JComponent implements Constantes { * The logger */ private Logger logger; - /** - * The magic portal - */ - private Celda portal; - /** - * The enemies - */ - private ArrayList enemies = new ArrayList<>(); - /** - * The chests - */ - private ArrayList chests = new ArrayList<>(); /** * Initialize the scene @@ -97,10 +108,6 @@ public class Escenario extends JComponent implements Constantes { this.canvas = canvas; loadResources(); - // TODO: change to player object later - player = new Celda(); - player.setCoords(PLAYER_START_X, PLAYER_START_Y); - celdas = new Celda[HORIZONTAL_CELLS][VERTICAL_CELLS]; if (GENERATE_SCENE) { @@ -141,24 +148,28 @@ public class Escenario extends JComponent implements Constantes { Gson gson = gsonBuilder.create(); Cell[][] cells = gson.fromJson(json, Cell[][].class); - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - celdas[i][j] = new Celda((i * CELL_PIXELS) + LEFT_MARGIN, (j * CELL_PIXELS) + TOP_MARGIN); - celdas[i][j].setType(cells[i][j].type); + for (int x = 0; x < cells.length; x++) { + for (int y = 0; y < cells[x].length; y++) { + celdas[x][y] = new Celda((x * CELL_PIXELS) + LEFT_MARGIN, (y * CELL_PIXELS) + TOP_MARGIN, x, y); + celdas[x][y].setType(cells[x][y].type); - if (cells[i][j].type == Celda.Type.PLAYER) { - celdas[i][j].setAnimation(sprites.get(Animation.SpriteType.PLAYER)); + if (cells[x][y].type == Celda.Type.PLAYER) { + celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.PLAYER)); + player = celdas[x][y]; } - else if (cells[i][j].type == Celda.Type.ENEMY) { - celdas[i][j].setAnimation(sprites.get(Animation.SpriteType.ENEMY)); + else if (cells[x][y].type == Celda.Type.ENEMY) { + celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.ENEMY)); } - else if (cells[i][j].type == Celda.Type.CHEST) { - celdas[i][j].setAnimation(sprites.get(Animation.SpriteType.CHEST)); + else if (cells[x][y].type == Celda.Type.CHEST) { + celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.CHEST)); + } + else if (cells[x][y].type == Celda.Type.KEY) { + celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.KEY)); } - for (int k = 0; k < cells[i][j].textures.size(); k++) { + for (int k = 0; k < cells[x][y].textures.size(); k++) { try { - celdas[i][j].addTexture(textureSheet.getTexture(cells[i][j].textures.get(k)), cells[i][j].textures.get(k)); + celdas[x][y].addTexture(textureSheet.getTexture(cells[x][y].textures.get(k)), cells[x][y].textures.get(k)); } catch (SheetException e) { logger.warning(e.getMessage()); @@ -205,12 +216,23 @@ public class Escenario extends JComponent implements Constantes { } arrayList.add(new RandomPositionList(random_x, random_y, Celda.Type.PORTAL)); + // Generate enough keys for the chests that will exist + for (int i = 0; i < CHESTS; i++) { + random_x = random(0, HORIZONTAL_CELLS - 1); + random_y = random(0, VERTICAL_CELLS - 1); + while (arrayList.contains(new RandomPositionList(random_x, random_y, Celda.Type.KEY)) || celdas[random_x][random_y].getType() != Celda.Type.SPACE) { + random_x = random(0, HORIZONTAL_CELLS - 1); + random_y = random(0, VERTICAL_CELLS - 1); + } + arrayList.add(new RandomPositionList(random_x, random_y, Celda.Type.KEY)); + } + // Chests need to be last to make sure they are openable for (int i = 0; i < CHESTS; i++) { random_x = random(0, HORIZONTAL_CELLS - 1); random_y = random(0, VERTICAL_CELLS - 1); // Don't put a chest if it can't be opened - while (arrayList.contains(new RandomPositionList(random_x, random_y, Celda.Type.CHEST)) || arrayList.contains(new RandomPositionList(random_x, random_y + 1, Celda.Type.CHEST)) || celdas[random_x][random_y].getType() != Celda.Type.SPACE || celdas[random_x][random_y + 1].getType() != Celda.Type.SPACE) { + while (arrayList.contains(new RandomPositionList(random_x, random_y, Celda.Type.CHEST)) || arrayList.contains(new RandomPositionList(random_x, random_y + 1, Celda.Type.CHEST)) || celdas[random_x][random_y].getType() != Celda.Type.SPACE || celdas[random_x][random_y + 1].getType() != Celda.Type.SPACE || celdas[random_x][random_y - 1].getType() != Celda.Type.SPACE) { random_x = random(0, HORIZONTAL_CELLS - 1); random_y = random(0, VERTICAL_CELLS - 1); } @@ -224,16 +246,18 @@ public class Escenario extends JComponent implements Constantes { switch (randomList.getType()) { case ENEMY: celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.ENEMY)); - celdas[x][y].setCoords(x, y); enemies.add(celdas[x][y]); break; case CHEST: celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.CHEST)); - celdas[x][y].setCoords(x, y); chests.add(celdas[x][y]); break; + case KEY: + celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.KEY)); + keys.add(celdas[x][y]); + break; case PORTAL: - celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.PORTAL)); + celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.INACTIVE_PORTAL)); portal = celdas[x][y]; break; case OBSTACLE: @@ -241,7 +265,7 @@ public class Escenario extends JComponent implements Constantes { celdas[x][y].addTexture(textureSheet.getTexture(30), 30); } catch (SheetException e) { - e.printStackTrace(); + logger.warning(e.getMessage()); } break; } @@ -255,7 +279,7 @@ public class Escenario extends JComponent implements Constantes { for (int x = 0; x < HORIZONTAL_CELLS; x++) { for (int y = 0; y < VERTICAL_CELLS; y++) { logger.info("Generate cell x: " + x + " y: " + y + " manually"); - celdas[x][y] = new Celda((x * CELL_PIXELS) + LEFT_MARGIN, (y * CELL_PIXELS) + TOP_MARGIN); + celdas[x][y] = new Celda((x * CELL_PIXELS) + LEFT_MARGIN, (y * CELL_PIXELS) + TOP_MARGIN, x, y); try { celdas[x][y].addTexture(textureSheet.getTexture(0), 0); } @@ -273,25 +297,6 @@ public class Escenario extends JComponent implements Constantes { logger.warning(e.getMessage()); } } - else if (x == 4 && y == 3) { - // Obstacle on floor - try { - celdas[x][y].setType(Celda.Type.OBSTACLE); - celdas[x][y].addTexture(textureSheet.getTexture(30), 30); - } - catch (SheetException e) { - logger.warning(e.getMessage()); - } - } - else if (x == 6 && y == 6) { - // Blood on floor - try { - celdas[x][y].addTexture(textureSheet.getTexture(12), 12); - } - catch (SheetException e) { - logger.warning(e.getMessage()); - } - } else if (x == HORIZONTAL_CELLS - 1 && y == 0) { // Top right corner celdas[x][y].setType(Celda.Type.OBSTACLE); @@ -445,18 +450,12 @@ public class Escenario extends JComponent implements Constantes { } } - if (x == PLAYER_START_X && y == PLAYER_START_Y) { + // The player starts at the door + if (x == 2 && y == 1) { celdas[x][y].setType(Celda.Type.PLAYER); celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.PLAYER)); + player = celdas[x][y]; } - /*else if (x == 10 && y == 3) { - celdas[x][y].setType(Celda.Type.ENEMY); - celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.ENEMY)); - } - else if (x == 10 && y == 7) { - celdas[x][y].setType(Celda.Type.ENEMY); - celdas[x][y].setAnimation(sprites.get(Animation.SpriteType.ENEMY)); - }*/ } } } @@ -512,6 +511,25 @@ public class Escenario extends JComponent implements Constantes { logger.warning(e.getMessage()); } + // Load the key animation + Sheet keySheet = new Sheet("/img/key/key.png", 24, 24); + try { + animation = new Animation(); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(0)); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(1)); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(2)); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(3)); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(4)); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(5)); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(6)); + animation.addImage(Animation.Direction.NONE, keySheet.getTexture(7)); + sprites.put(Animation.SpriteType.KEY, animation); + } + catch (SheetException e) { + logger.warning(e.getMessage()); + } + + // Load the active portal animation = new Animation(); for (int i = 0; i < 120; i++) { StringBuilder stringBuilder = new StringBuilder(); @@ -522,10 +540,55 @@ public class Escenario extends JComponent implements Constantes { stringBuilder.append(".png"); animation.addImage(Animation.Direction.NONE, "/img/portal/green/" + stringBuilder.toString()); } - sprites.put(Animation.SpriteType.PORTAL, animation); + sprites.put(Animation.SpriteType.ACTIVE_PORTAL, animation); + + // Load the inactive portal + animation = new Animation(); + for (int i = 0; i < 120; i++) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(i); + while (stringBuilder.length() < 3) { + stringBuilder.insert(0, 0); + } + stringBuilder.append(".png"); + animation.addImage(Animation.Direction.NONE, "/img/portal/gray/" + stringBuilder.toString()); + } + sprites.put(Animation.SpriteType.INACTIVE_PORTAL, animation); + + // Load the hearts + animation = new Animation(); + for (int i = 0; i < 5; i++) { + animation.addImage(Animation.Direction.NONE, "/img/heart/heart" + i + ".png"); + } + sprites.put(Animation.SpriteType.HEART, animation); + + // Load the game over + animation = new Animation(); + animation.addImage(Animation.Direction.NONE, "/img/gameover/gameover.png"); + sprites.put(Animation.SpriteType.GAME_OVER, animation); // Load the background textures - textureSheet = new Sheet("/img/textures/3.png", 64, 64); + textureSheet = new Sheet("/img/textures/dungeon.png", 64, 64); + + try { + Sound sound = new Sound("/snd/OpenChest.wav"); + sounds.put(Sound.SoundType.OPEN_CHEST, sound); + + sound = new Sound("/snd/EnemyAttack.wav"); + sounds.put(Sound.SoundType.ENEMY_ATTACK, sound); + + sound = new Sound("/snd/GameOver.wav"); + sounds.put(Sound.SoundType.GAME_OVER, sound); + + sound = new Sound("/snd/GameLoop.wav"); + sounds.put(Sound.SoundType.BACKGROUND, sound); + + sound = new Sound("/snd/GetKey.wav"); + sounds.put(Sound.SoundType.GET_KEY, sound); + } + catch (SoundException e) { + logger.warning(e.getMessage()); + } } /** @@ -561,173 +624,6 @@ public class Escenario extends JComponent implements Constantes { return celdas; } - /** - * Handle keys being pressed in the game - * - * @param event The event from the keyboard - */ - public void keyPressed(KeyEvent event) { - if (!doorClosed) { - try { - celdas[2][0].addTexture(textureSheet.getTexture(193), 193); - } - catch (SheetException e) { - e.printStackTrace(); - } - doorClosed = true; - } - switch (event.getKeyCode()) { - case KeyEvent.VK_UP: - moveUp(); - break; - case KeyEvent.VK_DOWN: - moveDown(); - break; - case KeyEvent.VK_LEFT: - moveLeft(); - break; - case KeyEvent.VK_RIGHT: - moveRight(); - break; - case KeyEvent.VK_SPACE: - interact(); - break; - } - } - - /** - * Move the player up - */ - private void moveUp() { - logger.info("Up key pressed"); - int x = player.getX(); - int y = player.getY(); - if (y > 0 && celdas[x][y - 1].getType() == Celda.Type.SPACE) { - celdas[x][y].setType(Celda.Type.SPACE); - player.setY(y - 1); - celdas[x][y - 1].setType(Celda.Type.PLAYER); - - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.UP) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.UP); - } - - celdas[x][y - 1].setAnimation(celdas[x][y].getAnimation()); - celdas[x][y].setAnimation(null); - } - else { - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.UP) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.UP); - } - } - } - - /** - * Move the player down - */ - private void moveDown() { - logger.info("Down key pressed"); - int x = player.getX(); - int y = player.getY(); - if (y < (VERTICAL_CELLS - 1) && celdas[x][y + 1].getType() == Celda.Type.SPACE) { - celdas[x][y].setType(Celda.Type.SPACE); - player.setY(y + 1); - celdas[x][y + 1].setType(Celda.Type.PLAYER); - - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.DOWN) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.DOWN); - } - - celdas[x][y + 1].setAnimation(celdas[x][y].getAnimation()); - celdas[x][y].setAnimation(null); - } - else { - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.DOWN) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.DOWN); - } - } - } - - /** - * Move the player to the left - */ - private void moveLeft() { - logger.info("Left key pressed"); - int x = player.getX(); - int y = player.getY(); - if (x > 0 && celdas[x - 1][y].getType() == Celda.Type.SPACE) { - celdas[x][y].setType(Celda.Type.SPACE); - player.setX(x - 1); - celdas[x - 1][y].setType(Celda.Type.PLAYER); - - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.LEFT) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.LEFT); - } - - celdas[x - 1][y].setAnimation(celdas[x][y].getAnimation()); - celdas[x][y].setAnimation(null); - } - else { - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.LEFT) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.LEFT); - } - } - } - - /** - * Move the player to the right - */ - private void moveRight() { - logger.info("Right key pressed"); - int x = player.getX(); - int y = player.getY(); - if (x < (HORIZONTAL_CELLS - 1) && celdas[x + 1][y].getType() == Celda.Type.SPACE) { - celdas[x][y].setType(Celda.Type.SPACE); - player.setX(x + 1); - celdas[x + 1][y].setType(Celda.Type.PLAYER); - - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.RIGHT) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.RIGHT); - } - - celdas[x + 1][y].setAnimation(celdas[x][y].getAnimation()); - celdas[x][y].setAnimation(null); - } - else { - if (celdas[x][y].getAnimation().getCurrentDirection() != Animation.Direction.RIGHT) { - celdas[x][y].getAnimation().setCurrentDirection(Animation.Direction.RIGHT); - } - } - } - - /** - * Interact with an object in the game - */ - private void interact() { - logger.info("Space bar pressed"); - int x = player.getX(); - int y = player.getY(); - if (celdas[x][y].getAnimation().getCurrentDirection() == Animation.Direction.UP) { - if (celdas[x][y - 1].getType() == Celda.Type.CHEST) { - logger.info("Opened chest"); - - try { - Sound chestOpenSound = new Sound("/snd/OpenChest.wav"); - chestOpenSound.play(); - } - catch (SoundException e) { - logger.warning(e.getMessage()); - } - - try { - celdas[x][y - 1].getAnimation().setFrame(3); - } - catch (AnimationException e) { - logger.warning(e.getMessage()); - } - } - } - } - /** * Override the paintComponent method of JComponent to paint the scene * @param g The graphics object to paint @@ -785,6 +681,15 @@ public class Escenario extends JComponent implements Constantes { return chests; } + /** + * Get the keys + * + * @return Returns an array list containing the keys + */ + public ArrayList getKeys() { + return keys; + } + /** * Get the parent canvas of this scene * @return Returns the parent canvas @@ -792,4 +697,61 @@ public class Escenario extends JComponent implements Constantes { public Lienzo getCanvas() { return canvas; } + + /** + * Check if door is closed or not + * + * @return Returns true if closed or false if open + */ + public boolean isDoorClosed() { + return doorClosed; + } + + /** + * Change the state of the door + * + * @param doorClosed Set to true to the close the door or false to open it + */ + public void setDoorClosed(boolean doorClosed) { + if (doorClosed && !isDoorClosed()) { + try { + celdas[2][0].addTexture(textureSheet.getTexture(193), 193); + } + catch (SheetException e) { + e.printStackTrace(); + } + this.doorClosed = true; + } + else if (!doorClosed && isDoorClosed()) { + celdas[2][0].removeTopTexture(); + this.doorClosed = false; + } + } + + /** + * Get the sprites available + * + * @return Returns all available sprites + */ + public Map getSprites() { + return sprites; + } + + /** + * Get the available sounds + * + * @return Returns all available sounds + */ + public Map getSounds() { + return sounds; + } + + /** + * Get the texture sheet + * + * @return Returns the texture sheet + */ + public Sheet getTextureSheet() { + return textureSheet; + } } \ No newline at end of file diff --git a/src/cl/cromer/game/Lienzo.java b/src/cl/cromer/game/Lienzo.java index 62d7df0..db7f64a 100644 --- a/src/cl/cromer/game/Lienzo.java +++ b/src/cl/cromer/game/Lienzo.java @@ -15,17 +15,20 @@ package cl.cromer.game; -import cl.cromer.game.object.Chest; -import cl.cromer.game.object.Enemy; -import cl.cromer.game.object.Portal; +import cl.cromer.game.object.Object; +import cl.cromer.game.object.*; import cl.cromer.game.sound.Sound; import cl.cromer.game.sound.SoundException; +import cl.cromer.game.sprite.Animation; +import cl.cromer.game.sprite.AnimationException; import javax.sound.sampled.Clip; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; @@ -49,15 +52,23 @@ public class Lienzo extends Canvas implements Constantes { /** * The threads for the objects */ - private ArrayList threads = new ArrayList<>(); + private HashMap threads = new HashMap<>(); + /** + * The player + */ + private Player player; /** * The enemies */ private ArrayList enemies = new ArrayList<>(); /** - * The current direction that is assigned to an enemy + * The keys */ - private Enemy.Direction enemyDirection = Enemy.Direction.DOWN; + private ArrayList keys = new ArrayList<>(); + /** + * The chests + */ + private ArrayList chests = new ArrayList<>(); /** * The magic portal */ @@ -70,6 +81,18 @@ public class Lienzo extends Canvas implements Constantes { * The background music of the game */ private Sound backgroundMusic; + /** + * The background music thread + */ + private Thread backgroundMusicThread; + /** + * Game over + */ + private boolean gameOver = false; + /** + * If the game over loop has been run at least once + */ + private boolean gameOverRan = false; /** * Initialize the canvas @@ -84,13 +107,19 @@ public class Lienzo extends Canvas implements Constantes { @Override public void keyPressed(KeyEvent event) { super.keyPressed(event); - escenario.keyPressed(event); - repaint(); + if (!gameOver) { + player.keyPressed(event); + repaint(); + } } }); + player = new Player(escenario, escenario.getPlayer()); + threads.put(player, new Thread(player)); + final Lock lock = new ReentrantLock(true); + Enemy.Direction enemyDirection = Enemy.Direction.DOWN; for (Celda celda : escenario.getEnemies()) { Enemy enemy = new Enemy(escenario, celda, lock); enemy.setDirection(enemyDirection); @@ -107,25 +136,34 @@ public class Lienzo extends Canvas implements Constantes { enemyDirection = Enemy.Direction.UP; } enemies.add(enemy); - threads.add(new Thread(enemy)); + threads.put(enemy, new Thread(enemy)); + } + + for (Celda celda : escenario.getKeys()) { + Key key = new Key(escenario, celda); + keys.add(key); + threads.put(key, new Thread(key)); } for (Celda celda : escenario.getChests()) { Chest chest = new Chest(escenario, celda); - threads.add(new Thread(chest)); + chests.add(chest); + threads.put(chest, new Thread(chest)); } - portal = new Portal(escenario); - threads.add(new Thread(portal)); + portal = new Portal(escenario, escenario.getPortal()); + threads.put(portal, new Thread(portal)); - for (Thread thread : threads) { + for (Map.Entry entry : threads.entrySet()) { + Thread thread = entry.getValue(); thread.start(); } try { - backgroundMusic = new Sound("/snd/GameLoop.wav"); + backgroundMusic = escenario.getSounds().get(Sound.SoundType.BACKGROUND); backgroundMusic.setLoops(Clip.LOOP_CONTINUOUSLY); - backgroundMusic.play(); + backgroundMusicThread = new Thread(backgroundMusic); + backgroundMusicThread.start(); } catch (SoundException e) { logger.warning(e.getMessage()); @@ -158,7 +196,118 @@ public class Lienzo extends Canvas implements Constantes { // This is needed if there is a background image //graphicBuffer.drawImage(); - escenario.paintComponent(graphicBuffer); + Animation keyAnimation = null; + switch (player.keyCount()) { + case 2: + try { + keyAnimation = escenario.getSprites().get(Animation.SpriteType.KEY); + keyAnimation.setFrame(4); + graphicBuffer.drawImage(keyAnimation.getFrame(), 69, 8, null); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + case 1: + try { + if (keyAnimation == null) { + keyAnimation = escenario.getSprites().get(Animation.SpriteType.KEY); + keyAnimation.setFrame(4); + } + graphicBuffer.drawImage(keyAnimation.getFrame(), 40, 8, null); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + break; + } + + int health = player.getHealth(); + if (health == 0) { + gameOver = true; + } + int hearts = Player.MAX_HEALTH / 4; + for (int i = 0; i < hearts; i++) { + Animation heartAnimation = escenario.getSprites().get(Animation.SpriteType.HEART); + if (health >= 4) { + try { + heartAnimation.setFrame(4); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + else { + try { + heartAnimation.setFrame(health); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + try { + int x = ((HORIZONTAL_CELLS) * CELL_PIXELS) + LEFT_MARGIN - heartAnimation.getFrame().getWidth() * hearts + heartAnimation.getFrame().getWidth() * i; + graphicBuffer.drawImage(heartAnimation.getFrame(), x, 8, null); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + if (health > 0) { + health = health - 4; + if (health < 0) { + health = 0; + } + } + } + + if (gameOver) { + if (!gameOverRan) { + try { + if (backgroundMusic.isPlaying()) { + backgroundMusic.stop(); + backgroundMusicThread.interrupt(); + } + } + catch (SoundException e) { + logger.warning(e.getMessage()); + } + + new Thread(escenario.getSounds().get(Sound.SoundType.GAME_OVER)).start(); + + // Stop all the active threads + for (Map.Entry entry : threads.entrySet()) { + Thread thread = entry.getValue(); + if (thread.isAlive()) { + Object object = entry.getKey(); + object.setActive(false); + thread.interrupt(); + try { + thread.join(); + } + catch (InterruptedException e) { + logger.info(e.getMessage()); + } + } + } + + gameOverRan = true; + } + + // Place the game over image on the screen + Animation gameOver = escenario.getSprites().get(Animation.SpriteType.GAME_OVER); + graphicBuffer.setColor(Color.black); + graphicBuffer.drawRect(0, 0, getWidth(), getHeight()); + try { + int x = (getWidth() - gameOver.getFrame().getWidth()) / 2; + int y = (getHeight() - gameOver.getFrame().getHeight()) / 2; + graphicBuffer.drawImage(gameOver.getFrame(), x, y, null); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + else { + escenario.paintComponent(graphicBuffer); + } g.drawImage(imageBuffer, 0, 0, null); } @@ -191,4 +340,40 @@ public class Lienzo extends Canvas implements Constantes { logger.warning(e.getMessage()); } } + + /** + * Get the player + * + * @return Returns the player object + */ + public Player getPlayer() { + return player; + } + + /** + * Get a list of the keys that exist + * + * @return Returns all the keys that are in the game + */ + public ArrayList getKeys() { + return keys; + } + + /** + * Get a list of the chests that exist + * + * @return Returns all the chests that are in the game + */ + public ArrayList getChests() { + return chests; + } + + /** + * Get the threads that have been created + * + * @return Returns the threads that run in the background + */ + public HashMap getThreads() { + return threads; + } } \ No newline at end of file diff --git a/src/cl/cromer/game/json/Json.java b/src/cl/cromer/game/json/Json.java index f05d0ed..1aa40fa 100644 --- a/src/cl/cromer/game/json/Json.java +++ b/src/cl/cromer/game/json/Json.java @@ -48,11 +48,11 @@ public class Json implements Constantes { */ public void exportScene(Celda[][] celdas) { Cell[][] cells = new Cell[celdas.length][celdas[0].length]; - for (int i = 0; i < celdas.length; i++) { - for (int j = 0; j < celdas[i].length; j++) { - cells[i][j] = new Cell(); - cells[i][j].type = celdas[i][j].getType(); - cells[i][j].textures = celdas[i][j].getTextureNumbers(); + for (int x = 0; x < celdas.length; x++) { + for (int y = 0; y < celdas[x].length; y++) { + cells[x][y] = new Cell(); + cells[x][y].type = celdas[x][y].getType(); + cells[x][y].textures = celdas[x][y].getTextureNumbers(); } } writeScene(cells); @@ -74,7 +74,7 @@ public class Json implements Constantes { Gson gson = gsonBuilder.create(); String json = gson.toJson(cells); - File file = new File("src/res/scene.json"); + File file = new File("res/scene.json"); try { FileOutputStream fileOutputStream = new FileOutputStream(file); fileOutputStream.write(json.getBytes()); diff --git a/src/cl/cromer/game/logging/HtmlFormatter.java b/src/cl/cromer/game/logging/HtmlFormatter.java index cd3eb71..86db5f7 100644 --- a/src/cl/cromer/game/logging/HtmlFormatter.java +++ b/src/cl/cromer/game/logging/HtmlFormatter.java @@ -34,37 +34,38 @@ public class HtmlFormatter extends Formatter { */ public String format(LogRecord logRecord) { StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("\n"); + stringBuilder.append("\t\n"); // colorize any levels >= WARNING in red if (logRecord.getLevel().intValue() >= Level.WARNING.intValue()) { - stringBuilder.append("\t"); + stringBuilder.append("\t\t"); stringBuilder.append(""); stringBuilder.append(StringUtils.encodeHtml(logRecord.getLevel().getName())); stringBuilder.append(""); } else { - stringBuilder.append("\t"); + stringBuilder.append("\t\t"); stringBuilder.append(StringUtils.encodeHtml(logRecord.getLevel().getName())); } stringBuilder.append("\n"); - stringBuilder.append("\t"); + stringBuilder.append("\t\t"); stringBuilder.append(StringUtils.encodeHtml(calculateDate(logRecord.getMillis()))); stringBuilder.append("\n"); - stringBuilder.append("\t"); + stringBuilder.append("\t\t"); stringBuilder.append(StringUtils.encodeHtml(logRecord.getSourceClassName())); stringBuilder.append("\n"); - stringBuilder.append("\t"); + + stringBuilder.append("\t\t"); stringBuilder.append(StringUtils.encodeHtml(logRecord.getSourceMethodName())); stringBuilder.append("\n"); - stringBuilder.append("\t"); + stringBuilder.append("\t\t"); stringBuilder.append(StringUtils.encodeHtml(formatMessage(logRecord))); stringBuilder.append("\n"); - stringBuilder.append("\n"); + stringBuilder.append("\t\n"); return stringBuilder.toString(); } @@ -90,21 +91,22 @@ public class HtmlFormatter extends Formatter { public String getHead(Handler handler) { return "\n\n\n" + + "Log\n" + "\n" + "\n" + "

" + (StringUtils.encodeHtml(new Date().toString())) + "

\n" + "\n" - + "\n" - + "\t\n" - + "\t\n" - + "\t\n" - + "\t\n" - + "\t\n" - + "\n"; + + "\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\t\n" + + "\t\n"; } /** diff --git a/src/cl/cromer/game/object/Chest.java b/src/cl/cromer/game/object/Chest.java index 6bf60e4..8635c59 100644 --- a/src/cl/cromer/game/object/Chest.java +++ b/src/cl/cromer/game/object/Chest.java @@ -16,34 +16,82 @@ package cl.cromer.game.object; import cl.cromer.game.Celda; +import cl.cromer.game.Constantes; import cl.cromer.game.Escenario; +import cl.cromer.game.sprite.AnimationException; + +import java.util.logging.Logger; /** * This class handles the chests */ -public class Chest implements Runnable { +public class Chest extends Object implements Constantes { /** * The current state of the chest */ private State state = State.CLOSED; /** - * The scene the chest is in + * The logger */ - private Escenario escenario; - /** - * The cell the chest is in - */ - private Celda celda; + private Logger logger; /** * Initialize the chest - * * @param escenario The scene the chest is in - * @param celda The cell that contains the chest + * @param celda The cell that contains the chest */ public Chest(Escenario escenario, Celda celda) { - this.escenario = escenario; - this.celda = celda; + super(escenario, celda); + logger = getLogger(this.getClass(), CHEST_LOG_LEVEL); + } + + /** + * Get the state of the chest + * + * @return Returns the current state + */ + public State getState() { + return state; + } + + /** + * Sets the state of the chest + * + * @param state The new state of the chest + */ + public void setState(State state) { + this.state = state; + if (state == State.OPENING) { + logger.info("Chest is opening"); + } + else if (state == State.OPENED) { + logger.info("Chest is opened"); + } + else if (state == State.CLOSED) { + logger.info("Chest is closed"); + try { + getCelda().getAnimation().setFrame(0); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + getEscenario().getCanvas().repaint(); + } + } + + /** + * Animate the chest opening + */ + private void animate() { + try { + getCelda().getAnimation().getNextFrame(); + if (getCelda().getAnimation().getFrameNumber() == getCelda().getAnimation().getFrameCount() - 1) { + setState(State.OPENED); + } + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } } /** @@ -51,13 +99,38 @@ public class Chest implements Runnable { */ @Override public void run() { + super.run(); + while (getActive()) { + try { + Thread.sleep(200); + } + catch (InterruptedException e) { + logger.info(e.getMessage()); + } + synchronized (this) { + if (state == State.OPENING) { + animate(); + getEscenario().getCanvas().repaint(); + } + } + } + } + /** + * Check what position the chest is located at + * + * @param x The x position to compare + * @param y The y position to compare + * @return Returns true if it is the same position or false otherwise + */ + public boolean checkPosition(int x, int y) { + return (getX() == x && getY() == y); } /** * The possible states of the chest */ - private enum State { + public enum State { /** * The chest is closed */ diff --git a/src/cl/cromer/game/object/Enemy.java b/src/cl/cromer/game/object/Enemy.java index f554366..24a3d1c 100644 --- a/src/cl/cromer/game/object/Enemy.java +++ b/src/cl/cromer/game/object/Enemy.java @@ -18,8 +18,10 @@ package cl.cromer.game.object; import cl.cromer.game.Celda; import cl.cromer.game.Constantes; import cl.cromer.game.Escenario; +import cl.cromer.game.sound.Sound; import cl.cromer.game.sprite.Animation; import cl.cromer.game.sprite.AnimationException; +import cl.cromer.game.sprite.SheetException; import java.util.concurrent.locks.Lock; import java.util.logging.Logger; @@ -27,23 +29,7 @@ import java.util.logging.Logger; /** * This class handles the enemy object */ -public class Enemy implements Runnable, Constantes { - /** - * The scene that contains the enemy - */ - private Escenario escenario; - /** - * The cell that contains the enemy - */ - private Celda celda; - /** - * The current x position of the enemy - */ - private int x; - /** - * The current y position of the enemy - */ - private int y; +public class Enemy extends Object implements Constantes { /** * The current direction the enemy is facing */ @@ -52,10 +38,6 @@ public class Enemy implements Runnable, Constantes { * The logger for this class */ private Logger logger; - /** - * If the enemy is alive - */ - private boolean alive = true; /** * The speed of the enemy */ @@ -65,7 +47,6 @@ public class Enemy implements Runnable, Constantes { */ private Lock lock; - // TODO: the cell is not needed, just it's x and y position /** * Initialize the enemy * @@ -74,23 +55,9 @@ public class Enemy implements Runnable, Constantes { * @param lock The lock used to prevent the threads from conflicting */ public Enemy(Escenario escenario, Celda celda, Lock lock) { - this.lock = lock; + super(escenario, celda); logger = getLogger(this.getClass(), ENEMY_LOG_LEVEL); - this.escenario = escenario; - this.celda = celda; - this.x = celda.getX(); - this.y = celda.getY(); - } - - /** - * Set the x and y coordinate of the enemy - * - * @param x The x coordinate - * @param y The y coordinate - */ - public void setCoordinates(int x, int y) { - this.x = x; - this.y = y; + this.lock = lock; } /** @@ -106,119 +73,143 @@ public class Enemy implements Runnable, Constantes { * This method handles the enemy's movements */ private void move() { + int x = getX(); + int y = getY(); if (direction == Direction.LEFT) { - if (x > 0 && escenario.getCeldas()[x - 1][y].getType() == Celda.Type.SPACE) { - escenario.getCeldas()[x - 1][y].setType(Celda.Type.ENEMY); - escenario.getCeldas()[x - 1][y].setAnimation(escenario.getCeldas()[x][y].getAnimation()); - escenario.getCeldas()[x][y].setType(Celda.Type.SPACE); - escenario.getCeldas()[x][y].setAnimation(null); + if (x > 0 && getEscenario().getCeldas()[x - 1][y].getType() == Celda.Type.SPACE) { + getEscenario().getCeldas()[x - 1][y].setType(Celda.Type.ENEMY); + getEscenario().getCeldas()[x - 1][y].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y].setAnimation(null); try { - escenario.getCeldas()[x - 1][y].getAnimation().getNextFrame(); + getEscenario().getCeldas()[x - 1][y].getAnimation().getNextFrame(); } catch (AnimationException e) { logger.warning(e.getMessage()); } - x--; + setX(getX() - 1); logger.info("Move left to x: " + x + " y: " + y); } + else if (x > 0 && getEscenario().getCeldas()[x - 1][y].getType() == Celda.Type.PLAYER) { + attackPlayer(x - 1, y); + } else { logger.info("Change to right direction"); - escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.RIGHT); + getEscenario().getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.RIGHT); direction = Direction.RIGHT; } } else if (direction == Direction.RIGHT) { - if (x < (HORIZONTAL_CELLS) - 1 && escenario.getCeldas()[x + 1][y].getType() == Celda.Type.SPACE) { - escenario.getCeldas()[x + 1][y].setType(Celda.Type.ENEMY); - escenario.getCeldas()[x + 1][y].setAnimation(escenario.getCeldas()[x][y].getAnimation()); - escenario.getCeldas()[x][y].setType(Celda.Type.SPACE); - escenario.getCeldas()[x][y].setAnimation(null); + if (x < (HORIZONTAL_CELLS - 1) && getEscenario().getCeldas()[x + 1][y].getType() == Celda.Type.SPACE) { + getEscenario().getCeldas()[x + 1][y].setType(Celda.Type.ENEMY); + getEscenario().getCeldas()[x + 1][y].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y].setAnimation(null); try { - escenario.getCeldas()[x + 1][y].getAnimation().getNextFrame(); + getEscenario().getCeldas()[x + 1][y].getAnimation().getNextFrame(); } catch (AnimationException e) { logger.warning(e.getMessage()); } - x++; + setX(getX() + 1); logger.info("Move right to x: " + x + " y: " + y); } + else if (x < (HORIZONTAL_CELLS - 1) && getEscenario().getCeldas()[x + 1][y].getType() == Celda.Type.PLAYER) { + attackPlayer(x + 1, y); + } else { logger.info("Change to left direction"); - escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.LEFT); + getEscenario().getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.LEFT); direction = Direction.LEFT; } } else if (direction == Direction.DOWN) { - if (y < (VERTICAL_CELLS) - 1 && escenario.getCeldas()[x][y + 1].getType() == Celda.Type.SPACE) { - escenario.getCeldas()[x][y + 1].setType(Celda.Type.ENEMY); - escenario.getCeldas()[x][y + 1].setAnimation(escenario.getCeldas()[x][y].getAnimation()); - escenario.getCeldas()[x][y].setType(Celda.Type.SPACE); - escenario.getCeldas()[x][y].setAnimation(null); + if (y < (VERTICAL_CELLS) - 1 && getEscenario().getCeldas()[x][y + 1].getType() == Celda.Type.SPACE) { + getEscenario().getCeldas()[x][y + 1].setType(Celda.Type.ENEMY); + getEscenario().getCeldas()[x][y + 1].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y].setAnimation(null); try { - escenario.getCeldas()[x][y + 1].getAnimation().getNextFrame(); + getEscenario().getCeldas()[x][y + 1].getAnimation().getNextFrame(); } catch (AnimationException e) { logger.warning(e.getMessage()); } - y++; + setY(getY() + 1); logger.info("Move down to x: " + x + " y: " + y); } + else if (y < (VERTICAL_CELLS - 1) && getEscenario().getCeldas()[x][y + 1].getType() == Celda.Type.PLAYER) { + attackPlayer(x, y + 1); + } else { logger.info("Change to up direction"); - escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.UP); + getEscenario().getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.UP); direction = Direction.UP; } } else if (direction == Direction.UP) { - if (y > 0 && escenario.getCeldas()[x][y - 1].getType() == Celda.Type.SPACE) { - escenario.getCeldas()[x][y - 1].setType(Celda.Type.ENEMY); - escenario.getCeldas()[x][y - 1].setAnimation(escenario.getCeldas()[x][y].getAnimation()); - escenario.getCeldas()[x][y].setType(Celda.Type.SPACE); - escenario.getCeldas()[x][y].setAnimation(null); + if (y > 0 && getEscenario().getCeldas()[x][y - 1].getType() == Celda.Type.SPACE) { + getEscenario().getCeldas()[x][y - 1].setType(Celda.Type.ENEMY); + getEscenario().getCeldas()[x][y - 1].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y].setAnimation(null); try { - escenario.getCeldas()[x][y - 1].getAnimation().getNextFrame(); + getEscenario().getCeldas()[x][y - 1].getAnimation().getNextFrame(); } catch (AnimationException e) { logger.warning(e.getMessage()); } - y--; + setY(getY() - 1); logger.info("Move up to x: " + x + " y: " + y); } + else if (y > 0 && getEscenario().getCeldas()[x][y - 1].getType() == Celda.Type.PLAYER) { + attackPlayer(x, y - 1); + } else { logger.info("Change to down direction"); - escenario.getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.DOWN); + getEscenario().getCeldas()[x][y].getAnimation().setCurrentDirection(Animation.Direction.DOWN); direction = Direction.DOWN; } } } + private void attackPlayer(int x, int y) { + if (getEscenario().getCanvas().getPlayer().getHealth() > 0) { + logger.info("Attacked player at x: " + x + " y: " + y); + + new Thread(getEscenario().getSounds().get(Sound.SoundType.ENEMY_ATTACK)).start(); + + getEscenario().getCanvas().getPlayer().loseHealth(2); + try { + getEscenario().getCeldas()[x][y].addTexture(getEscenario().getTextureSheet().getTexture(12), 12); + } + catch (SheetException e) { + e.printStackTrace(); + } + } + } + /** * This method is run constantly by the runnable */ public void run() { - while (alive) { + super.run(); + while (getActive()) { try { Thread.sleep(speed); } catch (InterruptedException e) { - logger.warning(e.getMessage()); + logger.info(e.getMessage()); } synchronized (this) { lock.lock(); move(); - escenario.getCanvas().repaint(); + getEscenario().getCanvas().repaint(); lock.unlock(); } } } - /** - * Kill the enemy - */ - public void kill() { - alive = false; - } - /** * Set the speed of the enemy * diff --git a/src/cl/cromer/game/object/Gem.java b/src/cl/cromer/game/object/Gem.java index 57f7f55..73e8671 100644 --- a/src/cl/cromer/game/object/Gem.java +++ b/src/cl/cromer/game/object/Gem.java @@ -15,10 +15,13 @@ package cl.cromer.game.object; +import cl.cromer.game.Celda; +import cl.cromer.game.Escenario; + /** * This class contains the gem */ -public class Gem { +public class Gem extends Object { /** * The current state of the gem */ @@ -37,4 +40,17 @@ public class Gem { */ PURIFIED } + + public Gem(Escenario escenario, Celda celda) { + super(escenario, celda); + } + + /** + * ` + * This method is run when the thread starts + */ + @Override + public void run() { + super.run(); + } } diff --git a/src/cl/cromer/game/object/Key.java b/src/cl/cromer/game/object/Key.java index 3c96127..eac91b2 100644 --- a/src/cl/cromer/game/object/Key.java +++ b/src/cl/cromer/game/object/Key.java @@ -15,8 +15,73 @@ package cl.cromer.game.object; +import cl.cromer.game.Celda; +import cl.cromer.game.Constantes; +import cl.cromer.game.Escenario; +import cl.cromer.game.sprite.AnimationException; + +import java.util.logging.Logger; + /** * This class contains the key */ -public class Key { +public class Key extends Object implements Constantes { + /** + * The logger + */ + private Logger logger; + + /** + * Initialize the key + * + * @param escenario The scene the key is in + * @param celda The cell the key is in + */ + public Key(Escenario escenario, Celda celda) { + super(escenario, celda); + logger = getLogger(this.getClass(), KEY_LOG_LEVEL); + } + + /** + * This method animates the portal + */ + private void animate() { + try { + getCelda().getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + + /** + * This is run when the thread starts + */ + @Override + public void run() { + super.run(); + while (getActive()) { + try { + Thread.sleep(100); + } + catch (InterruptedException e) { + logger.info(e.getMessage()); + } + synchronized (this) { + animate(); + getEscenario().getCanvas().repaint(); + } + } + } + + /** + * Check what position the key is located at + * + * @param x The x position to compare + * @param y The y position to compare + * @return Returns true if it is the same position or false otherwise + */ + public boolean checkPosition(int x, int y) { + return (getX() == x && getY() == y); + } } diff --git a/src/cl/cromer/game/object/Object.java b/src/cl/cromer/game/object/Object.java new file mode 100644 index 0000000..dd59dab --- /dev/null +++ b/src/cl/cromer/game/object/Object.java @@ -0,0 +1,147 @@ +/* + * Copyright 2019 Chris Cromer + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package cl.cromer.game.object; + +import cl.cromer.game.Celda; +import cl.cromer.game.Escenario; + +/** + * All game objects extend this class + */ +public class Object implements Runnable { + /** + * The current x position of the object + */ + private int x; + /** + * The current y position of the object + */ + private int y; + /** + * The scene the object is in + */ + private Escenario escenario; + /** + * The cell the object is in + */ + private Celda celda; + /** + * Whether or not the run loop of the object is active + */ + private boolean active; + + /** + * Initialize the object + * + * @param escenario The scene the object is in + * @param celda The cell the object is in + */ + public Object(Escenario escenario, Celda celda) { + this.escenario = escenario; + this.celda = celda; + this.x = celda.getX(); + this.y = celda.getY(); + } + + /** + * Get the x position of the object + * + * @return Returns the x coordinate + */ + public int getX() { + return x; + } + + /** + * Set the x position of the object + * + * @param x The new x coordinate + */ + public void setX(int x) { + this.x = x; + } + + /** + * Gets the y position of the object + * + * @return Returns the y coordinate + */ + public int getY() { + return y; + } + + /** + * Set the y position of the object + * + * @param y The new y coordinate + */ + public void setY(int y) { + this.y = y; + } + + /** + * Get the scene the object is in + * + * @return Returns the scene + */ + public Escenario getEscenario() { + return escenario; + } + + /** + * Get the cell the object is in + * + * @return Returns the cell + */ + public Celda getCelda() { + return celda; + } + + /** + * Get the cell the object is in + * + * @param celda The cell + */ + public void setCelda(Celda celda) { + this.celda = celda; + } + + /** + * Get the active state of the GameObject + * + * @return Returns true if the object is active or false otherwise + */ + public boolean getActive() { + return active; + } + + /** + * Set the active state for the GameObject loop + * + * @param active Set to true to have the run method loop run indefinitely or false to stop the loop + */ + public void setActive(boolean active) { + this.active = active; + } + + /** + * The run method + */ + @Override + public void run() { + setActive(true); + } +} diff --git a/src/cl/cromer/game/object/Player.java b/src/cl/cromer/game/object/Player.java index 6a44586..449f460 100644 --- a/src/cl/cromer/game/object/Player.java +++ b/src/cl/cromer/game/object/Player.java @@ -15,30 +15,462 @@ package cl.cromer.game.object; +import cl.cromer.game.Celda; +import cl.cromer.game.Constantes; +import cl.cromer.game.Escenario; +import cl.cromer.game.sound.Sound; +import cl.cromer.game.sprite.Animation; +import cl.cromer.game.sprite.AnimationException; + +import java.awt.event.KeyEvent; import java.util.ArrayList; +import java.util.logging.Logger; /** * This class contains the player */ -public class Player { +public class Player extends Object implements Constantes { /** * The maximum health of the player */ - private final int MAX_HEALTH = 10; + public final static int MAX_HEALTH = 8; + /** + * The logger + */ + private Logger logger; /** * The current health of the player */ private int health = MAX_HEALTH; - /** - * The current x position of the player - */ - private int x; - /** - * The current y position of the player - */ - private int y; /** * Objects that the player is carrying */ - private ArrayList carrying; + private ArrayList carrying = new ArrayList<>(); + + /** + * Initialize the player + * + * @param escenario The scene the player is in + * @param celda The cell the player is in + */ + public Player(Escenario escenario, Celda celda) { + super(escenario, celda); + logger = getLogger(this.getClass(), PLAYER_LOG_LEVEL); + } + + /** + * Handle keys being pressed in the game + * + * @param event The event from the keyboard + */ + public void keyPressed(KeyEvent event) { + if (!getEscenario().isDoorClosed()) { + getEscenario().setDoorClosed(true); + } + switch (event.getKeyCode()) { + case KeyEvent.VK_UP: + moveUp(); + break; + case KeyEvent.VK_DOWN: + moveDown(); + break; + case KeyEvent.VK_LEFT: + moveLeft(); + break; + case KeyEvent.VK_RIGHT: + moveRight(); + break; + case KeyEvent.VK_SPACE: + interact(); + break; + } + } + + /** + * Move the player up + */ + private void moveUp() { + int x = getX(); + int y = getY(); + logger.info("Up key pressed"); + if (y > 0) { + Celda.Type type = getEscenario().getCeldas()[x][y - 1].getType(); + if (type == Celda.Type.SPACE || type == Celda.Type.KEY) { + if (type == Celda.Type.KEY) { + for (Key key : getEscenario().getCanvas().getKeys()) { + if (key.checkPosition(x, y - 1)) { + // Get the key + getKey(key); + // Remove the key from the cell + getEscenario().getCeldas()[x][y - 1].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y - 1].setAnimation(null); + break; + } + } + } + + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y - 1].setType(Celda.Type.PLAYER); + + if (changeDirection(Animation.Direction.UP)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + + getEscenario().getCeldas()[x][y - 1].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setAnimation(null); + setY(getY() - 1); + } + else { + if (changeDirection(Animation.Direction.UP)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + } + } + else { + if (changeDirection(Animation.Direction.UP)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + } + } + + /** + * Move the player down + */ + private void moveDown() { + int x = getX(); + int y = getY(); + logger.info("Down key pressed"); + Celda.Type type = getEscenario().getCeldas()[x][y + 1].getType(); + if (y < (VERTICAL_CELLS - 1) && (type == Celda.Type.SPACE || type == Celda.Type.KEY)) { + if (type == Celda.Type.KEY) { + for (Key key : getEscenario().getCanvas().getKeys()) { + if (key.checkPosition(x, y + 1)) { + // Get the key + getKey(key); + // Remove the key from the cell + getEscenario().getCeldas()[x][y + 1].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y + 1].setAnimation(null); + break; + } + } + } + + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x][y + 1].setType(Celda.Type.PLAYER); + + if (changeDirection(Animation.Direction.DOWN)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + + getEscenario().getCeldas()[x][y + 1].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setAnimation(null); + setY(getY() + 1); + } + else { + if (changeDirection(Animation.Direction.DOWN)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + } + } + + /** + * Move the player to the left + */ + private void moveLeft() { + int x = getX(); + int y = getY(); + logger.info("Left key pressed"); + if (x > 0) { + Celda.Type type = getEscenario().getCeldas()[x - 1][y].getType(); + if (type == Celda.Type.SPACE || type == Celda.Type.KEY) { + if (type == Celda.Type.KEY) { + for (Key key : getEscenario().getCanvas().getKeys()) { + if (key.checkPosition(x - 1, y)) { + // Get the key + getKey(key); + // Remove the key from the cell + getEscenario().getCeldas()[x - 1][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x - 1][y].setAnimation(null); + break; + } + } + } + + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x - 1][y].setType(Celda.Type.PLAYER); + + if (changeDirection(Animation.Direction.LEFT)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + + getEscenario().getCeldas()[x - 1][y].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setAnimation(null); + setX(getX() - 1); + } + else { + if (changeDirection(Animation.Direction.LEFT)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + } + } + } + + /** + * Move the player to the right + */ + private void moveRight() { + int x = getX(); + int y = getY(); + logger.info("Right key pressed"); + Celda.Type type = getEscenario().getCeldas()[x + 1][y].getType(); + if (x < (HORIZONTAL_CELLS - 1) && (type == Celda.Type.SPACE || type == Celda.Type.KEY)) { + if (type == Celda.Type.KEY) { + for (Key key : getEscenario().getCanvas().getKeys()) { + if (key.checkPosition(x + 1, y)) { + // Get the key + getKey(key); + // Remove the key from the cell + getEscenario().getCeldas()[x + 1][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x + 1][y].setAnimation(null); + break; + } + } + } + + getEscenario().getCeldas()[x][y].setType(Celda.Type.SPACE); + getEscenario().getCeldas()[x + 1][y].setType(Celda.Type.PLAYER); + + if (changeDirection(Animation.Direction.RIGHT)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + + getEscenario().getCeldas()[x + 1][y].setAnimation(getEscenario().getCeldas()[x][y].getAnimation()); + getEscenario().getCeldas()[x][y].setAnimation(null); + setX(getX() + 1); + } + else { + if (changeDirection(Animation.Direction.RIGHT)) { + try { + getEscenario().getCeldas()[x][y].getAnimation().getNextFrame(); + } + catch (AnimationException e) { + logger.warning(e.getMessage()); + } + } + } + } + + /** + * Change the direction of the player sprite + * + * @param direction The new direction + * @return Returns true if a direction change is not necessary + */ + private boolean changeDirection(Animation.Direction direction) { + int x = getX(); + int y = getY(); + if (getEscenario().getCeldas()[x][y].getAnimation().getCurrentDirection() != direction) { + getEscenario().getCeldas()[x][y].getAnimation().setCurrentDirection(direction); + return false; + } + else { + return true; + } + } + + /** + * Get the key + * + * @param key The key to get + */ + private void getKey(Key key) { + gainHealth(1); + // Kill the loop in the thread + key.setActive(false); + // Add key to inventory + carrying.add(key); + // Stop the thread + try { + getEscenario().getCanvas().getThreads().get(key).join(); + } + catch (InterruptedException e) { + logger.warning(e.getMessage()); + } + // Play sound + new Thread(getEscenario().getSounds().get(Sound.SoundType.GET_KEY)).start(); + } + + /** + * Interact with an object in the game + */ + private void interact() { + int x = getX(); + int y = getY(); + logger.info("Space bar pressed"); + if (getEscenario().getCeldas()[x][y].getAnimation().getCurrentDirection() == Animation.Direction.UP) { + if (getEscenario().getCeldas()[x][y - 1].getType() == Celda.Type.CHEST) { + if (hasKey()) { + logger.info("Player opened chest"); + + new Thread(getEscenario().getSounds().get(Sound.SoundType.OPEN_CHEST)).start(); + + gainHealth(1); + + for (Chest chest : getEscenario().getCanvas().getChests()) { + if (chest.checkPosition(x, y - 1)) { + if (chest.getState() == Chest.State.OPENED) { + return; + } + chest.setState(Chest.State.OPENING); + break; + } + } + + useKey(); + } + } + } + } + + /** + * Checks if the player has a key + * + * @return Returns true if the player has a key or false if they have no keys + */ + private boolean hasKey() { + for (Object object : carrying) { + if (object instanceof Key) { + return true; + } + } + return false; + } + + /** + * Check how many keys the player has + * + * @return Returns the number of key in the inventory + */ + public int keyCount() { + int i = 0; + for (Object object : carrying) { + if (object instanceof Key) { + i++; + } + } + return i; + } + + /** + * Removes a key from the player inventory + */ + private void useKey() { + for (Object object : carrying) { + if (object instanceof Key) { + logger.info("Used key"); + carrying.remove(object); + return; + } + } + } + + /** + * Get the current health of the player + * + * @return Returns the health value + */ + public int getHealth() { + return health; + } + + /** + * Lose a variable amount of health + * + * @param amount The amount to lose + */ + public void loseHealth(int amount) { + if (health > 0) { + logger.info("Lose " + amount + " health"); + health = health - amount; + if (health < 0) { + logger.info("Player is dead"); + health = 0; + } + } + } + + /** + * Gain a variable amount of health + * + * @param amount The amount of health to gain + */ + private void gainHealth(int amount) { + if (health < MAX_HEALTH) { + logger.info("Gain " + amount + " health"); + health = health + amount; + if (health > MAX_HEALTH) { + health = MAX_HEALTH; + } + } + } + + /** + * This runs when the thread starts + */ + @Override + public void run() { + super.run(); + while (getActive()) { + try { + Thread.sleep(5000); + } + catch (InterruptedException e) { + logger.info(e.getMessage()); + } + synchronized (this) { + loseHealth(1); + getEscenario().getCanvas().repaint(); + } + } + } } diff --git a/src/cl/cromer/game/object/Portal.java b/src/cl/cromer/game/object/Portal.java index 40fadd0..2473278 100644 --- a/src/cl/cromer/game/object/Portal.java +++ b/src/cl/cromer/game/object/Portal.java @@ -25,33 +25,20 @@ import java.util.logging.Logger; /** * This class handles the portal functionality */ -public class Portal implements Runnable, Constantes { - /** - * The scene the portal is in - */ - private Escenario escenario; - /** - * The cell that contains the portal - */ - private Celda celda; - /** - * If the portal is active or not - */ - private boolean active = true; +public class Portal extends Object implements Constantes { /** * The logger */ private Logger logger; - /** * Initialize the portal * * @param escenario The scene that contains the portal + * @param celda The cell the portal is in */ - public Portal(Escenario escenario) { - this.escenario = escenario; + public Portal(Escenario escenario, Celda celda) { + super(escenario, celda); logger = getLogger(this.getClass(), PORTAL_LOG_LEVEL); - celda = escenario.getPortal(); } /** @@ -59,48 +46,38 @@ public class Portal implements Runnable, Constantes { */ private void animate() { try { - celda.getAnimation().getNextFrame(); + getCelda().getAnimation().getNextFrame(); } catch (AnimationException e) { logger.warning(e.getMessage()); } } - /** - * Check of the portal is active or not - * - * @return Returns true if active or false is inactive - */ - public boolean isActive() { - return active; - } - - /** - * Set the portal's active state - * - * @param active True if active or false if inactive - */ - public void setActive(boolean active) { - this.active = active; - } - /** * This method is run when the thread starts */ @Override public void run() { - while (active) { + super.run(); + while (getActive()) { try { - // 1000 / 30 = 33 30 frames per second(1000 milliseconds) is 33.33 - Thread.sleep(33); + Thread.sleep(35); } catch (InterruptedException e) { - logger.warning(e.getMessage()); + logger.info(e.getMessage()); } synchronized (this) { animate(); - escenario.getCanvas().repaint(); + getEscenario().getCanvas().repaint(); } } } + + /** + * The current state of the portal + */ + public enum State { + ACTIVE, + INACTIVE + } } diff --git a/src/cl/cromer/game/sound/Sound.java b/src/cl/cromer/game/sound/Sound.java index 52e69e5..cff021d 100644 --- a/src/cl/cromer/game/sound/Sound.java +++ b/src/cl/cromer/game/sound/Sound.java @@ -26,7 +26,7 @@ import java.util.logging.Logger; /** * This class handles sound */ -public class Sound implements Constantes { +public class Sound implements Runnable, Constantes { /** * The sound clip to play */ @@ -39,7 +39,6 @@ public class Sound implements Constantes { * The logger */ private Logger logger; - /** * Load the sound * @@ -79,6 +78,7 @@ public class Sound implements Constantes { catch (LineUnavailableException | IOException e) { logger.warning(e.getMessage()); } + logger.info("Opened sound: " + path); } @@ -86,15 +86,35 @@ public class Sound implements Constantes { * Play the sound * @throws SoundException Thrown if the sound clip is null */ - public void play() throws SoundException { + private void play() throws SoundException { if (sound == null) { throw new SoundException("Sound is null!"); } + // Stop the sound if it was already playing + if (isPlaying()) { + sound.stop(); + } + + sound.setFramePosition(0); sound.start(); logger.info("Play sound: " + path); } + /** + * Check if the sound clip is playing or not + * + * @return Returns true if the sound is playing or false otherwise + * @throws SoundException Thrown if the sound clip is null + */ + public boolean isPlaying() throws SoundException { + if (sound == null) { + throw new SoundException("Sound is null!"); + } + + return sound.isActive(); + } + /** * Stop the sound * @throws SoundException Thrown if the sound clip is null @@ -104,10 +124,17 @@ public class Sound implements Constantes { throw new SoundException("Sound is null!"); } - sound.stop(); + if (isPlaying()) { + sound.stop(); + } logger.info("Stop sound: " + path); } + /** + * Set the number of loops to play + * @param loops The number of loops, should be n-1 + * @throws SoundException Thrown if the sound is null + */ public void setLoops(int loops) throws SoundException { if (sound == null) { throw new SoundException("Sound is null!"); @@ -115,6 +142,19 @@ public class Sound implements Constantes { sound.loop(loops); } + /** + * Run the sound in a thread + */ + @Override + public void run() { + try { + play(); + } + catch (SoundException e) { + logger.warning(e.getMessage()); + } + } + /** * Set the volume of the sound * @@ -150,4 +190,30 @@ public class Sound implements Constantes { logger.info("No control to modify volume"); } } + + /** + * The types of sounds + */ + public enum SoundType { + /** + * Background music + */ + BACKGROUND, + /** + * Open chest sound + */ + OPEN_CHEST, + /** + * Get key sound + */ + GET_KEY, + /** + * Game over music + */ + GAME_OVER, + /** + * Enemy attack sound + */ + ENEMY_ATTACK + } } diff --git a/src/cl/cromer/game/sprite/Animation.java b/src/cl/cromer/game/sprite/Animation.java index 67c85af..25183ed 100644 --- a/src/cl/cromer/game/sprite/Animation.java +++ b/src/cl/cromer/game/sprite/Animation.java @@ -55,15 +55,11 @@ public class Animation implements Cloneable, Constantes { private Logger logger; /** - * The sprite type + * Get the frame number + * @return The current frame number */ - public enum SpriteType { - PLAYER, - ENEMY, - CHEST, - GEM, - KEY, - PORTAL + public int getFrameNumber() { + return currentFrame; } /** @@ -209,6 +205,31 @@ public class Animation implements Cloneable, Constantes { return images.get(currentFrame); } + /** + * Get the number of frames in the + * + * @return Returns the amount of frames in the sprite + * @throws AnimationException Thrown if there are no images in the animation + */ + public int getFrameCount() throws AnimationException { + return getImagesFromHash().size(); + } + + /** + * The sprite type + */ + public enum SpriteType { + PLAYER, + ENEMY, + CHEST, + GEM, + KEY, + HEART, + GAME_OVER, + INACTIVE_PORTAL, + ACTIVE_PORTAL + } + /** * Set which frame is to be shown in the sprite manually * diff --git a/test/cl/cromer/game/test/RandomPositionListTest.java b/test/cl/cromer/game/test/RandomPositionListTest.java new file mode 100644 index 0000000..c166b4f --- /dev/null +++ b/test/cl/cromer/game/test/RandomPositionListTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Chris Cromer + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package cl.cromer.game.test; + +import cl.cromer.game.Celda; +import cl.cromer.game.RandomPositionList; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Test the random position position list to make sure it has expected values + */ +class RandomPositionListTest { + private RandomPositionList randomPositionList; + + /** + * Create a random position list + */ + @BeforeEach + void setUp() { + randomPositionList = new RandomPositionList(2, 3, Celda.Type.PLAYER); + } + + /** + * Destroy the random position list + */ + @AfterEach + void tearDown() { + randomPositionList = null; + } + + /** + * Check if the x position is correct + */ + @Test + void getX() { + assertEquals(2, randomPositionList.getX(), "The position should be 2"); + } + + /** + * Check if the y position is correct + */ + @Test + void getY() { + assertEquals(3, randomPositionList.getY(), "The position should be 3"); + } + + /** + * Check if the type is correct + */ + @Test + void getType() { + assertEquals(Celda.Type.PLAYER, randomPositionList.getType(), "The type should be player"); + } +} \ No newline at end of file diff --git a/test/cl/cromer/game/test/object/PlayerTest.java b/test/cl/cromer/game/test/object/PlayerTest.java new file mode 100644 index 0000000..f011eb4 --- /dev/null +++ b/test/cl/cromer/game/test/object/PlayerTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2019 Chris Cromer + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package cl.cromer.game.test.object; + +import cl.cromer.game.Celda; +import cl.cromer.game.Escenario; +import cl.cromer.game.Lienzo; +import cl.cromer.game.object.Player; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.awt.*; +import java.awt.event.KeyEvent; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Test the playser object + */ +class PlayerTest { + /** + * A player object to test + */ + private Player player; + /** + * The canvas the scene is in + */ + private Lienzo lienzo; + /** + * The scene the player is in + */ + private Escenario escenario; + + /** + * Create the canvas, scene, and then the player + */ + @BeforeEach + void setUp() { + lienzo = new Lienzo(); + escenario = new Escenario(lienzo); + player = new Player(escenario, escenario.getPlayer()); + } + + /** + * Destroy it all + */ + @AfterEach + void tearDown() { + player = null; + escenario = null; + lienzo = null; + } + + /** + * Test key press events and see if the player is where he should be + */ + @Test + void keyPressed() { + int expected = 2; + if (escenario.getCeldas()[player.getX() - 1][player.getY()].getType() == Celda.Type.SPACE) { + expected--; + } + player.keyPressed(new KeyEvent(new Component() { + }, 0, 0, 0, KeyEvent.VK_LEFT, KeyEvent.getKeyText(KeyEvent.VK_LEFT).charAt(0))); + assertEquals(expected, player.getX(), "The player should be at x = 1" + expected); + + if (escenario.getCeldas()[player.getX() + 1][player.getY()].getType() == Celda.Type.SPACE) { + expected++; + } + player.keyPressed(new KeyEvent(new Component() { + }, 0, 0, 0, KeyEvent.VK_RIGHT, KeyEvent.getKeyText(KeyEvent.VK_RIGHT).charAt(0))); + assertEquals(expected, player.getX(), "The player should be at x = 2" + expected); + + expected = 1; + if (escenario.getCeldas()[player.getX()][player.getY() + 1].getType() == Celda.Type.SPACE) { + expected++; + } + player.keyPressed(new KeyEvent(new Component() { + }, 0, 0, 0, KeyEvent.VK_DOWN, KeyEvent.getKeyText(KeyEvent.VK_DOWN).charAt(0))); + assertEquals(expected, player.getY(), "The player should be at y = " + expected); + + if (escenario.getCeldas()[player.getX()][player.getY() - 1].getType() == Celda.Type.SPACE) { + expected--; + } + player.keyPressed(new KeyEvent(new Component() { + }, 0, 0, 0, KeyEvent.VK_UP, KeyEvent.getKeyText(KeyEvent.VK_UP).charAt(0))); + assertEquals(expected, player.getY(), "The player should be at y = " + expected); + } +} \ No newline at end of file
LoglevelTimeClassMethodLog Message
LoglevelTimeClassMethodLog Message